diff --git a/contrib/wpa/hostapd/ChangeLog b/contrib/wpa/hostapd/ChangeLog index 327ee3b46ede..6c4410e8ec62 100644 --- a/contrib/wpa/hostapd/ChangeLog +++ b/contrib/wpa/hostapd/ChangeLog @@ -1,5 +1,29 @@ ChangeLog for hostapd +2019-08-07 - v2.9 + * SAE changes + - disable use of groups using Brainpool curves + - improved protection against side channel attacks + [https://w1.fi/security/2019-6/] + * EAP-pwd changes + - disable use of groups using Brainpool curves + - improved protection against side channel attacks + [https://w1.fi/security/2019-6/] + * fixed FT-EAP initial mobility domain association using PMKSA caching + * added configuration of airtime policy + * fixed FILS to and RSNE into (Re)Association Response frames + * fixed DPP bootstrapping URI parser of channel list + * added support for regulatory WMM limitation (for ETSI) + * added support for MACsec Key Agreement using IEEE 802.1X/PSK + * added experimental support for EAP-TEAP server (RFC 7170) + * added experimental support for EAP-TLS server with TLS v1.3 + * added support for two server certificates/keys (RSA/ECC) + * added AKMSuiteSelector into "STA " control interface data to + determine with AKM was used for an association + * added eap_sim_id parameter to allow EAP-SIM/AKA server pseudonym and + fast reauthentication use to be disabled + * fixed an ECDH operation corner case with OpenSSL + 2019-04-21 - v2.8 * SAE changes - added support for SAE Password Identifier diff --git a/contrib/wpa/hostapd/config_file.c b/contrib/wpa/hostapd/config_file.c index 42f3b40ef48b..e09e6e141ba4 100644 --- a/contrib/wpa/hostapd/config_file.c +++ b/contrib/wpa/hostapd/config_file.c @@ -24,14 +24,6 @@ #include "config_file.h" -#ifndef CONFIG_NO_RADIUS -#ifdef EAP_SERVER -static struct hostapd_radius_attr * -hostapd_parse_radius_attr(const char *value); -#endif /* EAP_SERVER */ -#endif /* CONFIG_NO_RADIUS */ - - #ifndef CONFIG_NO_VLAN static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, const char *fname) @@ -660,75 +652,6 @@ hostapd_config_read_radius_addr(struct hostapd_radius_server **server, } -static struct hostapd_radius_attr * -hostapd_parse_radius_attr(const char *value) -{ - const char *pos; - char syntax; - struct hostapd_radius_attr *attr; - size_t len; - - attr = os_zalloc(sizeof(*attr)); - if (attr == NULL) - return NULL; - - attr->type = atoi(value); - - pos = os_strchr(value, ':'); - if (pos == NULL) { - attr->val = wpabuf_alloc(1); - if (attr->val == NULL) { - os_free(attr); - return NULL; - } - wpabuf_put_u8(attr->val, 0); - return attr; - } - - pos++; - if (pos[0] == '\0' || pos[1] != ':') { - os_free(attr); - return NULL; - } - syntax = *pos++; - pos++; - - switch (syntax) { - case 's': - attr->val = wpabuf_alloc_copy(pos, os_strlen(pos)); - break; - case 'x': - len = os_strlen(pos); - if (len & 1) - break; - len /= 2; - attr->val = wpabuf_alloc(len); - if (attr->val == NULL) - break; - if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) { - wpabuf_free(attr->val); - os_free(attr); - return NULL; - } - break; - case 'd': - attr->val = wpabuf_alloc(4); - if (attr->val) - wpabuf_put_be32(attr->val, atoi(pos)); - break; - default: - os_free(attr); - return NULL; - } - - if (attr->val == NULL) { - os_free(attr); - return NULL; - } - - return attr; -} - static int hostapd_parse_das_client(struct hostapd_bss_config *bss, char *val) { @@ -2313,6 +2236,42 @@ static unsigned int parse_tls_flags(const char *val) #endif /* EAP_SERVER */ +#ifdef CONFIG_AIRTIME_POLICY +static int add_airtime_weight(struct hostapd_bss_config *bss, char *value) +{ + struct airtime_sta_weight *wt; + char *pos, *next; + + wt = os_zalloc(sizeof(*wt)); + if (!wt) + return -1; + + /* 02:01:02:03:04:05 10 */ + pos = value; + next = os_strchr(pos, ' '); + if (next) + *next++ = '\0'; + if (!next || hwaddr_aton(pos, wt->addr)) { + wpa_printf(MSG_ERROR, "Invalid station address: '%s'", pos); + os_free(wt); + return -1; + } + + pos = next; + wt->weight = atoi(pos); + if (!wt->weight) { + wpa_printf(MSG_ERROR, "Invalid weight: '%s'", pos); + os_free(wt); + return -1; + } + + wt->next = bss->airtime_weight_list; + bss->airtime_weight_list = wt; + return 0; +} +#endif /* CONFIG_AIRTIME_POLICY */ + + #ifdef CONFIG_SAE static int parse_sae_password(struct hostapd_bss_config *bss, const char *val) { @@ -2376,6 +2335,36 @@ fail: #endif /* CONFIG_SAE */ +#ifdef CONFIG_DPP2 +static int hostapd_dpp_controller_parse(struct hostapd_bss_config *bss, + const char *pos) +{ + struct dpp_controller_conf *conf; + char *val; + + conf = os_zalloc(sizeof(*conf)); + if (!conf) + return -1; + val = get_param(pos, "ipaddr="); + if (!val || hostapd_parse_ip_addr(val, &conf->ipaddr)) + goto fail; + os_free(val); + val = get_param(pos, "pkhash="); + if (!val || os_strlen(val) != 2 * SHA256_MAC_LEN || + hexstr2bin(val, conf->pkhash, SHA256_MAC_LEN) < 0) + goto fail; + os_free(val); + conf->next = bss->dpp_controller; + bss->dpp_controller = conf; + return 0; +fail: + os_free(val); + os_free(conf); + return -1; +} +#endif /* CONFIG_DPP2 */ + + static int hostapd_config_fill(struct hostapd_config *conf, struct hostapd_bss_config *bss, const char *buf, char *pos, int line) @@ -2496,7 +2485,11 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "eapol_version") == 0) { int eapol_version = atoi(pos); +#ifdef CONFIG_MACSEC + if (eapol_version < 1 || eapol_version > 3) { +#else /* CONFIG_MACSEC */ if (eapol_version < 1 || eapol_version > 2) { +#endif /* CONFIG_MACSEC */ wpa_printf(MSG_ERROR, "Line %d: invalid EAPOL version (%d): '%s'.", line, eapol_version, pos); @@ -2519,12 +2512,21 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "server_cert") == 0) { os_free(bss->server_cert); bss->server_cert = os_strdup(pos); + } else if (os_strcmp(buf, "server_cert2") == 0) { + os_free(bss->server_cert2); + bss->server_cert2 = os_strdup(pos); } else if (os_strcmp(buf, "private_key") == 0) { os_free(bss->private_key); bss->private_key = os_strdup(pos); + } else if (os_strcmp(buf, "private_key2") == 0) { + os_free(bss->private_key2); + bss->private_key2 = os_strdup(pos); } else if (os_strcmp(buf, "private_key_passwd") == 0) { os_free(bss->private_key_passwd); bss->private_key_passwd = os_strdup(pos); + } else if (os_strcmp(buf, "private_key_passwd2") == 0) { + os_free(bss->private_key_passwd2); + bss->private_key_passwd2 = os_strdup(pos); } else if (os_strcmp(buf, "check_cert_subject") == 0) { if (!pos[0]) { wpa_printf(MSG_ERROR, "Line %d: unknown check_cert_subject '%s'", @@ -2605,6 +2607,20 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "pac_key_refresh_time") == 0) { bss->pac_key_refresh_time = atoi(pos); #endif /* EAP_SERVER_FAST */ +#ifdef EAP_SERVER_TEAP + } else if (os_strcmp(buf, "eap_teap_auth") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 1) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid eap_teap_auth value", + line); + return 1; + } + bss->eap_teap_auth = val; + } else if (os_strcmp(buf, "eap_teap_pac_no_inner") == 0) { + bss->eap_teap_pac_no_inner = atoi(pos); +#endif /* EAP_SERVER_TEAP */ #ifdef EAP_SERVER_SIM } else if (os_strcmp(buf, "eap_sim_db") == 0) { os_free(bss->eap_sim_db); @@ -2613,6 +2629,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->eap_sim_db_timeout = atoi(pos); } else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) { bss->eap_sim_aka_result_ind = atoi(pos); + } else if (os_strcmp(buf, "eap_sim_id") == 0) { + bss->eap_sim_id = atoi(pos); #endif /* EAP_SERVER_SIM */ #ifdef EAP_SERVER_TNC } else if (os_strcmp(buf, "tnc") == 0) { @@ -2816,6 +2834,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, a = a->next; a->next = attr; } + } else if (os_strcmp(buf, "radius_req_attr_sqlite") == 0) { + os_free(bss->radius_req_attr_sqlite); + bss->radius_req_attr_sqlite = os_strdup(pos); } else if (os_strcmp(buf, "radius_das_port") == 0) { bss->radius_das_port = atoi(pos); } else if (os_strcmp(buf, "radius_das_client") == 0) { @@ -3442,6 +3463,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, conf->he_op.he_twt_required = atoi(pos); } else if (os_strcmp(buf, "he_rts_threshold") == 0) { conf->he_op.he_rts_threshold = atoi(pos); + } else if (os_strcmp(buf, "he_basic_mcs_nss_set") == 0) { + conf->he_op.he_basic_mcs_nss_set = atoi(pos); } else if (os_strcmp(buf, "he_mu_edca_qos_info_param_count") == 0) { conf->he_mu_edca.he_qos_info |= set_he_cap(atoi(pos), HE_QOS_INFO_EDCA_PARAM_SET_COUNT); @@ -3526,6 +3549,20 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "he_mu_edca_ac_vo_timer") == 0) { conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_TIMER_IDX] = atoi(pos) & 0xff; + } else if (os_strcmp(buf, "he_spr_sr_control") == 0) { + conf->spr.sr_control = atoi(pos) & 0xff; + } else if (os_strcmp(buf, "he_spr_non_srg_obss_pd_max_offset") == 0) { + conf->spr.non_srg_obss_pd_max_offset = atoi(pos); + } else if (os_strcmp(buf, "he_spr_srg_obss_pd_min_offset") == 0) { + conf->spr.srg_obss_pd_min_offset = atoi(pos); + } else if (os_strcmp(buf, "he_spr_srg_obss_pd_max_offset") == 0) { + conf->spr.srg_obss_pd_max_offset = atoi(pos); + } else if (os_strcmp(buf, "he_oper_chwidth") == 0) { + conf->he_oper_chwidth = atoi(pos); + } else if (os_strcmp(buf, "he_oper_centr_freq_seg0_idx") == 0) { + conf->he_oper_centr_freq_seg0_idx = atoi(pos); + } else if (os_strcmp(buf, "he_oper_centr_freq_seg1_idx") == 0) { + conf->he_oper_centr_freq_seg1_idx = atoi(pos); #endif /* CONFIG_IEEE80211AX */ } else if (os_strcmp(buf, "max_listen_interval") == 0) { bss->max_listen_interval = atoi(pos); @@ -4298,6 +4335,11 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "dpp_csign") == 0) { if (parse_wpabuf_hex(line, buf, &bss->dpp_csign, pos)) return 1; +#ifdef CONFIG_DPP2 + } else if (os_strcmp(buf, "dpp_controller") == 0) { + if (hostapd_dpp_controller_parse(bss, pos)) + return 1; +#endif /* CONFIG_DPP2 */ #endif /* CONFIG_DPP */ #ifdef CONFIG_OWE } else if (os_strcmp(buf, "owe_transition_bssid") == 0) { @@ -4349,6 +4391,121 @@ static int hostapd_config_fill(struct hostapd_config *conf, conf->rssi_reject_assoc_timeout = atoi(pos); } else if (os_strcmp(buf, "pbss") == 0) { bss->pbss = atoi(pos); +#ifdef CONFIG_AIRTIME_POLICY + } else if (os_strcmp(buf, "airtime_mode") == 0) { + int val = atoi(pos); + + if (val < 0 || val > AIRTIME_MODE_MAX) { + wpa_printf(MSG_ERROR, "Line %d: Unknown airtime_mode", + line); + return 1; + } + conf->airtime_mode = val; + } else if (os_strcmp(buf, "airtime_update_interval") == 0) { + conf->airtime_update_interval = atoi(pos); + } else if (os_strcmp(buf, "airtime_bss_weight") == 0) { + bss->airtime_weight = atoi(pos); + } else if (os_strcmp(buf, "airtime_bss_limit") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 1) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid airtime_bss_limit (must be 0 or 1)", + line); + return 1; + } + bss->airtime_limit = val; + } else if (os_strcmp(buf, "airtime_sta_weight") == 0) { + if (add_airtime_weight(bss, pos) < 0) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid airtime weight '%s'", + line, pos); + return 1; + } +#endif /* CONFIG_AIRTIME_POLICY */ +#ifdef CONFIG_MACSEC + } else if (os_strcmp(buf, "macsec_policy") == 0) { + int macsec_policy = atoi(pos); + + if (macsec_policy < 0 || macsec_policy > 1) { + wpa_printf(MSG_ERROR, + "Line %d: invalid macsec_policy (%d): '%s'.", + line, macsec_policy, pos); + return 1; + } + bss->macsec_policy = macsec_policy; + } else if (os_strcmp(buf, "macsec_integ_only") == 0) { + int macsec_integ_only = atoi(pos); + + if (macsec_integ_only < 0 || macsec_integ_only > 1) { + wpa_printf(MSG_ERROR, + "Line %d: invalid macsec_integ_only (%d): '%s'.", + line, macsec_integ_only, pos); + return 1; + } + bss->macsec_integ_only = macsec_integ_only; + } else if (os_strcmp(buf, "macsec_replay_protect") == 0) { + int macsec_replay_protect = atoi(pos); + + if (macsec_replay_protect < 0 || macsec_replay_protect > 1) { + wpa_printf(MSG_ERROR, + "Line %d: invalid macsec_replay_protect (%d): '%s'.", + line, macsec_replay_protect, pos); + return 1; + } + bss->macsec_replay_protect = macsec_replay_protect; + } else if (os_strcmp(buf, "macsec_replay_window") == 0) { + bss->macsec_replay_window = atoi(pos); + } else if (os_strcmp(buf, "macsec_port") == 0) { + int macsec_port = atoi(pos); + + if (macsec_port < 1 || macsec_port > 65534) { + wpa_printf(MSG_ERROR, + "Line %d: invalid macsec_port (%d): '%s'.", + line, macsec_port, pos); + return 1; + } + bss->macsec_port = macsec_port; + } else if (os_strcmp(buf, "mka_priority") == 0) { + int mka_priority = atoi(pos); + + if (mka_priority < 0 || mka_priority > 255) { + wpa_printf(MSG_ERROR, + "Line %d: invalid mka_priority (%d): '%s'.", + line, mka_priority, pos); + return 1; + } + bss->mka_priority = mka_priority; + } else if (os_strcmp(buf, "mka_cak") == 0) { + size_t len = os_strlen(pos); + + if (len > 2 * MACSEC_CAK_MAX_LEN || + (len != 2 * 16 && len != 2 * 32) || + hexstr2bin(pos, bss->mka_cak, len / 2)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CAK '%s'.", + line, pos); + return 1; + } + bss->mka_cak_len = len / 2; + bss->mka_psk_set |= MKA_PSK_SET_CAK; + } else if (os_strcmp(buf, "mka_ckn") == 0) { + size_t len = os_strlen(pos); + + if (len > 2 * MACSEC_CKN_MAX_LEN || /* too long */ + len < 2 || /* too short */ + len % 2 != 0 /* not an integral number of bytes */) { + wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.", + line, pos); + return 1; + } + bss->mka_ckn_len = len / 2; + if (hexstr2bin(pos, bss->mka_ckn, bss->mka_ckn_len)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.", + line, pos); + return -1; + } + bss->mka_psk_set |= MKA_PSK_SET_CKN; +#endif /* CONFIG_MACSEC */ } else { wpa_printf(MSG_ERROR, "Line %d: unknown configuration item '%s'", diff --git a/contrib/wpa/hostapd/ctrl_iface.c b/contrib/wpa/hostapd/ctrl_iface.c index e4b16e61af0e..0f6dfa13d8f3 100644 --- a/contrib/wpa/hostapd/ctrl_iface.c +++ b/contrib/wpa/hostapd/ctrl_iface.c @@ -1830,26 +1830,40 @@ static void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, struct iphdr ip; const u8 *pos; unsigned int i; + char extra[30]; - if (len != HWSIM_PACKETLEN) + if (len < sizeof(*eth) + sizeof(ip) || len > HWSIM_PACKETLEN) { + wpa_printf(MSG_DEBUG, + "test data: RX - ignore unexpected length %d", + (int) len); return; + } eth = (const struct ether_header *) buf; os_memcpy(&ip, eth + 1, sizeof(ip)); pos = &buf[sizeof(*eth) + sizeof(ip)]; if (ip.ihl != 5 || ip.version != 4 || - ntohs(ip.tot_len) != HWSIM_IP_LEN) + ntohs(ip.tot_len) > HWSIM_IP_LEN) { + wpa_printf(MSG_DEBUG, + "test data: RX - ignore unexpect IP header"); return; + } - for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) { - if (*pos != (u8) i) + for (i = 0; i < ntohs(ip.tot_len) - sizeof(ip); i++) { + if (*pos != (u8) i) { + wpa_printf(MSG_DEBUG, + "test data: RX - ignore mismatching payload"); return; + } pos++; } - wpa_msg(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR, - MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost)); + extra[0] = '\0'; + if (ntohs(ip.tot_len) != HWSIM_IP_LEN) + os_snprintf(extra, sizeof(extra), " len=%d", ntohs(ip.tot_len)); + wpa_msg(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR "%s", + MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost), extra); } @@ -1894,7 +1908,7 @@ static int hostapd_ctrl_iface_data_test_config(struct hostapd_data *hapd, static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) { u8 dst[ETH_ALEN], src[ETH_ALEN]; - char *pos; + char *pos, *pos2; int used; long int val; u8 tos; @@ -1903,11 +1917,12 @@ static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) struct iphdr *ip; u8 *dpos; unsigned int i; + size_t send_len = HWSIM_IP_LEN; if (hapd->l2_test == NULL) return -1; - /* format: */ + /* format: [len=] */ pos = cmd; used = hwaddr_aton2(pos, dst); @@ -1921,11 +1936,19 @@ static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) return -1; pos += used; - val = strtol(pos, NULL, 0); + val = strtol(pos, &pos2, 0); if (val < 0 || val > 0xff) return -1; tos = val; + pos = os_strstr(pos2, " len="); + if (pos) { + i = atoi(pos + 5); + if (i < sizeof(*ip) || i > HWSIM_IP_LEN) + return -1; + send_len = i; + } + eth = (struct ether_header *) &buf[2]; os_memcpy(eth->ether_dhost, dst, ETH_ALEN); os_memcpy(eth->ether_shost, src, ETH_ALEN); @@ -1936,17 +1959,17 @@ static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) ip->version = 4; ip->ttl = 64; ip->tos = tos; - ip->tot_len = htons(HWSIM_IP_LEN); + ip->tot_len = htons(send_len); ip->protocol = 1; ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1); ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2); ip->check = ipv4_hdr_checksum(ip, sizeof(*ip)); dpos = (u8 *) (ip + 1); - for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) + for (i = 0; i < send_len - sizeof(*ip); i++) *dpos++ = i; if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2], - HWSIM_PACKETLEN) < 0) + sizeof(struct ether_header) + send_len) < 0) return -1; wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX dst=" MACSTR diff --git a/contrib/wpa/hostapd/defconfig b/contrib/wpa/hostapd/defconfig index ea5e2c9de04f..01871c951f3d 100644 --- a/contrib/wpa/hostapd/defconfig +++ b/contrib/wpa/hostapd/defconfig @@ -108,11 +108,18 @@ CONFIG_EAP_TTLS=y #CONFIG_EAP_GPSK_SHA256=y # EAP-FAST for the integrated EAP server -# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed -# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g., -# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions. #CONFIG_EAP_FAST=y +# EAP-TEAP for the integrated EAP server +# Note: The current EAP-TEAP implementation is experimental and should not be +# enabled for production use. The IETF RFC 7170 that defines EAP-TEAP has number +# of conflicting statements and missing details and the implementation has +# vendor specific workarounds for those and as such, may not interoperate with +# any other implementation. This should not be used for anything else than +# experimentation and interoperability testing until those issues has been +# resolved. +#CONFIG_EAP_TEAP=y + # Wi-Fi Protected Setup (WPS) #CONFIG_WPS=y # Enable UPnP support for external WPS Registrars @@ -376,6 +383,9 @@ CONFIG_IPV6=y # Experimental implementation of draft-harkins-owe-07.txt #CONFIG_OWE=y +# Airtime policy support +#CONFIG_AIRTIME_POLICY=y + # Override default value for the wpa_disable_eapol_key_retries configuration # parameter. See that parameter in hostapd.conf for more details. #CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1 diff --git a/contrib/wpa/hostapd/eap_register.c b/contrib/wpa/hostapd/eap_register.c index 8477c2154379..3e870c7f0670 100644 --- a/contrib/wpa/hostapd/eap_register.c +++ b/contrib/wpa/hostapd/eap_register.c @@ -121,6 +121,11 @@ int eap_server_register_methods(void) ret = eap_server_fast_register(); #endif /* EAP_SERVER_FAST */ +#ifdef EAP_SERVER_TEAP + if (ret == 0) + ret = eap_server_teap_register(); +#endif /* EAP_SERVER_TEAP */ + #ifdef EAP_SERVER_WSC if (ret == 0) ret = eap_server_wsc_register(); diff --git a/contrib/wpa/hostapd/hostapd.conf b/contrib/wpa/hostapd/hostapd.conf index f8caa56239d3..ce3ecdddf157 100644 --- a/contrib/wpa/hostapd/hostapd.conf +++ b/contrib/wpa/hostapd/hostapd.conf @@ -782,10 +782,8 @@ wmm_ac_vo_acm=0 # 1 = supported #he_mu_beamformer=1 -# he_bss_color: BSS color -# 0 = no BSS color (default) -# unsigned integer = BSS color -#he_bss_color=0 +# he_bss_color: BSS color (1-63) +#he_bss_color=1 #he_default_pe_duration: The duration of PE field in an HE PPDU in us # Possible values are 0 us (default), 4 us, 8 us, 12 us, and 16 us @@ -801,6 +799,17 @@ wmm_ac_vo_acm=0 # unsigned integer = duration in units of 16 us #he_rts_threshold=0 +# HE operating channel information; see matching vht_* parameters for details. +#he_oper_chwidth +#he_oper_centr_freq_seg0_idx +#he_oper_centr_freq_seg1_idx + +#he_basic_mcs_nss_set: Basic NSS/MCS set +# 16-bit combination of 2-bit values of Max HE-MCS For 1..8 SS; each 2-bit +# value having following meaning: +# 0 = HE-MCS 0-7, 1 = HE-MCS 0-9, 2 = HE-MCS 0-11, 3 = not supported +#he_basic_mcs_nss_set + #he_mu_edca_qos_info_param_count #he_mu_edca_qos_info_q_ack #he_mu_edca_qos_info_queue_request=1 @@ -825,6 +834,12 @@ wmm_ac_vo_acm=0 #he_mu_edca_ac_vo_ecwmax=15 #he_mu_edca_ac_vo_timer=255 +# Spatial Reuse Parameter Set +#he_spr_sr_control +#he_spr_non_srg_obss_pd_max_offset +#he_spr_srg_obss_pd_min_offset +#he_spr_srg_obss_pd_max_offset + ##### IEEE 802.1X-2004 related configuration ################################## # Require IEEE 802.1X authorization @@ -836,6 +851,8 @@ wmm_ac_vo_acm=0 # the new version number correctly (they seem to drop the frames completely). # In order to make hostapd interoperate with these clients, the version number # can be set to the older version (1) with this configuration value. +# Note: When using MACsec, eapol_version shall be set to 3, which is +# defined in IEEE Std 802.1X-2010. #eapol_version=2 # Optional displayable message sent with EAP Request-Identity. The first \0 @@ -879,6 +896,54 @@ eapol_key_index_workaround=0 # ERP is enabled (eap_server_erp=1). #erp_domain=example.com +##### MACsec ################################################################## + +# macsec_policy: IEEE 802.1X/MACsec options +# This determines how sessions are secured with MACsec (only for MACsec +# drivers). +# 0: MACsec not in use (default) +# 1: MACsec enabled - Should secure, accept key server's advice to +# determine whether to use a secure session or not. +# +# macsec_integ_only: IEEE 802.1X/MACsec transmit mode +# This setting applies only when MACsec is in use, i.e., +# - macsec_policy is enabled +# - the key server has decided to enable MACsec +# 0: Encrypt traffic (default) +# 1: Integrity only +# +# macsec_replay_protect: IEEE 802.1X/MACsec replay protection +# This setting applies only when MACsec is in use, i.e., +# - macsec_policy is enabled +# - the key server has decided to enable MACsec +# 0: Replay protection disabled (default) +# 1: Replay protection enabled +# +# macsec_replay_window: IEEE 802.1X/MACsec replay protection window +# This determines a window in which replay is tolerated, to allow receipt +# of frames that have been misordered by the network. +# This setting applies only when MACsec replay protection active, i.e., +# - macsec_replay_protect is enabled +# - the key server has decided to enable MACsec +# 0: No replay window, strict check (default) +# 1..2^32-1: number of packets that could be misordered +# +# macsec_port: IEEE 802.1X/MACsec port +# Port component of the SCI +# Range: 1-65534 (default: 1) +# +# mka_priority (Priority of MKA Actor) +# Range: 0..255 (default: 255) +# +# mka_cak, mka_ckn, and mka_priority: IEEE 802.1X/MACsec pre-shared key mode +# This allows to configure MACsec with a pre-shared key using a (CAK,CKN) pair. +# In this mode, instances of hostapd can act as MACsec peers. The peer +# with lower priority will become the key server and start distributing SAKs. +# mka_cak (CAK = Secure Connectivity Association Key) takes a 16-byte (128-bit) +# hex-string (32 hex-digits) or a 32-byte (256-bit) hex-string (64 hex-digits) +# mka_ckn (CKN = CAK Name) takes a 1..32-bytes (8..256 bit) hex-string +# (2..64 hex-digits) + ##### Integrated EAP server ################################################### # Optionally, hostapd can be configured to use an integrated EAP server @@ -912,6 +977,23 @@ eap_server=0 # Passphrase for private key #private_key_passwd=secret passphrase +# An alternative server certificate and private key can be configured with the +# following parameters (with values just like the parameters above without the +# '2' suffix). The ca_cert file (in PEM encoding) is used to add the trust roots +# for both server certificates and/or client certificates). +# +# The main use case for this alternative server certificate configuration is to +# enable both RSA and ECC public keys. The server will pick which one to use +# based on the client preferences for the cipher suite (in the TLS ClientHello +# message). It should be noted that number of deployed EAP peer implementations +# do not filter out the cipher suite list based on their local configuration and +# as such, configuration of alternative types of certificates on the server may +# result in interoperability issues. +#server_cert2=/etc/hostapd.server-ecc.pem +#private_key2=/etc/hostapd.server-ecc.prv +#private_key_passwd2=secret passphrase + + # Server identity # EAP methods that provide mechanism for authenticated server identity delivery # use this value. If not set, "hostapd" is used as a default. @@ -1109,10 +1191,27 @@ eap_server=0 # (or fewer) of the lifetime remains. #pac_key_refresh_time=86400 +# EAP-TEAP authentication type +# 0 = inner EAP (default) +# 1 = Basic-Password-Auth +#eap_teap_auth=0 + +# EAP-TEAP authentication behavior when using PAC +# 0 = perform inner authentication (default) +# 1 = skip inner authentication (inner EAP/Basic-Password-Auth) +#eap_teap_pac_no_inner=0 + # EAP-SIM and EAP-AKA protected success/failure indication using AT_RESULT_IND # (default: 0 = disabled). #eap_sim_aka_result_ind=1 +# EAP-SIM and EAP-AKA identity options +# 0 = do not use pseudonyms or fast reauthentication +# 1 = use pseudonyms, but not fast reauthentication +# 2 = do not use pseudonyms, but use fast reauthentication +# 3 = use pseudonyms and use fast reauthentication (default) +#eap_sim_id=3 + # Trusted Network Connect (TNC) # If enabled, TNC validation will be required before the peer is allowed to # connect. Note: This is only used with EAP-TTLS and EAP-FAST. If any other @@ -1292,6 +1391,17 @@ own_ip_addr=127.0.0.1 # Operator-Name = "Operator" #radius_acct_req_attr=126:s:Operator +# If SQLite support is included, path to a database from which additional +# RADIUS request attributes are extracted based on the station MAC address. +# +# The schema for the radius_attributes table is: +# id | sta | reqtype | attr : multi-key (sta, reqtype) +# id = autonumber +# sta = station MAC address in `11:22:33:44:55:66` format. +# type = `auth` | `acct` | NULL (match any) +# attr = existing config file format, e.g. `126:s:Test Operator` +#radius_req_attr_sqlite=radius_attr.sqlite + # Dynamic Authorization Extensions (RFC 5176) # This mechanism can be used to allow dynamic changes to user session based on # commands from a RADIUS server (or some other disconnect client that has the @@ -2492,6 +2602,42 @@ own_ip_addr=127.0.0.1 # that allows sending of such data. Default: 0. #stationary_ap=0 +##### Airtime policy configuration ########################################### + +# Set the airtime policy operating mode: +# 0 = disabled (default) +# 1 = static config +# 2 = per-BSS dynamic config +# 3 = per-BSS limit mode +#airtime_mode=0 + +# Interval (in milliseconds) to poll the kernel for updated station activity in +# dynamic and limit modes +#airtime_update_interval=200 + +# Static configuration of station weights (when airtime_mode=1). Kernel default +# weight is 256; set higher for larger airtime share, lower for smaller share. +# Each entry is a MAC address followed by a weight. +#airtime_sta_weight=02:01:02:03:04:05 256 +#airtime_sta_weight=02:01:02:03:04:06 512 + +# Per-BSS airtime weight. In multi-BSS mode, set for each BSS and hostapd will +# configure station weights to enforce the correct ratio between BSS weights +# depending on the number of active stations. The *ratios* between different +# BSSes is what's important, not the absolute numbers. +# Must be set for all BSSes if airtime_mode=2 or 3, has no effect otherwise. +#airtime_bss_weight=1 + +# Whether the current BSS should be limited (when airtime_mode=3). +# +# If set, the BSS weight ratio will be applied in the case where the current BSS +# would exceed the share defined by the BSS weight ratio. E.g., if two BSSes are +# set to the same weights, and one is set to limited, the limited BSS will get +# no more than half the available airtime, but if the non-limited BSS has more +# stations active, that *will* be allowed to exceed its half of the available +# airtime. +#airtime_bss_limit=1 + ##### TESTING OPTIONS ######################################################### # # The options in this section are only available when the build configuration diff --git a/contrib/wpa/hostapd/hostapd_cli.c b/contrib/wpa/hostapd/hostapd_cli.c index 23c592a6b0a6..046024390f84 100644 --- a/contrib/wpa/hostapd/hostapd_cli.c +++ b/contrib/wpa/hostapd/hostapd_cli.c @@ -1214,6 +1214,13 @@ static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_cmd_update_beacon(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "UPDATE_BEACON"); +} + + static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; @@ -1617,6 +1624,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { "= reload configuration for current interface" }, { "disable", hostapd_cli_cmd_disable, NULL, "= disable hostapd on current interface" }, + { "update_beacon", hostapd_cli_cmd_update_beacon, NULL, + "= update Beacon frame contents\n"}, { "erp_flush", hostapd_cli_cmd_erp_flush, NULL, "= drop all ERP keys"}, { "log_level", hostapd_cli_cmd_log_level, NULL, diff --git a/contrib/wpa/hostapd/main.c b/contrib/wpa/hostapd/main.c index 93d2dd34c08e..08896ffe2a79 100644 --- a/contrib/wpa/hostapd/main.c +++ b/contrib/wpa/hostapd/main.c @@ -653,6 +653,9 @@ int main(int argc, char *argv[]) int start_ifaces_in_sync = 0; char **if_names = NULL; size_t if_names_size = 0; +#ifdef CONFIG_DPP + struct dpp_global_config dpp_conf; +#endif /* CONFIG_DPP */ if (os_program_init()) return -1; @@ -672,7 +675,9 @@ int main(int argc, char *argv[]) dl_list_init(&interfaces.eth_p_oui); #endif /* CONFIG_ETH_P_OUI */ #ifdef CONFIG_DPP - interfaces.dpp = dpp_global_init(); + os_memset(&dpp_conf, 0, sizeof(dpp_conf)); + /* TODO: dpp_conf.msg_ctx? */ + interfaces.dpp = dpp_global_init(&dpp_conf); if (!interfaces.dpp) return -1; #endif /* CONFIG_DPP */ diff --git a/contrib/wpa/hs20/client/osu_client.c b/contrib/wpa/hs20/client/osu_client.c index 1f594ce8a25a..fd99600da788 100644 --- a/contrib/wpa/hs20/client/osu_client.c +++ b/contrib/wpa/hs20/client/osu_client.c @@ -1588,6 +1588,7 @@ static void set_pps_cred_digital_cert(struct hs20_osu_client *ctx, int id, xml_node_t *node, const char *fqdn) { char buf[200], dir[200]; + int res; wpa_printf(MSG_INFO, "- Credential/DigitalCertificate"); @@ -1599,14 +1600,20 @@ static void set_pps_cred_digital_cert(struct hs20_osu_client *ctx, int id, wpa_printf(MSG_INFO, "Failed to set username"); } - snprintf(buf, sizeof(buf), "%s/SP/%s/client-cert.pem", dir, fqdn); + res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/client-cert.pem", dir, + fqdn); + if (os_snprintf_error(sizeof(buf), res)) + return; if (os_file_exists(buf)) { if (set_cred_quoted(ctx->ifname, id, "client_cert", buf) < 0) { wpa_printf(MSG_INFO, "Failed to set client_cert"); } } - snprintf(buf, sizeof(buf), "%s/SP/%s/client-key.pem", dir, fqdn); + res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/client-key.pem", dir, + fqdn); + if (os_snprintf_error(sizeof(buf), res)) + return; if (os_file_exists(buf)) { if (set_cred_quoted(ctx->ifname, id, "private_key", buf) < 0) { wpa_printf(MSG_INFO, "Failed to set private_key"); @@ -1620,6 +1627,7 @@ static void set_pps_cred_realm(struct hs20_osu_client *ctx, int id, { char *str = xml_node_get_text(ctx->xml, node); char buf[200], dir[200]; + int res; if (str == NULL) return; @@ -1634,7 +1642,9 @@ static void set_pps_cred_realm(struct hs20_osu_client *ctx, int id, if (getcwd(dir, sizeof(dir)) == NULL) return; - snprintf(buf, sizeof(buf), "%s/SP/%s/aaa-ca.pem", dir, fqdn); + res = os_snprintf(buf, sizeof(buf), "%s/SP/%s/aaa-ca.pem", dir, fqdn); + if (os_snprintf_error(sizeof(buf), res)) + return; if (os_file_exists(buf)) { if (set_cred_quoted(ctx->ifname, id, "ca_cert", buf) < 0) { wpa_printf(MSG_INFO, "Failed to set CA cert"); @@ -2717,6 +2727,8 @@ static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address, if (!pps_fname) { char buf[256]; + int res; + wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information"); if (address && os_strncmp(address, "fqdn=", 5) == 0) { wpa_printf(MSG_INFO, "Use requested FQDN from command line"); @@ -2737,8 +2749,13 @@ static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address, "SP/%s/pps.xml", ctx->fqdn); pps_fname = pps_fname_buf; - os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem", - buf); + res = os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), + "SP/%s/ca.pem", buf); + if (os_snprintf_error(sizeof(ca_fname_buf), res)) { + os_free(ctx->fqdn); + ctx->fqdn = NULL; + return -1; + } ca_fname = ca_fname_buf; } diff --git a/contrib/wpa/src/ap/accounting.c b/contrib/wpa/src/ap/accounting.c index 0aacc3c95b08..9fc1886a2cff 100644 --- a/contrib/wpa/src/ap/accounting.c +++ b/contrib/wpa/src/ap/accounting.c @@ -97,6 +97,9 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, msg) < 0) goto fail; + if (sta && add_sqlite_radius_attr(hapd, sta, msg, 1) < 0) + goto fail; + if (sta) { for (i = 0; ; i++) { val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, diff --git a/contrib/wpa/src/ap/acs.c b/contrib/wpa/src/ap/acs.c index 3b4507575328..11178a1f047c 100644 --- a/contrib/wpa/src/ap/acs.c +++ b/contrib/wpa/src/ap/acs.c @@ -594,12 +594,12 @@ acs_find_ideal_chan(struct hostapd_iface *iface) iface->conf->secondary_channel) n_chans = 2; - if (iface->conf->ieee80211ac) { - switch (iface->conf->vht_oper_chwidth) { - case VHT_CHANWIDTH_80MHZ: + if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) { + switch (hostapd_get_oper_chwidth(iface->conf)) { + case CHANWIDTH_80MHZ: n_chans = 4; break; - case VHT_CHANWIDTH_160MHZ: + case CHANWIDTH_160MHZ: n_chans = 8; break; } @@ -607,7 +607,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface) bw = num_chan_to_bw(n_chans); - /* TODO: VHT80+80. Update acs_adjust_vht_center_freq() too. */ + /* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */ wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz", bw); @@ -647,9 +647,9 @@ acs_find_ideal_chan(struct hostapd_iface *iface) } if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && - iface->conf->ieee80211ac) { - if (iface->conf->vht_oper_chwidth == - VHT_CHANWIDTH_80MHZ && + (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) { + if (hostapd_get_oper_chwidth(iface->conf) == + CHANWIDTH_80MHZ && !acs_usable_vht80_chan(chan)) { wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80", @@ -657,8 +657,8 @@ acs_find_ideal_chan(struct hostapd_iface *iface) continue; } - if (iface->conf->vht_oper_chwidth == - VHT_CHANWIDTH_160MHZ && + if (hostapd_get_oper_chwidth(iface->conf) == + CHANWIDTH_160MHZ && !acs_usable_vht160_chan(chan)) { wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT160", @@ -783,20 +783,20 @@ acs_find_ideal_chan(struct hostapd_iface *iface) } -static void acs_adjust_vht_center_freq(struct hostapd_iface *iface) +static void acs_adjust_center_freq(struct hostapd_iface *iface) { int offset; wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency"); - switch (iface->conf->vht_oper_chwidth) { - case VHT_CHANWIDTH_USE_HT: + switch (hostapd_get_oper_chwidth(iface->conf)) { + case CHANWIDTH_USE_HT: offset = 2 * iface->conf->secondary_channel; break; - case VHT_CHANWIDTH_80MHZ: + case CHANWIDTH_80MHZ: offset = 6; break; - case VHT_CHANWIDTH_160MHZ: + case CHANWIDTH_160MHZ: offset = 14; break; default: @@ -807,8 +807,8 @@ static void acs_adjust_vht_center_freq(struct hostapd_iface *iface) return; } - iface->conf->vht_oper_centr_freq_seg0_idx = - iface->conf->channel + offset; + hostapd_set_oper_centr_freq_seg0_idx(iface->conf, + iface->conf->channel + offset); } @@ -863,8 +863,8 @@ static void acs_study(struct hostapd_iface *iface) iface->conf->channel = ideal_chan->chan; - if (iface->conf->ieee80211ac) - acs_adjust_vht_center_freq(iface); + if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) + acs_adjust_center_freq(iface); err = 0; fail: diff --git a/contrib/wpa/src/ap/airtime_policy.c b/contrib/wpa/src/ap/airtime_policy.c new file mode 100644 index 000000000000..f56ca5bddc3f --- /dev/null +++ b/contrib/wpa/src/ap/airtime_policy.c @@ -0,0 +1,269 @@ +/* + * Airtime policy configuration + * Copyright (c) 2018-2019, Toke Høiland-Jørgensen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "ap_drv_ops.h" +#include "sta_info.h" +#include "airtime_policy.h" + +/* Idea: + * Two modes of airtime enforcement: + * 1. Static weights: specify weights per MAC address with a per-BSS default + * 2. Per-BSS limits: Dynamically calculate weights of backlogged stations to + * enforce relative total shares between BSSes. + * + * - Periodic per-station callback to update queue status. + * + * Copy accounting_sta_update_stats() to get TXQ info and airtime weights and + * keep them updated in sta_info. + * + * - Separate periodic per-bss (or per-iface?) callback to update weights. + * + * Just need to loop through all interfaces, count sum the active stations (or + * should the per-STA callback just adjust that for the BSS?) and calculate new + * weights. + */ + +static int get_airtime_policy_update_timeout(struct hostapd_iface *iface, + unsigned int *sec, + unsigned int *usec) +{ + unsigned int update_int = iface->conf->airtime_update_interval; + + if (!update_int) { + wpa_printf(MSG_ERROR, + "Airtime policy: Invalid airtime policy update interval %u", + update_int); + return -1; + } + + *sec = update_int / 1000; + *usec = (update_int % 1000) * 1000; + + return 0; +} + + +static void set_new_backlog_time(struct hostapd_data *hapd, + struct sta_info *sta, + struct os_reltime *now) +{ + sta->backlogged_until = *now; + sta->backlogged_until.usec += hapd->iconf->airtime_update_interval * + AIRTIME_BACKLOG_EXPIRY_FACTOR; + while (sta->backlogged_until.usec >= 1000000) { + sta->backlogged_until.sec++; + sta->backlogged_until.usec -= 1000000; + } +} + + +static void count_backlogged_sta(struct hostapd_data *hapd) +{ + struct sta_info *sta; + struct hostap_sta_driver_data data = {}; + unsigned int num_backlogged = 0; + struct os_reltime now; + + os_get_reltime(&now); + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (hostapd_drv_read_sta_data(hapd, &data, sta->addr)) + continue; + + if (data.backlog_bytes > 0) + set_new_backlog_time(hapd, sta, &now); + if (os_reltime_before(&now, &sta->backlogged_until)) + num_backlogged++; + } + hapd->num_backlogged_sta = num_backlogged; +} + + +static int sta_set_airtime_weight(struct hostapd_data *hapd, + struct sta_info *sta, + unsigned int weight) +{ + int ret = 0; + + if (weight != sta->airtime_weight && + (ret = hostapd_sta_set_airtime_weight(hapd, sta->addr, weight))) + return ret; + + sta->airtime_weight = weight; + return ret; +} + + +static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight) +{ + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) + sta_set_airtime_weight(hapd, sta, weight); +} + + +static unsigned int get_airtime_quantum(unsigned int max_wt) +{ + unsigned int quantum = AIRTIME_QUANTUM_TARGET / max_wt; + + if (quantum < AIRTIME_QUANTUM_MIN) + quantum = AIRTIME_QUANTUM_MIN; + else if (quantum > AIRTIME_QUANTUM_MAX) + quantum = AIRTIME_QUANTUM_MAX; + + return quantum; +} + + +static void update_airtime_weights(void *eloop_data, void *user_data) +{ + struct hostapd_iface *iface = eloop_data; + struct hostapd_data *bss; + unsigned int sec, usec; + unsigned int num_sta_min = 0, num_sta_prod = 1, num_sta_sum = 0, + wt_sum = 0; + unsigned int quantum; + Boolean all_div_min = TRUE; + Boolean apply_limit = iface->conf->airtime_mode == AIRTIME_MODE_DYNAMIC; + int wt, num_bss = 0, max_wt = 0; + size_t i; + + for (i = 0; i < iface->num_bss; i++) { + bss = iface->bss[i]; + if (!bss->started || !bss->conf->airtime_weight) + continue; + + count_backlogged_sta(bss); + if (!bss->num_backlogged_sta) + continue; + + if (!num_sta_min || bss->num_backlogged_sta < num_sta_min) + num_sta_min = bss->num_backlogged_sta; + + num_sta_prod *= bss->num_backlogged_sta; + num_sta_sum += bss->num_backlogged_sta; + wt_sum += bss->conf->airtime_weight; + num_bss++; + } + + if (num_sta_min) { + for (i = 0; i < iface->num_bss; i++) { + bss = iface->bss[i]; + if (!bss->started || !bss->conf->airtime_weight) + continue; + + /* Check if we can divide all sta numbers by the + * smallest number to keep weights as small as possible. + * This is a lazy way to avoid having to factor + * integers. */ + if (bss->num_backlogged_sta && + bss->num_backlogged_sta % num_sta_min > 0) + all_div_min = FALSE; + + /* If we're in LIMIT mode, we only apply the weight + * scaling when the BSS(es) marked as limited would a + * larger share than the relative BSS weights indicates + * it should. */ + if (!apply_limit && bss->conf->airtime_limit) { + if (bss->num_backlogged_sta * wt_sum > + bss->conf->airtime_weight * num_sta_sum) + apply_limit = TRUE; + } + } + if (all_div_min) + num_sta_prod /= num_sta_min; + } + + for (i = 0; i < iface->num_bss; i++) { + bss = iface->bss[i]; + if (!bss->started || !bss->conf->airtime_weight) + continue; + + /* We only set the calculated weight if the BSS has active + * stations and there are other active interfaces as well - + * otherwise we just set a unit weight. This ensures that + * the weights are set reasonably when stations transition from + * inactive to active. */ + if (apply_limit && bss->num_backlogged_sta && num_bss > 1) + wt = bss->conf->airtime_weight * num_sta_prod / + bss->num_backlogged_sta; + else + wt = 1; + + bss->airtime_weight = wt; + if (wt > max_wt) + max_wt = wt; + } + + quantum = get_airtime_quantum(max_wt); + + for (i = 0; i < iface->num_bss; i++) { + bss = iface->bss[i]; + if (!bss->started || !bss->conf->airtime_weight) + continue; + set_sta_weights(bss, bss->airtime_weight * quantum); + } + + if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0) + return; + + eloop_register_timeout(sec, usec, update_airtime_weights, iface, + NULL); +} + + +static int get_weight_for_sta(struct hostapd_data *hapd, const u8 *sta) +{ + struct airtime_sta_weight *wt; + + wt = hapd->conf->airtime_weight_list; + while (wt && os_memcmp(wt->addr, sta, ETH_ALEN) != 0) + wt = wt->next; + + return wt ? wt->weight : hapd->conf->airtime_weight; +} + + +int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta) +{ + unsigned int weight; + + if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) { + weight = get_weight_for_sta(hapd, sta->addr); + if (weight) + return sta_set_airtime_weight(hapd, sta, weight); + } + return 0; +} + + +int airtime_policy_update_init(struct hostapd_iface *iface) +{ + unsigned int sec, usec; + + if (iface->conf->airtime_mode < AIRTIME_MODE_DYNAMIC) + return 0; + + if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0) + return -1; + + eloop_register_timeout(sec, usec, update_airtime_weights, iface, NULL); + return 0; +} + + +void airtime_policy_update_deinit(struct hostapd_iface *iface) +{ + eloop_cancel_timeout(update_airtime_weights, iface, NULL); +} diff --git a/contrib/wpa/src/ap/airtime_policy.h b/contrib/wpa/src/ap/airtime_policy.h new file mode 100644 index 000000000000..c2a9b0080629 --- /dev/null +++ b/contrib/wpa/src/ap/airtime_policy.h @@ -0,0 +1,48 @@ +/* + * Airtime policy configuration + * Copyright (c) 2018-2019, Toke Høiland-Jørgensen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef AIRTIME_POLICY_H +#define AIRTIME_POLICY_H + +struct hostapd_iface; + +#ifdef CONFIG_AIRTIME_POLICY + +#define AIRTIME_DEFAULT_UPDATE_INTERVAL 200 /* ms */ +#define AIRTIME_BACKLOG_EXPIRY_FACTOR 2500 /* 2.5 intervals + convert to usec */ + +/* scale quantum so this becomes the effective quantum after applying the max + * weight, but never go below min or above max */ +#define AIRTIME_QUANTUM_MIN 8 /* usec */ +#define AIRTIME_QUANTUM_MAX 256 /* usec */ +#define AIRTIME_QUANTUM_TARGET 1024 /* usec */ + +int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta); +int airtime_policy_update_init(struct hostapd_iface *iface); +void airtime_policy_update_deinit(struct hostapd_iface *iface); + +#else /* CONFIG_AIRTIME_POLICY */ + +static inline int airtime_policy_new_sta(struct hostapd_data *hapd, + struct sta_info *sta) +{ + return -1; +} + +static inline int airtime_policy_update_init(struct hostapd_iface *iface) +{ + return -1; +} + +static inline void airtime_policy_update_deinit(struct hostapd_iface *iface) +{ +} + +#endif /* CONFIG_AIRTIME_POLICY */ + +#endif /* AIRTIME_POLICY_H */ diff --git a/contrib/wpa/src/ap/ap_config.c b/contrib/wpa/src/ap/ap_config.c index e640e9984b70..90348e1ddb97 100644 --- a/contrib/wpa/src/ap/ap_config.c +++ b/contrib/wpa/src/ap/ap_config.c @@ -13,12 +13,14 @@ #include "crypto/tls.h" #include "radius/radius_client.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_1x_defs.h" #include "common/eapol_common.h" #include "common/dhcp.h" #include "eap_common/eap_wsc_common.h" #include "eap_server/eap.h" #include "wpa_auth.h" #include "sta_info.h" +#include "airtime_policy.h" #include "ap_config.h" @@ -76,6 +78,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->radius_server_auth_port = 1812; bss->eap_sim_db_timeout = 1; + bss->eap_sim_id = 3; bss->ap_max_inactivity = AP_MAX_INACTIVITY; bss->eapol_version = EAPOL_VERSION; @@ -138,6 +141,11 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->hs20_release = (HS20_VERSION >> 4) + 1; #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MACSEC + bss->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER; + bss->macsec_port = 1; +#endif /* CONFIG_MACSEC */ + /* Default to strict CRL checking. */ bss->check_crl_strict = 1; } @@ -236,6 +244,13 @@ struct hostapd_config * hostapd_config_defaults(void) conf->acs_num_scans = 5; #endif /* CONFIG_ACS */ +#ifdef CONFIG_IEEE80211AX + conf->he_op.he_rts_threshold = HE_OPERATION_RTS_THRESHOLD_MASK >> + HE_OPERATION_RTS_THRESHOLD_OFFSET; + /* Set default basic MCS/NSS set to single stream MCS 0-7 */ + conf->he_op.he_basic_mcs_nss_set = 0xfffc; +#endif /* CONFIG_IEEE80211AX */ + /* The third octet of the country string uses an ASCII space character * by default to indicate that the regulations encompass all * environments for the current frequency band in the country. */ @@ -244,6 +259,10 @@ struct hostapd_config * hostapd_config_defaults(void) conf->rssi_reject_assoc_rssi = 0; conf->rssi_reject_assoc_timeout = 30; +#ifdef CONFIG_AIRTIME_POLICY + conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL; +#endif /* CONFIG_AIRTIME_POLICY */ + return conf; } @@ -458,7 +477,76 @@ hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type) } -static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr) +struct hostapd_radius_attr * hostapd_parse_radius_attr(const char *value) +{ + const char *pos; + char syntax; + struct hostapd_radius_attr *attr; + size_t len; + + attr = os_zalloc(sizeof(*attr)); + if (!attr) + return NULL; + + attr->type = atoi(value); + + pos = os_strchr(value, ':'); + if (!pos) { + attr->val = wpabuf_alloc(1); + if (!attr->val) { + os_free(attr); + return NULL; + } + wpabuf_put_u8(attr->val, 0); + return attr; + } + + pos++; + if (pos[0] == '\0' || pos[1] != ':') { + os_free(attr); + return NULL; + } + syntax = *pos++; + pos++; + + switch (syntax) { + case 's': + attr->val = wpabuf_alloc_copy(pos, os_strlen(pos)); + break; + case 'x': + len = os_strlen(pos); + if (len & 1) + break; + len /= 2; + attr->val = wpabuf_alloc(len); + if (!attr->val) + break; + if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) { + wpabuf_free(attr->val); + os_free(attr); + return NULL; + } + break; + case 'd': + attr->val = wpabuf_alloc(4); + if (attr->val) + wpabuf_put_be32(attr->val, atoi(pos)); + break; + default: + os_free(attr); + return NULL; + } + + if (!attr->val) { + os_free(attr); + return NULL; + } + + return attr; +} + + +void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr) { struct hostapd_radius_attr *prev; @@ -559,8 +647,26 @@ static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf) } +#ifdef CONFIG_DPP2 +static void hostapd_dpp_controller_conf_free(struct dpp_controller_conf *conf) +{ + struct dpp_controller_conf *prev; + + while (conf) { + prev = conf; + conf = conf->next; + os_free(prev); + } +} +#endif /* CONFIG_DPP2 */ + + void hostapd_config_free_bss(struct hostapd_bss_config *conf) { +#if defined(CONFIG_WPS) || defined(CONFIG_HS20) + size_t i; +#endif + if (conf == NULL) return; @@ -589,12 +695,16 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) } hostapd_config_free_radius_attr(conf->radius_auth_req_attr); hostapd_config_free_radius_attr(conf->radius_acct_req_attr); + os_free(conf->radius_req_attr_sqlite); os_free(conf->rsn_preauth_interfaces); os_free(conf->ctrl_interface); os_free(conf->ca_cert); os_free(conf->server_cert); + os_free(conf->server_cert2); os_free(conf->private_key); + os_free(conf->private_key2); os_free(conf->private_key_passwd); + os_free(conf->private_key_passwd2); os_free(conf->check_cert_subject); os_free(conf->ocsp_stapling_response); os_free(conf->ocsp_stapling_response_multi); @@ -653,12 +763,8 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->model_description); os_free(conf->model_url); os_free(conf->upc); - { - unsigned int i; - - for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) - wpabuf_free(conf->wps_vendor_ext[i]); - } + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) + wpabuf_free(conf->wps_vendor_ext[i]); wpabuf_free(conf->wps_nfc_dh_pubkey); wpabuf_free(conf->wps_nfc_dh_privkey); wpabuf_free(conf->wps_nfc_dev_pw); @@ -684,7 +790,6 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->hs20_operating_class); os_free(conf->hs20_icons); if (conf->hs20_osu_providers) { - size_t i; for (i = 0; i < conf->hs20_osu_providers_count; i++) { struct hs20_osu_provider *p; size_t j; @@ -702,8 +807,6 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->hs20_osu_providers); } if (conf->hs20_operator_icon) { - size_t i; - for (i = 0; i < conf->hs20_operator_icon_count; i++) os_free(conf->hs20_operator_icon[i]); os_free(conf->hs20_operator_icon); @@ -740,10 +843,27 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->dpp_connector); wpabuf_free(conf->dpp_netaccesskey); wpabuf_free(conf->dpp_csign); +#ifdef CONFIG_DPP2 + hostapd_dpp_controller_conf_free(conf->dpp_controller); +#endif /* CONFIG_DPP2 */ #endif /* CONFIG_DPP */ hostapd_config_free_sae_passwords(conf); +#ifdef CONFIG_AIRTIME_POLICY + { + struct airtime_sta_weight *wt, *wt_prev; + + wt = conf->airtime_weight_list; + conf->airtime_weight_list = NULL; + while (wt) { + wt_prev = wt; + wt = wt->next; + os_free(wt_prev); + } + } +#endif /* CONFIG_AIRTIME_POLICY */ + os_free(conf); } @@ -1140,6 +1260,13 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config) return -1; } +#ifdef CONFIG_AIRTIME_POLICY + if (full_config && conf->airtime_mode > AIRTIME_MODE_STATIC && + !conf->airtime_update_interval) { + wpa_printf(MSG_ERROR, "Airtime update interval cannot be zero"); + return -1; + } +#endif /* CONFIG_AIRTIME_POLICY */ for (i = 0; i < NUM_TX_QUEUES; i++) { if (hostapd_config_check_cw(conf, i)) return -1; diff --git a/contrib/wpa/src/ap/ap_config.h b/contrib/wpa/src/ap/ap_config.h index 509677a45f05..ea581a822277 100644 --- a/contrib/wpa/src/ap/ap_config.h +++ b/contrib/wpa/src/ap/ap_config.h @@ -15,6 +15,7 @@ #include "common/wpa_common.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "crypto/sha256.h" #include "wps/wps.h" #include "fst/fst.h" #include "vlan.h" @@ -252,6 +253,18 @@ struct sae_password_entry { int vlan_id; }; +struct dpp_controller_conf { + struct dpp_controller_conf *next; + u8 pkhash[SHA256_MAC_LEN]; + struct hostapd_ip_addr ipaddr; +}; + +struct airtime_sta_weight { + struct airtime_sta_weight *next; + unsigned int weight; + u8 addr[ETH_ALEN]; +}; + /** * struct hostapd_bss_config - Per-BSS configuration */ @@ -288,6 +301,7 @@ struct hostapd_bss_config { int radius_request_cui; struct hostapd_radius_attr *radius_auth_req_attr; struct hostapd_radius_attr *radius_acct_req_attr; + char *radius_req_attr_sqlite; int radius_das_port; unsigned int radius_das_time_window; int radius_das_require_event_timestamp; @@ -390,8 +404,11 @@ struct hostapd_bss_config { char *ca_cert; char *server_cert; + char *server_cert2; char *private_key; + char *private_key2; char *private_key_passwd; + char *private_key_passwd2; char *check_cert_subject; int check_crl; int check_crl_strict; @@ -410,7 +427,10 @@ struct hostapd_bss_config { int eap_fast_prov; int pac_key_lifetime; int pac_key_refresh_time; + int eap_teap_auth; + int eap_teap_pac_no_inner; int eap_sim_aka_result_ind; + int eap_sim_id; int tnc; int fragment_size; u16 pwd_group; @@ -570,6 +590,7 @@ struct hostapd_bss_config { int osen; int proxy_arp; int na_mcast_to_ucast; + #ifdef CONFIG_HS20 int hs20; int hs20_release; @@ -692,6 +713,9 @@ struct hostapd_bss_config { struct wpabuf *dpp_netaccesskey; unsigned int dpp_netaccesskey_expiry; struct wpabuf *dpp_csign; +#ifdef CONFIG_DPP2 + struct dpp_controller_conf *dpp_controller; +#endif /* CONFIG_DPP2 */ #endif /* CONFIG_DPP */ #ifdef CONFIG_OWE @@ -709,6 +733,100 @@ struct hostapd_bss_config { #define BACKHAUL_BSS 1 #define FRONTHAUL_BSS 2 int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */ + +#ifdef CONFIG_AIRTIME_POLICY + unsigned int airtime_weight; + int airtime_limit; + struct airtime_sta_weight *airtime_weight_list; +#endif /* CONFIG_AIRTIME_POLICY */ + +#ifdef CONFIG_MACSEC + /** + * macsec_policy - Determines the policy for MACsec secure session + * + * 0: MACsec not in use (default) + * 1: MACsec enabled - Should secure, accept key server's advice to + * determine whether to use a secure session or not. + */ + int macsec_policy; + + /** + * macsec_integ_only - Determines how MACsec are transmitted + * + * This setting applies only when MACsec is in use, i.e., + * - macsec_policy is enabled + * - the key server has decided to enable MACsec + * + * 0: Encrypt traffic (default) + * 1: Integrity only + */ + int macsec_integ_only; + + /** + * macsec_replay_protect - Enable MACsec replay protection + * + * This setting applies only when MACsec is in use, i.e., + * - macsec_policy is enabled + * - the key server has decided to enable MACsec + * + * 0: Replay protection disabled (default) + * 1: Replay protection enabled + */ + int macsec_replay_protect; + + /** + * macsec_replay_window - MACsec replay protection window + * + * A window in which replay is tolerated, to allow receipt of frames + * that have been misordered by the network. + * + * This setting applies only when MACsec replay protection active, i.e., + * - macsec_replay_protect is enabled + * - the key server has decided to enable MACsec + * + * 0: No replay window, strict check (default) + * 1..2^32-1: number of packets that could be misordered + */ + u32 macsec_replay_window; + + /** + * macsec_port - MACsec port (in SCI) + * + * Port component of the SCI. + * + * Range: 1-65534 (default: 1) + */ + int macsec_port; + + /** + * mka_priority - Priority of MKA Actor + * + * Range: 0-255 (default: 255) + */ + int mka_priority; + + /** + * mka_ckn - MKA pre-shared CKN + */ +#define MACSEC_CKN_MAX_LEN 32 + size_t mka_ckn_len; + u8 mka_ckn[MACSEC_CKN_MAX_LEN]; + + /** + * mka_cak - MKA pre-shared CAK + */ +#define MACSEC_CAK_MAX_LEN 32 + size_t mka_cak_len; + u8 mka_cak[MACSEC_CAK_MAX_LEN]; + +#define MKA_PSK_SET_CKN BIT(0) +#define MKA_PSK_SET_CAK BIT(1) +#define MKA_PSK_SET (MKA_PSK_SET_CKN | MKA_PSK_SET_CAK) + /** + * mka_psk_set - Whether mka_ckn and mka_cak are set + */ + u8 mka_psk_set; +#endif /* CONFIG_MACSEC */ }; /** @@ -727,7 +845,20 @@ struct he_operation { u8 he_bss_color; u8 he_default_pe_duration; u8 he_twt_required; - u8 he_rts_threshold; + u16 he_rts_threshold; + u16 he_basic_mcs_nss_set; +}; + +/** + * struct spatial_reuse - Spatial reuse + */ +struct spatial_reuse { + u8 sr_control; + u8 non_srg_obss_pd_max_offset; + u8 srg_obss_pd_min_offset; + u8 srg_obss_pd_max_offset; + u8 srg_obss_color_bitmap; + u8 srg_obss_color_partial_bitmap; }; /** @@ -852,6 +983,10 @@ struct hostapd_config { struct he_phy_capabilities_info he_phy_capab; struct he_operation he_op; struct ieee80211_he_mu_edca_parameter_set he_mu_edca; + struct spatial_reuse spr; + u8 he_oper_chwidth; + u8 he_oper_centr_freq_seg0_idx; + u8 he_oper_centr_freq_seg1_idx; #endif /* CONFIG_IEEE80211AX */ /* VHT enable/disable config from CHAN_SWITCH */ @@ -861,12 +996,87 @@ struct hostapd_config { int rssi_reject_assoc_rssi; int rssi_reject_assoc_timeout; + +#ifdef CONFIG_AIRTIME_POLICY + enum { + AIRTIME_MODE_OFF = 0, + AIRTIME_MODE_STATIC = 1, + AIRTIME_MODE_DYNAMIC = 2, + AIRTIME_MODE_LIMIT = 3, + __AIRTIME_MODE_MAX, + } airtime_mode; + unsigned int airtime_update_interval; +#define AIRTIME_MODE_MAX (__AIRTIME_MODE_MAX - 1) +#endif /* CONFIG_AIRTIME_POLICY */ }; +static inline u8 hostapd_get_oper_chwidth(struct hostapd_config *conf) +{ +#ifdef CONFIG_IEEE80211AX + if (conf->ieee80211ax) + return conf->he_oper_chwidth; +#endif /* CONFIG_IEEE80211AX */ + return conf->vht_oper_chwidth; +} + +static inline void +hostapd_set_oper_chwidth(struct hostapd_config *conf, u8 oper_chwidth) +{ +#ifdef CONFIG_IEEE80211AX + if (conf->ieee80211ax) + conf->he_oper_chwidth = oper_chwidth; +#endif /* CONFIG_IEEE80211AX */ + conf->vht_oper_chwidth = oper_chwidth; +} + +static inline u8 +hostapd_get_oper_centr_freq_seg0_idx(struct hostapd_config *conf) +{ +#ifdef CONFIG_IEEE80211AX + if (conf->ieee80211ax) + return conf->he_oper_centr_freq_seg0_idx; +#endif /* CONFIG_IEEE80211AX */ + return conf->vht_oper_centr_freq_seg0_idx; +} + +static inline void +hostapd_set_oper_centr_freq_seg0_idx(struct hostapd_config *conf, + u8 oper_centr_freq_seg0_idx) +{ +#ifdef CONFIG_IEEE80211AX + if (conf->ieee80211ax) + conf->he_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx; +#endif /* CONFIG_IEEE80211AX */ + conf->vht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx; +} + +static inline u8 +hostapd_get_oper_centr_freq_seg1_idx(struct hostapd_config *conf) +{ +#ifdef CONFIG_IEEE80211AX + if (conf->ieee80211ax) + return conf->he_oper_centr_freq_seg1_idx; +#endif /* CONFIG_IEEE80211AX */ + return conf->vht_oper_centr_freq_seg1_idx; +} + +static inline void +hostapd_set_oper_centr_freq_seg1_idx(struct hostapd_config *conf, + u8 oper_centr_freq_seg1_idx) +{ +#ifdef CONFIG_IEEE80211AX + if (conf->ieee80211ax) + conf->he_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx; +#endif /* CONFIG_IEEE80211AX */ + conf->vht_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx; +} + + int hostapd_mac_comp(const void *a, const void *b); struct hostapd_config * hostapd_config_defaults(void); void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); +void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr); void hostapd_config_free_eap_user(struct hostapd_eap_user *user); void hostapd_config_free_eap_users(struct hostapd_eap_user *user); void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p); @@ -885,6 +1095,7 @@ const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id); struct hostapd_radius_attr * hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type); +struct hostapd_radius_attr * hostapd_parse_radius_attr(const char *value); int hostapd_config_check(struct hostapd_config *conf, int full_config); void hostapd_set_security_params(struct hostapd_bss_config *bss, int full_config); diff --git a/contrib/wpa/src/ap/ap_drv_ops.c b/contrib/wpa/src/ap/ap_drv_ops.c index 067cf863e84c..c0ededabe979 100644 --- a/contrib/wpa/src/ap/ap_drv_ops.c +++ b/contrib/wpa/src/ap/ap_drv_ops.c @@ -413,6 +413,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, + const struct ieee80211_he_capabilities *he_capab, + size_t he_capab_len, u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, int set) { @@ -432,6 +434,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, params.listen_interval = listen_interval; params.ht_capabilities = ht_capab; params.vht_capabilities = vht_capab; + params.he_capab = he_capab; + params.he_capab_len = he_capab_len; params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED); params.vht_opmode = vht_opmode; params.flags = hostapd_sta_flags_to_drv(flags); @@ -537,17 +541,20 @@ int hostapd_flush(struct hostapd_data *hapd) int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode, int freq, int channel, int ht_enabled, int vht_enabled, - int sec_channel_offset, int vht_oper_chwidth, + int he_enabled, + int sec_channel_offset, int oper_chwidth, int center_segment0, int center_segment1) { struct hostapd_freq_params data; + struct hostapd_hw_modes *cmode = hapd->iface->current_mode; if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled, - vht_enabled, sec_channel_offset, - vht_oper_chwidth, + vht_enabled, he_enabled, sec_channel_offset, + oper_chwidth, center_segment0, center_segment1, - hapd->iface->current_mode ? - hapd->iface->current_mode->vht_capab : 0)) + cmode ? cmode->vht_capab : 0, + cmode ? + &cmode->he_capab[IEEE80211_MODE_AP] : NULL)) return -1; if (hapd->driver == NULL) @@ -583,6 +590,16 @@ int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, } +int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr, + unsigned int weight) +{ + if (!hapd->driver || !hapd->driver->sta_set_airtime_weight) + return 0; + return hapd->driver->sta_set_airtime_weight(hapd->drv_priv, addr, + weight); +} + + int hostapd_set_country(struct hostapd_data *hapd, const char *country) { if (hapd->driver == NULL || @@ -775,14 +792,16 @@ int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd, int hostapd_start_dfs_cac(struct hostapd_iface *iface, enum hostapd_hw_mode mode, int freq, int channel, int ht_enabled, int vht_enabled, - int sec_channel_offset, int vht_oper_chwidth, + int he_enabled, + int sec_channel_offset, int oper_chwidth, int center_segment0, int center_segment1) { struct hostapd_data *hapd = iface->bss[0]; struct hostapd_freq_params data; int res; + struct hostapd_hw_modes *cmode = iface->current_mode; - if (!hapd->driver || !hapd->driver->start_dfs_cac) + if (!hapd->driver || !hapd->driver->start_dfs_cac || !cmode) return 0; if (!iface->conf->ieee80211h) { @@ -792,10 +811,11 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface, } if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled, - vht_enabled, sec_channel_offset, - vht_oper_chwidth, center_segment0, + vht_enabled, he_enabled, sec_channel_offset, + oper_chwidth, center_segment0, center_segment1, - iface->current_mode->vht_capab)) { + cmode->vht_capab, + &cmode->he_capab[IEEE80211_MODE_AP])) { wpa_printf(MSG_ERROR, "Can't set freq params"); return -1; } @@ -919,15 +939,17 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd) if (hapd->iface->conf->ieee80211n && params.ht40_enabled) params.ch_width = 40; - /* Note: VHT20 is defined by combination of ht_capab & vht_oper_chwidth + /* Note: VHT20 is defined by combination of ht_capab & oper_chwidth */ - if (hapd->iface->conf->ieee80211ac && params.ht40_enabled) { - if (hapd->iface->conf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ) + if ((hapd->iface->conf->ieee80211ax || + hapd->iface->conf->ieee80211ac) && + params.ht40_enabled) { + u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iface->conf); + + if (oper_chwidth == CHANWIDTH_80MHZ) params.ch_width = 80; - else if (hapd->iface->conf->vht_oper_chwidth == - VHT_CHANWIDTH_160MHZ || - hapd->iface->conf->vht_oper_chwidth == - VHT_CHANWIDTH_80P80MHZ) + else if (oper_chwidth == CHANWIDTH_160MHZ || + oper_chwidth == CHANWIDTH_80P80MHZ) params.ch_width = 160; } @@ -936,3 +958,13 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd) return ret; } + + +int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer, + u16 reason_code, const u8 *ie, size_t ielen) +{ + if (!hapd->driver || !hapd->driver->update_dh_ie || !hapd->drv_priv) + return 0; + return hapd->driver->update_dh_ie(hapd->drv_priv, peer, reason_code, + ie, ielen); +} diff --git a/contrib/wpa/src/ap/ap_drv_ops.h b/contrib/wpa/src/ap/ap_drv_ops.h index de40171e18dc..ca7f7abe01fd 100644 --- a/contrib/wpa/src/ap/ap_drv_ops.h +++ b/contrib/wpa/src/ap/ap_drv_ops.h @@ -41,6 +41,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, + const struct ieee80211_he_capabilities *he_capab, + size_t he_capab_len, u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, int set); int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); @@ -61,12 +63,14 @@ int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd, int hostapd_flush(struct hostapd_data *hapd); int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode, int freq, int channel, int ht_enabled, int vht_enabled, - int sec_channel_offset, int vht_oper_chwidth, + int he_enabled, int sec_channel_offset, int oper_chwidth, int center_segment0, int center_segment1); int hostapd_set_rts(struct hostapd_data *hapd, int rts); int hostapd_set_frag(struct hostapd_data *hapd, int frag); int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, int total_flags, int flags_or, int flags_and); +int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr, + unsigned int weight); int hostapd_set_country(struct hostapd_data *hapd, const char *country); int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, int cw_min, int cw_max, int burst_time); @@ -122,9 +126,12 @@ int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, int hostapd_start_dfs_cac(struct hostapd_iface *iface, enum hostapd_hw_mode mode, int freq, int channel, int ht_enabled, int vht_enabled, - int sec_channel_offset, int vht_oper_chwidth, + int he_enabled, + int sec_channel_offset, int oper_chwidth, int center_segment0, int center_segment1); int hostapd_drv_do_acs(struct hostapd_data *hapd); +int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer, + u16 reason_code, const u8 *ie, size_t ielen); #include "drivers/driver.h" diff --git a/contrib/wpa/src/ap/authsrv.c b/contrib/wpa/src/ap/authsrv.c index eced6c7c6d94..4f5fe7db4482 100644 --- a/contrib/wpa/src/ap/authsrv.c +++ b/contrib/wpa/src/ap/authsrv.c @@ -120,7 +120,10 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) srv.eap_fast_prov = conf->eap_fast_prov; srv.pac_key_lifetime = conf->pac_key_lifetime; srv.pac_key_refresh_time = conf->pac_key_refresh_time; + srv.eap_teap_auth = conf->eap_teap_auth; + srv.eap_teap_pac_no_inner = conf->eap_teap_pac_no_inner; srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; + srv.eap_sim_id = conf->eap_sim_id; srv.tnc = conf->tnc; srv.wps = hapd->wps; srv.ipv6 = conf->radius_server_ipv6; @@ -195,7 +198,8 @@ int authsrv_init(struct hostapd_data *hapd) #ifdef EAP_TLS_FUNCS if (hapd->conf->eap_server && (hapd->conf->ca_cert || hapd->conf->server_cert || - hapd->conf->private_key || hapd->conf->dh_file)) { + hapd->conf->private_key || hapd->conf->dh_file || + hapd->conf->server_cert2 || hapd->conf->private_key2)) { struct tls_config conf; struct tls_connection_params params; @@ -224,8 +228,11 @@ int authsrv_init(struct hostapd_data *hapd) os_memset(¶ms, 0, sizeof(params)); params.ca_cert = hapd->conf->ca_cert; params.client_cert = hapd->conf->server_cert; + params.client_cert2 = hapd->conf->server_cert2; params.private_key = hapd->conf->private_key; + params.private_key2 = hapd->conf->private_key2; params.private_key_passwd = hapd->conf->private_key_passwd; + params.private_key_passwd2 = hapd->conf->private_key_passwd2; params.dh_file = hapd->conf->dh_file; params.openssl_ciphers = hapd->conf->openssl_ciphers; params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves; diff --git a/contrib/wpa/src/ap/beacon.c b/contrib/wpa/src/ap/beacon.c index 3e62991d07af..a51b94960bed 100644 --- a/contrib/wpa/src/ap/beacon.c +++ b/contrib/wpa/src/ap/beacon.c @@ -347,7 +347,7 @@ static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid) if (ieee80211_freq_to_channel_ext(hapd->iface->freq, hapd->iconf->secondary_channel, - hapd->iconf->vht_oper_chwidth, + hostapd_get_oper_chwidth(hapd->iconf), &op_class, &channel) == NUM_HOSTAPD_MODES) return eid; @@ -398,7 +398,8 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, if (hapd->iconf->ieee80211ax) { buflen += 3 + sizeof(struct ieee80211_he_capabilities) + 3 + sizeof(struct ieee80211_he_operation) + - 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set); + 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) + + 3 + sizeof(struct ieee80211_spatial_reuse); } #endif /* CONFIG_IEEE80211AX */ @@ -509,9 +510,10 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211AX if (hapd->iconf->ieee80211ax) { - pos = hostapd_eid_he_capab(hapd, pos); + pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP); pos = hostapd_eid_he_operation(hapd, pos); pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos); + pos = hostapd_eid_spatial_reuse(hapd, pos); } #endif /* CONFIG_IEEE80211AX */ @@ -593,7 +595,7 @@ static enum ssid_match_result ssid_match(struct hostapd_data *hapd, pos = ssid_list; end = ssid_list + ssid_list_len; - while (end - pos >= 1) { + while (end - pos >= 2) { if (2 + pos[1] > end - pos) break; if (pos[1] == 0) @@ -1088,7 +1090,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, if (hapd->iconf->ieee80211ax) { tail_len += 3 + sizeof(struct ieee80211_he_capabilities) + 3 + sizeof(struct ieee80211_he_operation) + - 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set); + 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) + + 3 + sizeof(struct ieee80211_spatial_reuse); } #endif /* CONFIG_IEEE80211AX */ @@ -1223,9 +1226,11 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211AX if (hapd->iconf->ieee80211ax) { - tailpos = hostapd_eid_he_capab(hapd, tailpos); + tailpos = hostapd_eid_he_capab(hapd, tailpos, + IEEE80211_MODE_AP); tailpos = hostapd_eid_he_operation(hapd, tailpos); tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos); + tailpos = hostapd_eid_spatial_reuse(hapd, tailpos); } #endif /* CONFIG_IEEE80211AX */ @@ -1394,6 +1399,7 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd) struct hostapd_freq_params freq; struct hostapd_iface *iface = hapd->iface; struct hostapd_config *iconf = iface->conf; + struct hostapd_hw_modes *cmode = iface->current_mode; struct wpabuf *beacon, *proberesp, *assocresp; int res, ret = -1; @@ -1417,15 +1423,16 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd) params.reenable = hapd->reenable_beacon; hapd->reenable_beacon = 0; - if (iface->current_mode && + if (cmode && hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq, iconf->channel, iconf->ieee80211n, - iconf->ieee80211ac, + iconf->ieee80211ac, iconf->ieee80211ax, iconf->secondary_channel, - iconf->vht_oper_chwidth, - iconf->vht_oper_centr_freq_seg0_idx, - iconf->vht_oper_centr_freq_seg1_idx, - iface->current_mode->vht_capab) == 0) + hostapd_get_oper_chwidth(iconf), + hostapd_get_oper_centr_freq_seg0_idx(iconf), + hostapd_get_oper_centr_freq_seg1_idx(iconf), + cmode->vht_capab, + &cmode->he_capab[IEEE80211_MODE_AP]) == 0) params.freq = &freq; res = hostapd_drv_set_ap(hapd, ¶ms); diff --git a/contrib/wpa/src/ap/ctrl_iface_ap.c b/contrib/wpa/src/ap/ctrl_iface_ap.c index c69371511634..2c4953d8bbcb 100644 --- a/contrib/wpa/src/ap/ctrl_iface_ap.c +++ b/contrib/wpa/src/ap/ctrl_iface_ap.c @@ -712,6 +712,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, "secondary_channel=%d\n" "ieee80211n=%d\n" "ieee80211ac=%d\n" + "ieee80211ax=%d\n" "beacon_int=%u\n" "dtim_period=%d\n", iface->conf->channel, @@ -720,6 +721,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, iface->conf->ieee80211n && !hapd->conf->disable_11n, iface->conf->ieee80211ac && !hapd->conf->disable_11ac, + iface->conf->ieee80211ax, iface->conf->beacon_int, hapd->conf->dtim_period); if (os_snprintf_error(buflen - len, ret)) diff --git a/contrib/wpa/src/ap/dfs.c b/contrib/wpa/src/ap/dfs.c index 79cd00f44a78..ac23c2b1bc9f 100644 --- a/contrib/wpa/src/ap/dfs.c +++ b/contrib/wpa/src/ap/dfs.c @@ -28,17 +28,17 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1) if (iface->conf->ieee80211n && iface->conf->secondary_channel) n_chans = 2; - if (iface->conf->ieee80211ac) { - switch (iface->conf->vht_oper_chwidth) { - case VHT_CHANWIDTH_USE_HT: + if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) { + switch (hostapd_get_oper_chwidth(iface->conf)) { + case CHANWIDTH_USE_HT: break; - case VHT_CHANWIDTH_80MHZ: + case CHANWIDTH_80MHZ: n_chans = 4; break; - case VHT_CHANWIDTH_160MHZ: + case CHANWIDTH_160MHZ: n_chans = 8; break; - case VHT_CHANWIDTH_80P80MHZ: + case CHANWIDTH_80P80MHZ: n_chans = 4; *seg1 = 4; break; @@ -188,8 +188,8 @@ static int is_in_chanlist(struct hostapd_iface *iface, * The function assumes HT40+ operation. * Make sure to adjust the following variables after calling this: * - hapd->secondary_channel - * - hapd->vht_oper_centr_freq_seg0_idx - * - hapd->vht_oper_centr_freq_seg1_idx + * - hapd->vht/he_oper_centr_freq_seg0_idx + * - hapd->vht/he_oper_centr_freq_seg1_idx */ static int dfs_find_channel(struct hostapd_iface *iface, struct hostapd_channel_data **ret_chan, @@ -232,44 +232,44 @@ static int dfs_find_channel(struct hostapd_iface *iface, } -static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface, - struct hostapd_channel_data *chan, - int secondary_channel, - u8 *vht_oper_centr_freq_seg0_idx, - u8 *vht_oper_centr_freq_seg1_idx) +static void dfs_adjust_center_freq(struct hostapd_iface *iface, + struct hostapd_channel_data *chan, + int secondary_channel, + u8 *oper_centr_freq_seg0_idx, + u8 *oper_centr_freq_seg1_idx) { - if (!iface->conf->ieee80211ac) + if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax) return; if (!chan) return; - *vht_oper_centr_freq_seg1_idx = 0; + *oper_centr_freq_seg1_idx = 0; - switch (iface->conf->vht_oper_chwidth) { - case VHT_CHANWIDTH_USE_HT: + switch (hostapd_get_oper_chwidth(iface->conf)) { + case CHANWIDTH_USE_HT: if (secondary_channel == 1) - *vht_oper_centr_freq_seg0_idx = chan->chan + 2; + *oper_centr_freq_seg0_idx = chan->chan + 2; else if (secondary_channel == -1) - *vht_oper_centr_freq_seg0_idx = chan->chan - 2; + *oper_centr_freq_seg0_idx = chan->chan - 2; else - *vht_oper_centr_freq_seg0_idx = chan->chan; + *oper_centr_freq_seg0_idx = chan->chan; break; - case VHT_CHANWIDTH_80MHZ: - *vht_oper_centr_freq_seg0_idx = chan->chan + 6; + case CHANWIDTH_80MHZ: + *oper_centr_freq_seg0_idx = chan->chan + 6; break; - case VHT_CHANWIDTH_160MHZ: - *vht_oper_centr_freq_seg0_idx = chan->chan + 14; + case CHANWIDTH_160MHZ: + *oper_centr_freq_seg0_idx = chan->chan + 14; break; default: wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now"); - *vht_oper_centr_freq_seg0_idx = 0; + *oper_centr_freq_seg0_idx = 0; break; } wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d", - *vht_oper_centr_freq_seg0_idx, - *vht_oper_centr_freq_seg1_idx); + *oper_centr_freq_seg0_idx, + *oper_centr_freq_seg1_idx); } @@ -288,24 +288,24 @@ static int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start) if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1) channel_no -= 4; - /* VHT */ - if (iface->conf->ieee80211ac) { - switch (iface->conf->vht_oper_chwidth) { - case VHT_CHANWIDTH_USE_HT: + /* VHT/HE */ + if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) { + switch (hostapd_get_oper_chwidth(iface->conf)) { + case CHANWIDTH_USE_HT: break; - case VHT_CHANWIDTH_80MHZ: - channel_no = - iface->conf->vht_oper_centr_freq_seg0_idx - 6; + case CHANWIDTH_80MHZ: + channel_no = hostapd_get_oper_centr_freq_seg0_idx( + iface->conf) - 6; break; - case VHT_CHANWIDTH_160MHZ: - channel_no = - iface->conf->vht_oper_centr_freq_seg0_idx - 14; + case CHANWIDTH_160MHZ: + channel_no = hostapd_get_oper_centr_freq_seg0_idx( + iface->conf) - 14; break; - case VHT_CHANWIDTH_80P80MHZ: - channel_no = - iface->conf->vht_oper_centr_freq_seg0_idx - 6; - chan_seg1 = - iface->conf->vht_oper_centr_freq_seg1_idx - 6; + case CHANWIDTH_80P80MHZ: + channel_no = hostapd_get_oper_centr_freq_seg0_idx( + iface->conf) - 6; + chan_seg1 = hostapd_get_oper_centr_freq_seg1_idx( + iface->conf) - 6; break; default: wpa_printf(MSG_INFO, @@ -348,7 +348,7 @@ static int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start) mode->num_channels, channel_no, iface->conf->channel, iface->conf->ieee80211n, iface->conf->secondary_channel, - iface->conf->vht_oper_chwidth); + hostapd_get_oper_chwidth(iface->conf)); for (i = 0; i < mode->num_channels; i++) { wpa_printf(MSG_DEBUG, "Available channel: %d", @@ -435,8 +435,8 @@ static int dfs_check_chans_unavailable(struct hostapd_iface *iface, static struct hostapd_channel_data * dfs_get_valid_channel(struct hostapd_iface *iface, int *secondary_channel, - u8 *vht_oper_centr_freq_seg0_idx, - u8 *vht_oper_centr_freq_seg1_idx, + u8 *oper_centr_freq_seg0_idx, + u8 *oper_centr_freq_seg1_idx, int skip_radar) { struct hostapd_hw_modes *mode; @@ -447,8 +447,8 @@ dfs_get_valid_channel(struct hostapd_iface *iface, wpa_printf(MSG_DEBUG, "DFS: Selecting random channel"); *secondary_channel = 0; - *vht_oper_centr_freq_seg0_idx = 0; - *vht_oper_centr_freq_seg1_idx = 0; + *oper_centr_freq_seg0_idx = 0; + *oper_centr_freq_seg1_idx = 0; if (iface->current_mode == NULL) return NULL; @@ -473,10 +473,10 @@ dfs_get_valid_channel(struct hostapd_iface *iface, else *secondary_channel = 0; - dfs_adjust_vht_center_freq(iface, chan, - *secondary_channel, - vht_oper_centr_freq_seg0_idx, - vht_oper_centr_freq_seg1_idx); + dfs_adjust_center_freq(iface, chan, + *secondary_channel, + oper_centr_freq_seg0_idx, + oper_centr_freq_seg1_idx); return chan; } @@ -724,8 +724,8 @@ int hostapd_handle_dfs(struct hostapd_iface *iface) iface->freq = channel->freq; iface->conf->channel = channel->chan; iface->conf->secondary_channel = sec; - iface->conf->vht_oper_centr_freq_seg0_idx = cf1; - iface->conf->vht_oper_centr_freq_seg1_idx = cf2; + hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1); + hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2); } } while (res); @@ -736,20 +736,19 @@ int hostapd_handle_dfs(struct hostapd_iface *iface) "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds", iface->freq, iface->conf->channel, iface->conf->secondary_channel, - iface->conf->vht_oper_chwidth, - iface->conf->vht_oper_centr_freq_seg0_idx, - iface->conf->vht_oper_centr_freq_seg1_idx, + hostapd_get_oper_chwidth(iface->conf), + hostapd_get_oper_centr_freq_seg0_idx(iface->conf), + hostapd_get_oper_centr_freq_seg1_idx(iface->conf), iface->dfs_cac_ms / 1000); - res = hostapd_start_dfs_cac(iface, iface->conf->hw_mode, - iface->freq, - iface->conf->channel, - iface->conf->ieee80211n, - iface->conf->ieee80211ac, - iface->conf->secondary_channel, - iface->conf->vht_oper_chwidth, - iface->conf->vht_oper_centr_freq_seg0_idx, - iface->conf->vht_oper_centr_freq_seg1_idx); + res = hostapd_start_dfs_cac( + iface, iface->conf->hw_mode, iface->freq, iface->conf->channel, + iface->conf->ieee80211n, iface->conf->ieee80211ac, + iface->conf->ieee80211ax, + iface->conf->secondary_channel, + hostapd_get_oper_chwidth(iface->conf), + hostapd_get_oper_centr_freq_seg0_idx(iface->conf), + hostapd_get_oper_centr_freq_seg1_idx(iface->conf)); if (res) { wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res); @@ -842,16 +841,16 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; int secondary_channel; - u8 vht_oper_centr_freq_seg0_idx = 0; - u8 vht_oper_centr_freq_seg1_idx = 0; + u8 oper_centr_freq_seg0_idx = 0; + u8 oper_centr_freq_seg1_idx = 0; int skip_radar = 0; int err = 1; /* Radar detected during active CAC */ iface->cac_started = 0; channel = dfs_get_valid_channel(iface, &secondary_channel, - &vht_oper_centr_freq_seg0_idx, - &vht_oper_centr_freq_seg1_idx, + &oper_centr_freq_seg0_idx, + &oper_centr_freq_seg1_idx, skip_radar); if (!channel) { @@ -868,10 +867,10 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) iface->freq = channel->freq; iface->conf->channel = channel->chan; iface->conf->secondary_channel = secondary_channel; - iface->conf->vht_oper_centr_freq_seg0_idx = - vht_oper_centr_freq_seg0_idx; - iface->conf->vht_oper_centr_freq_seg1_idx = - vht_oper_centr_freq_seg1_idx; + hostapd_set_oper_centr_freq_seg0_idx(iface->conf, + oper_centr_freq_seg0_idx); + hostapd_set_oper_centr_freq_seg1_idx(iface->conf, + oper_centr_freq_seg1_idx); err = 0; hostapd_setup_interface_complete(iface, err); @@ -883,12 +882,13 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; int secondary_channel; - u8 vht_oper_centr_freq_seg0_idx; - u8 vht_oper_centr_freq_seg1_idx; + u8 oper_centr_freq_seg0_idx; + u8 oper_centr_freq_seg1_idx; int skip_radar = 1; struct csa_settings csa_settings; unsigned int i; int err = 1; + struct hostapd_hw_modes *cmode = iface->current_mode; wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)", __func__, iface->cac_started ? "yes" : "no", @@ -911,8 +911,8 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) /* Perform channel switch/CSA */ channel = dfs_get_valid_channel(iface, &secondary_channel, - &vht_oper_centr_freq_seg0_idx, - &vht_oper_centr_freq_seg1_idx, + &oper_centr_freq_seg0_idx, + &oper_centr_freq_seg1_idx, skip_radar); if (!channel) { @@ -923,8 +923,8 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) */ skip_radar = 0; channel = dfs_get_valid_channel(iface, &secondary_channel, - &vht_oper_centr_freq_seg0_idx, - &vht_oper_centr_freq_seg1_idx, + &oper_centr_freq_seg0_idx, + &oper_centr_freq_seg1_idx, skip_radar); if (!channel) { wpa_printf(MSG_INFO, @@ -936,10 +936,10 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) iface->freq = channel->freq; iface->conf->channel = channel->chan; iface->conf->secondary_channel = secondary_channel; - iface->conf->vht_oper_centr_freq_seg0_idx = - vht_oper_centr_freq_seg0_idx; - iface->conf->vht_oper_centr_freq_seg1_idx = - vht_oper_centr_freq_seg1_idx; + hostapd_set_oper_centr_freq_seg0_idx(iface->conf, + oper_centr_freq_seg0_idx); + hostapd_set_oper_centr_freq_seg1_idx(iface->conf, + oper_centr_freq_seg1_idx); hostapd_disable_iface(iface); hostapd_enable_iface(iface); @@ -962,11 +962,13 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) channel->chan, iface->conf->ieee80211n, iface->conf->ieee80211ac, + iface->conf->ieee80211ax, secondary_channel, - iface->conf->vht_oper_chwidth, - vht_oper_centr_freq_seg0_idx, - vht_oper_centr_freq_seg1_idx, - iface->current_mode->vht_capab); + hostapd_get_oper_chwidth(iface->conf), + oper_centr_freq_seg0_idx, + oper_centr_freq_seg1_idx, + cmode->vht_capab, + &cmode->he_capab[IEEE80211_MODE_AP]); if (err) { wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params"); @@ -986,10 +988,10 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) iface->freq = channel->freq; iface->conf->channel = channel->chan; iface->conf->secondary_channel = secondary_channel; - iface->conf->vht_oper_centr_freq_seg0_idx = - vht_oper_centr_freq_seg0_idx; - iface->conf->vht_oper_centr_freq_seg1_idx = - vht_oper_centr_freq_seg1_idx; + hostapd_set_oper_centr_freq_seg0_idx(iface->conf, + oper_centr_freq_seg0_idx); + hostapd_set_oper_centr_freq_seg1_idx(iface->conf, + oper_centr_freq_seg1_idx); hostapd_disable_iface(iface); hostapd_enable_iface(iface); diff --git a/contrib/wpa/src/ap/dpp_hostapd.c b/contrib/wpa/src/ap/dpp_hostapd.c index 75edbc909e7a..697c3bad34ab 100644 --- a/contrib/wpa/src/ap/dpp_hostapd.c +++ b/contrib/wpa/src/ap/dpp_hostapd.c @@ -16,6 +16,7 @@ #include "hostapd.h" #include "ap_drv_ops.h" #include "gas_query_ap.h" +#include "gas_serv.h" #include "wpa_auth.h" #include "dpp_hostapd.h" @@ -557,6 +558,14 @@ static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src, * received hash values */ dpp_bootstrap_find_pair(hapd->iface->interfaces->dpp, i_bootstrap, r_bootstrap, &own_bi, &peer_bi); +#ifdef CONFIG_DPP2 + if (!own_bi) { + if (dpp_relay_rx_action(hapd->iface->interfaces->dpp, + src, hdr, buf, len, freq, i_bootstrap, + r_bootstrap) == 0) + return; + } +#endif /* CONFIG_DPP2 */ if (!own_bi) { wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "No matching own bootstrapping key found - ignore message"); @@ -1357,6 +1366,12 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR " freq=%u type=%d", MAC2STR(src), freq, type); +#ifdef CONFIG_DPP2 + if (dpp_relay_rx_action(hapd->iface->interfaces->dpp, + src, hdr, buf, len, freq, NULL, NULL) == 0) + return; +#endif /* CONFIG_DPP2 */ + switch (type) { case DPP_PA_AUTHENTICATION_REQ: hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq); @@ -1410,7 +1425,8 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, struct wpabuf * hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, - const u8 *query, size_t query_len) + const u8 *query, size_t query_len, + const u8 *data, size_t data_len) { struct dpp_authentication *auth = hapd->dpp_auth; struct wpabuf *resp; @@ -1418,6 +1434,13 @@ hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa)); if (!auth || !auth->auth_success || os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) { +#ifdef CONFIG_DPP2 + if (dpp_relay_rx_gas_req(hapd->iface->interfaces->dpp, sa, data, + data_len) == 0) { + /* Response will be forwarded once received over TCP */ + return NULL; + } +#endif /* CONFIG_DPP2 */ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); return NULL; } @@ -1609,11 +1632,67 @@ void hostapd_dpp_stop(struct hostapd_data *hapd) } +#ifdef CONFIG_DPP2 + +static void hostapd_dpp_relay_tx(void *ctx, const u8 *addr, unsigned int freq, + const u8 *msg, size_t len) +{ + struct hostapd_data *hapd = ctx; + u8 *buf; + + wpa_printf(MSG_DEBUG, "DPP: Send action frame dst=" MACSTR " freq=%u", + MAC2STR(addr), freq); + buf = os_malloc(2 + len); + if (!buf) + return; + buf[0] = WLAN_ACTION_PUBLIC; + buf[1] = WLAN_PA_VENDOR_SPECIFIC; + os_memcpy(buf + 2, msg, len); + hostapd_drv_send_action(hapd, freq, 0, addr, buf, 2 + len); + os_free(buf); +} + + +static void hostapd_dpp_relay_gas_resp_tx(void *ctx, const u8 *addr, + u8 dialog_token, int prot, + struct wpabuf *buf) +{ + struct hostapd_data *hapd = ctx; + + gas_serv_req_dpp_processing(hapd, addr, dialog_token, prot, buf); +} + +#endif /* CONFIG_DPP2 */ + + +static int hostapd_dpp_add_controllers(struct hostapd_data *hapd) +{ +#ifdef CONFIG_DPP2 + struct dpp_controller_conf *ctrl; + struct dpp_relay_config config; + + os_memset(&config, 0, sizeof(config)); + config.cb_ctx = hapd; + config.tx = hostapd_dpp_relay_tx; + config.gas_resp_tx = hostapd_dpp_relay_gas_resp_tx; + for (ctrl = hapd->conf->dpp_controller; ctrl; ctrl = ctrl->next) { + config.ipaddr = &ctrl->ipaddr; + config.pkhash = ctrl->pkhash; + if (dpp_relay_add_controller(hapd->iface->interfaces->dpp, + &config) < 0) + return -1; + } +#endif /* CONFIG_DPP2 */ + + return 0; +} + + int hostapd_dpp_init(struct hostapd_data *hapd) { hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE; hapd->dpp_init_done = 1; - return 0; + return hostapd_dpp_add_controllers(hapd); } diff --git a/contrib/wpa/src/ap/dpp_hostapd.h b/contrib/wpa/src/ap/dpp_hostapd.h index 449ca16d118f..c1ec5d70e411 100644 --- a/contrib/wpa/src/ap/dpp_hostapd.h +++ b/contrib/wpa/src/ap/dpp_hostapd.h @@ -19,7 +19,8 @@ void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, const u8 *data, size_t data_len, int ok); struct wpabuf * hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, - const u8 *query, size_t query_len); + const u8 *query, size_t query_len, + const u8 *data, size_t data_len); void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok); int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd); int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id); diff --git a/contrib/wpa/src/ap/drv_callbacks.c b/contrib/wpa/src/ap/drv_callbacks.c index 8ddf754f60a7..31587685fe3b 100644 --- a/contrib/wpa/src/ap/drv_callbacks.c +++ b/contrib/wpa/src/ap/drv_callbacks.c @@ -772,7 +772,8 @@ void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr, void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, - int offset, int width, int cf1, int cf2) + int offset, int width, int cf1, int cf2, + int finished) { /* TODO: If OCV is enabled deauth STAs that don't perform a SA Query */ @@ -783,10 +784,18 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, - "driver had channel switch: freq=%d, ht=%d, vht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d", + "driver %s channel switch: freq=%d, ht=%d, vht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d", + finished ? "had" : "starting", freq, ht, hapd->iconf->ch_switch_vht_config, offset, width, channel_width_to_string(width), cf1, cf2); + if (!hapd->iface->current_mode) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "ignore channel switch since the interface is not yet ready"); + return; + } + hapd->iface->freq = freq; channel = hostapd_hw_get_channel(hapd, freq); @@ -799,19 +808,19 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, switch (width) { case CHAN_WIDTH_80: - chwidth = VHT_CHANWIDTH_80MHZ; + chwidth = CHANWIDTH_80MHZ; break; case CHAN_WIDTH_80P80: - chwidth = VHT_CHANWIDTH_80P80MHZ; + chwidth = CHANWIDTH_80P80MHZ; break; case CHAN_WIDTH_160: - chwidth = VHT_CHANWIDTH_160MHZ; + chwidth = CHANWIDTH_160MHZ; break; case CHAN_WIDTH_20_NOHT: case CHAN_WIDTH_20: case CHAN_WIDTH_40: default: - chwidth = VHT_CHANWIDTH_USE_HT; + chwidth = CHANWIDTH_USE_HT; break; } @@ -844,13 +853,22 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, hapd->iconf->ch_switch_vht_config = 0; hapd->iconf->secondary_channel = offset; - hapd->iconf->vht_oper_chwidth = chwidth; - hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx; - hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx; + hostapd_set_oper_chwidth(hapd->iconf, chwidth); + hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx); + hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx); is_dfs = ieee80211_is_dfs(freq, hapd->iface->hw_features, hapd->iface->num_hw_features); + wpa_msg(hapd->msg_ctx, MSG_INFO, + "%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d dfs=%d", + finished ? WPA_EVENT_CHANNEL_SWITCH : + WPA_EVENT_CHANNEL_SWITCH_STARTED, + freq, ht, offset, channel_width_to_string(width), + cf1, cf2, is_dfs); + if (!finished) + return; + if (hapd->csa_in_progress && freq == hapd->cs_freq_params.freq) { hostapd_cleanup_cs_params(hapd); @@ -942,28 +960,31 @@ void hostapd_acs_channel_selected(struct hostapd_data *hapd, goto out; } - if (hapd->iface->conf->ieee80211ac) { + if (hapd->iface->conf->ieee80211ac || hapd->iface->conf->ieee80211ax) { /* set defaults for backwards compatibility */ - hapd->iconf->vht_oper_centr_freq_seg1_idx = 0; - hapd->iconf->vht_oper_centr_freq_seg0_idx = 0; - hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; + hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, 0); + hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, 0); + hostapd_set_oper_chwidth(hapd->iconf, CHANWIDTH_USE_HT); if (acs_res->ch_width == 80) { - hapd->iconf->vht_oper_centr_freq_seg0_idx = - acs_res->vht_seg0_center_ch; - hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ; + hostapd_set_oper_centr_freq_seg0_idx( + hapd->iconf, acs_res->vht_seg0_center_ch); + hostapd_set_oper_chwidth(hapd->iconf, CHANWIDTH_80MHZ); } else if (acs_res->ch_width == 160) { if (acs_res->vht_seg1_center_ch == 0) { - hapd->iconf->vht_oper_centr_freq_seg0_idx = - acs_res->vht_seg0_center_ch; - hapd->iconf->vht_oper_chwidth = - VHT_CHANWIDTH_160MHZ; + hostapd_set_oper_centr_freq_seg0_idx( + hapd->iconf, + acs_res->vht_seg0_center_ch); + hostapd_set_oper_chwidth(hapd->iconf, + CHANWIDTH_160MHZ); } else { - hapd->iconf->vht_oper_centr_freq_seg0_idx = - acs_res->vht_seg0_center_ch; - hapd->iconf->vht_oper_centr_freq_seg1_idx = - acs_res->vht_seg1_center_ch; - hapd->iconf->vht_oper_chwidth = - VHT_CHANWIDTH_80P80MHZ; + hostapd_set_oper_centr_freq_seg0_idx( + hapd->iconf, + acs_res->vht_seg0_center_ch); + hostapd_set_oper_centr_freq_seg1_idx( + hapd->iconf, + acs_res->vht_seg1_center_ch); + hostapd_set_oper_chwidth(hapd->iconf, + CHANWIDTH_80P80MHZ); } } } @@ -1568,6 +1589,73 @@ static void hostapd_event_wds_sta_interface_status(struct hostapd_data *hapd, } +#ifdef CONFIG_OWE +static int hostapd_notif_update_dh_ie(struct hostapd_data *hapd, + const u8 *peer, const u8 *ie, + size_t ie_len) +{ + u16 status; + struct sta_info *sta; + struct ieee802_11_elems elems; + + if (!hapd || !hapd->wpa_auth) { + wpa_printf(MSG_DEBUG, "OWE: Invalid hapd context"); + return -1; + } + if (!peer) { + wpa_printf(MSG_DEBUG, "OWE: Peer unknown"); + return -1; + } + if (!(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) { + wpa_printf(MSG_DEBUG, "OWE: No OWE AKM configured"); + status = WLAN_STATUS_AKMP_NOT_VALID; + goto err; + } + if (ieee802_11_parse_elems(ie, ie_len, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, "OWE: Failed to parse OWE IE for " + MACSTR, MAC2STR(peer)); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto err; + } + status = owe_validate_request(hapd, peer, elems.rsn_ie, + elems.rsn_ie_len, + elems.owe_dh, elems.owe_dh_len); + if (status != WLAN_STATUS_SUCCESS) + goto err; + + sta = ap_get_sta(hapd, peer); + if (sta) { + ap_sta_no_session_timeout(hapd, sta); + accounting_sta_stop(hapd, sta); + + /* + * Make sure that the previously registered inactivity timer + * will not remove the STA immediately. + */ + sta->timeout_next = STA_NULLFUNC; + } else { + sta = ap_sta_add(hapd, peer); + if (!sta) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto err; + } + } + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); + + status = owe_process_rsn_ie(hapd, sta, elems.rsn_ie, + elems.rsn_ie_len, elems.owe_dh, + elems.owe_dh_len); + if (status != WLAN_STATUS_SUCCESS) + ap_free_sta(hapd, sta); + + return 0; +err: + hostapd_drv_update_dh_ie(hapd, peer, status, NULL, 0); + return 0; +} +#endif /* CONFIG_OWE */ + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -1673,6 +1761,15 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->assoc_info.req_ies_len, data->assoc_info.reassoc); break; +#ifdef CONFIG_OWE + case EVENT_UPDATE_DH: + if (!data) + return; + hostapd_notif_update_dh_ie(hapd, data->update_dh.peer, + data->update_dh.ie, + data->update_dh.ie_len); + break; +#endif /* CONFIG_OWE */ case EVENT_DISASSOC: if (data) hostapd_notif_disassoc(hapd, data->disassoc_info.addr); @@ -1689,6 +1786,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, case EVENT_AUTH: hostapd_notif_auth(hapd, &data->auth); break; + case EVENT_CH_SWITCH_STARTED: case EVENT_CH_SWITCH: if (!data) break; @@ -1697,7 +1795,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->ch_switch.ch_offset, data->ch_switch.ch_width, data->ch_switch.cf1, - data->ch_switch.cf2); + data->ch_switch.cf2, + event == EVENT_CH_SWITCH); break; case EVENT_CONNECT_FAILED_REASON: if (!data) diff --git a/contrib/wpa/src/ap/gas_serv.c b/contrib/wpa/src/ap/gas_serv.c index a7df81032477..9567e202a7f0 100644 --- a/contrib/wpa/src/ap/gas_serv.c +++ b/contrib/wpa/src/ap/gas_serv.c @@ -1522,9 +1522,9 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, #ifdef CONFIG_DPP -static void gas_serv_req_dpp_processing(struct hostapd_data *hapd, - const u8 *sa, u8 dialog_token, - int prot, struct wpabuf *buf) +void gas_serv_req_dpp_processing(struct hostapd_data *hapd, + const u8 *sa, u8 dialog_token, + int prot, struct wpabuf *buf) { struct wpabuf *tx_buf; @@ -1681,7 +1681,8 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, if (dpp) { struct wpabuf *msg; - msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen); + msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen, + data, len); if (!msg) return; gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg); diff --git a/contrib/wpa/src/ap/gas_serv.h b/contrib/wpa/src/ap/gas_serv.h index 2cf1817298f7..1528af4afd91 100644 --- a/contrib/wpa/src/ap/gas_serv.h +++ b/contrib/wpa/src/ap/gas_serv.h @@ -88,4 +88,8 @@ void gas_serv_dialog_clear(struct gas_dialog_info *dialog); int gas_serv_init(struct hostapd_data *hapd); void gas_serv_deinit(struct hostapd_data *hapd); +void gas_serv_req_dpp_processing(struct hostapd_data *hapd, + const u8 *sa, u8 dialog_token, + int prot, struct wpabuf *buf); + #endif /* GAS_SERV_H */ diff --git a/contrib/wpa/src/ap/hostapd.c b/contrib/wpa/src/ap/hostapd.c index 20c8e8f5a4f7..bf1975fbd283 100644 --- a/contrib/wpa/src/ap/hostapd.c +++ b/contrib/wpa/src/ap/hostapd.c @@ -7,6 +7,9 @@ */ #include "utils/includes.h" +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ #include "utils/common.h" #include "utils/eloop.h" @@ -50,6 +53,8 @@ #include "fils_hlp.h" #include "acs.h" #include "hs20.h" +#include "airtime_policy.h" +#include "wpa_auth_kay.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); @@ -260,11 +265,14 @@ int hostapd_reload_config(struct hostapd_iface *iface) hapd->iconf->ieee80211ac = oldconf->ieee80211ac; hapd->iconf->ht_capab = oldconf->ht_capab; hapd->iconf->vht_capab = oldconf->vht_capab; - hapd->iconf->vht_oper_chwidth = oldconf->vht_oper_chwidth; - hapd->iconf->vht_oper_centr_freq_seg0_idx = - oldconf->vht_oper_centr_freq_seg0_idx; - hapd->iconf->vht_oper_centr_freq_seg1_idx = - oldconf->vht_oper_centr_freq_seg1_idx; + hostapd_set_oper_chwidth(hapd->iconf, + hostapd_get_oper_chwidth(oldconf)); + hostapd_set_oper_centr_freq_seg0_idx( + hapd->iconf, + hostapd_get_oper_centr_freq_seg0_idx(oldconf)); + hostapd_set_oper_centr_freq_seg1_idx( + hapd->iconf, + hostapd_get_oper_centr_freq_seg1_idx(oldconf)); hapd->conf = newconf->bss[j]; hostapd_reload_bss(hapd); } @@ -369,6 +377,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) #endif /* CONFIG_NO_RADIUS */ hostapd_deinit_wps(hapd); + ieee802_1x_dealloc_kay_sm_hapd(hapd); #ifdef CONFIG_DPP hostapd_dpp_deinit(hapd); gas_query_ap_deinit(hapd->gas); @@ -491,6 +500,7 @@ static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) iface->basic_rates = NULL; ap_list_deinit(iface); sta_track_deinit(iface); + airtime_policy_update_deinit(iface); } @@ -1018,6 +1028,43 @@ hostapd_das_coa(void *ctx, struct radius_das_attrs *attr) #define hostapd_das_coa NULL #endif /* CONFIG_HS20 */ + +#ifdef CONFIG_SQLITE + +static int db_table_exists(sqlite3 *db, const char *name) +{ + char cmd[128]; + + os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); + return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; +} + + +static int db_table_create_radius_attributes(sqlite3 *db) +{ + char *err = NULL; + const char *sql = + "CREATE TABLE radius_attributes(" + " id INTEGER PRIMARY KEY," + " sta TEXT," + " reqtype TEXT," + " attr TEXT" + ");" + "CREATE INDEX idx_sta_reqtype ON radius_attributes(sta,reqtype);"; + + wpa_printf(MSG_DEBUG, + "Adding database table for RADIUS attribute information"); + if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { + wpa_printf(MSG_ERROR, "SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + +#endif /* CONFIG_SQLITE */ + #endif /* CONFIG_NO_RADIUS */ @@ -1171,6 +1218,24 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) if (wpa_debug_level <= MSG_MSGDUMP) conf->radius->msg_dumps = 1; #ifndef CONFIG_NO_RADIUS + +#ifdef CONFIG_SQLITE + if (conf->radius_req_attr_sqlite) { + if (sqlite3_open(conf->radius_req_attr_sqlite, + &hapd->rad_attr_db)) { + wpa_printf(MSG_ERROR, "Could not open SQLite file '%s'", + conf->radius_req_attr_sqlite); + return -1; + } + + wpa_printf(MSG_DEBUG, "Opening RADIUS attribute database: %s", + conf->radius_req_attr_sqlite); + if (!db_table_exists(hapd->rad_attr_db, "radius_attributes") && + db_table_create_radius_attributes(hapd->rad_attr_db) < 0) + return -1; + } +#endif /* CONFIG_SQLITE */ + hapd->radius = radius_client_init(hapd, conf->radius); if (hapd->radius == NULL) { wpa_printf(MSG_ERROR, "RADIUS client initialization failed."); @@ -1863,10 +1928,13 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, hapd->iconf->channel, hapd->iconf->ieee80211n, hapd->iconf->ieee80211ac, + hapd->iconf->ieee80211ax, hapd->iconf->secondary_channel, - hapd->iconf->vht_oper_chwidth, - hapd->iconf->vht_oper_centr_freq_seg0_idx, - hapd->iconf->vht_oper_centr_freq_seg1_idx)) { + hostapd_get_oper_chwidth(hapd->iconf), + hostapd_get_oper_centr_freq_seg0_idx( + hapd->iconf), + hostapd_get_oper_centr_freq_seg1_idx( + hapd->iconf))) { wpa_printf(MSG_ERROR, "Could not set channel for " "kernel driver"); goto fail; @@ -1976,6 +2044,7 @@ dfs_offload: hostapd_set_state(iface, HAPD_IFACE_ENABLED); hostapd_owe_update_trans(iface); + airtime_policy_update_init(iface); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED); if (hapd->setup_complete_cb) hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); @@ -2183,6 +2252,12 @@ static void hostapd_bss_deinit(struct hostapd_data *hapd) hapd->conf ? hapd->conf->iface : "N/A"); hostapd_bss_deinit_no_free(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); +#ifdef CONFIG_SQLITE + if (hapd->rad_attr_db) { + sqlite3_close(hapd->rad_attr_db); + hapd->rad_attr_db = NULL; + } +#endif /* CONFIG_SQLITE */ hostapd_cleanup(hapd); } @@ -2486,8 +2561,12 @@ static void hostapd_deinit_driver(const struct wpa_driver_ops *driver, wpa_printf(MSG_DEBUG, "%s:bss[%d]->drv_priv=%p", __func__, (int) j, hapd_iface->bss[j]->drv_priv); - if (hapd_iface->bss[j]->drv_priv == drv_priv) + if (hapd_iface->bss[j]->drv_priv == drv_priv) { hapd_iface->bss[j]->drv_priv = NULL; + hapd_iface->extended_capa = NULL; + hapd_iface->extended_capa_mask = NULL; + hapd_iface->extended_capa_len = 0; + } } } } @@ -2992,6 +3071,8 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_P2P */ + airtime_policy_new_sta(hapd, sta); + /* Start accounting here, if IEEE 802.1X and WPA are not used. * IEEE 802.1X/WPA code will start accounting after the station has * been authorized. */ @@ -3032,6 +3113,14 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, ap_handle_timer, hapd, sta); } + +#ifdef CONFIG_MACSEC + if (hapd->conf->wpa_key_mgmt == WPA_KEY_MGMT_NONE && + hapd->conf->mka_psk_set) + ieee802_1x_create_preshared_mka_hapd(hapd, sta); + else + ieee802_1x_alloc_kay_sm_hapd(hapd, sta); +#endif /* CONFIG_MACSEC */ } @@ -3191,6 +3280,8 @@ static int hostapd_change_config_freq(struct hostapd_data *hapd, struct hostapd_freq_params *old_params) { int channel; + u8 seg0, seg1; + struct hostapd_hw_modes *mode; if (!params->channel) { /* check if the new channel is supported by hw */ @@ -3201,33 +3292,37 @@ static int hostapd_change_config_freq(struct hostapd_data *hapd, if (!channel) return -1; + mode = hapd->iface->current_mode; + /* if a pointer to old_params is provided we save previous state */ if (old_params && hostapd_set_freq_params(old_params, conf->hw_mode, hostapd_hw_get_freq(hapd, conf->channel), conf->channel, conf->ieee80211n, - conf->ieee80211ac, + conf->ieee80211ac, conf->ieee80211ax, conf->secondary_channel, - conf->vht_oper_chwidth, - conf->vht_oper_centr_freq_seg0_idx, - conf->vht_oper_centr_freq_seg1_idx, - conf->vht_capab)) + hostapd_get_oper_chwidth(conf), + hostapd_get_oper_centr_freq_seg0_idx(conf), + hostapd_get_oper_centr_freq_seg1_idx(conf), + conf->vht_capab, + mode ? &mode->he_capab[IEEE80211_MODE_AP] : + NULL)) return -1; switch (params->bandwidth) { case 0: case 20: case 40: - conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; + hostapd_set_oper_chwidth(conf, CHANWIDTH_USE_HT); break; case 80: if (params->center_freq2) - conf->vht_oper_chwidth = VHT_CHANWIDTH_80P80MHZ; + hostapd_set_oper_chwidth(conf, CHANWIDTH_80P80MHZ); else - conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ; + hostapd_set_oper_chwidth(conf, CHANWIDTH_80MHZ); break; case 160: - conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ; + hostapd_set_oper_chwidth(conf, CHANWIDTH_160MHZ); break; default: return -1; @@ -3237,9 +3332,11 @@ static int hostapd_change_config_freq(struct hostapd_data *hapd, conf->ieee80211n = params->ht_enabled; conf->secondary_channel = params->sec_channel_offset; ieee80211_freq_to_chan(params->center_freq1, - &conf->vht_oper_centr_freq_seg0_idx); + &seg0); ieee80211_freq_to_chan(params->center_freq2, - &conf->vht_oper_centr_freq_seg1_idx); + &seg1); + hostapd_set_oper_centr_freq_seg0_idx(conf, seg0); + hostapd_set_oper_centr_freq_seg1_idx(conf, seg1); /* TODO: maybe call here hostapd_config_check here? */ @@ -3253,7 +3350,7 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd, struct hostapd_iface *iface = hapd->iface; struct hostapd_freq_params old_freq; int ret; - u8 chan, vht_bandwidth; + u8 chan, bandwidth; os_memset(&old_freq, 0, sizeof(old_freq)); if (!iface || !iface->freq || hapd->csa_in_progress) @@ -3262,29 +3359,30 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd, switch (settings->freq_params.bandwidth) { case 80: if (settings->freq_params.center_freq2) - vht_bandwidth = VHT_CHANWIDTH_80P80MHZ; + bandwidth = CHANWIDTH_80P80MHZ; else - vht_bandwidth = VHT_CHANWIDTH_80MHZ; + bandwidth = CHANWIDTH_80MHZ; break; case 160: - vht_bandwidth = VHT_CHANWIDTH_160MHZ; + bandwidth = CHANWIDTH_160MHZ; break; default: - vht_bandwidth = VHT_CHANWIDTH_USE_HT; + bandwidth = CHANWIDTH_USE_HT; break; } if (ieee80211_freq_to_channel_ext( settings->freq_params.freq, settings->freq_params.sec_channel_offset, - vht_bandwidth, + bandwidth, &hapd->iface->cs_oper_class, &chan) == NUM_HOSTAPD_MODES) { wpa_printf(MSG_DEBUG, - "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d)", + "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d, he_enabled=%d)", settings->freq_params.freq, settings->freq_params.sec_channel_offset, - settings->freq_params.vht_enabled); + settings->freq_params.vht_enabled, + settings->freq_params.he_enabled); return -1; } @@ -3384,29 +3482,29 @@ void hostapd_switch_channel_fallback(struct hostapd_iface *iface, const struct hostapd_freq_params *freq_params) { - int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT; + int seg0_idx = 0, seg1_idx = 0, bw = CHANWIDTH_USE_HT; wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes"); if (freq_params->center_freq1) - vht_seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5; + seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5; if (freq_params->center_freq2) - vht_seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5; + seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5; switch (freq_params->bandwidth) { case 0: case 20: case 40: - vht_bw = VHT_CHANWIDTH_USE_HT; + bw = CHANWIDTH_USE_HT; break; case 80: if (freq_params->center_freq2) - vht_bw = VHT_CHANWIDTH_80P80MHZ; + bw = CHANWIDTH_80P80MHZ; else - vht_bw = VHT_CHANWIDTH_80MHZ; + bw = CHANWIDTH_80MHZ; break; case 160: - vht_bw = VHT_CHANWIDTH_160MHZ; + bw = CHANWIDTH_160MHZ; break; default: wpa_printf(MSG_WARNING, "Unknown CSA bandwidth: %d", @@ -3417,11 +3515,12 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface, iface->freq = freq_params->freq; iface->conf->channel = freq_params->channel; iface->conf->secondary_channel = freq_params->sec_channel_offset; - iface->conf->vht_oper_centr_freq_seg0_idx = vht_seg0_idx; - iface->conf->vht_oper_centr_freq_seg1_idx = vht_seg1_idx; - iface->conf->vht_oper_chwidth = vht_bw; + hostapd_set_oper_centr_freq_seg0_idx(iface->conf, seg0_idx); + hostapd_set_oper_centr_freq_seg1_idx(iface->conf, seg1_idx); + hostapd_set_oper_chwidth(iface->conf, bw); iface->conf->ieee80211n = freq_params->ht_enabled; iface->conf->ieee80211ac = freq_params->vht_enabled; + iface->conf->ieee80211ax = freq_params->he_enabled; /* * cs_params must not be cleared earlier because the freq_params diff --git a/contrib/wpa/src/ap/hostapd.h b/contrib/wpa/src/ap/hostapd.h index 790d37754870..518c7f10bc37 100644 --- a/contrib/wpa/src/ap/hostapd.h +++ b/contrib/wpa/src/ap/hostapd.h @@ -9,6 +9,10 @@ #ifndef HOSTAPD_H #define HOSTAPD_H +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ + #include "common/defs.h" #include "utils/list.h" #include "ap_config.h" @@ -232,6 +236,10 @@ struct hostapd_data { struct wps_stat wps_stats; #endif /* CONFIG_WPS */ +#ifdef CONFIG_MACSEC + struct ieee802_1x_kay *kay; +#endif /* CONFIG_MACSEC */ + struct hostapd_probereq_cb *probereq_cb; size_t num_probereq_cb; @@ -379,6 +387,17 @@ struct hostapd_data { unsigned int dpp_ignore_netaccesskey_mismatch:1; #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_DPP */ + +#ifdef CONFIG_AIRTIME_POLICY + unsigned int num_backlogged_sta; + unsigned int airtime_weight; +#endif /* CONFIG_AIRTIME_POLICY */ + + u8 last_1x_eapol_key_replay_counter[8]; + +#ifdef CONFIG_SQLITE + sqlite3 *rad_attr_db; +#endif /* CONFIG_SQLITE */ }; @@ -541,6 +560,12 @@ struct hostapd_iface { unsigned int num_sta_seen; u8 dfs_domain; +#ifdef CONFIG_AIRTIME_POLICY + unsigned int airtime_quantum; +#endif /* CONFIG_AIRTIME_POLICY */ + + /* Previous WMM element information */ + struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM]; }; /* hostapd.c */ @@ -607,7 +632,8 @@ int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len, int ssi_signal); void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, - int offset, int width, int cf1, int cf2); + int offset, int width, int cf1, int cf2, + int finished); struct survey_results; void hostapd_event_get_survey(struct hostapd_iface *iface, struct survey_results *survey_results); diff --git a/contrib/wpa/src/ap/hw_features.c b/contrib/wpa/src/ap/hw_features.c index 9d3d990a281f..c1f19e26b550 100644 --- a/contrib/wpa/src/ap/hw_features.c +++ b/contrib/wpa/src/ap/hw_features.c @@ -329,9 +329,9 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) res = ieee80211n_allowed_ht40_channel_pair(iface); if (!res) { iface->conf->secondary_channel = 0; - iface->conf->vht_oper_centr_freq_seg0_idx = 0; - iface->conf->vht_oper_centr_freq_seg1_idx = 0; - iface->conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; + hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0); + hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 0); + hostapd_set_oper_chwidth(iface->conf, CHANWIDTH_USE_HT); res = 1; wpa_printf(MSG_INFO, "Fallback to 20 MHz"); } @@ -655,6 +655,14 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) } #endif /* CONFIG_IEEE80211AC */ + +#ifdef CONFIG_IEEE80211AX +static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface) +{ + return 1; +} +#endif /* CONFIG_IEEE80211AX */ + #endif /* CONFIG_IEEE80211N */ @@ -675,6 +683,11 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) if (!ieee80211n_supported_ht_capab(iface)) return -1; +#ifdef CONFIG_IEEE80211AX + if (iface->conf->ieee80211ax && + !ieee80211ax_supported_he_capab(iface)) + return -1; +#endif /* CONFIG_IEEE80211AX */ #ifdef CONFIG_IEEE80211AC if (iface->conf->ieee80211ac && !ieee80211ac_supported_vht_capab(iface)) @@ -863,12 +876,14 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) return -1; if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G || - iface->conf->ieee80211n || iface->conf->ieee80211ac) && + iface->conf->ieee80211n || iface->conf->ieee80211ac || + iface->conf->ieee80211ax) && iface->conf->channel == 14) { - wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT on channel 14"); + wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT/HE on channel 14"); iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B; iface->conf->ieee80211n = 0; iface->conf->ieee80211ac = 0; + iface->conf->ieee80211ax = 0; } iface->current_mode = NULL; @@ -936,11 +951,16 @@ int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) int i, channel; struct hostapd_hw_modes *mode; - channel = hw_get_chan(hapd->iface->current_mode, freq); - if (channel) - return channel; + if (hapd->iface->current_mode) { + channel = hw_get_chan(hapd->iface->current_mode, freq); + if (channel) + return channel; + } + /* Check other available modes since the channel list for the current * mode did not include the specified frequency. */ + if (!hapd->iface->hw_features) + return 0; for (i = 0; i < hapd->iface->num_hw_features; i++) { mode = &hapd->iface->hw_features[i]; channel = hw_get_chan(mode, freq); diff --git a/contrib/wpa/src/ap/ieee802_11.c b/contrib/wpa/src/ap/ieee802_11.c index fde19b526f05..c85a28db44b7 100644 --- a/contrib/wpa/src/ap/ieee802_11.c +++ b/contrib/wpa/src/ap/ieee802_11.c @@ -23,6 +23,7 @@ #include "common/sae.h" #include "common/dpp.h" #include "common/ocv.h" +#include "common/wpa_common.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" @@ -709,7 +710,8 @@ static void sae_sme_send_external_auth_status(struct hostapd_data *hapd, os_memset(¶ms, 0, sizeof(params)); params.status = status; params.bssid = sta->addr; - if (status == WLAN_STATUS_SUCCESS && sta->sae) + if (status == WLAN_STATUS_SUCCESS && sta->sae && + !hapd->conf->disable_pmksa_caching) params.pmkid = sta->sae->pmkid; hostapd_drv_send_external_auth_status(hapd, ¶ms); @@ -1038,8 +1040,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "start SAE authentication (RX commit, status=%u)", - status_code); + "start SAE authentication (RX commit, status=%u (%s))", + status_code, status2str(status_code)); if ((hapd->conf->mesh & MESH_ENABLED) && status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ && @@ -1182,8 +1184,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } else if (auth_transaction == 2) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "SAE authentication (RX confirm, status=%u)", - status_code); + "SAE authentication (RX confirm, status=%u (%s))", + status_code, status2str(status_code)); if (status_code != WLAN_STATUS_SUCCESS) goto remove_sta; if (sta->sae->state >= SAE_CONFIRMED || @@ -1224,8 +1226,9 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "unexpected SAE authentication transaction %u (status=%u)", - auth_transaction, status_code); + "unexpected SAE authentication transaction %u (status=%u (%s))", + auth_transaction, status_code, + status2str(status_code)); if (status_code != WLAN_STATUS_SUCCESS) goto remove_sta; resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; @@ -2323,8 +2326,11 @@ static void handle_auth(struct hostapd_data *hapd, sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH | WLAN_STA_AUTHORIZED); - if (hostapd_sta_add(hapd, sta->addr, 0, 0, NULL, 0, 0, - NULL, NULL, sta->flags, 0, 0, 0, 0)) { + if (hostapd_sta_add(hapd, sta->addr, 0, 0, + sta->supported_rates, + sta->supported_rates_len, + 0, NULL, NULL, NULL, 0, + sta->flags, 0, 0, 0, 0)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, @@ -2790,6 +2796,123 @@ static u16 owe_process_assoc_req(struct hostapd_data *hapd, return WLAN_STATUS_SUCCESS; } + +u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer, + const u8 *rsn_ie, size_t rsn_ie_len, + const u8 *owe_dh, size_t owe_dh_len) +{ + struct wpa_ie_data data; + int res; + + if (!rsn_ie || rsn_ie_len < 2) { + wpa_printf(MSG_DEBUG, "OWE: Invalid RSNE from " MACSTR, + MAC2STR(peer)); + return WLAN_STATUS_INVALID_IE; + } + rsn_ie -= 2; + rsn_ie_len += 2; + + res = wpa_parse_wpa_ie_rsn(rsn_ie, rsn_ie_len, &data); + if (res) { + wpa_printf(MSG_DEBUG, "Failed to parse RSNE from " MACSTR + " (res=%d)", MAC2STR(peer), res); + wpa_hexdump(MSG_DEBUG, "RSNE", rsn_ie, rsn_ie_len); + return wpa_res_to_status_code(res); + } + if (!(data.key_mgmt & WPA_KEY_MGMT_OWE)) { + wpa_printf(MSG_DEBUG, + "OWE: Unexpected key mgmt 0x%x from " MACSTR, + (unsigned int) data.key_mgmt, MAC2STR(peer)); + return WLAN_STATUS_AKMP_NOT_VALID; + } + if (!owe_dh) { + wpa_printf(MSG_DEBUG, + "OWE: No Diffie-Hellman Parameter element from " + MACSTR, MAC2STR(peer)); + return WLAN_STATUS_AKMP_NOT_VALID; + } + + return WLAN_STATUS_SUCCESS; +} + + +u16 owe_process_rsn_ie(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *rsn_ie, size_t rsn_ie_len, + const u8 *owe_dh, size_t owe_dh_len) +{ + u16 status; + u8 *owe_buf, ie[256 * 2]; + size_t ie_len = 0; + int res; + + if (!rsn_ie || rsn_ie_len < 2) { + wpa_printf(MSG_DEBUG, "OWE: No RSNE in (Re)AssocReq"); + status = WLAN_STATUS_INVALID_IE; + goto end; + } + + if (!sta->wpa_sm) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, + NULL); + if (!sta->wpa_sm) { + wpa_printf(MSG_WARNING, + "OWE: Failed to initialize WPA state machine"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto end; + } + rsn_ie -= 2; + rsn_ie_len += 2; + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + hapd->iface->freq, rsn_ie, rsn_ie_len, + NULL, 0, owe_dh, owe_dh_len); + status = wpa_res_to_status_code(res); + if (status != WLAN_STATUS_SUCCESS) + goto end; + status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len); + if (status != WLAN_STATUS_SUCCESS) + goto end; + owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, ie, sizeof(ie), + NULL, 0); + if (!owe_buf) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto end; + } + + if (sta->owe_ecdh) { + struct wpabuf *pub; + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto end; + } + + /* OWE Diffie-Hellman Parameter element */ + *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */ + *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */ + *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension + */ + WPA_PUT_LE16(owe_buf, sta->owe_group); + owe_buf += 2; + os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub)); + owe_buf += wpabuf_len(pub); + wpabuf_free(pub); + sta->external_dh_updated = 1; + } + ie_len = owe_buf - ie; + +end: + wpa_printf(MSG_DEBUG, "OWE: Update status %d, ie len %d for peer " + MACSTR, status, (unsigned int) ie_len, + MAC2STR(sta->addr)); + hostapd_drv_update_dh_ie(hapd, sta->addr, status, + status == WLAN_STATUS_SUCCESS ? ie : NULL, + ie_len); + + return status; +} + #endif /* CONFIG_OWE */ @@ -2845,10 +2968,6 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) return resp; - resp = copy_sta_vht_oper(hapd, sta, elems.vht_operation); - if (resp != WLAN_STATUS_SUCCESS) - return resp; - resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif); if (resp != WLAN_STATUS_SUCCESS) return resp; @@ -2869,6 +2988,15 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, return resp; } #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP, + elems.he_capabilities, + elems.he_capabilities_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + } +#endif /* CONFIG_IEEE80211AX */ #ifdef CONFIG_P2P if (elems.p2p) { @@ -3231,6 +3359,7 @@ static int add_associated_sta(struct hostapd_data *hapd, { struct ieee80211_ht_capabilities ht_cap; struct ieee80211_vht_capabilities vht_cap; + struct ieee80211_he_capabilities he_cap; int set = 1; /* @@ -3283,6 +3412,12 @@ static int add_associated_sta(struct hostapd_data *hapd, if (sta->flags & WLAN_STA_VHT) hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + if (sta->flags & WLAN_STA_HE) { + hostapd_get_he_capab(hapd, sta->he_capab, &he_cap, + sta->he_capab_len); + } +#endif /* CONFIG_IEEE80211AX */ /* * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags @@ -3294,6 +3429,8 @@ static int add_associated_sta(struct hostapd_data *hapd, sta->listen_interval, sta->flags & WLAN_STA_HT ? &ht_cap : NULL, sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, + sta->flags & WLAN_STA_HE ? &he_cap : NULL, + sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0, sta->flags | WLAN_STA_ASSOC, sta->qosinfo, sta->vht_opmode, sta->p2p_ie ? 1 : 0, set)) { @@ -3331,6 +3468,8 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_FILS if (sta && sta->fils_hlp_resp) buflen += wpabuf_len(sta->fils_hlp_resp); + if (sta) + buflen += 150; #endif /* CONFIG_FILS */ #ifdef CONFIG_OWE if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) @@ -3392,6 +3531,15 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } } #endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_FILS + if (sta && status_code == WLAN_STATUS_SUCCESS && + (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK)) + p = wpa_auth_write_assoc_resp_fils(sta->wpa_sm, p, + buf + buflen - p, + ies, ies_len); +#endif /* CONFIG_FILS */ #ifdef CONFIG_OWE if (sta && status_code == WLAN_STATUS_SUCCESS && @@ -3434,6 +3582,15 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP); + p = hostapd_eid_he_operation(hapd, p); + p = hostapd_eid_spatial_reuse(hapd, p); + p = hostapd_eid_he_mu_edca_parameter_set(hapd, p); + } +#endif /* CONFIG_IEEE80211AX */ + p = hostapd_eid_ext_capab(hapd, p); p = hostapd_eid_bss_max_idle_period(hapd, p); if (sta && sta->qos_map_enabled) @@ -3610,6 +3767,12 @@ u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, return owe_buf; } + if (sta->owe_pmk && sta->external_dh_updated) { + wpa_printf(MSG_DEBUG, "OWE: Using previously derived PMK"); + *reason = WLAN_STATUS_SUCCESS; + return owe_buf; + } + *reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len); if (*reason != WLAN_STATUS_SUCCESS) return NULL; diff --git a/contrib/wpa/src/ap/ieee802_11.h b/contrib/wpa/src/ap/ieee802_11.h index db7badcfffaf..b8453c992a9c 100644 --- a/contrib/wpa/src/ap/ieee802_11.h +++ b/contrib/wpa/src/ap/ieee802_11.h @@ -18,6 +18,7 @@ struct ieee80211_vht_capabilities; struct ieee80211_mgmt; struct vlan_description; struct hostapd_sta_wpa_psk_short; +enum ieee80211_op_mode; int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, struct hostapd_frame_info *fi); @@ -57,9 +58,11 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid); -u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid, + enum ieee80211_op_mode opmode); u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid); int hostapd_ht_operation_update(struct hostapd_iface *iface); void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, @@ -70,6 +73,10 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd, void hostapd_get_vht_capab(struct hostapd_data *hapd, struct ieee80211_vht_capabilities *vht_cap, struct ieee80211_vht_capabilities *neg_vht_cap); +void hostapd_get_he_capab(struct hostapd_data *hapd, + const struct ieee80211_he_capabilities *he_cap, + struct ieee80211_he_capabilities *neg_he_cap, + size_t he_capab_len); int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta); u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab); @@ -85,6 +92,9 @@ u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_oper); u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_opmode); +u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta, + enum ieee80211_op_mode opmode, const u8 *he_capab, + size_t he_capab_len); void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, const u8 *buf, size_t len, int ack); void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, @@ -153,6 +163,12 @@ void ieee802_11_finish_fils_auth(struct hostapd_data *hapd, u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, const u8 *owe_dh, u8 owe_dh_len, u8 *owe_buf, size_t owe_buf_len, u16 *reason); +u16 owe_process_rsn_ie(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *rsn_ie, size_t rsn_ie_len, + const u8 *owe_dh, size_t owe_dh_len); +u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer, + const u8 *rsn_ie, size_t rsn_ie_len, + const u8 *owe_dh, size_t owe_dh_len); void fils_hlp_timeout(void *eloop_ctx, void *eloop_data); void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta); void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, diff --git a/contrib/wpa/src/ap/ieee802_11_he.c b/contrib/wpa/src/ap/ieee802_11_he.c index 072135863682..a51f3fcb0eae 100644 --- a/contrib/wpa/src/ap/ieee802_11_he.c +++ b/contrib/wpa/src/ap/ieee802_11_he.c @@ -1,6 +1,7 @@ /* * hostapd / IEEE 802.11ax HE * Copyright (c) 2016-2017, Qualcomm Atheros, Inc. + * Copyright (c) 2019 John Crispin * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,37 +14,113 @@ #include "hostapd.h" #include "ap_config.h" #include "beacon.h" +#include "sta_info.h" #include "ieee802_11.h" #include "dfs.h" -u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid) +static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info) +{ + u8 sz = 0, ru; + + if ((phy_cap_info[HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] & + HE_PHYCAP_PPE_THRESHOLD_PRESENT) == 0) + return 0; + + ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) & + HE_PPE_THRES_RU_INDEX_BITMASK_MASK; + while (ru) { + if (ru & 0x1) + sz++; + ru >>= 1; + } + + sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK); + sz = (sz * 6) + 7; + if (sz % 8) + sz += 8; + sz /= 8; + + return sz; +} + + +u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid, + enum ieee80211_op_mode opmode) { struct ieee80211_he_capabilities *cap; + struct hostapd_hw_modes *mode = hapd->iface->current_mode; + u8 he_oper_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK; u8 *pos = eid; + u8 ie_size = 0, mcs_nss_size = 0, ppet_size = 0; - if (!hapd->iface->current_mode) + if (!mode) return eid; + ie_size = sizeof(struct ieee80211_he_capabilities); + ppet_size = ieee80211_he_ppet_size(mode->he_capab[opmode].ppet[0], + mode->he_capab[opmode].phy_cap); + + switch (hapd->iface->conf->he_oper_chwidth) { + case CHANWIDTH_80P80MHZ: + he_oper_chwidth |= + HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G; + mcs_nss_size += 4; + /* fall through */ + case CHANWIDTH_160MHZ: + he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + mcs_nss_size += 4; + /* fall through */ + case CHANWIDTH_80MHZ: + case CHANWIDTH_USE_HT: + he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G | + HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; + mcs_nss_size += 4; + break; + } + + ie_size += mcs_nss_size + ppet_size; + *pos++ = WLAN_EID_EXTENSION; - *pos++ = 1 + sizeof(struct ieee80211_he_capabilities); + *pos++ = 1 + ie_size; *pos++ = WLAN_EID_EXT_HE_CAPABILITIES; cap = (struct ieee80211_he_capabilities *) pos; os_memset(cap, 0, sizeof(*cap)); + os_memcpy(cap->he_mac_capab_info, mode->he_capab[opmode].mac_cap, + HE_MAX_MAC_CAPAB_SIZE); + os_memcpy(cap->he_phy_capab_info, mode->he_capab[opmode].phy_cap, + HE_MAX_PHY_CAPAB_SIZE); + os_memcpy(cap->optional, mode->he_capab[opmode].mcs, mcs_nss_size); + if (ppet_size) + os_memcpy(&cap->optional[mcs_nss_size], + mode->he_capab[opmode].ppet, ppet_size); + if (hapd->iface->conf->he_phy_capab.he_su_beamformer) cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |= HE_PHYCAP_SU_BEAMFORMER_CAPAB; + else + cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] &= + ~HE_PHYCAP_SU_BEAMFORMER_CAPAB; if (hapd->iface->conf->he_phy_capab.he_su_beamformee) cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |= HE_PHYCAP_SU_BEAMFORMEE_CAPAB; + else + cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] &= + ~HE_PHYCAP_SU_BEAMFORMEE_CAPAB; if (hapd->iface->conf->he_phy_capab.he_mu_beamformer) cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |= HE_PHYCAP_MU_BEAMFORMER_CAPAB; + else + cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &= + ~HE_PHYCAP_MU_BEAMFORMER_CAPAB; - pos += sizeof(*cap); + cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &= + he_oper_chwidth; + + pos += ie_size; return pos; } @@ -53,36 +130,43 @@ u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid) { struct ieee80211_he_operation *oper; u8 *pos = eid; + int oper_size = 6; + u32 params = 0; if (!hapd->iface->current_mode) return eid; *pos++ = WLAN_EID_EXTENSION; - *pos++ = 1 + sizeof(struct ieee80211_he_operation); + *pos++ = 1 + oper_size; *pos++ = WLAN_EID_EXT_HE_OPERATION; oper = (struct ieee80211_he_operation *) pos; os_memset(oper, 0, sizeof(*oper)); - if (hapd->iface->conf->he_op.he_bss_color) - oper->he_oper_params |= hapd->iface->conf->he_op.he_bss_color; - if (hapd->iface->conf->he_op.he_default_pe_duration) - oper->he_oper_params |= - (hapd->iface->conf->he_op.he_default_pe_duration << - HE_OPERATION_DFLT_PE_DURATION_OFFSET); + params |= (hapd->iface->conf->he_op.he_default_pe_duration << + HE_OPERATION_DFLT_PE_DURATION_OFFSET); if (hapd->iface->conf->he_op.he_twt_required) - oper->he_oper_params |= HE_OPERATION_TWT_REQUIRED; + params |= HE_OPERATION_TWT_REQUIRED; if (hapd->iface->conf->he_op.he_rts_threshold) - oper->he_oper_params |= - (hapd->iface->conf->he_op.he_rts_threshold << - HE_OPERATION_RTS_THRESHOLD_OFFSET); + params |= (hapd->iface->conf->he_op.he_rts_threshold << + HE_OPERATION_RTS_THRESHOLD_OFFSET); + + if (hapd->iface->conf->he_op.he_bss_color) + params |= (hapd->iface->conf->he_op.he_bss_color << + HE_OPERATION_BSS_COLOR_OFFSET); + + /* HE minimum required basic MCS and NSS for STAs */ + oper->he_mcs_nss_set = + host_to_le16(hapd->iface->conf->he_op.he_basic_mcs_nss_set); /* TODO: conditional MaxBSSID Indicator subfield */ - pos += sizeof(*oper); + oper->he_oper_params = host_to_le32(params); + + pos += oper_size; return pos; } @@ -117,3 +201,148 @@ u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid) return pos; } + + +u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_spatial_reuse *spr; + u8 *pos = eid, *spr_param; + u8 sz = 1; + + if (!hapd->iface->conf->spr.sr_control) + return eid; + + if (hapd->iface->conf->spr.sr_control & + SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) + sz++; + + if (hapd->iface->conf->spr.sr_control & + SPATIAL_REUSE_SRG_INFORMATION_PRESENT) + sz += 18; + + *pos++ = WLAN_EID_EXTENSION; + *pos++ = 1 + sz; + *pos++ = WLAN_EID_EXT_SPATIAL_REUSE; + + spr = (struct ieee80211_spatial_reuse *) pos; + os_memset(spr, 0, sizeof(*spr)); + + spr->sr_ctrl = hapd->iface->conf->spr.sr_control; + pos++; + spr_param = spr->params; + if (spr->sr_ctrl & SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) { + *spr_param++ = + hapd->iface->conf->spr.non_srg_obss_pd_max_offset; + pos++; + } + if (spr->sr_ctrl & SPATIAL_REUSE_SRG_INFORMATION_PRESENT) { + *spr_param++ = hapd->iface->conf->spr.srg_obss_pd_min_offset; + *spr_param++ = hapd->iface->conf->spr.srg_obss_pd_max_offset; + pos += 18; + } + + return pos; +} + + +void hostapd_get_he_capab(struct hostapd_data *hapd, + const struct ieee80211_he_capabilities *he_cap, + struct ieee80211_he_capabilities *neg_he_cap, + size_t he_capab_len) +{ + if (!he_cap) + return; + + if (he_capab_len > sizeof(*neg_he_cap)) + he_capab_len = sizeof(*neg_he_cap); + /* TODO: mask out unsupported features */ + + os_memcpy(neg_he_cap, he_cap, he_capab_len); +} + + +static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab, + enum ieee80211_op_mode opmode) +{ + u16 sta_rx_mcs_set, ap_tx_mcs_set; + u8 mcs_count = 0; + const u16 *ap_mcs_set, *sta_mcs_set; + int i; + + if (!hapd->iface->current_mode) + return 1; + ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab[opmode].mcs; + sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *) + sta_he_capab)->optional; + + /* + * Disable HE capabilities for STAs for which there is not even a single + * allowed MCS in any supported number of streams, i.e., STA is + * advertising 3 (not supported) as HE MCS rates for all supported + * band/stream cases. + */ + switch (hapd->iface->conf->he_oper_chwidth) { + case CHANWIDTH_80P80MHZ: + mcs_count = 3; + break; + case CHANWIDTH_160MHZ: + mcs_count = 2; + break; + default: + mcs_count = 1; + break; + } + + for (i = 0; i < mcs_count; i++) { + int j; + + /* AP Tx MCS map vs. STA Rx MCS map */ + sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]); + ap_tx_mcs_set = WPA_GET_LE16((const u8 *) + &ap_mcs_set[(i * 2) + 1]); + + for (j = 0; j < HE_NSS_MAX_STREAMS; j++) { + if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3) + continue; + + if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3) + continue; + + return 1; + } + } + + wpa_printf(MSG_DEBUG, + "No matching HE MCS found between AP TX and STA RX"); + + return 0; +} + + +u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta, + enum ieee80211_op_mode opmode, const u8 *he_capab, + size_t he_capab_len) +{ + if (!he_capab || !hapd->iconf->ieee80211ax || + !check_valid_he_mcs(hapd, he_capab, opmode) || + he_capab_len > sizeof(struct ieee80211_he_capabilities)) { + sta->flags &= ~WLAN_STA_HE; + os_free(sta->he_capab); + sta->he_capab = NULL; + return WLAN_STATUS_SUCCESS; + } + + if (!sta->he_capab) { + sta->he_capab = + os_zalloc(sizeof(struct ieee80211_he_capabilities)); + if (!sta->he_capab) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->flags |= WLAN_STA_HE; + os_memset(sta->he_capab, 0, sizeof(struct ieee80211_he_capabilities)); + os_memcpy(sta->he_capab, he_capab, he_capab_len); + sta->he_capab_len = he_capab_len; + + return WLAN_STATUS_SUCCESS; +} diff --git a/contrib/wpa/src/ap/ieee802_11_vht.c b/contrib/wpa/src/ap/ieee802_11_vht.c index 54ee080a43f0..269345fbf85b 100644 --- a/contrib/wpa/src/ap/ieee802_11_vht.c +++ b/contrib/wpa/src/ap/ieee802_11_vht.c @@ -242,7 +242,7 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) return eid; switch (iface->conf->vht_oper_chwidth) { - case VHT_CHANWIDTH_USE_HT: + case CHANWIDTH_USE_HT: if (iconf->secondary_channel == 0) { /* Max Transmit Power count = 0 (20 MHz) */ tx_pwr_count = 0; @@ -251,12 +251,12 @@ u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) tx_pwr_count = 1; } break; - case VHT_CHANWIDTH_80MHZ: + case CHANWIDTH_80MHZ: /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */ tx_pwr_count = 2; break; - case VHT_CHANWIDTH_80P80MHZ: - case VHT_CHANWIDTH_160MHZ: + case CHANWIDTH_80P80MHZ: + case CHANWIDTH_160MHZ: /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */ tx_pwr_count = 3; break; diff --git a/contrib/wpa/src/ap/ieee802_1x.c b/contrib/wpa/src/ap/ieee802_1x.c index 97f503f75cc3..e0614710f6c3 100644 --- a/contrib/wpa/src/ap/ieee802_1x.c +++ b/contrib/wpa/src/ap/ieee802_1x.c @@ -7,6 +7,9 @@ */ #include "utils/includes.h" +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ #include "utils/common.h" #include "utils/eloop.h" @@ -34,6 +37,7 @@ /* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */ #include "ieee802_11.h" #include "ieee802_1x.h" +#include "wpa_auth_kay.h" #ifdef CONFIG_HS20 @@ -63,6 +67,10 @@ static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, xhdr = (struct ieee802_1x_hdr *) buf; xhdr->version = hapd->conf->eapol_version; +#ifdef CONFIG_MACSEC + if (xhdr->version > 2 && hapd->conf->macsec_policy == 0) + xhdr->version = 2; +#endif /* CONFIG_MACSEC */ xhdr->type = type; xhdr->length = host_to_be16(datalen); @@ -157,6 +165,21 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, key->type = EAPOL_KEY_TYPE_RC4; WPA_PUT_BE16(key->key_length, key_len); wpa_get_ntp_timestamp(key->replay_counter); + if (os_memcmp(key->replay_counter, + hapd->last_1x_eapol_key_replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN) <= 0) { + /* NTP timestamp did not increment from last EAPOL-Key frame; + * use previously used value + 1 instead. */ + inc_byte_array(hapd->last_1x_eapol_key_replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN); + os_memcpy(key->replay_counter, + hapd->last_1x_eapol_key_replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN); + } else { + os_memcpy(hapd->last_1x_eapol_key_replay_counter, + key->replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN); + } if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) { wpa_printf(MSG_ERROR, "Could not get random numbers"); @@ -197,6 +220,10 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, /* This header is needed here for HMAC-MD5, but it will be regenerated * in ieee802_1x_send() */ hdr->version = hapd->conf->eapol_version; +#ifdef CONFIG_MACSEC + if (hdr->version > 2) + hdr->version = 2; +#endif /* CONFIG_MACSEC */ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; hdr->length = host_to_be16(len); hmac_md5(sm->eap_if->eapKeyData + 32, 32, buf, sizeof(*hdr) + len, @@ -591,6 +618,63 @@ int add_common_radius_attr(struct hostapd_data *hapd, } +int add_sqlite_radius_attr(struct hostapd_data *hapd, struct sta_info *sta, + struct radius_msg *msg, int acct) +{ +#ifdef CONFIG_SQLITE + const char *attrtxt; + char addrtxt[3 * ETH_ALEN]; + char *sql; + sqlite3_stmt *stmt = NULL; + + if (!hapd->rad_attr_db) + return 0; + + os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, MAC2STR(sta->addr)); + + sql = "SELECT attr FROM radius_attributes WHERE sta=? AND (reqtype=? OR reqtype IS NULL);"; + if (sqlite3_prepare_v2(hapd->rad_attr_db, sql, os_strlen(sql), &stmt, + NULL) != SQLITE_OK) { + wpa_printf(MSG_ERROR, "DB: Failed to prepare SQL statement: %s", + sqlite3_errmsg(hapd->rad_attr_db)); + return -1; + } + sqlite3_bind_text(stmt, 1, addrtxt, os_strlen(addrtxt), SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, acct ? "acct" : "auth", 4, SQLITE_STATIC); + while (sqlite3_step(stmt) == SQLITE_ROW) { + struct hostapd_radius_attr *attr; + struct radius_attr_hdr *hdr; + + attrtxt = (const char *) sqlite3_column_text(stmt, 0); + attr = hostapd_parse_radius_attr(attrtxt); + if (!attr) { + wpa_printf(MSG_ERROR, + "Skipping invalid attribute from SQL: %s", + attrtxt); + continue; + } + wpa_printf(MSG_DEBUG, "Adding RADIUS attribute from SQL: %s", + attrtxt); + hdr = radius_msg_add_attr(msg, attr->type, + wpabuf_head(attr->val), + wpabuf_len(attr->val)); + hostapd_config_free_radius_attr(attr); + if (!hdr) { + wpa_printf(MSG_ERROR, + "Could not add RADIUS attribute from SQL"); + continue; + } + } + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + sqlite3_finalize(stmt); +#endif /* CONFIG_SQLITE */ + + return 0; +} + + void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, struct sta_info *sta, const u8 *eap, size_t len) @@ -630,6 +714,9 @@ void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, msg) < 0) goto fail; + if (sta && add_sqlite_radius_attr(hapd, sta, msg, 0) < 0) + goto fail; + /* TODO: should probably check MTU from driver config; 2304 is max for * IEEE 802.11, but use 1400 to avoid problems with too large packets */ @@ -1104,6 +1191,13 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, /* TODO: implement support for this; show data */ break; +#ifdef CONFIG_MACSEC + case IEEE802_1X_TYPE_EAPOL_MKA: + wpa_printf(MSG_EXCESSIVE, + "EAPOL type %d will be handled by MKA", hdr->type); + break; +#endif /* CONFIG_MACSEC */ + default: wpa_printf(MSG_DEBUG, " unknown IEEE 802.1X packet type"); sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++; @@ -1385,6 +1479,8 @@ static void ieee802_1x_get_keys(struct hostapd_data *hapd, size_t shared_secret_len) { struct radius_ms_mppe_keys *keys; + u8 *buf; + size_t len; struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL) return; @@ -1393,7 +1489,7 @@ static void ieee802_1x_get_keys(struct hostapd_data *hapd, shared_secret_len); if (keys && keys->send && keys->recv) { - size_t len = keys->send_len + keys->recv_len; + len = keys->send_len + keys->recv_len; wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key", keys->send, keys->send_len); wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key", @@ -1421,6 +1517,20 @@ static void ieee802_1x_get_keys(struct hostapd_data *hapd, os_free(keys->recv); os_free(keys); } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_EAP_KEY_NAME, &buf, &len, + NULL) == 0) { + os_free(sm->eap_if->eapSessionId); + sm->eap_if->eapSessionId = os_memdup(buf, len); + if (sm->eap_if->eapSessionId) { + sm->eap_if->eapSessionIdLen = len; + wpa_hexdump(MSG_DEBUG, "EAP-Key Name", + sm->eap_if->eapSessionId, + sm->eap_if->eapSessionIdLen); + } + } else { + sm->eap_if->eapSessionIdLen = 0; + } } @@ -2324,7 +2434,10 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.eap_fast_prov = hapd->conf->eap_fast_prov; conf.pac_key_lifetime = hapd->conf->pac_key_lifetime; conf.pac_key_refresh_time = hapd->conf->pac_key_refresh_time; + conf.eap_teap_auth = hapd->conf->eap_teap_auth; + conf.eap_teap_pac_no_inner = hapd->conf->eap_teap_pac_no_inner; conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind; + conf.eap_sim_id = hapd->conf->eap_sim_id; conf.tnc = hapd->conf->tnc; conf.wps = hapd->wps; conf.fragment_size = hapd->conf->fragment_size; @@ -2543,6 +2656,20 @@ const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len) } +#ifdef CONFIG_MACSEC +const u8 * ieee802_1x_get_session_id(struct eapol_state_machine *sm, + size_t *len) +{ + *len = 0; + if (!sm || !sm->eap_if) + return NULL; + + *len = sm->eap_if->eapSessionIdLen; + return sm->eap_if->eapSessionId; +} +#endif /* CONFIG_MACSEC */ + + void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, int enabled) { @@ -2833,6 +2960,10 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, } #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MACSEC + ieee802_1x_notify_create_actor_hapd(hapd, sta); +#endif /* CONFIG_MACSEC */ + key = ieee802_1x_get_key(sta->eapol_sm, &len); if (sta->session_timeout_set) { os_get_reltime(&now); diff --git a/contrib/wpa/src/ap/ieee802_1x.h b/contrib/wpa/src/ap/ieee802_1x.h index 9594661be8ee..bb85b93d69a5 100644 --- a/contrib/wpa/src/ap/ieee802_1x.h +++ b/contrib/wpa/src/ap/ieee802_1x.h @@ -39,6 +39,8 @@ u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, int idx); struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm); const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len); +const u8 * ieee802_1x_get_session_id(struct eapol_state_machine *sm, + size_t *len); void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, int enabled); void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, @@ -57,6 +59,8 @@ int add_common_radius_attr(struct hostapd_data *hapd, struct hostapd_radius_attr *req_attr, struct sta_info *sta, struct radius_msg *msg); +int add_sqlite_radius_attr(struct hostapd_data *hapd, struct sta_info *sta, + struct radius_msg *msg, int acct); void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, struct sta_info *sta, const u8 *eap, size_t len); diff --git a/contrib/wpa/src/ap/neighbor_db.c b/contrib/wpa/src/ap/neighbor_db.c index 2b6f72726b64..54154432286b 100644 --- a/contrib/wpa/src/ap/neighbor_db.c +++ b/contrib/wpa/src/ap/neighbor_db.c @@ -139,19 +139,21 @@ void hostapd_free_neighbor_db(struct hostapd_data *hapd) #ifdef NEED_AP_MLME static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd, - int ht, int vht) + int ht, int vht, int he) { - if (!ht && !vht) + u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf); + + if (!ht && !vht && !he) return NR_CHAN_WIDTH_20; if (!hapd->iconf->secondary_channel) return NR_CHAN_WIDTH_20; - if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT) + if ((!vht && !he) || oper_chwidth == CHANWIDTH_USE_HT) return NR_CHAN_WIDTH_40; - if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ) + if (oper_chwidth == CHANWIDTH_80MHZ) return NR_CHAN_WIDTH_80; - if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ) + if (oper_chwidth == CHANWIDTH_160MHZ) return NR_CHAN_WIDTH_160; - if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) + if (oper_chwidth == CHANWIDTH_80P80MHZ) return NR_CHAN_WIDTH_80P80; return NR_CHAN_WIDTH_20; } @@ -164,6 +166,7 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd) u16 capab = hostapd_own_capab_info(hapd); int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n; int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac; + int he = hapd->iconf->ieee80211ax; struct wpa_ssid_value ssid; u8 channel, op_class; u8 center_freq1_idx = 0, center_freq2_idx = 0; @@ -205,16 +208,18 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd) if (ieee80211_freq_to_channel_ext(hapd->iface->freq, hapd->iconf->secondary_channel, - hapd->iconf->vht_oper_chwidth, + hostapd_get_oper_chwidth(hapd->iconf), &op_class, &channel) == NUM_HOSTAPD_MODES) return; - width = hostapd_get_nr_chan_width(hapd, ht, vht); + width = hostapd_get_nr_chan_width(hapd, ht, vht, he); if (vht) { - center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx; + center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx( + hapd->iconf); if (width == NR_CHAN_WIDTH_80P80) center_freq2_idx = - hapd->iconf->vht_oper_centr_freq_seg1_idx; + hostapd_get_oper_centr_freq_seg1_idx( + hapd->iconf); } else if (ht) { ieee80211_freq_to_chan(hapd->iface->freq + 10 * hapd->iconf->secondary_channel, diff --git a/contrib/wpa/src/ap/sta_info.c b/contrib/wpa/src/ap/sta_info.c index 4f9eae8477b6..51d788436548 100644 --- a/contrib/wpa/src/ap/sta_info.c +++ b/contrib/wpa/src/ap/sta_info.c @@ -330,6 +330,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) os_free(sta->ht_capabilities); os_free(sta->vht_capabilities); os_free(sta->vht_operation); + os_free(sta->he_capab); hostapd_free_psk_list(sta->psk); os_free(sta->identity); os_free(sta->radius_cui); @@ -670,6 +671,7 @@ void ap_sta_session_warning_timeout(struct hostapd_data *hapd, struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta; + int i; sta = ap_get_sta(hapd, addr); if (sta) @@ -694,6 +696,15 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) return NULL; } + for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) { + if (!hapd->iface->basic_rates) + break; + if (hapd->iface->basic_rates[i] < 0) + break; + sta->supported_rates[i] = hapd->iface->basic_rates[i] / 5; + } + sta->supported_rates_len = i; + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " "for " MACSTR " (%d seconds - ap_max_inactivity)", diff --git a/contrib/wpa/src/ap/sta_info.h b/contrib/wpa/src/ap/sta_info.h index ece0c60abd36..5456a63a7c26 100644 --- a/contrib/wpa/src/ap/sta_info.h +++ b/contrib/wpa/src/ap/sta_info.h @@ -37,6 +37,7 @@ #define WLAN_STA_VENDOR_VHT BIT(21) #define WLAN_STA_PENDING_FILS_ERP BIT(22) #define WLAN_STA_MULTI_AP BIT(23) +#define WLAN_STA_HE BIT(24) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) @@ -119,6 +120,7 @@ struct sta_info { unsigned int agreed_to_steer:1; unsigned int hs20_t_c_filtering:1; unsigned int ft_over_ds:1; + unsigned int external_dh_updated:1; u16 auth_alg; @@ -166,6 +168,8 @@ struct sta_info { struct ieee80211_vht_capabilities *vht_capabilities; struct ieee80211_vht_operation *vht_operation; u8 vht_opmode; + struct ieee80211_he_capabilities *he_capab; + size_t he_capab_len; #ifdef CONFIG_IEEE80211W int sa_query_count; /* number of pending SA Query requests; @@ -275,6 +279,10 @@ struct sta_info { u8 last_tk[WPA_TK_MAX_LEN]; size_t last_tk_len; #endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_AIRTIME_POLICY + unsigned int airtime_weight; + struct os_reltime backlogged_until; +#endif /* CONFIG_AIRTIME_POLICY */ }; diff --git a/contrib/wpa/src/ap/wmm.c b/contrib/wpa/src/ap/wmm.c index 8054c5d2f243..dc734933738d 100644 --- a/contrib/wpa/src/ap/wmm.c +++ b/contrib/wpa/src/ap/wmm.c @@ -20,6 +20,13 @@ #include "ap_drv_ops.h" #include "wmm.h" +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci) { @@ -39,6 +46,62 @@ static inline u8 wmm_ecw(int ecwmin, int ecwmax) } +static void +wmm_set_regulatory_limit(const struct hostapd_wmm_ac_params *wmm_conf, + struct hostapd_wmm_ac_params *wmm, + const struct hostapd_wmm_rule *wmm_reg) +{ + int ac; + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + wmm[ac].cwmin = MAX(wmm_conf[ac].cwmin, wmm_reg[ac].min_cwmin); + wmm[ac].cwmax = MAX(wmm_conf[ac].cwmax, wmm_reg[ac].min_cwmax); + wmm[ac].aifs = MAX(wmm_conf[ac].aifs, wmm_reg[ac].min_aifs); + wmm[ac].txop_limit = + MIN(wmm_conf[ac].txop_limit, wmm_reg[ac].max_txop); + wmm[ac].admission_control_mandatory = + wmm_conf[ac].admission_control_mandatory; + } +} + + +/* + * Calculate WMM regulatory limit if any. + */ +static void wmm_calc_regulatory_limit(struct hostapd_data *hapd, + struct hostapd_wmm_ac_params *acp) +{ + struct hostapd_hw_modes *mode = hapd->iface->current_mode; + int c; + + os_memcpy(acp, hapd->iconf->wmm_ac_params, + sizeof(hapd->iconf->wmm_ac_params)); + + for (c = 0; mode && c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + + if (chan->freq != hapd->iface->freq) + continue; + + if (chan->wmm_rules_valid) + wmm_set_regulatory_limit(hapd->iconf->wmm_ac_params, + acp, chan->wmm_rules); + break; + } + + /* + * Check if we need to update set count. Since both were initialized to + * zero we can compare the whole array in one shot. + */ + if (os_memcmp(acp, hapd->iface->prev_wmm, + sizeof(hapd->iconf->wmm_ac_params)) != 0) { + os_memcpy(hapd->iface->prev_wmm, acp, + sizeof(hapd->iconf->wmm_ac_params)); + hapd->parameter_set_count++; + } +} + + /* * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association * Response frames. @@ -48,10 +111,12 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) u8 *pos = eid; struct wmm_parameter_element *wmm = (struct wmm_parameter_element *) (pos + 2); + struct hostapd_wmm_ac_params wmmp[WMM_AC_NUM] = { 0 }; int e; if (!hapd->conf->wmm_enabled) return eid; + wmm_calc_regulatory_limit(hapd, wmmp); eid[0] = WLAN_EID_VENDOR_SPECIFIC; wmm->oui[0] = 0x00; wmm->oui[1] = 0x50; @@ -70,8 +135,7 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) /* fill in a parameter set record for each AC */ for (e = 0; e < 4; e++) { struct wmm_ac_parameter *ac = &wmm->ac[e]; - struct hostapd_wmm_ac_params *acp = - &hapd->iconf->wmm_ac_params[e]; + struct hostapd_wmm_ac_params *acp = &wmmp[e]; ac->aci_aifsn = wmm_aci_aifsn(acp->aifs, acp->admission_control_mandatory, diff --git a/contrib/wpa/src/ap/wpa_auth.c b/contrib/wpa/src/ap/wpa_auth.c index f2e028c1599e..c56077001efa 100644 --- a/contrib/wpa/src/ap/wpa_auth.c +++ b/contrib/wpa/src/ap/wpa_auth.c @@ -934,6 +934,7 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN); os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); + forced_memzero(&PTK, sizeof(PTK)); sm->PTK_valid = TRUE; return 0; @@ -1407,6 +1408,8 @@ static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr, #endif /* CONFIG_SHA256 */ #endif /* CONFIG_SHA384 */ + forced_memzero(data, sizeof(data)); + return ret; } @@ -2046,7 +2049,7 @@ SM_STATE(WPA_PTK, INITPMK) sm->Disconnect = TRUE; return; } - os_memset(msk, 0, sizeof(msk)); + forced_memzero(msk, sizeof(msk)); sm->req_replay_counter_used = 0; /* IEEE 802.11i does not set keyRun to FALSE, but not doing this @@ -2285,12 +2288,12 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name); - os_memset(fils_ft, 0, sizeof(fils_ft)); + forced_memzero(fils_ft, sizeof(fils_ft)); res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder, sm->addr, sm->pmk_r1_name, use_sha384); - os_memset(pmk_r0, 0, PMK_LEN_MAX); + forced_memzero(pmk_r0, PMK_LEN_MAX); if (res < 0) return -1; wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name, @@ -2308,7 +2311,7 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, sm->wpa_key_mgmt, sm->fils_key_auth_sta, sm->fils_key_auth_ap, &sm->fils_key_auth_len); - os_memset(ick, 0, sizeof(ick)); + forced_memzero(ick, sizeof(ick)); /* Store nonces for (Re)Association Request/Response frame processing */ os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN); @@ -2610,7 +2613,7 @@ int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf, if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) { wpa_printf(MSG_DEBUG, "FILS: Not enough room for FILS elements"); - wpabuf_free(plain); + wpabuf_clear_free(plain); return -1; } @@ -2620,7 +2623,7 @@ int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf, if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, wpabuf_head(plain), wpabuf_len(plain), 5, aad, aad_len, pos) < 0) { - wpabuf_free(plain); + wpabuf_clear_free(plain); return -1; } @@ -2628,7 +2631,7 @@ int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf, "FILS: Encrypted Association Response elements", pos, AES_BLOCK_SIZE + wpabuf_len(plain)); current_len += wpabuf_len(plain) + AES_BLOCK_SIZE; - wpabuf_free(plain); + wpabuf_clear_free(plain); sm->fils_completed = 1; @@ -2682,7 +2685,7 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, * of GTK in the BSS. */ if (random_get_bytes(dummy_gtk, gtk_len) < 0) { - wpabuf_free(plain); + wpabuf_clear_free(plain); return NULL; } gtk = dummy_gtk; @@ -2709,13 +2712,13 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { wpa_printf(MSG_WARNING, "FILS: Failed to get channel info for OCI element"); - wpabuf_free(plain); + wpabuf_clear_free(plain); return NULL; } pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN); if (ocv_insert_extended_oci(&ci, pos) < 0) { - wpabuf_free(plain); + wpabuf_clear_free(plain); return NULL; } } @@ -2778,7 +2781,7 @@ u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf, wpa_printf(MSG_DEBUG, "%s: plain buf_len: %u", __func__, (unsigned int) wpabuf_len(plain)); - wpabuf_free(plain); + wpabuf_clear_free(plain); sm->fils_completed = 1; return pos; } @@ -3030,6 +3033,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) sm->MICVerified = TRUE; os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); + forced_memzero(&PTK, sizeof(PTK)); sm->PTK_valid = TRUE; } @@ -4246,8 +4250,12 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) /* Private MIB */ ret = os_snprintf(buf + len, buflen - len, + "wpa=%d\n" + "AKMSuiteSelector=" RSN_SUITE "\n" "hostapdWPAPTKState=%d\n" "hostapdWPAPTKGroupState=%d\n", + sm->wpa, + RSN_SUITE_ARG(wpa_akm_to_suite(sm->wpa_key_mgmt)), sm->wpa_ptk_state, sm->wpa_ptk_group_state); if (os_snprintf_error(buflen - len, ret)) @@ -4359,6 +4367,15 @@ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, sm->wpa_auth->conf.disable_pmksa_caching) return -1; +#ifdef CONFIG_IEEE80211R_AP + if (pmk_len >= 2 * PMK_LEN && wpa_key_mgmt_ft(sm->wpa_key_mgmt) && + wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && + !wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) { + /* Cache MPMK/XXKey instead of initial part from MSK */ + pmk = pmk + PMK_LEN; + pmk_len = PMK_LEN; + } else +#endif /* CONFIG_IEEE80211R_AP */ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) { if (pmk_len > PMK_LEN_SUITE_B_192) pmk_len = PMK_LEN_SUITE_B_192; @@ -4366,6 +4383,7 @@ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, pmk_len = PMK_LEN; } + wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK", pmk, pmk_len); if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL, sm->PTK.kck, sm->PTK.kck_len, sm->wpa_auth->addr, sm->addr, session_timeout, @@ -4384,6 +4402,7 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, if (wpa_auth == NULL) return -1; + wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from preauth", pmk, len); if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL, NULL, 0, wpa_auth->addr, @@ -4401,6 +4420,7 @@ int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, if (wpa_auth->conf.disable_pmksa_caching) return -1; + wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, PMK_LEN); if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid, NULL, 0, wpa_auth->addr, addr, 0, NULL, @@ -4425,6 +4445,7 @@ int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr, if (wpa_auth->conf.disable_pmksa_caching) return -1; + wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (2)", pmk, PMK_LEN); if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid, NULL, 0, wpa_auth->addr, addr, session_timeout, NULL, akmp)) diff --git a/contrib/wpa/src/ap/wpa_auth.h b/contrib/wpa/src/ap/wpa_auth.h index df1e17a003f8..a348bc25abd9 100644 --- a/contrib/wpa/src/ap/wpa_auth.h +++ b/contrib/wpa/src/ap/wpa_auth.h @@ -475,6 +475,9 @@ void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk, u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, u8 *pos, size_t max_len, const u8 *req_ies, size_t req_ies_len); +u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm, + u8 *pos, size_t max_len, + const u8 *req_ies, size_t req_ies_len); void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg); void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z); diff --git a/contrib/wpa/src/ap/wpa_auth_ft.c b/contrib/wpa/src/ap/wpa_auth_ft.c index ac16199a6006..696f8d5fa49b 100644 --- a/contrib/wpa/src/ap/wpa_auth_ft.c +++ b/contrib/wpa/src/ap/wpa_auth_ft.c @@ -25,6 +25,7 @@ #include "wmm.h" #include "wpa_auth.h" #include "wpa_auth_i.h" +#include "pmksa_cache_auth.h" #ifdef CONFIG_IEEE80211R_AP @@ -2094,8 +2095,16 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk) const u8 *identity, *radius_cui; size_t identity_len, radius_cui_len; int session_timeout; + const u8 *mpmk; + size_t mpmk_len; - if (sm->xxkey_len == 0) { + if (sm->xxkey_len > 0) { + mpmk = sm->xxkey; + mpmk_len = sm->xxkey_len; + } else if (sm->pmksa) { + mpmk = sm->pmksa->pmk; + mpmk_len = sm->pmksa->pmk_len; + } else { wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " "derivation"); return -1; @@ -2112,7 +2121,7 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk) &radius_cui); session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr); - if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, + if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid, r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name, wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0) @@ -2217,6 +2226,7 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) return NULL; } + forced_memzero(keybuf, sizeof(keybuf)); *len = subelem_len; return subelem; } @@ -3090,8 +3100,9 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, status = res; wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR - " auth_transaction=%d status=%d", - MAC2STR(sm->addr), auth_transaction + 1, status); + " auth_transaction=%d status=%u (%s)", + MAC2STR(sm->addr), auth_transaction + 1, status, + status2str(status)); wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); cb(ctx, sm->addr, bssid, auth_transaction + 1, status, resp_ies, resp_ies_len); @@ -3449,8 +3460,9 @@ static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm, u8 *pos; wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR - " CurrentAP=" MACSTR " status=%d", - MAC2STR(sm->addr), MAC2STR(current_ap), status); + " CurrentAP=" MACSTR " status=%u (%s)", + MAC2STR(sm->addr), MAC2STR(current_ap), status, + status2str(status)); wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); /* RRB - Forward action frame response to the Current AP */ @@ -3556,7 +3568,7 @@ static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len, pmk_r0->vlan, src_addr, type, packet, packet_len); - os_memset(pmk_r1, 0, sizeof(pmk_r1)); + forced_memzero(pmk_r1, sizeof(pmk_r1)); return ret; } @@ -3882,10 +3894,7 @@ static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, ret = 0; out: - if (plain) { - os_memset(plain, 0, plain_len); - os_free(plain); - } + bin_clear_free(plain, plain_len); return ret; diff --git a/contrib/wpa/src/ap/wpa_auth_glue.c b/contrib/wpa/src/ap/wpa_auth_glue.c index 45172c69a9fa..0800a874875a 100644 --- a/contrib/wpa/src/ap/wpa_auth_glue.c +++ b/contrib/wpa/src/ap/wpa_auth_glue.c @@ -53,6 +53,10 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->rsn_pairwise = conf->rsn_pairwise; wconf->rsn_preauth = conf->rsn_preauth; wconf->eapol_version = conf->eapol_version; +#ifdef CONFIG_MACSEC + if (wconf->eapol_version > 2) + wconf->eapol_version = 2; +#endif /* CONFIG_MACSEC */ wconf->wmm_enabled = conf->wmm_enabled; wconf->wmm_uapsd = conf->wmm_uapsd; wconf->disable_pmksa_caching = conf->disable_pmksa_caching; diff --git a/contrib/wpa/src/ap/wpa_auth_ie.c b/contrib/wpa/src/ap/wpa_auth_ie.c index 8580a5a69be8..2e5c9160d18f 100644 --- a/contrib/wpa/src/ap/wpa_auth_ie.c +++ b/contrib/wpa/src/ap/wpa_auth_ie.c @@ -1176,3 +1176,23 @@ u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, return pos + res; } #endif /* CONFIG_OWE */ + + +#ifdef CONFIG_FILS +u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm, + u8 *pos, size_t max_len, + const u8 *req_ies, size_t req_ies_len) +{ + int res; + + if (!sm || + sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA384)) + return pos; + + res = wpa_write_rsn_ie(&sm->wpa_auth->conf, pos, max_len, NULL); + if (res < 0) + return pos; + return pos + res; +} +#endif /* CONFIG_FILS */ diff --git a/contrib/wpa/src/ap/wpa_auth_kay.c b/contrib/wpa/src/ap/wpa_auth_kay.c new file mode 100644 index 000000000000..b6e47979bea8 --- /dev/null +++ b/contrib/wpa/src/ap/wpa_auth_kay.c @@ -0,0 +1,523 @@ +/* + * IEEE 802.1X-2010 KaY Interface + * Copyright (c) 2019, The Linux Foundation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "pae/ieee802_1x_key.h" +#include "pae/ieee802_1x_kay.h" +#include "hostapd.h" +#include "sta_info.h" +#include "wpa_auth_kay.h" +#include "ieee802_1x.h" + + +#define DEFAULT_KEY_LEN 16 +/* secure Connectivity Association Key Name (CKN) */ +#define DEFAULT_CKN_LEN 16 + + +static int hapd_macsec_init(void *priv, struct macsec_init_params *params) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->macsec_init) + return -1; + return hapd->driver->macsec_init(hapd->drv_priv, params); +} + + +static int hapd_macsec_deinit(void *priv) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->macsec_deinit) + return -1; + return hapd->driver->macsec_deinit(hapd->drv_priv); +} + + +static int hapd_macsec_get_capability(void *priv, enum macsec_cap *cap) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->macsec_get_capability) + return -1; + return hapd->driver->macsec_get_capability(hapd->drv_priv, cap); +} + + +static int hapd_enable_protect_frames(void *priv, Boolean enabled) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->enable_protect_frames) + return -1; + return hapd->driver->enable_protect_frames(hapd->drv_priv, enabled); +} + + +static int hapd_enable_encrypt(void *priv, Boolean enabled) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->enable_encrypt) + return -1; + return hapd->driver->enable_encrypt(hapd->drv_priv, enabled); +} + + +static int hapd_set_replay_protect(void *priv, Boolean enabled, u32 window) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->set_replay_protect) + return -1; + return hapd->driver->set_replay_protect(hapd->drv_priv, enabled, + window); +} + + +static int hapd_set_current_cipher_suite(void *priv, u64 cs) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->set_current_cipher_suite) + return -1; + return hapd->driver->set_current_cipher_suite(hapd->drv_priv, cs); +} + + +static int hapd_enable_controlled_port(void *priv, Boolean enabled) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->enable_controlled_port) + return -1; + return hapd->driver->enable_controlled_port(hapd->drv_priv, enabled); +} + + +static int hapd_get_receive_lowest_pn(void *priv, struct receive_sa *sa) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->get_receive_lowest_pn) + return -1; + return hapd->driver->get_receive_lowest_pn(hapd->drv_priv, sa); +} + + +static int hapd_get_transmit_next_pn(void *priv, struct transmit_sa *sa) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->get_transmit_next_pn) + return -1; + return hapd->driver->get_transmit_next_pn(hapd->drv_priv, sa); +} + + +static int hapd_set_transmit_next_pn(void *priv, struct transmit_sa *sa) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->set_transmit_next_pn) + return -1; + return hapd->driver->set_transmit_next_pn(hapd->drv_priv, sa); +} + + +static unsigned int conf_offset_val(enum confidentiality_offset co) +{ + switch (co) { + case CONFIDENTIALITY_OFFSET_30: + return 30; + break; + case CONFIDENTIALITY_OFFSET_50: + return 50; + default: + return 0; + } +} + + +static int hapd_create_receive_sc(void *priv, struct receive_sc *sc, + enum validate_frames vf, + enum confidentiality_offset co) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->create_receive_sc) + return -1; + return hapd->driver->create_receive_sc(hapd->drv_priv, sc, + conf_offset_val(co), vf); +} + + +static int hapd_delete_receive_sc(void *priv, struct receive_sc *sc) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->delete_receive_sc) + return -1; + return hapd->driver->delete_receive_sc(hapd->drv_priv, sc); +} + + +static int hapd_create_receive_sa(void *priv, struct receive_sa *sa) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->create_receive_sa) + return -1; + return hapd->driver->create_receive_sa(hapd->drv_priv, sa); +} + + +static int hapd_delete_receive_sa(void *priv, struct receive_sa *sa) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->delete_receive_sa) + return -1; + return hapd->driver->delete_receive_sa(hapd->drv_priv, sa); +} + + +static int hapd_enable_receive_sa(void *priv, struct receive_sa *sa) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->enable_receive_sa) + return -1; + return hapd->driver->enable_receive_sa(hapd->drv_priv, sa); +} + + +static int hapd_disable_receive_sa(void *priv, struct receive_sa *sa) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->disable_receive_sa) + return -1; + return hapd->driver->disable_receive_sa(hapd->drv_priv, sa); +} + + +static int +hapd_create_transmit_sc(void *priv, struct transmit_sc *sc, + enum confidentiality_offset co) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->create_transmit_sc) + return -1; + return hapd->driver->create_transmit_sc(hapd->drv_priv, sc, + conf_offset_val(co)); +} + + +static int hapd_delete_transmit_sc(void *priv, struct transmit_sc *sc) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->delete_transmit_sc) + return -1; + return hapd->driver->delete_transmit_sc(hapd->drv_priv, sc); +} + + +static int hapd_create_transmit_sa(void *priv, struct transmit_sa *sa) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->create_transmit_sa) + return -1; + return hapd->driver->create_transmit_sa(hapd->drv_priv, sa); +} + + +static int hapd_delete_transmit_sa(void *priv, struct transmit_sa *sa) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->delete_transmit_sa) + return -1; + return hapd->driver->delete_transmit_sa(hapd->drv_priv, sa); +} + + +static int hapd_enable_transmit_sa(void *priv, struct transmit_sa *sa) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->enable_transmit_sa) + return -1; + return hapd->driver->enable_transmit_sa(hapd->drv_priv, sa); +} + + +static int hapd_disable_transmit_sa(void *priv, struct transmit_sa *sa) +{ + struct hostapd_data *hapd = priv; + + if (!hapd->driver->disable_transmit_sa) + return -1; + return hapd->driver->disable_transmit_sa(hapd->drv_priv, sa); +} + + +int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct ieee802_1x_kay_ctx *kay_ctx; + struct ieee802_1x_kay *res = NULL; + enum macsec_policy policy; + + ieee802_1x_dealloc_kay_sm_hapd(hapd); + + if (!hapd->conf || hapd->conf->macsec_policy == 0) + return 0; + + if (hapd->conf->macsec_policy == 1) { + if (hapd->conf->macsec_integ_only == 1) + policy = SHOULD_SECURE; + else + policy = SHOULD_ENCRYPT; + } else { + policy = DO_NOT_SECURE; + } + + wpa_printf(MSG_DEBUG, "%s: if_name=%s", __func__, hapd->conf->iface); + kay_ctx = os_zalloc(sizeof(*kay_ctx)); + if (!kay_ctx) + return -1; + + kay_ctx->ctx = hapd; + + kay_ctx->macsec_init = hapd_macsec_init; + kay_ctx->macsec_deinit = hapd_macsec_deinit; + kay_ctx->macsec_get_capability = hapd_macsec_get_capability; + kay_ctx->enable_protect_frames = hapd_enable_protect_frames; + kay_ctx->enable_encrypt = hapd_enable_encrypt; + kay_ctx->set_replay_protect = hapd_set_replay_protect; + kay_ctx->set_current_cipher_suite = hapd_set_current_cipher_suite; + kay_ctx->enable_controlled_port = hapd_enable_controlled_port; + kay_ctx->get_receive_lowest_pn = hapd_get_receive_lowest_pn; + kay_ctx->get_transmit_next_pn = hapd_get_transmit_next_pn; + kay_ctx->set_transmit_next_pn = hapd_set_transmit_next_pn; + kay_ctx->create_receive_sc = hapd_create_receive_sc; + kay_ctx->delete_receive_sc = hapd_delete_receive_sc; + kay_ctx->create_receive_sa = hapd_create_receive_sa; + kay_ctx->delete_receive_sa = hapd_delete_receive_sa; + kay_ctx->enable_receive_sa = hapd_enable_receive_sa; + kay_ctx->disable_receive_sa = hapd_disable_receive_sa; + kay_ctx->create_transmit_sc = hapd_create_transmit_sc; + kay_ctx->delete_transmit_sc = hapd_delete_transmit_sc; + kay_ctx->create_transmit_sa = hapd_create_transmit_sa; + kay_ctx->delete_transmit_sa = hapd_delete_transmit_sa; + kay_ctx->enable_transmit_sa = hapd_enable_transmit_sa; + kay_ctx->disable_transmit_sa = hapd_disable_transmit_sa; + + res = ieee802_1x_kay_init(kay_ctx, policy, + hapd->conf->macsec_replay_protect, + hapd->conf->macsec_replay_window, + hapd->conf->macsec_port, + hapd->conf->mka_priority, hapd->conf->iface, + hapd->own_addr); + /* ieee802_1x_kay_init() frees kay_ctx on failure */ + if (!res) + return -1; + + hapd->kay = res; + + return 0; +} + + +void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd) +{ + if (!hapd->kay) + return; + + ieee802_1x_kay_deinit(hapd->kay); + hapd->kay = NULL; +} + + +static int ieee802_1x_auth_get_session_id(struct hostapd_data *hapd, + struct sta_info *sta, u8 *sid, + size_t *len) +{ + const u8 *session_id; + size_t id_len, need_len; + + session_id = ieee802_1x_get_session_id(sta->eapol_sm, &id_len); + if (!session_id) { + wpa_printf(MSG_DEBUG, + "MACsec: Failed to get SessionID from EAPOL state machines"); + return -1; + } + + need_len = 1 + 2 * 32 /* random size */; + if (need_len > id_len) { + wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough"); + return -1; + } + + os_memcpy(sid, session_id, need_len); + *len = need_len; + + return 0; +} + + +static int ieee802_1x_auth_get_msk(struct hostapd_data *hapd, + struct sta_info *sta, u8 *msk, size_t *len) +{ + const u8 *key; + size_t keylen; + + if (!sta->eapol_sm) + return -1; + + key = ieee802_1x_get_key(sta->eapol_sm, &keylen); + if (key == NULL) { + wpa_printf(MSG_DEBUG, + "MACsec: Failed to get MSK from EAPOL state machines"); + return -1; + } + wpa_printf(MSG_DEBUG, "MACsec: Successfully fetched key (len=%lu)", + (unsigned long) keylen); + wpa_hexdump_key(MSG_DEBUG, "MSK: ", key, keylen); + + if (keylen > *len) + keylen = *len; + os_memcpy(msk, key, keylen); + *len = keylen; + + return 0; +} + + +void * ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd, + struct sta_info *sta) +{ + u8 *sid; + size_t sid_len = 128; + struct mka_key_name *ckn; + struct mka_key *cak; + struct mka_key *msk; + void *res = NULL; + + if (!hapd->kay || hapd->kay->policy == DO_NOT_SECURE) + return NULL; + + wpa_printf(MSG_DEBUG, + "IEEE 802.1X: External notification - Create MKA for " + MACSTR, MAC2STR(sta->addr)); + + msk = os_zalloc(sizeof(*msk)); + sid = os_zalloc(sid_len); + ckn = os_zalloc(sizeof(*ckn)); + cak = os_zalloc(sizeof(*cak)); + if (!msk || !sid || !ckn || !cak) + goto fail; + + msk->len = DEFAULT_KEY_LEN; + if (ieee802_1x_auth_get_msk(hapd, sta, msk->key, &msk->len)) { + wpa_printf(MSG_ERROR, "IEEE 802.1X: Could not get MSK"); + goto fail; + } + + if (ieee802_1x_auth_get_session_id(hapd, sta, sid, &sid_len)) + { + wpa_printf(MSG_ERROR, + "IEEE 802.1X: Could not get EAP Session Id"); + goto fail; + } + + wpa_hexdump(MSG_DEBUG, "own_addr", hapd->own_addr, ETH_ALEN); + wpa_hexdump(MSG_DEBUG, "sta_addr", sta->addr, ETH_ALEN); + + /* Derive CAK from MSK */ + cak->len = DEFAULT_KEY_LEN; + if (ieee802_1x_cak_aes_cmac(msk->key, msk->len, hapd->own_addr, + sta->addr, cak->key, cak->len)) { + wpa_printf(MSG_ERROR, "IEEE 802.1X: Deriving CAK failed"); + goto fail; + } + wpa_hexdump_key(MSG_DEBUG, "Derived CAK", cak->key, cak->len); + + /* Derive CKN from MSK */ + ckn->len = DEFAULT_CKN_LEN; + if (ieee802_1x_ckn_aes_cmac(msk->key, msk->len, hapd->own_addr, + sta->addr, sid, sid_len, ckn->name)) { + wpa_printf(MSG_ERROR, "IEEE 802.1X: Deriving CKN failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "Derived CKN", ckn->name, ckn->len); + + res = ieee802_1x_kay_create_mka(hapd->kay, ckn, cak, 0, EAP_EXCHANGE, + TRUE); + +fail: + bin_clear_free(msk, sizeof(*msk)); + os_free(sid); + os_free(ckn); + bin_clear_free(cak, sizeof(*cak)); + + return res; +} + + +void * ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct mka_key *cak; + struct mka_key_name *ckn; + void *res = NULL; + + if ((hapd->conf->mka_psk_set & MKA_PSK_SET) != MKA_PSK_SET) + goto end; + + ckn = os_zalloc(sizeof(*ckn)); + if (!ckn) + goto end; + + cak = os_zalloc(sizeof(*cak)); + if (!cak) + goto free_ckn; + + if (ieee802_1x_alloc_kay_sm_hapd(hapd, sta) < 0 || !hapd->kay) + goto free_cak; + + if (hapd->kay->policy == DO_NOT_SECURE) + goto dealloc; + + cak->len = hapd->conf->mka_cak_len; + os_memcpy(cak->key, hapd->conf->mka_cak, cak->len); + + ckn->len = hapd->conf->mka_ckn_len;; + os_memcpy(ckn->name, hapd->conf->mka_ckn, ckn->len); + + res = ieee802_1x_kay_create_mka(hapd->kay, ckn, cak, 0, PSK, TRUE); + if (res) + goto free_cak; + +dealloc: + /* Failed to create MKA */ + ieee802_1x_dealloc_kay_sm_hapd(hapd); +free_cak: + os_free(cak); +free_ckn: + os_free(ckn); +end: + return res; +} diff --git a/contrib/wpa/src/ap/wpa_auth_kay.h b/contrib/wpa/src/ap/wpa_auth_kay.h new file mode 100644 index 000000000000..0dd7e41248d9 --- /dev/null +++ b/contrib/wpa/src/ap/wpa_auth_kay.h @@ -0,0 +1,51 @@ +/* + * IEEE 802.1X-2010 KaY Interface + * Copyright (c) 2019, The Linux Foundation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_AUTH_KAY_H +#define WPA_AUTH_KAY_H + +#ifdef CONFIG_MACSEC + +int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd, + struct sta_info *sta); +void * ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd, + struct sta_info *sta); +void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd); + +void * ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd, + struct sta_info *sta); + +#else /* CONFIG_MACSEC */ + +static inline int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd, + struct sta_info *sta) +{ + return 0; +} + +static inline void * +ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd, + struct sta_info *sta) +{ + return NULL; +} + +static inline void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd) +{ +} + +static inline void * +ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd, + struct sta_info *sta) +{ + return NULL; +} + +#endif /* CONFIG_MACSEC */ + +#endif /* WPA_AUTH_KAY_H */ diff --git a/contrib/wpa/src/common/dpp.c b/contrib/wpa/src/common/dpp.c index 49de47697384..dcbc80b6b84b 100644 --- a/contrib/wpa/src/common/dpp.c +++ b/contrib/wpa/src/common/dpp.c @@ -8,6 +8,7 @@ */ #include "utils/includes.h" +#include #include #include #include @@ -16,6 +17,8 @@ #include "utils/common.h" #include "utils/base64.h" #include "utils/json.h" +#include "utils/ip_addr.h" +#include "utils/eloop.h" #include "common/ieee802_11_common.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" @@ -70,9 +73,62 @@ static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, #endif +struct dpp_connection { + struct dl_list list; + struct dpp_controller *ctrl; + struct dpp_relay_controller *relay; + struct dpp_global *global; + struct dpp_authentication *auth; + int sock; + u8 mac_addr[ETH_ALEN]; + unsigned int freq; + u8 msg_len[4]; + size_t msg_len_octets; + struct wpabuf *msg; + struct wpabuf *msg_out; + size_t msg_out_pos; + unsigned int read_eloop:1; + unsigned int write_eloop:1; + unsigned int on_tcp_tx_complete_gas_done:1; + unsigned int on_tcp_tx_complete_remove:1; + unsigned int on_tcp_tx_complete_auth_ok:1; +}; + +/* Remote Controller */ +struct dpp_relay_controller { + struct dl_list list; + struct dpp_global *global; + u8 pkhash[SHA256_MAC_LEN]; + struct hostapd_ip_addr ipaddr; + void *cb_ctx; + void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg, + size_t len); + void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token, + int prot, struct wpabuf *buf); + struct dl_list conn; /* struct dpp_connection */ +}; + +/* Local Controller */ +struct dpp_controller { + struct dpp_global *global; + u8 allowed_roles; + int qr_mutual; + int sock; + struct dl_list conn; /* struct dpp_connection */ + char *configurator_params; +}; + struct dpp_global { + void *msg_ctx; struct dl_list bootstrap; /* struct dpp_bootstrap_info */ struct dl_list configurator; /* struct dpp_configurator */ +#ifdef CONFIG_DPP2 + struct dl_list controllers; /* struct dpp_relay_controller */ + struct dpp_controller *controller; + struct dl_list tcp_init; /* struct dpp_connection */ + void *cb_ctx; + int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth); +#endif /* CONFIG_DPP2 */ }; static const struct dpp_curve_params dpp_curves[] = { @@ -554,6 +610,91 @@ static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, } +static int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer, + u8 *secret, size_t *secret_len) +{ + EVP_PKEY_CTX *ctx; + int ret = -1; + + ERR_clear_error(); + *secret_len = 0; + + ctx = EVP_PKEY_CTX_new(own, NULL); + if (!ctx) { + wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_CTX_new failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + if (EVP_PKEY_derive_init(ctx) != 1) { + wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive_init failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (EVP_PKEY_derive_set_peer(ctx, peer) != 1) { + wpa_printf(MSG_ERROR, + "DPP: EVP_PKEY_derive_set_peet failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (EVP_PKEY_derive(ctx, NULL, secret_len) != 1) { + wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive(NULL) failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) { + u8 buf[200]; + int level = *secret_len > 200 ? MSG_ERROR : MSG_DEBUG; + + /* It looks like OpenSSL can return unexpectedly large buffer + * need for shared secret from EVP_PKEY_derive(NULL) in some + * cases. For example, group 19 has shown cases where secret_len + * is set to 72 even though the actual length ends up being + * updated to 32 when EVP_PKEY_derive() is called with a buffer + * for the value. Work around this by trying to fetch the value + * and continue if it is within supported range even when the + * initial buffer need is claimed to be larger. */ + wpa_printf(level, + "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()", + (int) *secret_len); + if (*secret_len > 200) + goto fail; + if (EVP_PKEY_derive(ctx, buf, secret_len) != 1) { + wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) { + wpa_printf(MSG_ERROR, + "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()", + (int) *secret_len); + goto fail; + } + wpa_hexdump_key(MSG_DEBUG, "DPP: Unexpected secret_len change", + buf, *secret_len); + os_memcpy(secret, buf, *secret_len); + forced_memzero(buf, sizeof(buf)); + goto done; + } + + if (EVP_PKEY_derive(ctx, secret, secret_len) != 1) { + wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + +done: + ret = 0; + +fail: + EVP_PKEY_CTX_free(ctx); + return ret; +} + + static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt) { wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt); @@ -689,17 +830,19 @@ static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri) int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi, const char *chan_list) { - const char *pos = chan_list; - int opclass, channel, freq; + const char *pos = chan_list, *pos2; + int opclass = -1, channel, freq; while (pos && *pos && *pos != ';') { - opclass = atoi(pos); + pos2 = pos; + while (*pos2 >= '0' && *pos2 <= '9') + pos2++; + if (*pos2 == '/') { + opclass = atoi(pos); + pos = pos2 + 1; + } if (opclass <= 0) goto fail; - pos = os_strchr(pos, '/'); - if (!pos) - goto fail; - pos++; channel = atoi(pos); if (channel <= 0) goto fail; @@ -1079,7 +1222,7 @@ static void dpp_debug_print_key(const char *title, EVP_PKEY *key) static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve) { EVP_PKEY_CTX *kctx = NULL; - EC_KEY *ec_params; + EC_KEY *ec_params = NULL; EVP_PKEY *params = NULL, *key = NULL; int nid; @@ -1110,19 +1253,18 @@ static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve) EVP_PKEY_keygen_init(kctx) != 1 || EVP_PKEY_keygen(kctx, &key) != 1) { wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key"); + key = NULL; goto fail; } if (wpa_debug_show_keys) dpp_debug_print_key("Own generated key", key); +fail: + EC_KEY_free(ec_params); EVP_PKEY_free(params); EVP_PKEY_CTX_free(kctx); return key; -fail: - EVP_PKEY_CTX_free(kctx); - EVP_PKEY_free(params); - return NULL; } @@ -2085,7 +2227,6 @@ struct dpp_authentication * dpp_auth_init(void *msg_ctx, { struct dpp_authentication *auth; size_t nonce_len; - EVP_PKEY_CTX *ctx = NULL; size_t secret_len; struct wpabuf *pi = NULL; const u8 *r_pubkey_hash, *i_pubkey_hash; @@ -2154,21 +2295,10 @@ struct dpp_authentication * dpp_auth_init(void *msg_ctx, goto fail; /* ECDH: M = pI * BR */ - ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); - if (!ctx || - EVP_PKEY_derive_init(ctx) != 1 || - EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 || - EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || - secret_len > DPP_MAX_SHARED_SECRET_LEN || - EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) { - wpa_printf(MSG_ERROR, - "DPP: Failed to derive ECDH shared secret: %s", - ERR_error_string(ERR_get_error(), NULL)); + if (dpp_ecdh(auth->own_protocol_key, auth->peer_bi->pubkey, + auth->Mx, &secret_len) < 0) goto fail; - } auth->secret_len = secret_len; - EVP_PKEY_CTX_free(ctx); - ctx = NULL; wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)", auth->Mx, auth->secret_len); @@ -2220,7 +2350,6 @@ struct dpp_authentication * dpp_auth_init(void *msg_ctx, out: wpabuf_free(pi); - EVP_PKEY_CTX_free(ctx); return auth; fail: dpp_auth_deinit(auth); @@ -2693,7 +2822,6 @@ fail: static int dpp_auth_build_resp_ok(struct dpp_authentication *auth) { size_t nonce_len; - EVP_PKEY_CTX *ctx = NULL; size_t secret_len; struct wpabuf *msg, *pr = NULL; u8 r_auth[4 + DPP_MAX_HASH_LEN]; @@ -2732,6 +2860,7 @@ static int dpp_auth_build_resp_ok(struct dpp_authentication *auth) #endif /* CONFIG_TESTING_OPTIONS */ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len); + EVP_PKEY_free(auth->own_protocol_key); #ifdef CONFIG_TESTING_OPTIONS if (dpp_protocol_key_override_len) { const struct dpp_curve_params *tmp_curve; @@ -2755,20 +2884,9 @@ static int dpp_auth_build_resp_ok(struct dpp_authentication *auth) goto fail; /* ECDH: N = pR * PI */ - ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); - if (!ctx || - EVP_PKEY_derive_init(ctx) != 1 || - EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 || - EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || - secret_len > DPP_MAX_SHARED_SECRET_LEN || - EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) { - wpa_printf(MSG_ERROR, - "DPP: Failed to derive ECDH shared secret: %s", - ERR_error_string(ERR_get_error(), NULL)); + if (dpp_ecdh(auth->own_protocol_key, auth->peer_protocol_key, + auth->Nx, &secret_len) < 0) goto fail; - } - EVP_PKEY_CTX_free(ctx); - ctx = NULL; wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", auth->Nx, auth->secret_len); @@ -3064,22 +3182,9 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, } dpp_debug_print_key("Peer (Initiator) Protocol Key", pi); - ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL); - if (!ctx || - EVP_PKEY_derive_init(ctx) != 1 || - EVP_PKEY_derive_set_peer(ctx, pi) != 1 || - EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || - secret_len > DPP_MAX_SHARED_SECRET_LEN || - EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) { - wpa_printf(MSG_ERROR, - "DPP: Failed to derive ECDH shared secret: %s", - ERR_error_string(ERR_get_error(), NULL)); - dpp_auth_fail(auth, "Failed to derive ECDH shared secret"); + if (dpp_ecdh(own_bi->pubkey, pi, auth->Mx, &secret_len) < 0) goto fail; - } auth->secret_len = secret_len; - EVP_PKEY_CTX_free(ctx); - ctx = NULL; wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)", auth->Mx, auth->secret_len); @@ -3533,7 +3638,6 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, const u8 *attr_start, size_t attr_len) { EVP_PKEY *pr; - EVP_PKEY_CTX *ctx = NULL; size_t secret_len; const u8 *addr[2]; size_t len[2]; @@ -3683,21 +3787,11 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, } dpp_debug_print_key("Peer (Responder) Protocol Key", pr); - ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); - if (!ctx || - EVP_PKEY_derive_init(ctx) != 1 || - EVP_PKEY_derive_set_peer(ctx, pr) != 1 || - EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || - secret_len > DPP_MAX_SHARED_SECRET_LEN || - EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) { - wpa_printf(MSG_ERROR, - "DPP: Failed to derive ECDH shared secret: %s", - ERR_error_string(ERR_get_error(), NULL)); + if (dpp_ecdh(auth->own_protocol_key, pr, auth->Nx, &secret_len) < 0) { dpp_auth_fail(auth, "Failed to derive ECDH shared secret"); goto fail; } - EVP_PKEY_CTX_free(ctx); - ctx = NULL; + EVP_PKEY_free(auth->peer_protocol_key); auth->peer_protocol_key = pr; pr = NULL; @@ -3868,7 +3962,6 @@ fail: bin_clear_free(unwrapped, unwrapped_len); bin_clear_free(unwrapped2, unwrapped2_len); EVP_PKEY_free(pr); - EVP_PKEY_CTX_free(ctx); return NULL; } @@ -5199,6 +5292,7 @@ static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk, pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y), wpabuf_len(x)); + EC_GROUP_free(group); *key_curve = curve; fail: @@ -6367,7 +6461,6 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector, const char *pos, *end; unsigned char *own_conn = NULL; size_t own_conn_len; - EVP_PKEY_CTX *ctx = NULL; size_t Nx_len; u8 Nx[DPP_MAX_SHARED_SECRET_LEN]; @@ -6481,18 +6574,8 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector, } /* ECDH: N = nk * PK */ - ctx = EVP_PKEY_CTX_new(own_key, NULL); - if (!ctx || - EVP_PKEY_derive_init(ctx) != 1 || - EVP_PKEY_derive_set_peer(ctx, peer_key) != 1 || - EVP_PKEY_derive(ctx, NULL, &Nx_len) != 1 || - Nx_len > DPP_MAX_SHARED_SECRET_LEN || - EVP_PKEY_derive(ctx, Nx, &Nx_len) != 1) { - wpa_printf(MSG_ERROR, - "DPP: Failed to derive ECDH shared secret: %s", - ERR_error_string(ERR_get_error(), NULL)); + if (dpp_ecdh(own_key, peer_key, Nx, &Nx_len) < 0) goto fail; - } wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", Nx, Nx_len); @@ -6515,7 +6598,6 @@ fail: if (ret != DPP_STATUS_OK) os_memset(intro, 0, sizeof(*intro)); os_memset(Nx, 0, sizeof(Nx)); - EVP_PKEY_CTX_free(ctx); os_free(own_conn); os_free(signed_connector); os_free(info.payload); @@ -6535,6 +6617,7 @@ static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve, EC_GROUP *group; size_t len = curve->prime_len; const u8 *x, *y; + EVP_PKEY *res; switch (curve->ike_group) { case 19: @@ -6568,14 +6651,16 @@ static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve, group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name)); if (!group) return NULL; - return dpp_set_pubkey_point_group(group, x, y, len); + res = dpp_set_pubkey_point_group(group, x, y, len); + EC_GROUP_free(group); + return res; } static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve, const u8 *mac_init, const char *code, const char *identifier, BN_CTX *bnctx, - const EC_GROUP **ret_group) + EC_GROUP **ret_group) { u8 hash[DPP_MAX_HASH_LEN]; const u8 *addr[3]; @@ -6644,8 +6729,10 @@ out: EC_KEY_free(Pi_ec); EVP_PKEY_free(Pi); BN_clear_free(hash_bn); - if (ret_group) + if (ret_group && Qi) *ret_group = group2; + else + EC_GROUP_free(group2); return Qi; fail: EC_POINT_free(Qi); @@ -6657,7 +6744,7 @@ fail: static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp, const char *code, const char *identifier, BN_CTX *bnctx, - const EC_GROUP **ret_group) + EC_GROUP **ret_group) { u8 hash[DPP_MAX_HASH_LEN]; const u8 *addr[3]; @@ -6726,8 +6813,10 @@ out: EC_KEY_free(Pr_ec); EVP_PKEY_free(Pr); BN_clear_free(hash_bn); - if (ret_group) + if (ret_group && Qr) *ret_group = group2; + else + EC_GROUP_free(group2); return Qr; fail: EC_POINT_free(Qr); @@ -6796,6 +6885,7 @@ fail: BN_free(y); EC_POINT_free(point); BN_CTX_free(ctx); + EC_GROUP_free(group); return ret; } @@ -6807,7 +6897,7 @@ static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex) EC_KEY *X_ec = NULL; const EC_POINT *X_point; BN_CTX *bnctx = NULL; - const EC_GROUP *group; + EC_GROUP *group = NULL; EC_POINT *Qi = NULL, *M = NULL; struct wpabuf *M_buf = NULL; BIGNUM *Mx = NULL, *My = NULL; @@ -6929,6 +7019,7 @@ out: BN_clear_free(Mx); BN_clear_free(My); BN_CTX_free(bnctx); + EC_GROUP_free(group); return msg; fail: wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request"); @@ -7173,7 +7264,7 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, struct dpp_pkex *pkex = NULL; EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL; BN_CTX *bnctx = NULL; - const EC_GROUP *group; + EC_GROUP *group = NULL; BIGNUM *Mx = NULL, *My = NULL; EC_KEY *Y_ec = NULL, *X_ec = NULL;; const EC_POINT *Y_point; @@ -7181,7 +7272,6 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, u8 Kx[DPP_MAX_SHARED_SECRET_LEN]; size_t Kx_len; int res; - EVP_PKEY_CTX *ctx = NULL; if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) { wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL @@ -7348,18 +7438,8 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, goto fail; /* K = y * X' */ - ctx = EVP_PKEY_CTX_new(pkex->y, NULL); - if (!ctx || - EVP_PKEY_derive_init(ctx) != 1 || - EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 || - EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 || - Kx_len > DPP_MAX_SHARED_SECRET_LEN || - EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) { - wpa_printf(MSG_ERROR, - "DPP: Failed to derive ECDH shared secret: %s", - ERR_error_string(ERR_get_error(), NULL)); + if (dpp_ecdh(pkex->y, pkex->x, Kx, &Kx_len) < 0) goto fail; - } wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)", Kx, Kx_len); @@ -7377,7 +7457,6 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, pkex->exchange_done = 1; out: - EVP_PKEY_CTX_free(ctx); BN_CTX_free(bnctx); EC_POINT_free(Qi); EC_POINT_free(Qr); @@ -7390,6 +7469,7 @@ out: EC_POINT_free(X); EC_KEY_free(X_ec); EC_KEY_free(Y_ec); + EC_GROUP_free(group); return pkex; fail: wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed"); @@ -7518,13 +7598,12 @@ struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex, { const u8 *attr_status, *attr_id, *attr_key, *attr_group; u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len; - const EC_GROUP *group; + EC_GROUP *group = NULL; BN_CTX *bnctx = NULL; struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL; const struct dpp_curve_params *curve = pkex->own_bi->curve; EC_POINT *Qr = NULL, *Y = NULL, *N = NULL; BIGNUM *Nx = NULL, *Ny = NULL; - EVP_PKEY_CTX *ctx = NULL; EC_KEY *Y_ec = NULL; size_t Jx_len, Kx_len; u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN]; @@ -7636,18 +7715,8 @@ struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex, if (!pkex->y || EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1) goto fail; - ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL); - if (!ctx || - EVP_PKEY_derive_init(ctx) != 1 || - EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 || - EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 || - Jx_len > DPP_MAX_SHARED_SECRET_LEN || - EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) { - wpa_printf(MSG_ERROR, - "DPP: Failed to derive ECDH shared secret: %s", - ERR_error_string(ERR_get_error(), NULL)); + if (dpp_ecdh(pkex->own_bi->pubkey, pkex->y, Jx, &Jx_len) < 0) goto fail; - } wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)", Jx, Jx_len); @@ -7671,19 +7740,8 @@ struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex, wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len); /* K = x * Y’ */ - EVP_PKEY_CTX_free(ctx); - ctx = EVP_PKEY_CTX_new(pkex->x, NULL); - if (!ctx || - EVP_PKEY_derive_init(ctx) != 1 || - EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 || - EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 || - Kx_len > DPP_MAX_SHARED_SECRET_LEN || - EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) { - wpa_printf(MSG_ERROR, - "DPP: Failed to derive ECDH shared secret: %s", - ERR_error_string(ERR_get_error(), NULL)); + if (dpp_ecdh(pkex->x, pkex->y, Kx, &Kx_len) < 0) goto fail; - } wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)", Kx, Kx_len); @@ -7713,8 +7771,8 @@ out: BN_free(Nx); BN_free(Ny); EC_KEY_free(Y_ec); - EVP_PKEY_CTX_free(ctx); BN_CTX_free(bnctx); + EC_GROUP_free(group); return msg; fail: wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed"); @@ -7840,7 +7898,6 @@ struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex, const u8 *buf, size_t buflen) { const struct dpp_curve_params *curve = pkex->own_bi->curve; - EVP_PKEY_CTX *ctx = NULL; size_t Jx_len, Lx_len; u8 Jx[DPP_MAX_SHARED_SECRET_LEN]; u8 Lx[DPP_MAX_SHARED_SECRET_LEN]; @@ -7924,18 +7981,8 @@ struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex, pkex->peer_bootstrap_key); /* ECDH: J' = y * A' */ - ctx = EVP_PKEY_CTX_new(pkex->y, NULL); - if (!ctx || - EVP_PKEY_derive_init(ctx) != 1 || - EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 || - EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 || - Jx_len > DPP_MAX_SHARED_SECRET_LEN || - EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) { - wpa_printf(MSG_ERROR, - "DPP: Failed to derive ECDH shared secret: %s", - ERR_error_string(ERR_get_error(), NULL)); + if (dpp_ecdh(pkex->y, pkex->peer_bootstrap_key, Jx, &Jx_len) < 0) goto fail; - } wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)", Jx, Jx_len); @@ -7971,19 +8018,8 @@ struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex, wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received"); /* ECDH: L = b * X' */ - EVP_PKEY_CTX_free(ctx); - ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL); - if (!ctx || - EVP_PKEY_derive_init(ctx) != 1 || - EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 || - EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 || - Lx_len > DPP_MAX_SHARED_SECRET_LEN || - EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) { - wpa_printf(MSG_ERROR, - "DPP: Failed to derive ECDH shared secret: %s", - ERR_error_string(ERR_get_error(), NULL)); + if (dpp_ecdh(pkex->own_bi->pubkey, pkex->x, Lx, &Lx_len) < 0) goto fail; - } wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)", Lx, Lx_len); @@ -8009,7 +8045,6 @@ struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex, goto fail; out: - EVP_PKEY_CTX_free(ctx); os_free(unwrapped); wpabuf_free(A_pub); wpabuf_free(B_pub); @@ -8038,7 +8073,6 @@ int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr, u8 v[DPP_MAX_HASH_LEN]; size_t Lx_len; u8 Lx[DPP_MAX_SHARED_SECRET_LEN]; - EVP_PKEY_CTX *ctx = NULL; struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL; #ifdef CONFIG_TESTING_OPTIONS @@ -8109,18 +8143,8 @@ int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr, pkex->peer_bootstrap_key); /* ECDH: L' = x * B' */ - ctx = EVP_PKEY_CTX_new(pkex->x, NULL); - if (!ctx || - EVP_PKEY_derive_init(ctx) != 1 || - EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 || - EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 || - Lx_len > DPP_MAX_SHARED_SECRET_LEN || - EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) { - wpa_printf(MSG_ERROR, - "DPP: Failed to derive ECDH shared secret: %s", - ERR_error_string(ERR_get_error(), NULL)); + if (dpp_ecdh(pkex->x, pkex->peer_bootstrap_key, Lx, &Lx_len) < 0) goto fail; - } wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)", Lx, Lx_len); @@ -8160,7 +8184,6 @@ out: wpabuf_free(B_pub); wpabuf_free(X_pub); wpabuf_free(Y_pub); - EVP_PKEY_CTX_free(ctx); os_free(unwrapped); return ret; fail: @@ -8533,20 +8556,25 @@ int dpp_bootstrap_info(struct dpp_global *dpp, int id, char *reply, int reply_size) { struct dpp_bootstrap_info *bi; + char pkhash[2 * SHA256_MAC_LEN + 1]; bi = dpp_bootstrap_get_id(dpp, id); if (!bi) return -1; + wpa_snprintf_hex(pkhash, sizeof(pkhash), bi->pubkey_hash, + SHA256_MAC_LEN); return os_snprintf(reply, reply_size, "type=%s\n" "mac_addr=" MACSTR "\n" "info=%s\n" "num_freq=%u\n" - "curve=%s\n", + "curve=%s\n" + "pkhash=%s\n", dpp_bootstrap_type_txt(bi->type), MAC2STR(bi->mac_addr), bi->info ? bi->info : "", bi->num_freq, - bi->curve->name); + bi->curve->name, + pkhash); } @@ -8689,16 +8717,88 @@ int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id, } -struct dpp_global * dpp_global_init(void) +#ifdef CONFIG_DPP2 + +static void dpp_connection_free(struct dpp_connection *conn) +{ + if (conn->sock >= 0) { + wpa_printf(MSG_DEBUG, "DPP: Close Controller socket %d", + conn->sock); + eloop_unregister_sock(conn->sock, EVENT_TYPE_READ); + eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE); + close(conn->sock); + } + wpabuf_free(conn->msg); + wpabuf_free(conn->msg_out); + dpp_auth_deinit(conn->auth); + os_free(conn); +} + + +static void dpp_connection_remove(struct dpp_connection *conn) +{ + dl_list_del(&conn->list); + dpp_connection_free(conn); +} + + +static void dpp_tcp_init_flush(struct dpp_global *dpp) +{ + struct dpp_connection *conn, *tmp; + + dl_list_for_each_safe(conn, tmp, &dpp->tcp_init, struct dpp_connection, + list) + dpp_connection_remove(conn); +} + + +static void dpp_relay_controller_free(struct dpp_relay_controller *ctrl) +{ + struct dpp_connection *conn, *tmp; + + dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection, + list) + dpp_connection_remove(conn); + os_free(ctrl); +} + + +static void dpp_relay_flush_controllers(struct dpp_global *dpp) +{ + struct dpp_relay_controller *ctrl, *tmp; + + if (!dpp) + return; + + dl_list_for_each_safe(ctrl, tmp, &dpp->controllers, + struct dpp_relay_controller, list) { + dl_list_del(&ctrl->list); + dpp_relay_controller_free(ctrl); + } +} + +#endif /* CONFIG_DPP2 */ + + +struct dpp_global * dpp_global_init(struct dpp_global_config *config) { struct dpp_global *dpp; dpp = os_zalloc(sizeof(*dpp)); if (!dpp) return NULL; + dpp->msg_ctx = config->msg_ctx; +#ifdef CONFIG_DPP2 + dpp->cb_ctx = config->cb_ctx; + dpp->process_conf_obj = config->process_conf_obj; +#endif /* CONFIG_DPP2 */ dl_list_init(&dpp->bootstrap); dl_list_init(&dpp->configurator); +#ifdef CONFIG_DPP2 + dl_list_init(&dpp->controllers); + dl_list_init(&dpp->tcp_init); +#endif /* CONFIG_DPP2 */ return dpp; } @@ -8711,6 +8811,11 @@ void dpp_global_clear(struct dpp_global *dpp) dpp_bootstrap_del(dpp, 0); dpp_configurator_del(dpp, 0); +#ifdef CONFIG_DPP2 + dpp_tcp_init_flush(dpp); + dpp_relay_flush_controllers(dpp); + dpp_controller_stop(dpp); +#endif /* CONFIG_DPP2 */ } @@ -8719,3 +8824,1233 @@ void dpp_global_deinit(struct dpp_global *dpp) dpp_global_clear(dpp); os_free(dpp); } + + +#ifdef CONFIG_DPP2 + +static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx); +static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx); +static void dpp_controller_auth_success(struct dpp_connection *conn, + int initiator); + + +int dpp_relay_add_controller(struct dpp_global *dpp, + struct dpp_relay_config *config) +{ + struct dpp_relay_controller *ctrl; + + if (!dpp) + return -1; + + ctrl = os_zalloc(sizeof(*ctrl)); + if (!ctrl) + return -1; + dl_list_init(&ctrl->conn); + ctrl->global = dpp; + os_memcpy(&ctrl->ipaddr, config->ipaddr, sizeof(*config->ipaddr)); + os_memcpy(ctrl->pkhash, config->pkhash, SHA256_MAC_LEN); + ctrl->cb_ctx = config->cb_ctx; + ctrl->tx = config->tx; + ctrl->gas_resp_tx = config->gas_resp_tx; + dl_list_add(&dpp->controllers, &ctrl->list); + return 0; +} + + +static struct dpp_relay_controller * +dpp_relay_controller_get(struct dpp_global *dpp, const u8 *pkhash) +{ + struct dpp_relay_controller *ctrl; + + if (!dpp) + return NULL; + + dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller, + list) { + if (os_memcmp(pkhash, ctrl->pkhash, SHA256_MAC_LEN) == 0) + return ctrl; + } + + return NULL; +} + + +static void dpp_controller_gas_done(struct dpp_connection *conn) +{ + struct dpp_authentication *auth = conn->auth; + + if (auth->peer_version >= 2 && + auth->conf_resp_status == DPP_STATUS_OK) { + wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result"); + auth->waiting_conf_result = 1; + return; + } + + wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT); + dpp_connection_remove(conn); +} + + +static int dpp_tcp_send(struct dpp_connection *conn) +{ + int res; + + if (!conn->msg_out) { + eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE); + conn->write_eloop = 0; + return -1; + } + res = send(conn->sock, + wpabuf_head_u8(conn->msg_out) + conn->msg_out_pos, + wpabuf_len(conn->msg_out) - conn->msg_out_pos, 0); + if (res < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to send buffer: %s", + strerror(errno)); + dpp_connection_remove(conn); + return -1; + } + + conn->msg_out_pos += res; + if (wpabuf_len(conn->msg_out) > conn->msg_out_pos) { + wpa_printf(MSG_DEBUG, + "DPP: %u/%u bytes of message sent to Controller", + (unsigned int) conn->msg_out_pos, + (unsigned int) wpabuf_len(conn->msg_out)); + if (!conn->write_eloop && + eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, + dpp_conn_tx_ready, conn, NULL) == 0) + conn->write_eloop = 1; + return 1; + } + + wpa_printf(MSG_DEBUG, "DPP: Full message sent over TCP"); + wpabuf_free(conn->msg_out); + conn->msg_out = NULL; + conn->msg_out_pos = 0; + eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE); + conn->write_eloop = 0; + if (!conn->read_eloop && + eloop_register_sock(conn->sock, EVENT_TYPE_READ, + dpp_controller_rx, conn, NULL) == 0) + conn->read_eloop = 1; + if (conn->on_tcp_tx_complete_remove) { + dpp_connection_remove(conn); + } else if (conn->ctrl && conn->on_tcp_tx_complete_gas_done && + conn->auth) { + dpp_controller_gas_done(conn); + } else if (conn->on_tcp_tx_complete_auth_ok) { + conn->on_tcp_tx_complete_auth_ok = 0; + dpp_controller_auth_success(conn, 1); + } + + return 0; +} + + +static void dpp_controller_start_gas_client(struct dpp_connection *conn) +{ + struct dpp_authentication *auth = conn->auth; + struct wpabuf *buf; + char json[100]; + int netrole_ap = 0; /* TODO: make this configurable */ + + os_snprintf(json, sizeof(json), + "{\"name\":\"Test\"," + "\"wi-fi_tech\":\"infra\"," + "\"netRole\":\"%s\"}", + netrole_ap ? "ap" : "sta"); +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Config Attr"); + json[29] = 'k'; /* replace "infra" with "knfra" */ + } +#endif /* CONFIG_TESTING_OPTIONS */ + wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json); + + buf = dpp_build_conf_req(auth, json); + if (!buf) { + wpa_printf(MSG_DEBUG, + "DPP: No configuration request data available"); + return; + } + + wpabuf_free(conn->msg_out); + conn->msg_out_pos = 0; + conn->msg_out = wpabuf_alloc(4 + wpabuf_len(buf) - 1); + if (!conn->msg_out) { + wpabuf_free(buf); + return; + } + wpabuf_put_be32(conn->msg_out, wpabuf_len(buf) - 1); + wpabuf_put_data(conn->msg_out, wpabuf_head_u8(buf) + 1, + wpabuf_len(buf) - 1); + wpabuf_free(buf); + + if (dpp_tcp_send(conn) == 1) { + if (!conn->write_eloop) { + if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, + dpp_conn_tx_ready, + conn, NULL) < 0) + return; + conn->write_eloop = 1; + } + } +} + + +static void dpp_controller_auth_success(struct dpp_connection *conn, + int initiator) +{ + struct dpp_authentication *auth = conn->auth; + + if (!auth) + return; + + wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded"); + wpa_msg(conn->global->msg_ctx, MSG_INFO, + DPP_EVENT_AUTH_SUCCESS "init=%d", initiator); +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Authentication Confirm"); + if (auth->configurator) { + /* Prevent GAS response */ + auth->auth_success = 0; + } + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!auth->configurator) + dpp_controller_start_gas_client(conn); +} + + +static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct dpp_connection *conn = eloop_ctx; + + wpa_printf(MSG_DEBUG, "DPP: TCP socket %d ready for TX", sock); + dpp_tcp_send(conn); +} + + +static int dpp_ipaddr_to_sockaddr(struct sockaddr *addr, socklen_t *addrlen, + const struct hostapd_ip_addr *ipaddr, + int port) +{ + struct sockaddr_in *dst; +#ifdef CONFIG_IPV6 + struct sockaddr_in6 *dst6; +#endif /* CONFIG_IPV6 */ + + switch (ipaddr->af) { + case AF_INET: + dst = (struct sockaddr_in *) addr; + os_memset(dst, 0, sizeof(*dst)); + dst->sin_family = AF_INET; + dst->sin_addr.s_addr = ipaddr->u.v4.s_addr; + dst->sin_port = htons(port); + *addrlen = sizeof(*dst); + break; +#ifdef CONFIG_IPV6 + case AF_INET6: + dst6 = (struct sockaddr_in6 *) addr; + os_memset(dst6, 0, sizeof(*dst6)); + dst6->sin6_family = AF_INET6; + os_memcpy(&dst6->sin6_addr, &ipaddr->u.v6, + sizeof(struct in6_addr)); + dst6->sin6_port = htons(port); + *addrlen = sizeof(*dst6); + break; +#endif /* CONFIG_IPV6 */ + default: + return -1; + } + + return 0; +} + + +static struct dpp_connection * +dpp_relay_new_conn(struct dpp_relay_controller *ctrl, const u8 *src, + unsigned int freq) +{ + struct dpp_connection *conn; + struct sockaddr_storage addr; + socklen_t addrlen; + char txt[100]; + + if (dl_list_len(&ctrl->conn) >= 15) { + wpa_printf(MSG_DEBUG, + "DPP: Too many ongoing Relay connections to the Controller - cannot start a new one"); + return NULL; + } + + if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &addr, &addrlen, + &ctrl->ipaddr, DPP_TCP_PORT) < 0) + return NULL; + + conn = os_zalloc(sizeof(*conn)); + if (!conn) + return NULL; + + conn->global = ctrl->global; + conn->relay = ctrl; + os_memcpy(conn->mac_addr, src, ETH_ALEN); + conn->freq = freq; + + conn->sock = socket(AF_INET, SOCK_STREAM, 0); + if (conn->sock < 0) + goto fail; + wpa_printf(MSG_DEBUG, "DPP: TCP relay socket %d connection to %s", + conn->sock, hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt))); + + if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) { + wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s", + strerror(errno)); + goto fail; + } + + if (connect(conn->sock, (struct sockaddr *) &addr, addrlen) < 0) { + if (errno != EINPROGRESS) { + wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s", + strerror(errno)); + goto fail; + } + + /* + * Continue connecting in the background; eloop will call us + * once the connection is ready (or failed). + */ + } + + if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, + dpp_conn_tx_ready, conn, NULL) < 0) + goto fail; + conn->write_eloop = 1; + + /* TODO: eloop timeout to clear a connection if it does not complete + * properly */ + + dl_list_add(&ctrl->conn, &conn->list); + return conn; +fail: + dpp_connection_free(conn); + return NULL; +} + + +static struct wpabuf * dpp_tcp_encaps(const u8 *hdr, const u8 *buf, size_t len) +{ + struct wpabuf *msg; + + msg = wpabuf_alloc(4 + 1 + DPP_HDR_LEN + len); + if (!msg) + return NULL; + wpabuf_put_be32(msg, 1 + DPP_HDR_LEN + len); + wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC); + wpabuf_put_data(msg, hdr, DPP_HDR_LEN); + wpabuf_put_data(msg, buf, len); + wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg); + return msg; +} + + +static int dpp_relay_tx(struct dpp_connection *conn, const u8 *hdr, + const u8 *buf, size_t len) +{ + u8 type = hdr[DPP_HDR_LEN - 1]; + + wpa_printf(MSG_DEBUG, + "DPP: Continue already established Relay/Controller connection for this session"); + wpabuf_free(conn->msg_out); + conn->msg_out_pos = 0; + conn->msg_out = dpp_tcp_encaps(hdr, buf, len); + if (!conn->msg_out) { + dpp_connection_remove(conn); + return -1; + } + + /* TODO: for proto ver 1, need to do remove connection based on GAS Resp + * TX status */ + if (type == DPP_PA_CONFIGURATION_RESULT) + conn->on_tcp_tx_complete_remove = 1; + dpp_tcp_send(conn); + return 0; +} + + +int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr, + const u8 *buf, size_t len, unsigned int freq, + const u8 *i_bootstrap, const u8 *r_bootstrap) +{ + struct dpp_relay_controller *ctrl; + struct dpp_connection *conn; + u8 type = hdr[DPP_HDR_LEN - 1]; + + /* Check if there is an already started session for this peer and if so, + * continue that session (send this over TCP) and return 0. + */ + if (type != DPP_PA_PEER_DISCOVERY_REQ && + type != DPP_PA_PEER_DISCOVERY_RESP) { + dl_list_for_each(ctrl, &dpp->controllers, + struct dpp_relay_controller, list) { + dl_list_for_each(conn, &ctrl->conn, + struct dpp_connection, list) { + if (os_memcmp(src, conn->mac_addr, + ETH_ALEN) == 0) + return dpp_relay_tx(conn, hdr, buf, len); + } + } + } + + if (!r_bootstrap) + return -1; + + ctrl = dpp_relay_controller_get(dpp, r_bootstrap); + if (!ctrl) + return -1; + + wpa_printf(MSG_DEBUG, + "DPP: Authentication Request for a configured Controller"); + conn = dpp_relay_new_conn(ctrl, src, freq); + if (!conn) + return -1; + + conn->msg_out = dpp_tcp_encaps(hdr, buf, len); + if (!conn->msg_out) { + dpp_connection_remove(conn); + return -1; + } + /* Message will be sent in dpp_conn_tx_ready() */ + + return 0; +} + + +int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data, + size_t data_len) +{ + struct dpp_relay_controller *ctrl; + struct dpp_connection *conn, *found = NULL; + struct wpabuf *msg; + + /* Check if there is a successfully completed authentication for this + * and if so, continue that session (send this over TCP) and return 0. + */ + dl_list_for_each(ctrl, &dpp->controllers, + struct dpp_relay_controller, list) { + if (found) + break; + dl_list_for_each(conn, &ctrl->conn, + struct dpp_connection, list) { + if (os_memcmp(src, conn->mac_addr, + ETH_ALEN) == 0) { + found = conn; + break; + } + } + } + + if (!found) + return -1; + + msg = wpabuf_alloc(4 + 1 + data_len); + if (!msg) + return -1; + wpabuf_put_be32(msg, 1 + data_len); + wpabuf_put_u8(msg, WLAN_PA_GAS_INITIAL_REQ); + wpabuf_put_data(msg, data, data_len); + wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg); + + wpabuf_free(conn->msg_out); + conn->msg_out_pos = 0; + conn->msg_out = msg; + dpp_tcp_send(conn); + return 0; +} + + +static void dpp_controller_free(struct dpp_controller *ctrl) +{ + struct dpp_connection *conn, *tmp; + + if (!ctrl) + return; + + dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection, + list) + dpp_connection_remove(conn); + + if (ctrl->sock >= 0) { + close(ctrl->sock); + eloop_unregister_sock(ctrl->sock, EVENT_TYPE_READ); + } + os_free(ctrl->configurator_params); + os_free(ctrl); +} + + +static int dpp_controller_rx_auth_req(struct dpp_connection *conn, + const u8 *hdr, const u8 *buf, size_t len) +{ + const u8 *r_bootstrap, *i_bootstrap; + u16 r_bootstrap_len, i_bootstrap_len; + struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL; + + if (!conn->ctrl) + return 0; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Request"); + + r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_bootstrap_len); + if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { + wpa_printf(MSG_INFO, + "Missing or invalid required Responder Bootstrapping Key Hash attribute"); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash", + r_bootstrap, r_bootstrap_len); + + i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) { + wpa_printf(MSG_INFO, + "Missing or invalid required Initiator Bootstrapping Key Hash attribute"); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash", + i_bootstrap, i_bootstrap_len); + + /* Try to find own and peer bootstrapping key matches based on the + * received hash values */ + dpp_bootstrap_find_pair(conn->ctrl->global, i_bootstrap, r_bootstrap, + &own_bi, &peer_bi); + if (!own_bi) { + wpa_printf(MSG_INFO, + "No matching own bootstrapping key found - ignore message"); + return -1; + } + + if (conn->auth) { + wpa_printf(MSG_INFO, + "Already in DPP authentication exchange - ignore new one"); + return 0; + } + + conn->auth = dpp_auth_req_rx(conn->ctrl->global->msg_ctx, + conn->ctrl->allowed_roles, + conn->ctrl->qr_mutual, + peer_bi, own_bi, -1, hdr, buf, len); + if (!conn->auth) { + wpa_printf(MSG_DEBUG, "DPP: No response generated"); + return -1; + } + + if (dpp_set_configurator(conn->ctrl->global, conn->ctrl->global->msg_ctx, + conn->auth, + conn->ctrl->configurator_params) < 0) { + dpp_connection_remove(conn); + return -1; + } + + wpabuf_free(conn->msg_out); + conn->msg_out_pos = 0; + conn->msg_out = wpabuf_alloc(4 + wpabuf_len(conn->auth->resp_msg) - 1); + if (!conn->msg_out) + return -1; + wpabuf_put_be32(conn->msg_out, wpabuf_len(conn->auth->resp_msg) - 1); + wpabuf_put_data(conn->msg_out, wpabuf_head_u8(conn->auth->resp_msg) + 1, + wpabuf_len(conn->auth->resp_msg) - 1); + + if (dpp_tcp_send(conn) == 1) { + if (!conn->write_eloop) { + if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, + dpp_conn_tx_ready, + conn, NULL) < 0) + return -1; + conn->write_eloop = 1; + } + } + + return 0; +} + + +static int dpp_controller_rx_auth_resp(struct dpp_connection *conn, + const u8 *hdr, const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = conn->auth; + struct wpabuf *msg; + + if (!auth) + return -1; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Response"); + + msg = dpp_auth_resp_rx(auth, hdr, buf, len); + if (!msg) { + if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) { + wpa_printf(MSG_DEBUG, + "DPP: Start wait for full response"); + return -1; + } + wpa_printf(MSG_DEBUG, "DPP: No confirm generated"); + dpp_connection_remove(conn); + return -1; + } + + wpabuf_free(conn->msg_out); + conn->msg_out_pos = 0; + conn->msg_out = wpabuf_alloc(4 + wpabuf_len(msg) - 1); + if (!conn->msg_out) { + wpabuf_free(msg); + return -1; + } + wpabuf_put_be32(conn->msg_out, wpabuf_len(msg) - 1); + wpabuf_put_data(conn->msg_out, wpabuf_head_u8(msg) + 1, + wpabuf_len(msg) - 1); + wpabuf_free(msg); + + conn->on_tcp_tx_complete_auth_ok = 1; + if (dpp_tcp_send(conn) == 1) { + if (!conn->write_eloop) { + if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, + dpp_conn_tx_ready, + conn, NULL) < 0) + return -1; + conn->write_eloop = 1; + } + } + + return 0; +} + + +static int dpp_controller_rx_auth_conf(struct dpp_connection *conn, + const u8 *hdr, const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = conn->auth; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation"); + + if (!auth) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Authentication in progress - drop"); + return -1; + } + + if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Authentication failed"); + return -1; + } + + dpp_controller_auth_success(conn, 0); + return 0; +} + + +static int dpp_controller_rx_conf_result(struct dpp_connection *conn, + const u8 *hdr, const u8 *buf, + size_t len) +{ + struct dpp_authentication *auth = conn->auth; + enum dpp_status_error status; + + if (!conn->ctrl) + return 0; + + wpa_printf(MSG_DEBUG, "DPP: Configuration Result"); + + if (!auth || !auth->waiting_conf_result) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Configuration waiting for result - drop"); + return -1; + } + + status = dpp_conf_result_rx(auth, hdr, buf, len); + if (status == DPP_STATUS_OK) + wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO, + DPP_EVENT_CONF_SENT); + else + wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO, + DPP_EVENT_CONF_FAILED); + return -1; /* to remove the completed connection */ +} + + +static int dpp_controller_rx_action(struct dpp_connection *conn, const u8 *msg, + size_t len) +{ + const u8 *pos, *end; + u8 type; + + wpa_printf(MSG_DEBUG, "DPP: Received DPP Action frame over TCP"); + pos = msg; + end = msg + len; + + if (end - pos < DPP_HDR_LEN || + WPA_GET_BE24(pos) != OUI_WFA || + pos[3] != DPP_OUI_TYPE) { + wpa_printf(MSG_DEBUG, "DPP: Unrecognized header"); + return -1; + } + + if (pos[4] != 1) { + wpa_printf(MSG_DEBUG, "DPP: Unsupported Crypto Suite %u", + pos[4]); + return -1; + } + type = pos[5]; + wpa_printf(MSG_DEBUG, "DPP: Received message type %u", type); + pos += DPP_HDR_LEN; + + wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", + pos, end - pos); + if (dpp_check_attrs(pos, end - pos) < 0) + return -1; + + if (conn->relay) { + wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN"); + conn->relay->tx(conn->relay->cb_ctx, conn->mac_addr, + conn->freq, msg, len); + return 0; + } + + switch (type) { + case DPP_PA_AUTHENTICATION_REQ: + return dpp_controller_rx_auth_req(conn, msg, pos, end - pos); + case DPP_PA_AUTHENTICATION_RESP: + return dpp_controller_rx_auth_resp(conn, msg, pos, end - pos); + case DPP_PA_AUTHENTICATION_CONF: + return dpp_controller_rx_auth_conf(conn, msg, pos, end - pos); + case DPP_PA_CONFIGURATION_RESULT: + return dpp_controller_rx_conf_result(conn, msg, pos, end - pos); + default: + /* TODO: missing messages types */ + wpa_printf(MSG_DEBUG, + "DPP: Unsupported frame subtype %d", type); + return -1; + } +} + + +static int dpp_controller_rx_gas_req(struct dpp_connection *conn, const u8 *msg, + size_t len) +{ + const u8 *pos, *end, *next; + u8 dialog_token; + const u8 *adv_proto; + u16 slen; + struct wpabuf *resp, *buf; + struct dpp_authentication *auth = conn->auth; + + if (len < 1 + 2) + return -1; + + wpa_printf(MSG_DEBUG, + "DPP: Received DPP Configuration Request over TCP"); + + if (!conn->ctrl || !auth || !auth->auth_success) { + wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); + return -1; + } + + pos = msg; + end = msg + len; + + dialog_token = *pos++; + adv_proto = pos++; + slen = *pos++; + if (*adv_proto != WLAN_EID_ADV_PROTO || + slen > end - pos || slen < 2) + return -1; + + next = pos + slen; + pos++; /* skip QueryRespLenLimit and PAME-BI */ + + if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC || + pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA || + pos[5] != DPP_OUI_TYPE || pos[6] != 0x01) + return -1; + + pos = next; + /* Query Request */ + if (end - pos < 2) + return -1; + slen = WPA_GET_LE16(pos); + pos += 2; + if (slen > end - pos) + return -1; + + resp = dpp_conf_req_rx(auth, pos, slen); + if (!resp) + return -1; + + buf = wpabuf_alloc(4 + 18 + wpabuf_len(resp)); + if (!buf) { + wpabuf_free(resp); + return -1; + } + + wpabuf_put_be32(buf, 18 + wpabuf_len(resp)); + + wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_RESP); + wpabuf_put_u8(buf, dialog_token); + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */ + + dpp_write_adv_proto(buf); + dpp_write_gas_query(buf, resp); + wpabuf_free(resp); + + /* Send Config Response over TCP; GAS fragmentation is taken care of by + * the Relay */ + wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf); + wpabuf_free(conn->msg_out); + conn->msg_out_pos = 0; + conn->msg_out = buf; + conn->on_tcp_tx_complete_gas_done = 1; + dpp_tcp_send(conn); + return 0; +} + + +static int dpp_tcp_rx_gas_resp(struct dpp_connection *conn, struct wpabuf *resp) +{ + struct dpp_authentication *auth = conn->auth; + int res; + struct wpabuf *msg, *encaps; + enum dpp_status_error status; + + wpa_printf(MSG_DEBUG, + "DPP: Configuration Response for local stack from TCP"); + + res = dpp_conf_resp_rx(auth, resp); + wpabuf_free(resp); + if (res < 0) { + wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed"); + return -1; + } + + if (conn->global->process_conf_obj) + res = conn->global->process_conf_obj(conn->global->cb_ctx, + auth); + else + res = 0; + + if (auth->peer_version < 2 || auth->conf_resp_status != DPP_STATUS_OK) + return -1; + + wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result"); + status = res < 0 ? DPP_STATUS_CONFIG_REJECTED : DPP_STATUS_OK; + msg = dpp_build_conf_result(auth, status); + if (!msg) + return -1; + + encaps = wpabuf_alloc(4 + wpabuf_len(msg) - 1); + if (!encaps) { + wpabuf_free(msg); + return -1; + } + wpabuf_put_be32(encaps, wpabuf_len(msg) - 1); + wpabuf_put_data(encaps, wpabuf_head_u8(msg) + 1, wpabuf_len(msg) - 1); + wpabuf_free(msg); + wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", encaps); + + wpabuf_free(conn->msg_out); + conn->msg_out_pos = 0; + conn->msg_out = encaps; + conn->on_tcp_tx_complete_remove = 1; + dpp_tcp_send(conn); + + /* This exchange will be terminated in the TX status handler */ + + return 0; +} + + +static int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg, + size_t len) +{ + struct wpabuf *buf; + u8 dialog_token; + const u8 *pos, *end, *next, *adv_proto; + u16 status, slen; + + if (len < 5 + 2) + return -1; + + wpa_printf(MSG_DEBUG, + "DPP: Received DPP Configuration Response over TCP"); + + pos = msg; + end = msg + len; + + dialog_token = *pos++; + status = WPA_GET_LE16(pos); + if (status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "DPP: Unexpected Status Code %u", status); + return -1; + } + pos += 2; + pos += 2; /* ignore GAS Comeback Delay */ + + adv_proto = pos++; + slen = *pos++; + if (*adv_proto != WLAN_EID_ADV_PROTO || + slen > end - pos || slen < 2) + return -1; + + next = pos + slen; + pos++; /* skip QueryRespLenLimit and PAME-BI */ + + if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC || + pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA || + pos[5] != DPP_OUI_TYPE || pos[6] != 0x01) + return -1; + + pos = next; + /* Query Response */ + if (end - pos < 2) + return -1; + slen = WPA_GET_LE16(pos); + pos += 2; + if (slen > end - pos) + return -1; + + buf = wpabuf_alloc(slen); + if (!buf) + return -1; + wpabuf_put_data(buf, pos, slen); + + if (!conn->relay && !conn->ctrl) + return dpp_tcp_rx_gas_resp(conn, buf); + + if (!conn->relay) { + wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); + wpabuf_free(buf); + return -1; + } + wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN"); + conn->relay->gas_resp_tx(conn->relay->cb_ctx, conn->mac_addr, + dialog_token, 0, buf); + + return 0; +} + + +static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx) +{ + struct dpp_connection *conn = eloop_ctx; + int res; + const u8 *pos; + + wpa_printf(MSG_DEBUG, "DPP: TCP data available for reading (sock %d)", + sd); + + if (conn->msg_len_octets < 4) { + u32 msglen; + + res = recv(sd, &conn->msg_len[conn->msg_len_octets], + 4 - conn->msg_len_octets, 0); + if (res < 0) { + wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", + strerror(errno)); + dpp_connection_remove(conn); + return; + } + if (res == 0) { + wpa_printf(MSG_DEBUG, + "DPP: No more data available over TCP"); + dpp_connection_remove(conn); + return; + } + wpa_printf(MSG_DEBUG, + "DPP: Received %d/%d octet(s) of message length field", + res, (int) (4 - conn->msg_len_octets)); + conn->msg_len_octets += res; + + if (conn->msg_len_octets < 4) { + wpa_printf(MSG_DEBUG, + "DPP: Need %d more octets of message length field", + (int) (4 - conn->msg_len_octets)); + return; + } + + msglen = WPA_GET_BE32(conn->msg_len); + wpa_printf(MSG_DEBUG, "DPP: Message length: %u", msglen); + if (msglen > 65535) { + wpa_printf(MSG_INFO, "DPP: Unexpectedly long message"); + dpp_connection_remove(conn); + return; + } + + wpabuf_free(conn->msg); + conn->msg = wpabuf_alloc(msglen); + } + + if (!conn->msg) { + wpa_printf(MSG_DEBUG, + "DPP: No buffer available for receiving the message"); + dpp_connection_remove(conn); + return; + } + + wpa_printf(MSG_DEBUG, "DPP: Need %u more octets of message payload", + (unsigned int) wpabuf_tailroom(conn->msg)); + + res = recv(sd, wpabuf_put(conn->msg, 0), wpabuf_tailroom(conn->msg), 0); + if (res < 0) { + wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", strerror(errno)); + dpp_connection_remove(conn); + return; + } + if (res == 0) { + wpa_printf(MSG_DEBUG, "DPP: No more data available over TCP"); + dpp_connection_remove(conn); + return; + } + wpa_printf(MSG_DEBUG, "DPP: Received %d octets", res); + wpabuf_put(conn->msg, res); + + if (wpabuf_tailroom(conn->msg) > 0) { + wpa_printf(MSG_DEBUG, + "DPP: Need %u more octets of message payload", + (unsigned int) wpabuf_tailroom(conn->msg)); + return; + } + + conn->msg_len_octets = 0; + wpa_hexdump_buf(MSG_DEBUG, "DPP: Received TCP message", conn->msg); + if (wpabuf_len(conn->msg) < 1) { + dpp_connection_remove(conn); + return; + } + + pos = wpabuf_head(conn->msg); + switch (*pos) { + case WLAN_PA_VENDOR_SPECIFIC: + if (dpp_controller_rx_action(conn, pos + 1, + wpabuf_len(conn->msg) - 1) < 0) + dpp_connection_remove(conn); + break; + case WLAN_PA_GAS_INITIAL_REQ: + if (dpp_controller_rx_gas_req(conn, pos + 1, + wpabuf_len(conn->msg) - 1) < 0) + dpp_connection_remove(conn); + break; + case WLAN_PA_GAS_INITIAL_RESP: + if (dpp_rx_gas_resp(conn, pos + 1, + wpabuf_len(conn->msg) - 1) < 0) + dpp_connection_remove(conn); + break; + default: + wpa_printf(MSG_DEBUG, "DPP: Ignore unsupported message type %u", + *pos); + break; + } +} + + +static void dpp_controller_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx) +{ + struct dpp_controller *ctrl = eloop_ctx; + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + int fd; + struct dpp_connection *conn; + + wpa_printf(MSG_DEBUG, "DPP: New TCP connection"); + + fd = accept(ctrl->sock, (struct sockaddr *) &addr, &addr_len); + if (fd < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to accept new connection: %s", + strerror(errno)); + return; + } + wpa_printf(MSG_DEBUG, "DPP: Connection from %s:%d", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + + conn = os_zalloc(sizeof(*conn)); + if (!conn) + goto fail; + + conn->global = ctrl->global; + conn->ctrl = ctrl; + conn->sock = fd; + + if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) { + wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s", + strerror(errno)); + goto fail; + } + + if (eloop_register_sock(conn->sock, EVENT_TYPE_READ, + dpp_controller_rx, conn, NULL) < 0) + goto fail; + conn->read_eloop = 1; + + /* TODO: eloop timeout to expire connections that do not complete in + * reasonable time */ + dl_list_add(&ctrl->conn, &conn->list); + return; + +fail: + close(fd); + os_free(conn); +} + + +int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth, + const struct hostapd_ip_addr *addr, int port) +{ + struct dpp_connection *conn; + struct sockaddr_storage saddr; + socklen_t addrlen; + const u8 *hdr, *pos, *end; + char txt[100]; + + wpa_printf(MSG_DEBUG, "DPP: Initialize TCP connection to %s port %d", + hostapd_ip_txt(addr, txt, sizeof(txt)), port); + if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &saddr, &addrlen, + addr, port) < 0) { + dpp_auth_deinit(auth); + return -1; + } + + conn = os_zalloc(sizeof(*conn)); + if (!conn) { + dpp_auth_deinit(auth); + return -1; + } + + conn->global = dpp; + conn->auth = auth; + conn->sock = socket(AF_INET, SOCK_STREAM, 0); + if (conn->sock < 0) + goto fail; + + if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) { + wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s", + strerror(errno)); + goto fail; + } + + if (connect(conn->sock, (struct sockaddr *) &saddr, addrlen) < 0) { + if (errno != EINPROGRESS) { + wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s", + strerror(errno)); + goto fail; + } + + /* + * Continue connecting in the background; eloop will call us + * once the connection is ready (or failed). + */ + } + + if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, + dpp_conn_tx_ready, conn, NULL) < 0) + goto fail; + conn->write_eloop = 1; + + hdr = wpabuf_head(auth->req_msg); + end = hdr + wpabuf_len(auth->req_msg); + hdr += 2; /* skip Category and Actiom */ + pos = hdr + DPP_HDR_LEN; + conn->msg_out = dpp_tcp_encaps(hdr, pos, end - pos); + if (!conn->msg_out) + goto fail; + /* Message will be sent in dpp_conn_tx_ready() */ + + /* TODO: eloop timeout to clear a connection if it does not complete + * properly */ + dl_list_add(&dpp->tcp_init, &conn->list); + return 0; +fail: + dpp_connection_free(conn); + return -1; +} + + +int dpp_controller_start(struct dpp_global *dpp, + struct dpp_controller_config *config) +{ + struct dpp_controller *ctrl; + int on = 1; + struct sockaddr_in sin; + int port; + + if (!dpp || dpp->controller) + return -1; + + ctrl = os_zalloc(sizeof(*ctrl)); + if (!ctrl) + return -1; + ctrl->global = dpp; + if (config->configurator_params) + ctrl->configurator_params = + os_strdup(config->configurator_params); + dl_list_init(&ctrl->conn); + /* TODO: configure these somehow */ + ctrl->allowed_roles = DPP_CAPAB_ENROLLEE | DPP_CAPAB_CONFIGURATOR; + ctrl->qr_mutual = 0; + + ctrl->sock = socket(AF_INET, SOCK_STREAM, 0); + if (ctrl->sock < 0) + goto fail; + + if (setsockopt(ctrl->sock, SOL_SOCKET, SO_REUSEADDR, + &on, sizeof(on)) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: setsockopt(SO_REUSEADDR) failed: %s", + strerror(errno)); + /* try to continue anyway */ + } + + if (fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0) { + wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s", + strerror(errno)); + goto fail; + } + + /* TODO: IPv6 */ + os_memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + port = config->tcp_port ? config->tcp_port : DPP_TCP_PORT; + sin.sin_port = htons(port); + if (bind(ctrl->sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { + wpa_printf(MSG_INFO, + "DPP: Failed to bind Controller TCP port: %s", + strerror(errno)); + goto fail; + } + if (listen(ctrl->sock, 10 /* max backlog */) < 0 || + fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0 || + eloop_register_sock(ctrl->sock, EVENT_TYPE_READ, + dpp_controller_tcp_cb, ctrl, NULL)) + goto fail; + + dpp->controller = ctrl; + wpa_printf(MSG_DEBUG, "DPP: Controller started on TCP port %d", port); + return 0; +fail: + dpp_controller_free(ctrl); + return -1; +} + + +void dpp_controller_stop(struct dpp_global *dpp) +{ + if (dpp) { + dpp_controller_free(dpp->controller); + dpp->controller = NULL; + } +} + +#endif /* CONFIG_DPP2 */ diff --git a/contrib/wpa/src/common/dpp.h b/contrib/wpa/src/common/dpp.h index 5a6d8cc79c2c..db640efe86b9 100644 --- a/contrib/wpa/src/common/dpp.h +++ b/contrib/wpa/src/common/dpp.h @@ -18,9 +18,11 @@ #include "crypto/sha256.h" struct crypto_ecdh; +struct hostapd_ip_addr; struct dpp_global; #define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */ +#define DPP_TCP_PORT 7871 enum dpp_public_action_frame_type { DPP_PA_AUTHENTICATION_REQ = 0, @@ -259,6 +261,22 @@ struct dpp_introduction { size_t pmk_len; }; +struct dpp_relay_config { + const struct hostapd_ip_addr *ipaddr; + const u8 *pkhash; + + void *cb_ctx; + void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg, + size_t len); + void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token, int prot, + struct wpabuf *buf); +}; + +struct dpp_controller_config { + const char *configurator_params; + int tcp_port; +}; + #ifdef CONFIG_TESTING_OPTIONS enum dpp_test_behavior { DPP_TEST_DISABLED = 0, @@ -497,7 +515,26 @@ int dpp_configurator_add(struct dpp_global *dpp, const char *cmd); int dpp_configurator_remove(struct dpp_global *dpp, const char *id); int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id, char *buf, size_t buflen); -struct dpp_global * dpp_global_init(void); +int dpp_relay_add_controller(struct dpp_global *dpp, + struct dpp_relay_config *config); +int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr, + const u8 *buf, size_t len, unsigned int freq, + const u8 *i_bootstrap, const u8 *r_bootstrap); +int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data, + size_t data_len); +int dpp_controller_start(struct dpp_global *dpp, + struct dpp_controller_config *config); +void dpp_controller_stop(struct dpp_global *dpp); +int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth, + const struct hostapd_ip_addr *addr, int port); + +struct dpp_global_config { + void *msg_ctx; + void *cb_ctx; + int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth); +}; + +struct dpp_global * dpp_global_init(struct dpp_global_config *config); void dpp_global_clear(struct dpp_global *dpp); void dpp_global_deinit(struct dpp_global *dpp); diff --git a/contrib/wpa/src/common/dragonfly.c b/contrib/wpa/src/common/dragonfly.c new file mode 100644 index 000000000000..547be66f1561 --- /dev/null +++ b/contrib/wpa/src/common/dragonfly.c @@ -0,0 +1,215 @@ +/* + * Shared Dragonfly functionality + * Copyright (c) 2012-2016, Jouni Malinen + * Copyright (c) 2019, The Linux Foundation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/const_time.h" +#include "crypto/crypto.h" +#include "dragonfly.h" + + +int dragonfly_suitable_group(int group, int ecc_only) +{ + /* Enforce REVmd rules on which SAE groups are suitable for production + * purposes: FFC groups whose prime is >= 3072 bits and ECC groups + * defined over a prime field whose prime is >= 256 bits. Furthermore, + * ECC groups defined over a characteristic 2 finite field and ECC + * groups with a co-factor greater than 1 are not suitable. Disable + * groups that use Brainpool curves as well for now since they leak more + * timing information due to the prime not being close to a power of + * two. */ + return group == 19 || group == 20 || group == 21 || + (!ecc_only && + (group == 15 || group == 16 || group == 17 || group == 18)); +} + + +unsigned int dragonfly_min_pwe_loop_iter(int group) +{ + if (group == 22 || group == 23 || group == 24) { + /* FFC groups for which pwd-value is likely to be >= p + * frequently */ + return 40; + } + + if (group == 1 || group == 2 || group == 5 || group == 14 || + group == 15 || group == 16 || group == 17 || group == 18) { + /* FFC groups that have prime that is close to a power of two */ + return 1; + } + + /* Default to 40 (this covers most ECC groups) */ + return 40; +} + + +int dragonfly_get_random_qr_qnr(const struct crypto_bignum *prime, + struct crypto_bignum **qr, + struct crypto_bignum **qnr) +{ + *qr = *qnr = NULL; + + while (!(*qr) || !(*qnr)) { + struct crypto_bignum *tmp; + int res; + + tmp = crypto_bignum_init(); + if (!tmp || crypto_bignum_rand(tmp, prime) < 0) { + crypto_bignum_deinit(tmp, 0); + break; + } + + res = crypto_bignum_legendre(tmp, prime); + if (res == 1 && !(*qr)) + *qr = tmp; + else if (res == -1 && !(*qnr)) + *qnr = tmp; + else + crypto_bignum_deinit(tmp, 0); + } + + if (*qr && *qnr) + return 0; + crypto_bignum_deinit(*qr, 0); + crypto_bignum_deinit(*qnr, 0); + *qr = *qnr = NULL; + return -1; +} + + +static struct crypto_bignum * +dragonfly_get_rand_1_to_p_1(const struct crypto_bignum *prime) +{ + struct crypto_bignum *tmp, *pm1, *one; + + tmp = crypto_bignum_init(); + pm1 = crypto_bignum_init(); + one = crypto_bignum_init_set((const u8 *) "\x01", 1); + if (!tmp || !pm1 || !one || + crypto_bignum_sub(prime, one, pm1) < 0 || + crypto_bignum_rand(tmp, pm1) < 0 || + crypto_bignum_add(tmp, one, tmp) < 0) { + crypto_bignum_deinit(tmp, 0); + tmp = NULL; + } + + crypto_bignum_deinit(pm1, 0); + crypto_bignum_deinit(one, 0); + return tmp; +} + + +int dragonfly_is_quadratic_residue_blind(struct crypto_ec *ec, + const u8 *qr, const u8 *qnr, + const struct crypto_bignum *val) +{ + struct crypto_bignum *r, *num, *qr_or_qnr = NULL; + int check, res = -1; + u8 qr_or_qnr_bin[DRAGONFLY_MAX_ECC_PRIME_LEN]; + const struct crypto_bignum *prime; + size_t prime_len; + unsigned int mask; + + prime = crypto_ec_get_prime(ec); + prime_len = crypto_ec_prime_len(ec); + + /* + * Use a blinding technique to mask val while determining whether it is + * a quadratic residue modulo p to avoid leaking timing information + * while determining the Legendre symbol. + * + * v = val + * r = a random number between 1 and p-1, inclusive + * num = (v * r * r) modulo p + */ + r = dragonfly_get_rand_1_to_p_1(prime); + if (!r) + return -1; + + num = crypto_bignum_init(); + if (!num || + crypto_bignum_mulmod(val, r, prime, num) < 0 || + crypto_bignum_mulmod(num, r, prime, num) < 0) + goto fail; + + /* + * Need to minimize differences in handling different cases, so try to + * avoid branches and timing differences. + * + * If r is odd: + * num = (num * qr) module p + * LGR(num, p) = 1 ==> quadratic residue + * else: + * num = (num * qnr) module p + * LGR(num, p) = -1 ==> quadratic residue + * + * mask is set to !odd(r) + */ + mask = const_time_is_zero(crypto_bignum_is_odd(r)); + const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin); + qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len); + if (!qr_or_qnr || + crypto_bignum_mulmod(num, qr_or_qnr, prime, num) < 0) + goto fail; + /* branchless version of check = odd(r) ? 1 : -1, */ + check = const_time_select_int(mask, -1, 1); + + /* Determine the Legendre symbol on the masked value */ + res = crypto_bignum_legendre(num, prime); + if (res == -2) { + res = -1; + goto fail; + } + /* branchless version of res = res == check + * (res is -1, 0, or 1; check is -1 or 1) */ + mask = const_time_eq(res, check); + res = const_time_select_int(mask, 1, 0); +fail: + crypto_bignum_deinit(num, 1); + crypto_bignum_deinit(r, 1); + crypto_bignum_deinit(qr_or_qnr, 1); + return res; +} + + +static int dragonfly_get_rand_2_to_r_1(struct crypto_bignum *val, + const struct crypto_bignum *order) +{ + return crypto_bignum_rand(val, order) == 0 && + !crypto_bignum_is_zero(val) && + !crypto_bignum_is_one(val); +} + + +int dragonfly_generate_scalar(const struct crypto_bignum *order, + struct crypto_bignum *_rand, + struct crypto_bignum *_mask, + struct crypto_bignum *scalar) +{ + int count; + + /* Select two random values rand,mask such that 1 < rand,mask < r and + * rand + mask mod r > 1. */ + for (count = 0; count < 100; count++) { + if (dragonfly_get_rand_2_to_r_1(_rand, order) && + dragonfly_get_rand_2_to_r_1(_mask, order) && + crypto_bignum_add(_rand, _mask, scalar) == 0 && + crypto_bignum_mod(scalar, order, scalar) == 0 && + !crypto_bignum_is_zero(scalar) && + !crypto_bignum_is_one(scalar)) + return 0; + } + + /* This should not be reachable in practice if the random number + * generation is working. */ + wpa_printf(MSG_INFO, + "dragonfly: Unable to get randomness for own scalar"); + return -1; +} diff --git a/contrib/wpa/src/common/dragonfly.h b/contrib/wpa/src/common/dragonfly.h new file mode 100644 index 000000000000..ec3dd593eda4 --- /dev/null +++ b/contrib/wpa/src/common/dragonfly.h @@ -0,0 +1,31 @@ +/* + * Shared Dragonfly functionality + * Copyright (c) 2012-2016, Jouni Malinen + * Copyright (c) 2019, The Linux Foundation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DRAGONFLY_H +#define DRAGONFLY_H + +#define DRAGONFLY_MAX_ECC_PRIME_LEN 66 + +struct crypto_bignum; +struct crypto_ec; + +int dragonfly_suitable_group(int group, int ecc_only); +unsigned int dragonfly_min_pwe_loop_iter(int group); +int dragonfly_get_random_qr_qnr(const struct crypto_bignum *prime, + struct crypto_bignum **qr, + struct crypto_bignum **qnr); +int dragonfly_is_quadratic_residue_blind(struct crypto_ec *ec, + const u8 *qr, const u8 *qnr, + const struct crypto_bignum *val); +int dragonfly_generate_scalar(const struct crypto_bignum *order, + struct crypto_bignum *_rand, + struct crypto_bignum *_mask, + struct crypto_bignum *scalar); + +#endif /* DRAGONFLY_H */ diff --git a/contrib/wpa/src/common/hw_features_common.c b/contrib/wpa/src/common/hw_features_common.c index 49ed80657521..3fdbf893d2b7 100644 --- a/contrib/wpa/src/common/hw_features_common.c +++ b/contrib/wpa/src/common/hw_features_common.c @@ -361,30 +361,35 @@ int check_40mhz_2g4(struct hostapd_hw_modes *mode, int hostapd_set_freq_params(struct hostapd_freq_params *data, enum hostapd_hw_mode mode, int freq, int channel, int ht_enabled, - int vht_enabled, int sec_channel_offset, - int vht_oper_chwidth, int center_segment0, - int center_segment1, u32 vht_caps) + int vht_enabled, int he_enabled, + int sec_channel_offset, + int oper_chwidth, int center_segment0, + int center_segment1, u32 vht_caps, + struct he_capabilities *he_cap) { + if (!he_cap) + he_enabled = 0; os_memset(data, 0, sizeof(*data)); data->mode = mode; data->freq = freq; data->channel = channel; data->ht_enabled = ht_enabled; data->vht_enabled = vht_enabled; + data->he_enabled = he_enabled; data->sec_channel_offset = sec_channel_offset; data->center_freq1 = freq + sec_channel_offset * 10; data->center_freq2 = 0; data->bandwidth = sec_channel_offset ? 40 : 20; - if (data->vht_enabled) switch (vht_oper_chwidth) { - case VHT_CHANWIDTH_USE_HT: + if (data->vht_enabled) switch (oper_chwidth) { + case CHANWIDTH_USE_HT: if (center_segment1 || (center_segment0 != 0 && 5000 + center_segment0 * 5 != data->center_freq1 && 2407 + center_segment0 * 5 != data->center_freq1)) return -1; break; - case VHT_CHANWIDTH_80P80MHZ: + case CHANWIDTH_80P80MHZ: if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) { wpa_printf(MSG_ERROR, "80+80 channel width is not supported!"); @@ -395,11 +400,11 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, return -1; data->center_freq2 = 5000 + center_segment1 * 5; /* fall through */ - case VHT_CHANWIDTH_80MHZ: + case CHANWIDTH_80MHZ: data->bandwidth = 80; - if ((vht_oper_chwidth == VHT_CHANWIDTH_80MHZ && + if ((oper_chwidth == CHANWIDTH_80MHZ && center_segment1) || - (vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ && + (oper_chwidth == CHANWIDTH_80P80MHZ && !center_segment1) || !sec_channel_offset) return -1; @@ -432,7 +437,7 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, return -1; } break; - case VHT_CHANWIDTH_160MHZ: + case CHANWIDTH_160MHZ: data->bandwidth = 160; if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) { diff --git a/contrib/wpa/src/common/hw_features_common.h b/contrib/wpa/src/common/hw_features_common.h index eb1f1c57f10f..2d2a539943c0 100644 --- a/contrib/wpa/src/common/hw_features_common.h +++ b/contrib/wpa/src/common/hw_features_common.h @@ -32,9 +32,11 @@ int check_40mhz_2g4(struct hostapd_hw_modes *mode, int hostapd_set_freq_params(struct hostapd_freq_params *data, enum hostapd_hw_mode mode, int freq, int channel, int ht_enabled, - int vht_enabled, int sec_channel_offset, - int vht_oper_chwidth, int center_segment0, - int center_segment1, u32 vht_caps); + int vht_enabled, int he_enabled, + int sec_channel_offset, + int oper_chwidth, int center_segment0, + int center_segment1, u32 vht_caps, + struct he_capabilities *he_caps); void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps, int disabled); int ieee80211ac_cap_check(u32 hw, u32 conf); diff --git a/contrib/wpa/src/common/ieee802_11_common.c b/contrib/wpa/src/common/ieee802_11_common.c index e42a327449eb..a081f87cc2a7 100644 --- a/contrib/wpa/src/common/ieee802_11_common.c +++ b/contrib/wpa/src/common/ieee802_11_common.c @@ -274,6 +274,10 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, elems->he_capabilities = pos; elems->he_capabilities_len = elen; break; + case WLAN_EID_EXT_HE_OPERATION: + elems->he_operation = pos; + elems->he_operation_len = elen; + break; case WLAN_EID_EXT_OCV_OCI: elems->oci = pos; elems->oci_len = elen; @@ -702,7 +706,7 @@ enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) { u8 op_class; - return ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT, + return ieee80211_freq_to_channel_ext(freq, 0, CHANWIDTH_USE_HT, &op_class, channel); } @@ -712,7 +716,7 @@ enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) * for HT40 and VHT. DFS channels are not covered. * @freq: Frequency (MHz) to convert * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below - * @vht: VHT channel width (VHT_CHANWIDTH_*) + * @vht: VHT channel width (CHANWIDTH_*) * @op_class: Buffer for returning operating class * @channel: Buffer for returning channel number * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure @@ -767,13 +771,13 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, } switch (vht) { - case VHT_CHANWIDTH_80MHZ: + case CHANWIDTH_80MHZ: vht_opclass = 128; break; - case VHT_CHANWIDTH_160MHZ: + case CHANWIDTH_160MHZ: vht_opclass = 129; break; - case VHT_CHANWIDTH_80P80MHZ: + case CHANWIDTH_80P80MHZ: vht_opclass = 130; break; default: @@ -892,16 +896,16 @@ int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth, case CHAN_WIDTH_20_NOHT: case CHAN_WIDTH_20: case CHAN_WIDTH_40: - vht = VHT_CHANWIDTH_USE_HT; + vht = CHANWIDTH_USE_HT; break; case CHAN_WIDTH_80: - vht = VHT_CHANWIDTH_80MHZ; + vht = CHANWIDTH_80MHZ; break; case CHAN_WIDTH_80P80: - vht = VHT_CHANWIDTH_80P80MHZ; + vht = CHANWIDTH_80P80MHZ; break; case CHAN_WIDTH_160: - vht = VHT_CHANWIDTH_160MHZ; + vht = CHANWIDTH_160MHZ; break; } @@ -1315,6 +1319,185 @@ const char * fc2str(u16 fc) } +const char * reason2str(u16 reason) +{ +#define R2S(r) case WLAN_REASON_ ## r: return #r; + switch (reason) { + R2S(UNSPECIFIED) + R2S(PREV_AUTH_NOT_VALID) + R2S(DEAUTH_LEAVING) + R2S(DISASSOC_DUE_TO_INACTIVITY) + R2S(DISASSOC_AP_BUSY) + R2S(CLASS2_FRAME_FROM_NONAUTH_STA) + R2S(CLASS3_FRAME_FROM_NONASSOC_STA) + R2S(DISASSOC_STA_HAS_LEFT) + R2S(STA_REQ_ASSOC_WITHOUT_AUTH) + R2S(PWR_CAPABILITY_NOT_VALID) + R2S(SUPPORTED_CHANNEL_NOT_VALID) + R2S(BSS_TRANSITION_DISASSOC) + R2S(INVALID_IE) + R2S(MICHAEL_MIC_FAILURE) + R2S(4WAY_HANDSHAKE_TIMEOUT) + R2S(GROUP_KEY_UPDATE_TIMEOUT) + R2S(IE_IN_4WAY_DIFFERS) + R2S(GROUP_CIPHER_NOT_VALID) + R2S(PAIRWISE_CIPHER_NOT_VALID) + R2S(AKMP_NOT_VALID) + R2S(UNSUPPORTED_RSN_IE_VERSION) + R2S(INVALID_RSN_IE_CAPAB) + R2S(IEEE_802_1X_AUTH_FAILED) + R2S(CIPHER_SUITE_REJECTED) + R2S(TDLS_TEARDOWN_UNREACHABLE) + R2S(TDLS_TEARDOWN_UNSPECIFIED) + R2S(SSP_REQUESTED_DISASSOC) + R2S(NO_SSP_ROAMING_AGREEMENT) + R2S(BAD_CIPHER_OR_AKM) + R2S(NOT_AUTHORIZED_THIS_LOCATION) + R2S(SERVICE_CHANGE_PRECLUDES_TS) + R2S(UNSPECIFIED_QOS_REASON) + R2S(NOT_ENOUGH_BANDWIDTH) + R2S(DISASSOC_LOW_ACK) + R2S(EXCEEDED_TXOP) + R2S(STA_LEAVING) + R2S(END_TS_BA_DLS) + R2S(UNKNOWN_TS_BA) + R2S(TIMEOUT) + R2S(PEERKEY_MISMATCH) + R2S(AUTHORIZED_ACCESS_LIMIT_REACHED) + R2S(EXTERNAL_SERVICE_REQUIREMENTS) + R2S(INVALID_FT_ACTION_FRAME_COUNT) + R2S(INVALID_PMKID) + R2S(INVALID_MDE) + R2S(INVALID_FTE) + R2S(MESH_PEERING_CANCELLED) + R2S(MESH_MAX_PEERS) + R2S(MESH_CONFIG_POLICY_VIOLATION) + R2S(MESH_CLOSE_RCVD) + R2S(MESH_MAX_RETRIES) + R2S(MESH_CONFIRM_TIMEOUT) + R2S(MESH_INVALID_GTK) + R2S(MESH_INCONSISTENT_PARAMS) + R2S(MESH_INVALID_SECURITY_CAP) + R2S(MESH_PATH_ERROR_NO_PROXY_INFO) + R2S(MESH_PATH_ERROR_NO_FORWARDING_INFO) + R2S(MESH_PATH_ERROR_DEST_UNREACHABLE) + R2S(MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS) + R2S(MESH_CHANNEL_SWITCH_REGULATORY_REQ) + R2S(MESH_CHANNEL_SWITCH_UNSPECIFIED) + } + return "UNKNOWN"; +#undef R2S +} + + +const char * status2str(u16 status) +{ +#define S2S(s) case WLAN_STATUS_ ## s: return #s; + switch (status) { + S2S(SUCCESS) + S2S(UNSPECIFIED_FAILURE) + S2S(TDLS_WAKEUP_ALTERNATE) + S2S(TDLS_WAKEUP_REJECT) + S2S(SECURITY_DISABLED) + S2S(UNACCEPTABLE_LIFETIME) + S2S(NOT_IN_SAME_BSS) + S2S(CAPS_UNSUPPORTED) + S2S(REASSOC_NO_ASSOC) + S2S(ASSOC_DENIED_UNSPEC) + S2S(NOT_SUPPORTED_AUTH_ALG) + S2S(UNKNOWN_AUTH_TRANSACTION) + S2S(CHALLENGE_FAIL) + S2S(AUTH_TIMEOUT) + S2S(AP_UNABLE_TO_HANDLE_NEW_STA) + S2S(ASSOC_DENIED_RATES) + S2S(ASSOC_DENIED_NOSHORT) + S2S(SPEC_MGMT_REQUIRED) + S2S(PWR_CAPABILITY_NOT_VALID) + S2S(SUPPORTED_CHANNEL_NOT_VALID) + S2S(ASSOC_DENIED_NO_SHORT_SLOT_TIME) + S2S(ASSOC_DENIED_NO_HT) + S2S(R0KH_UNREACHABLE) + S2S(ASSOC_DENIED_NO_PCO) + S2S(ASSOC_REJECTED_TEMPORARILY) + S2S(ROBUST_MGMT_FRAME_POLICY_VIOLATION) + S2S(UNSPECIFIED_QOS_FAILURE) + S2S(DENIED_INSUFFICIENT_BANDWIDTH) + S2S(DENIED_POOR_CHANNEL_CONDITIONS) + S2S(DENIED_QOS_NOT_SUPPORTED) + S2S(REQUEST_DECLINED) + S2S(INVALID_PARAMETERS) + S2S(REJECTED_WITH_SUGGESTED_CHANGES) + S2S(INVALID_IE) + S2S(GROUP_CIPHER_NOT_VALID) + S2S(PAIRWISE_CIPHER_NOT_VALID) + S2S(AKMP_NOT_VALID) + S2S(UNSUPPORTED_RSN_IE_VERSION) + S2S(INVALID_RSN_IE_CAPAB) + S2S(CIPHER_REJECTED_PER_POLICY) + S2S(TS_NOT_CREATED) + S2S(DIRECT_LINK_NOT_ALLOWED) + S2S(DEST_STA_NOT_PRESENT) + S2S(DEST_STA_NOT_QOS_STA) + S2S(ASSOC_DENIED_LISTEN_INT_TOO_LARGE) + S2S(INVALID_FT_ACTION_FRAME_COUNT) + S2S(INVALID_PMKID) + S2S(INVALID_MDIE) + S2S(INVALID_FTIE) + S2S(REQUESTED_TCLAS_NOT_SUPPORTED) + S2S(INSUFFICIENT_TCLAS_PROCESSING_RESOURCES) + S2S(TRY_ANOTHER_BSS) + S2S(GAS_ADV_PROTO_NOT_SUPPORTED) + S2S(NO_OUTSTANDING_GAS_REQ) + S2S(GAS_RESP_NOT_RECEIVED) + S2S(STA_TIMED_OUT_WAITING_FOR_GAS_RESP) + S2S(GAS_RESP_LARGER_THAN_LIMIT) + S2S(REQ_REFUSED_HOME) + S2S(ADV_SRV_UNREACHABLE) + S2S(REQ_REFUSED_SSPN) + S2S(REQ_REFUSED_UNAUTH_ACCESS) + S2S(INVALID_RSNIE) + S2S(U_APSD_COEX_NOT_SUPPORTED) + S2S(U_APSD_COEX_MODE_NOT_SUPPORTED) + S2S(BAD_INTERVAL_WITH_U_APSD_COEX) + S2S(ANTI_CLOGGING_TOKEN_REQ) + S2S(FINITE_CYCLIC_GROUP_NOT_SUPPORTED) + S2S(CANNOT_FIND_ALT_TBTT) + S2S(TRANSMISSION_FAILURE) + S2S(REQ_TCLAS_NOT_SUPPORTED) + S2S(TCLAS_RESOURCES_EXCHAUSTED) + S2S(REJECTED_WITH_SUGGESTED_BSS_TRANSITION) + S2S(REJECT_WITH_SCHEDULE) + S2S(REJECT_NO_WAKEUP_SPECIFIED) + S2S(SUCCESS_POWER_SAVE_MODE) + S2S(PENDING_ADMITTING_FST_SESSION) + S2S(PERFORMING_FST_NOW) + S2S(PENDING_GAP_IN_BA_WINDOW) + S2S(REJECT_U_PID_SETTING) + S2S(REFUSED_EXTERNAL_REASON) + S2S(REFUSED_AP_OUT_OF_MEMORY) + S2S(REJECTED_EMERGENCY_SERVICE_NOT_SUPPORTED) + S2S(QUERY_RESP_OUTSTANDING) + S2S(REJECT_DSE_BAND) + S2S(TCLAS_PROCESSING_TERMINATED) + S2S(TS_SCHEDULE_CONFLICT) + S2S(DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL) + S2S(MCCAOP_RESERVATION_CONFLICT) + S2S(MAF_LIMIT_EXCEEDED) + S2S(MCCA_TRACK_LIMIT_EXCEEDED) + S2S(DENIED_DUE_TO_SPECTRUM_MANAGEMENT) + S2S(ASSOC_DENIED_NO_VHT) + S2S(ENABLEMENT_DENIED) + S2S(RESTRICTION_FROM_AUTHORIZED_GDB) + S2S(AUTHORIZATION_DEENABLED) + S2S(FILS_AUTHENTICATION_FAILURE) + S2S(UNKNOWN_AUTHENTICATION_SERVER) + S2S(UNKNOWN_PASSWORD_IDENTIFIER) + } + return "UNKNOWN"; +#undef S2S +} + + int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, size_t ies_len) { diff --git a/contrib/wpa/src/common/ieee802_11_common.h b/contrib/wpa/src/common/ieee802_11_common.h index d41bd39e7a93..9b045b41a386 100644 --- a/contrib/wpa/src/common/ieee802_11_common.h +++ b/contrib/wpa/src/common/ieee802_11_common.h @@ -94,6 +94,7 @@ struct ieee802_11_elems { const u8 *oci; const u8 *multi_ap; const u8 *he_capabilities; + const u8 *he_operation; u8 ssid_len; u8 supp_rates_len; @@ -143,6 +144,7 @@ struct ieee802_11_elems { u8 oci_len; u8 multi_ap_len; u8 he_capabilities_len; + u8 he_operation_len; struct mb_ies_info mb_ies; }; @@ -185,6 +187,8 @@ int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, struct wpabuf * mb_ies_by_info(struct mb_ies_info *info); const char * fc2str(u16 fc); +const char * reason2str(u16 reason); +const char * status2str(u16 status); struct oper_class_map { enum hostapd_hw_mode mode; diff --git a/contrib/wpa/src/common/ieee802_11_defs.h b/contrib/wpa/src/common/ieee802_11_defs.h index adaa8931093e..b0aa913bbc9a 100644 --- a/contrib/wpa/src/common/ieee802_11_defs.h +++ b/contrib/wpa/src/common/ieee802_11_defs.h @@ -468,6 +468,7 @@ #define WLAN_EID_EXT_HE_CAPABILITIES 35 #define WLAN_EID_EXT_HE_OPERATION 36 #define WLAN_EID_EXT_HE_MU_EDCA_PARAMS 38 +#define WLAN_EID_EXT_SPATIAL_REUSE 39 #define WLAN_EID_EXT_OCV_OCI 54 /* Extended Capabilities field */ @@ -1274,10 +1275,12 @@ struct ieee80211_ampe_ie { #define VHT_RX_NSS_MAX_STREAMS 8 /* VHT channel widths */ -#define VHT_CHANWIDTH_USE_HT 0 -#define VHT_CHANWIDTH_80MHZ 1 -#define VHT_CHANWIDTH_160MHZ 2 -#define VHT_CHANWIDTH_80P80MHZ 3 +#define CHANWIDTH_USE_HT 0 +#define CHANWIDTH_80MHZ 1 +#define CHANWIDTH_160MHZ 2 +#define CHANWIDTH_80P80MHZ 3 + +#define HE_NSS_MAX_STREAMS 8 #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) * 00:50:F2 */ @@ -2091,7 +2094,7 @@ enum phy_type { /* * IEEE P802.11-REVmc/D5.0 Table 9-152 - HT/VHT Operation Information * subfields. - * Note: These definitions are not the same as other VHT_CHANWIDTH_*. + * Note: These definitions are not the same as other CHANWIDTH_*. */ enum nr_chan_width { NR_CHAN_WIDTH_20 = 0, @@ -2104,21 +2107,46 @@ enum nr_chan_width { struct ieee80211_he_capabilities { u8 he_mac_capab_info[6]; u8 he_phy_capab_info[11]; - u8 he_txrx_mcs_support[12]; /* TODO: 4, 8, or 12 octets */ - /* PPE Thresholds (optional) */ + /* Followed by 4, 8, or 12 octets of Supported HE-MCS And NSS Set field + * and optional variable length PPE Thresholds field. */ + u8 optional[]; } STRUCT_PACKED; struct ieee80211_he_operation { - u32 he_oper_params; /* HE Operation Parameters[3] and - * BSS Color Information[1] */ - u8 he_mcs_nss_set[2]; + le32 he_oper_params; /* HE Operation Parameters[3] and + * BSS Color Information[1] */ + le16 he_mcs_nss_set; u8 vht_op_info_chwidth; u8 vht_op_info_chan_center_freq_seg0_idx; u8 vht_op_info_chan_center_freq_seg1_idx; /* Followed by conditional MaxBSSID Indicator subfield (u8) */ } STRUCT_PACKED; +/* + * IEEE P802.11ax/D4.0, 9.4.2.246 Spatial Reuse Parameter Set element + */ +struct ieee80211_spatial_reuse { + u8 sr_ctrl; /* SR Control */ + /* Up to 19 octets of parameters: + * Non-SRG OBSS PD Max Offset[0 or 1] + * SRG OBSS PD Min Offset[0 or 1] + * SRG OBSS PD Max Offset[0 or 1] + * SRG BSS Color Bitmap[0 or 8] + * SRG Partial BSSID Bitmap[0 or 8] + */ + u8 params[19]; +} STRUCT_PACKED; + /* HE Capabilities Information defines */ + +#define HE_PHYCAP_CHANNEL_WIDTH_SET_IDX 0 +#define HE_PHYCAP_CHANNEL_WIDTH_MASK ((u8) (BIT(1) | BIT(2) | \ + BIT(3) | BIT(4))) +#define HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G ((u8) BIT(1)) +#define HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G ((u8) BIT(2)) +#define HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G ((u8) BIT(3)) +#define HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G ((u8) BIT(4)) + #define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX 3 #define HE_PHYCAP_SU_BEAMFORMER_CAPAB ((u8) BIT(7)) #define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX 4 @@ -2126,23 +2154,39 @@ struct ieee80211_he_operation { #define HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX 4 #define HE_PHYCAP_MU_BEAMFORMER_CAPAB ((u8) BIT(1)) +#define HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX 6 +#define HE_PHYCAP_PPE_THRESHOLD_PRESENT ((u8) BIT(7)) + +/* HE PPE Threshold define */ +#define HE_PPE_THRES_RU_INDEX_BITMASK_MASK 0xf +#define HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT 3 +#define HE_PPE_THRES_NSS_MASK 0x7 + /* HE Operation defines */ /* HE Operation Parameters and BSS Color Information fields */ -#define HE_OPERATION_BSS_COLOR_MASK ((u32) (BIT(0) | BIT(1) | \ - BIT(2) | BIT(3) | \ - BIT(4) | BIT(5))) -#define HE_OPERATION_PARTIAL_BSS_COLOR ((u32) BIT(6)) -#define HE_OPERATION_BSS_COLOR_DISABLED ((u32) BIT(7)) -#define HE_OPERATION_DFLT_PE_DURATION_MASK ((u32) (BIT(8) | BIT(9) | \ - BIT(10))) -#define HE_OPERATION_DFLT_PE_DURATION_OFFSET 8 -#define HE_OPERATION_TWT_REQUIRED ((u32) BIT(11)) -#define HE_OPERATION_RTS_THRESHOLD_MASK ((u32) (BIT(12) | BIT(13) | \ - BIT(14) | BIT(15) | \ - BIT(16) | BIT(17) | \ - BIT(18) | BIT(19) | \ - BIT(20) | BIT(21))) -#define HE_OPERATION_RTS_THRESHOLD_OFFSET 12 +#define HE_OPERATION_DFLT_PE_DURATION_MASK ((u32) (BIT(0) | BIT(1) | \ + BIT(2))) +#define HE_OPERATION_DFLT_PE_DURATION_OFFSET 0 +#define HE_OPERATION_TWT_REQUIRED ((u32) BIT(3)) +#define HE_OPERATION_RTS_THRESHOLD_MASK ((u32) (BIT(4) | BIT(5) | \ + BIT(6) | BIT(7) | \ + BIT(8) | BIT(9) | \ + BIT(10) | BIT(11) | \ + BIT(12) | BIT(13))) +#define HE_OPERATION_RTS_THRESHOLD_OFFSET 4 +#define HE_OPERATION_BSS_COLOR_MASK ((u32) (BIT(24) | BIT(25) | \ + BIT(26) | BIT(27) | \ + BIT(28) | BIT(29))) +#define HE_OPERATION_PARTIAL_BSS_COLOR ((u32) BIT(30)) +#define HE_OPERATION_BSS_COLOR_DISABLED ((u32) BIT(31)) +#define HE_OPERATION_BSS_COLOR_OFFSET 24 + +/* Spatial Reuse defines */ +#define SPATIAL_REUSE_SRP_DISALLOWED BIT(0) +#define SPATIAL_REUSE_NON_SRG_OBSS_PD_SR_DISALLOWED BIT(1) +#define SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT BIT(2) +#define SPATIAL_REUSE_SRG_INFORMATION_PRESENT BIT(3) +#define SPATIAL_REUSE_HESIGA_SR_VAL15_ALLOWED BIT(4) struct ieee80211_he_mu_edca_parameter_set { u8 he_qos_info; diff --git a/contrib/wpa/src/common/qca-vendor.h b/contrib/wpa/src/common/qca-vendor.h index c34a3bc1f3e4..ff8c22a7546c 100644 --- a/contrib/wpa/src/common/qca-vendor.h +++ b/contrib/wpa/src/common/qca-vendor.h @@ -1,7 +1,7 @@ /* * Qualcomm Atheros OUI and vendor specific assignments * Copyright (c) 2014-2017, Qualcomm Atheros, Inc. - * Copyright (c) 2018, The Linux Foundation + * Copyright (c) 2018-2019, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -98,6 +98,9 @@ enum qca_radiotap_vendor_ids { * which supports DFS offloading, to indicate a radar pattern has been * detected. The channel is now unusable. * + * @QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO: Get information from the driver. + * Attributes defined in enum qca_wlan_vendor_attr_get_wifi_info. + * * @QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET: Get the feature bitmap * based on enum wifi_logger_supported_features. Attributes defined in * enum qca_wlan_vendor_attr_get_logger_features. @@ -373,7 +376,9 @@ enum qca_radiotap_vendor_ids { * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START: Start spectral scan. The scan * parameters are specified by enum qca_wlan_vendor_attr_spectral_scan. * This returns a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE) - * identifying the operation in success case. + * identifying the operation in success case. In failure cases an + * error code (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_ERROR_CODE) + * describing the reason for the failure is returned. * * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP: Stop spectral scan. This uses * a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE) from @@ -524,6 +529,65 @@ enum qca_radiotap_vendor_ids { * parameters including Zigbee state and specific WLAN periods to enhance * PTA master. All these parameters are delivered by the attributes * defined in enum qca_mpta_helper_vendor_attr. + * @QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING: This sub command is used to + * implement Beacon frame reporting feature. + * + * Userspace can request the driver/firmware to periodically report + * received Beacon frames whose BSSID is same as the current connected + * BSS's MAC address. + * + * In case the STA seamlessly (without sending disconnect indication to + * userspace) roams to a different BSS, Beacon frame reporting will be + * automatically enabled for the Beacon frames whose BSSID is same as the + * MAC address of the new BSS. Beacon reporting will be stopped when the + * STA is disconnected (when the disconnect indication is sent to + * userspace) and need to be explicitly enabled by userspace for next + * connection. + * + * When a Beacon frame matching configured conditions is received, and if + * userspace has requested to send asynchronous beacon reports, the + * driver/firmware will encapsulate the details of the Beacon frame in an + * event and send it to userspace along with updating the BSS information + * in cfg80211 scan cache, otherwise driver will only update the cfg80211 + * scan cache with the information from the received Beacon frame but will + * not send any active report to userspace. + * + * The userspace can request the driver/firmware to stop reporting Beacon + * frames. If the driver/firmware is not able to receive Beacon frames due + * to other Wi-Fi operations such as off-channel activities, etc., the + * driver/firmware will send a pause event to userspace and stop reporting + * Beacon frames. Whether the beacon reporting will be automatically + * resumed or not by the driver/firmware later will be reported to + * userspace using the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES + * flag. The beacon reporting shall be resumed for all the cases except + * either when userspace sets + * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_DO_NOT_RESUME flag in the command + * which triggered the current beacon reporting or during any disconnection + * case as indicated by setting + * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAUSE_REASON to + * QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_DISCONNECTED by the + * driver. + * + * After QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_PAUSE event is received + * by userspace with QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES + * flag not set, the next first + * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO event from the driver + * shall be considered as un-pause event. + * + * All the attributes used with this command are defined in + * enum qca_wlan_vendor_attr_beacon_reporting_params. + * @QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP: In practice, some APs have + * interop issues with the DUT. This sub command is used to transfer the + * AP info between the driver and user space. This works both as a command + * and an event. As a command, it configures the stored list of APs from + * user space to firmware; as an event, it indicates the AP info detected + * by the firmware to user space for persistent storage. The attributes + * defined in enum qca_vendor_attr_interop_issues_ap are used to deliver + * the parameters. + * @QCA_NL80211_VENDOR_SUBCMD_OEM_DATA: This command is used to send OEM data + * binary blobs from application/service to firmware. The attributes + * defined in enum qca_wlan_vendor_attr_oem_data_params are used to deliver + * the parameters. */ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, @@ -692,6 +756,9 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE = 177, QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH = 178, QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG = 179, + QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING = 180, + QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP = 181, + QCA_NL80211_VENDOR_SUBCMD_OEM_DATA = 182, }; enum qca_wlan_vendor_attr { @@ -1788,6 +1855,30 @@ enum qca_wlan_vendor_attr_config { */ QCA_WLAN_VENDOR_ATTR_CONFIG_GTX = 57, + /* Attribute to configure disconnect IEs to the driver. + * This carries an array of unsigned 8-bit characters. + * + * If this is configured, driver shall fill the IEs in disassoc/deauth + * frame. + * These IEs are expected to be considered only for the next + * immediate disconnection (disassoc/deauth frame) originated by + * the DUT, irrespective of the entity (user space/driver/firmware) + * triggering the disconnection. + * The host drivers are not expected to use the IEs set through + * this interface for further disconnections after the first immediate + * disconnection initiated post the configuration. + * If the IEs are also updated through cfg80211 interface (after the + * enhancement to cfg80211_disconnect), host driver is expected to + * take the union of IEs from both of these interfaces and send in + * further disassoc/deauth frames. + */ + QCA_WLAN_VENDOR_ATTR_DISCONNECT_IES = 58, + + /* 8-bit unsigned value for ELNA bypass. + * 1-Enable, 0-Disable + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ELNA_BYPASS = 59, + /* keep last */ QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX = @@ -3204,11 +3295,28 @@ enum qca_vendor_attr_sar_limits { /** * enum qca_wlan_vendor_attr_get_wifi_info: Attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO sub command. + * + * @QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION: In a request this attribute + * should be set to any U8 value to indicate that the driver version + * should be returned. When enabled in this manner, in a response this + * attribute will contain a string representation of the driver version. + * + * @QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION: In a request this attribute + * should be set to any U8 value to indicate that the firmware version + * should be returned. When enabled in this manner, in a response this + * attribute will contain a string representation of the firmware version. + * + * @QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX: In a request this attribute + * should be set to any U32 value to indicate that the current radio + * index should be returned. When enabled in this manner, in a response + * this attribute will contain a U32 radio index value. + * */ enum qca_wlan_vendor_attr_get_wifi_info { QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_INVALID = 0, QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION = 1, QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION = 2, + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX = 3, /* keep last */ QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST, @@ -4481,6 +4589,44 @@ enum qca_wlan_vendor_attr_spectral_scan { * qca_wlan_vendor_attr_spectral_scan_request_type. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE = 23, + /* This specifies the frequency span over which spectral + * scan would be carried out. Its value depends on the + * value of QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_MODE and + * the relation is as follows. + * QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL + * Not applicable. Spectral scan would happen in the + * operating span. + * QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_AGILE + * Center frequency (in MHz) of the span of interest or + * for convenience, center frequency (in MHz) of any channel + * in the span of interest. If agile spectral scan is initiated + * without setting a valid frequency it returns the error code + * (QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_NOT_INITIALIZED). + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FREQUENCY = 24, + /* Spectral scan mode. u32 attribute. + * It uses values defined in enum qca_wlan_vendor_spectral_scan_mode. + * If this attribute is not present, it is assumed to be + * normal mode (QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL). + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_MODE = 25, + /* Spectral scan error code. u32 attribute. + * It uses values defined in enum + * qca_wlan_vendor_spectral_scan_error_code. + * This attribute is included only in failure scenarios. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_ERROR_CODE = 26, + /* 8-bit unsigned value to enable/disable debug of the + * Spectral DMA ring. + * 1-enable, 0-disable + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DMA_RING_DEBUG = 27, + /* 8-bit unsigned value to enable/disable debug of the + * Spectral DMA buffers. + * 1-enable, 0-disable + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DMA_BUFFER_DEBUG = 28, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX = @@ -4559,6 +4705,8 @@ enum qca_wlan_vendor_attr_spectral_cap { * u8 attribute. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_DEFAULT_AGC_MAX_GAIN = 10, + /* Flag attribute to indicate agile spectral scan capability */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AGILE_SPECTRAL = 11, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_MAX = @@ -4575,6 +4723,13 @@ enum qca_wlan_vendor_attr_spectral_scan_status { QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ENABLED = 1, /* Flag attribute to indicate whether spectral scan is in progress*/ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ACTIVE = 2, + /* Spectral scan mode. u32 attribute. + * It uses values defined in enum qca_wlan_vendor_spectral_scan_mode. + * If this attribute is not present, normal mode + * (QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL is assumed to be + * requested. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MODE = 3, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MAX = @@ -4599,6 +4754,43 @@ enum qca_wlan_vendor_attr_spectral_scan_request_type { QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG, }; +/** + * qca_wlan_vendor_spectral_scan_mode: Attribute values for + * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_MODE in the vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START and + * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MODE in the vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS. This represents the + * spectral scan modes. + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL: Normal spectral scan: + * spectral scan in the current operating span. + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_AGILE: Agile spectral scan: + * spectral scan in the configured agile span. + */ +enum qca_wlan_vendor_spectral_scan_mode { + QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL = 0, + QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_AGILE = 1, +}; + +/** + * qca_wlan_vendor_spectral_scan_error_code: Attribute values for + * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_ERROR_CODE in the vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START. + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_UNSUPPORTED: Changing the value + * of a parameter is not supported. + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_MODE_UNSUPPORTED: Requested spectral scan + * mode is not supported. + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_INVALID_VALUE: A parameter + * has invalid value. + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_NOT_INITIALIZED: A parameter + * is not initialized. + */ +enum qca_wlan_vendor_spectral_scan_error_code { + QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_UNSUPPORTED = 0, + QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_MODE_UNSUPPORTED = 1, + QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_INVALID_VALUE = 2, + QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_NOT_INITIALIZED = 3, +}; + /** * qca_wlan_vendor_spectral_scan_cap_hw_gen: Attribute values for * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN to the vendor subcmd @@ -6709,4 +6901,251 @@ enum qca_mpta_helper_vendor_attr { QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST - 1 }; +/** + * enum qca_wlan_vendor_beacon_reporting_op_types - Defines different types of + * operations for which %QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING can be used. + * Will be used by %QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE. + * + * @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START: Sent by userspace to the driver + * to request the driver to start reporting Beacon frames. + * @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_STOP: Sent by userspace to the driver to + * request the driver to stop reporting Beacon frames. + * @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO: Sent by the driver to + * userspace to report received Beacon frames. + * @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE: Sent by the driver to userspace + * to indicate that the driver is going to pause reporting Beacon frames. + */ +enum qca_wlan_vendor_beacon_reporting_op_types { + QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START = 0, + QCA_WLAN_VENDOR_BEACON_REPORTING_OP_STOP = 1, + QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO = 2, + QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE = 3, +}; + +/** + * enum qca_wlan_vendor_beacon_reporting_pause_reasons - Defines different types + * of reasons for which the driver is pausing reporting Beacon frames. Will be + * used by %QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAUSE_REASON. + * + * @QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_UNSPECIFIED: For unspecified + * reasons. + * @QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_SCAN_STARTED: When the + * driver/firmware is starting a scan. + * @QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_DISCONNECTED: When the + * driver/firmware disconnects from the ESS and indicates the disconnection to + * userspace (non-seamless roaming case). This reason code will be used by the + * driver/firmware to indicate stopping of beacon report events. Userspace will + * need to start beacon reporting again (if desired) by sending vendor command + * QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING with + * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE set to + * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START after the next connection is + * completed. + */ +enum qca_wlan_vendor_beacon_reporting_pause_reasons { + QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_UNSPECIFIED = 0, + QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_SCAN_STARTED = 1, + QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_DISCONNECTED = 2, +}; + +/* + * enum qca_wlan_vendor_attr_beacon_reporting_params - List of attributes used + * in vendor sub-command QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING. + */ +enum qca_wlan_vendor_attr_beacon_reporting_params { + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_INVALID = 0, + /* Specifies the type of operation that the vendor command/event is + * intended for. Possible values for this attribute are defined in + * enum qca_wlan_vendor_beacon_reporting_op_types. u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE = 1, + /* Optionally set by userspace to request the driver to report Beacon + * frames using asynchronous vendor events when the + * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to + * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START. NLA_FLAG attribute. + * If this flag is not set, the driver will only update Beacon frames in + * cfg80211 scan cache but not send any vendor events. + */ + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_ACTIVE_REPORTING = 2, + /* Optionally used by userspace to request the driver/firmware to report + * Beacon frames periodically when the + * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to + * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START. + * u32 attribute, indicates the period of Beacon frames to be reported + * and in the units of beacon interval. + * If this attribute is missing in the command, then the default value + * of 1 will be assumed by driver, i.e., to report every Beacon frame. + * Zero is an invalid value. + * If a valid value is received for this attribute, the driver will + * update the cfg80211 scan cache periodically as per the value received + * in this attribute in addition to updating the cfg80211 scan cache + * when there is significant change in Beacon frame IEs. + */ + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PERIOD = 3, + /* Used by the driver to encapsulate the SSID when the + * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to + * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. + * u8 array with a maximum size of 32. + * + * When generating beacon report from non-MBSSID Beacon frame, the SSID + * will be taken from the SSID element of the received Beacon frame. + * + * When generating beacon report from Multiple BSSID Beacon frame and if + * the BSSID of the current connected BSS matches the BSSID of the + * transmitting BSS, the SSID will be taken from the SSID element of the + * received Beacon frame. + * + * When generating beacon report from Multiple BSSID Beacon frame and if + * the BSSID of the current connected BSS matches the BSSID of one of + * the* nontransmitting BSSs, the SSID will be taken from the SSID field + * included in the nontransmitted BSS profile whose derived BSSID is + * same as the BSSID of the current connected BSS. When there is no + * nontransmitted BSS profile whose derived BSSID is same as the BSSID + * of current connected* BSS, this attribute will not be present. + */ + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_SSID = 4, + /* Used by the driver to encapsulate the BSSID of the AP to which STA is + * currently connected to when the + * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to + * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. u8 array with a + * fixed size of 6 bytes. + * + * When generating beacon report from a Multiple BSSID beacon and the + * current connected BSSID matches one of the nontransmitted BSSIDs in a + * Multiple BSSID set, this BSSID will be that particular nontransmitted + * BSSID and not the transmitted BSSID (i.e., the transmitting address + * of the Beacon frame). + */ + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BSSID = 5, + /* Used by the driver to encapsulate the frequency in MHz on which + * the Beacon frame was received when the + * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is + * set to QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_FREQ = 6, + /* Used by the driver to encapsulate the Beacon interval + * when the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to + * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. + * u16 attribute. The value will be copied from the Beacon frame and the + * units are TUs. + */ + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BI = 7, + /* Used by the driver to encapsulate the Timestamp field from the Beacon + * frame when the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set + * to QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. + * u64 attribute. + */ + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_TSF = 8, + /* Used by the driver to encapsulate the CLOCK_BOOTTIME when this + * Beacon frame is received in the driver when the + * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to + * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. u64 attribute, in + * the units of nanoseconds. This value is expected to have accuracy of + * about 10 ms. + */ + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BOOTTIME_WHEN_RECEIVED = 9, + /* Used by the driver to encapsulate the IEs of the Beacon frame from + * which this event is generated when the + * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to + * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. u8 array. + */ + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_IES = 10, + /* Used by the driver to specify the reason for the driver/firmware to + * pause sending beacons to userspace when the + * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to + * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE. Possible values are + * defined in enum qca_wlan_vendor_beacon_reporting_pause_reasons, u32 + * attribute. + */ + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAUSE_REASON = 11, + /* Used by the driver to specify whether the driver will automatically + * resume reporting beacon events to userspace later (for example after + * the ongoing off-channel activity is completed etc.) when the + * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to + * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE. NLA_FLAG attribute. + */ + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES = 12, + /* Optionally set by userspace to request the driver not to resume + * beacon reporting after a pause is completed, when the + * QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to + * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START. NLA_FLAG attribute. + * If this flag is set, the driver will not resume beacon reporting + * after any pause in beacon reporting is completed. Userspace has to + * send QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START command again in order + * to initiate beacon reporting again. If this flag is set in the recent + * QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START command, then in the + * subsequent QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE event (if any) + * the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES shall not be + * set by the driver. Setting this flag until and unless there is a + * specific need is not recommended as there is a chance of some beacons + * received after pause command and next start command being not + * reported. + */ + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_DO_NOT_RESUME = 13, + + /* Keep last */ + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_LAST, + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_MAX = + QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_LAST - 1 +}; + +/** + * enum qca_vendor_interop_issues_ap_type - Interop issue types + * This enum defines the valid set of values of interop issue types. These + * values are used by attribute %QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE. + * + * @QCA_VENDOR_INTEROP_ISSUES_AP_ON_STA_PS: The AP has power save interop issue + * when the STA's Qpower feature is enabled. + */ +enum qca_vendor_interop_issues_ap_type { + QCA_VENDOR_INTEROP_ISSUES_AP_INVALID = 0, + QCA_VENDOR_INTEROP_ISSUES_AP_ON_STA_PS = 1, +}; + +/** + * enum qca_vendor_attr_interop_issues_ap - attribute for AP with interop issues + * Values are used by %QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP. + * + * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_INVALID: Invalid value + * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE: Interop issue type + * 32-bit unsigned value. The values defined in enum + * qca_vendor_interop_issues_ap_type are used. + * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST: APs' BSSID container + * array of nested QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID attributes. + * It is present and mandatory for the command but is not used for the event + * since only a single BSSID is reported in an event. + * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID: AP's BSSID 6-byte MAC address. + * It is used within the nested QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST + * attribute in command case and without such encapsulation in the event case. + * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_AFTER_LAST: last value + * @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX: max value + */ +enum qca_vendor_attr_interop_issues_ap { + QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_INVALID, + QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE, + QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST, + QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX = + QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_AFTER_LAST - 1 +}; + +/* + * enum qca_wlan_vendor_attr_oem_data_params - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_OEM_DATA. + * + * @QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA: The binary blob for the vendor + * command QCA_NL80211_VENDOR_SUBCMD_OEM_DATA are carried through this attribute. + * NLA_BINARY attribute, the max size is 1024 bytes. + */ +enum qca_wlan_vendor_attr_oem_data_params { + QCA_WLAN_VENDOR_ATTR_OEM_DATA_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA = 1, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_MAX = + QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_AFTER_LAST - 1 +}; #endif /* QCA_VENDOR_H */ diff --git a/contrib/wpa/src/common/sae.c b/contrib/wpa/src/common/sae.c index 5a50294a6dc8..08fdbfd18173 100644 --- a/contrib/wpa/src/common/sae.c +++ b/contrib/wpa/src/common/sae.c @@ -15,35 +15,22 @@ #include "crypto/random.h" #include "crypto/dh_groups.h" #include "ieee802_11_defs.h" +#include "dragonfly.h" #include "sae.h" -static int sae_suitable_group(int group) -{ -#ifdef CONFIG_TESTING_OPTIONS - /* Allow all groups for testing purposes in non-production builds. */ - return 1; -#else /* CONFIG_TESTING_OPTIONS */ - /* Enforce REVmd rules on which SAE groups are suitable for production - * purposes: FFC groups whose prime is >= 3072 bits and ECC groups - * defined over a prime field whose prime is >= 256 bits. Furthermore, - * ECC groups defined over a characteristic 2 finite field and ECC - * groups with a co-factor greater than 1 are not suitable. */ - return group == 19 || group == 20 || group == 21 || - group == 28 || group == 29 || group == 30 || - group == 15 || group == 16 || group == 17 || group == 18; -#endif /* CONFIG_TESTING_OPTIONS */ -} - - int sae_set_group(struct sae_data *sae, int group) { struct sae_temporary_data *tmp; - if (!sae_suitable_group(group)) { +#ifdef CONFIG_TESTING_OPTIONS + /* Allow all groups for testing purposes in non-production builds. */ +#else /* CONFIG_TESTING_OPTIONS */ + if (!dragonfly_suitable_group(group, 0)) { wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group); return -1; } +#endif /* CONFIG_TESTING_OPTIONS */ sae_clear_data(sae); tmp = sae->tmp = os_zalloc(sizeof(*tmp)); @@ -58,6 +45,7 @@ int sae_set_group(struct sae_data *sae, int group) sae->group = group; tmp->prime_len = crypto_ec_prime_len(tmp->ec); tmp->prime = crypto_ec_get_prime(tmp->ec); + tmp->order_len = crypto_ec_order_len(tmp->ec); tmp->order = crypto_ec_get_order(tmp->ec); return 0; } @@ -82,6 +70,7 @@ int sae_set_group(struct sae_data *sae, int group) } tmp->prime = tmp->prime_buf; + tmp->order_len = tmp->dh->order_len; tmp->order_buf = crypto_bignum_init_set(tmp->dh->order, tmp->dh->order_len); if (tmp->order_buf == NULL) { @@ -134,58 +123,6 @@ void sae_clear_data(struct sae_data *sae) } -static void buf_shift_right(u8 *buf, size_t len, size_t bits) -{ - size_t i; - for (i = len - 1; i > 0; i--) - buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits); - buf[0] >>= bits; -} - - -static struct crypto_bignum * sae_get_rand(struct sae_data *sae) -{ - u8 val[SAE_MAX_PRIME_LEN]; - int iter = 0; - struct crypto_bignum *bn = NULL; - int order_len_bits = crypto_bignum_bits(sae->tmp->order); - size_t order_len = (order_len_bits + 7) / 8; - - if (order_len > sizeof(val)) - return NULL; - - for (;;) { - if (iter++ > 100 || random_get_bytes(val, order_len) < 0) - return NULL; - if (order_len_bits % 8) - buf_shift_right(val, order_len, 8 - order_len_bits % 8); - bn = crypto_bignum_init_set(val, order_len); - if (bn == NULL) - return NULL; - if (crypto_bignum_is_zero(bn) || - crypto_bignum_is_one(bn) || - crypto_bignum_cmp(bn, sae->tmp->order) >= 0) { - crypto_bignum_deinit(bn, 0); - continue; - } - break; - } - - os_memset(val, 0, order_len); - return bn; -} - - -static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae) -{ - crypto_bignum_deinit(sae->tmp->sae_rand, 1); - sae->tmp->sae_rand = sae_get_rand(sae); - if (sae->tmp->sae_rand == NULL) - return NULL; - return sae_get_rand(sae); -} - - static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) { wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR @@ -200,103 +137,6 @@ static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) } -static struct crypto_bignum * -get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits, - int *r_odd) -{ - for (;;) { - struct crypto_bignum *r; - u8 tmp[SAE_MAX_ECC_PRIME_LEN]; - - if (random_get_bytes(tmp, prime_len) < 0) - break; - if (prime_bits % 8) - buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); - if (os_memcmp(tmp, prime, prime_len) >= 0) - continue; - r = crypto_bignum_init_set(tmp, prime_len); - if (!r) - break; - if (crypto_bignum_is_zero(r)) { - crypto_bignum_deinit(r, 0); - continue; - } - - *r_odd = tmp[prime_len - 1] & 0x01; - return r; - } - - return NULL; -} - - -static int is_quadratic_residue_blind(struct sae_data *sae, - const u8 *prime, size_t bits, - const u8 *qr, const u8 *qnr, - const struct crypto_bignum *y_sqr) -{ - struct crypto_bignum *r, *num, *qr_or_qnr = NULL; - int r_odd, check, res = -1; - u8 qr_or_qnr_bin[SAE_MAX_ECC_PRIME_LEN]; - size_t prime_len = sae->tmp->prime_len; - unsigned int mask; - - /* - * Use the blinding technique to mask y_sqr while determining - * whether it is a quadratic residue modulo p to avoid leaking - * timing information while determining the Legendre symbol. - * - * v = y_sqr - * r = a random number between 1 and p-1, inclusive - * num = (v * r * r) modulo p - */ - r = get_rand_1_to_p_1(prime, prime_len, bits, &r_odd); - if (!r) - return -1; - - num = crypto_bignum_init(); - if (!num || - crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 || - crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0) - goto fail; - - /* - * Need to minimize differences in handling different cases, so try to - * avoid branches and timing differences. - * - * If r_odd: - * num = (num * qr) module p - * LGR(num, p) = 1 ==> quadratic residue - * else: - * num = (num * qnr) module p - * LGR(num, p) = -1 ==> quadratic residue - */ - mask = const_time_is_zero(r_odd); - const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin); - qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len); - if (!qr_or_qnr || - crypto_bignum_mulmod(num, qr_or_qnr, sae->tmp->prime, num) < 0) - goto fail; - /* r_odd is 0 or 1; branchless version of check = r_odd ? 1 : -1, */ - check = const_time_select_int(mask, -1, 1); - - res = crypto_bignum_legendre(num, sae->tmp->prime); - if (res == -2) { - res = -1; - goto fail; - } - /* branchless version of res = res == check - * (res is -1, 0, or 1; check is -1 or 1) */ - mask = const_time_eq(res, check); - res = const_time_select_int(mask, 1, 0); -fail: - crypto_bignum_deinit(num, 1); - crypto_bignum_deinit(r, 1); - crypto_bignum_deinit(qr_or_qnr, 1); - return res; -} - - static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, const u8 *prime, const u8 *qr, const u8 *qnr, u8 *pwd_value) @@ -304,6 +144,8 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, struct crypto_bignum *y_sqr, *x_cand; int res; size_t bits; + int cmp_prime; + unsigned int in_range; wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); @@ -317,8 +159,13 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, sae->tmp->prime_len); - if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0) - return 0; + cmp_prime = const_time_memcmp(pwd_value, prime, sae->tmp->prime_len); + /* Create a const_time mask for selection based on prf result + * being smaller than prime. */ + in_range = const_time_fill_msb((unsigned int) cmp_prime); + /* The algorithm description would skip the next steps if + * cmp_prime >= 0 (reutnr 0 here), but go through them regardless to + * minimize externally observable differences in behavior. */ x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); if (!x_cand) @@ -328,9 +175,12 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, if (!y_sqr) return -1; - res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr); + res = dragonfly_is_quadratic_residue_blind(sae->tmp->ec, qr, qnr, + y_sqr); crypto_bignum_deinit(y_sqr, 1); - return res; + if (res < 0) + return res; + return const_time_select_int(in_range, res, 0); } @@ -423,47 +273,11 @@ fail: } -static int get_random_qr_qnr(const u8 *prime, size_t prime_len, - const struct crypto_bignum *prime_bn, - size_t prime_bits, struct crypto_bignum **qr, - struct crypto_bignum **qnr) -{ - *qr = NULL; - *qnr = NULL; - - while (!(*qr) || !(*qnr)) { - u8 tmp[SAE_MAX_ECC_PRIME_LEN]; - struct crypto_bignum *q; - int res; - - if (random_get_bytes(tmp, prime_len) < 0) - break; - if (prime_bits % 8) - buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); - if (os_memcmp(tmp, prime, prime_len) >= 0) - continue; - q = crypto_bignum_init_set(tmp, prime_len); - if (!q) - break; - res = crypto_bignum_legendre(q, prime_bn); - - if (res == 1 && !(*qr)) - *qr = q; - else if (res == -1 && !(*qnr)) - *qnr = q; - else - crypto_bignum_deinit(q, 0); - } - - return (*qr && *qnr) ? 0 : -1; -} - - static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, const char *identifier) { - u8 counter, k = 40; + u8 counter, k; u8 addrs[2 * ETH_ALEN]; const u8 *addr[3]; size_t len[3]; @@ -477,7 +291,6 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN]; u8 qr_bin[SAE_MAX_ECC_PRIME_LEN]; u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN]; - size_t bits; int res = -1; u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* * mask */ @@ -494,14 +307,12 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), prime_len) < 0) goto fail; - bits = crypto_ec_prime_len_bits(sae->tmp->ec); /* * Create a random quadratic residue (qr) and quadratic non-residue * (qnr) modulo p for blinding purposes during the loop. */ - if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits, - &qr, &qnr) < 0 || + if (dragonfly_get_random_qr_qnr(sae->tmp->prime, &qr, &qnr) < 0 || crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 || crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0) goto fail; @@ -537,6 +348,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, * attacks that attempt to determine the number of iterations required * in the loop. */ + k = dragonfly_min_pwe_loop_iter(sae->group); + for (counter = 1; counter <= k || !found; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; @@ -618,13 +431,6 @@ fail: } -static int sae_modp_group_require_masking(int group) -{ - /* Groups for which pwd-value is likely to be >= p frequently */ - return group == 22 || group == 23 || group == 24; -} - - static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, const char *identifier) @@ -673,7 +479,7 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, len[num_elem] = sizeof(counter); num_elem++; - k = sae_modp_group_require_masking(sae->group) ? 40 : 1; + k = dragonfly_min_pwe_loop_iter(sae->group); for (counter = 1; counter <= k || !found; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; @@ -768,48 +574,23 @@ static int sae_derive_commit_element_ffc(struct sae_data *sae, static int sae_derive_commit(struct sae_data *sae) { struct crypto_bignum *mask; - int ret = -1; - unsigned int counter = 0; + int ret; - do { - counter++; - if (counter > 100) { - /* - * This cannot really happen in practice if the random - * number generator is working. Anyway, to avoid even a - * theoretical infinite loop, break out after 100 - * attemps. - */ - return -1; - } - - mask = sae_get_rand_and_mask(sae); - if (mask == NULL) { - wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); - return -1; - } - - /* commit-scalar = (rand + mask) modulo r */ - if (!sae->tmp->own_commit_scalar) { - sae->tmp->own_commit_scalar = crypto_bignum_init(); - if (!sae->tmp->own_commit_scalar) - goto fail; - } - crypto_bignum_add(sae->tmp->sae_rand, mask, - sae->tmp->own_commit_scalar); - crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, - sae->tmp->own_commit_scalar); - } while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) || - crypto_bignum_is_one(sae->tmp->own_commit_scalar)); - - if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) || - (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0)) - goto fail; - - ret = 0; -fail: + mask = crypto_bignum_init(); + if (!sae->tmp->sae_rand) + sae->tmp->sae_rand = crypto_bignum_init(); + if (!sae->tmp->own_commit_scalar) + sae->tmp->own_commit_scalar = crypto_bignum_init(); + ret = !mask || !sae->tmp->sae_rand || !sae->tmp->own_commit_scalar || + dragonfly_generate_scalar(sae->tmp->order, sae->tmp->sae_rand, + mask, + sae->tmp->own_commit_scalar) < 0 || + (sae->tmp->ec && + sae_derive_commit_element_ecc(sae, mask) < 0) || + (sae->tmp->dh && + sae_derive_commit_element_ffc(sae, mask) < 0); crypto_bignum_deinit(mask, 1); - return ret; + return ret ? -1 : 0; } @@ -930,10 +711,16 @@ static int sae_derive_keys(struct sae_data *sae, const u8 *k) crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar, tmp); crypto_bignum_mod(tmp, sae->tmp->order, tmp); - crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len); + /* IEEE Std 802.11-2016 is not exactly clear on the encoding of the bit + * string that is needed for KCK, PMK, and PMKID derivation, but it + * seems to make most sense to encode the + * (commit-scalar + peer-commit-scalar) mod r part as a bit string by + * zero padding it from left to the length of the order (in full + * octets). */ + crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len); wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", - val, sae->tmp->prime_len, keys, sizeof(keys)) < 0) + val, sae->tmp->order_len, keys, sizeof(keys)) < 0) goto fail; os_memset(keyseed, 0, sizeof(keyseed)); os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); diff --git a/contrib/wpa/src/common/sae.h b/contrib/wpa/src/common/sae.h index 3eb6e323a68f..10f9302e3d63 100644 --- a/contrib/wpa/src/common/sae.h +++ b/contrib/wpa/src/common/sae.h @@ -33,6 +33,7 @@ struct sae_temporary_data { struct crypto_bignum *sae_rand; struct crypto_ec *ec; int prime_len; + int order_len; const struct dh_group *dh; const struct crypto_bignum *prime; const struct crypto_bignum *order; diff --git a/contrib/wpa/src/common/version.h b/contrib/wpa/src/common/version.h index 06fc5e4d25a3..c2a3a80d7f71 100644 --- a/contrib/wpa/src/common/version.h +++ b/contrib/wpa/src/common/version.h @@ -9,6 +9,6 @@ #define GIT_VERSION_STR_POSTFIX "" #endif /* GIT_VERSION_STR_POSTFIX */ -#define VERSION_STR "2.8" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX +#define VERSION_STR "2.9" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/contrib/wpa/src/common/wpa_common.c b/contrib/wpa/src/common/wpa_common.c index ed2d1c2a0236..64e5c5f4cd84 100644 --- a/contrib/wpa/src/common/wpa_common.c +++ b/contrib/wpa/src/common/wpa_common.c @@ -2075,6 +2075,16 @@ u32 wpa_akm_to_suite(int akm) return RSN_AUTH_KEY_MGMT_FT_FILS_SHA256; if (akm & WPA_KEY_MGMT_FT_FILS_SHA384) return RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; + if (akm & WPA_KEY_MGMT_SAE) + return RSN_AUTH_KEY_MGMT_SAE; + if (akm & WPA_KEY_MGMT_FT_SAE) + return RSN_AUTH_KEY_MGMT_FT_SAE; + if (akm & WPA_KEY_MGMT_OWE) + return RSN_AUTH_KEY_MGMT_OWE; + if (akm & WPA_KEY_MGMT_DPP) + return RSN_AUTH_KEY_MGMT_DPP; + if (akm & WPA_KEY_MGMT_OSEN) + return RSN_AUTH_KEY_MGMT_OSEN; return 0; } diff --git a/contrib/wpa/src/common/wpa_ctrl.h b/contrib/wpa/src/common/wpa_ctrl.h index f65077e04282..b24ae63e59ea 100644 --- a/contrib/wpa/src/common/wpa_ctrl.h +++ b/contrib/wpa/src/common/wpa_ctrl.h @@ -87,6 +87,9 @@ extern "C" { #define WPA_EVENT_BEACON_LOSS "CTRL-EVENT-BEACON-LOSS " /** Regulatory domain channel */ #define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE " +/** Channel switch started (followed by freq= and other channel parameters) + */ +#define WPA_EVENT_CHANNEL_SWITCH_STARTED "CTRL-EVENT-STARTED-CHANNEL-SWITCH " /** Channel switch (followed by freq= and other channel parameters) */ #define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH " /** SAE authentication failed due to unknown password identifier */ diff --git a/contrib/wpa/src/crypto/aes_i.h b/contrib/wpa/src/crypto/aes_i.h index 54375cf35583..b20ec92203ad 100644 --- a/contrib/wpa/src/crypto/aes_i.h +++ b/contrib/wpa/src/crypto/aes_i.h @@ -65,7 +65,7 @@ extern const u8 rcons[10]; #else /* AES_SMALL_TABLES */ -#define RCON(i) (rcons[(i)] << 24) +#define RCON(i) ((u32) rcons[(i)] << 24) static inline u32 rotr(u32 val, int bits) { @@ -94,10 +94,10 @@ static inline u32 rotr(u32 val, int bits) #define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8) #define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16) #define TD3(i) rotr(Td0[(i) & 0xff], 24) -#define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24) -#define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16) -#define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8) -#define TD44(i) (Td4s[(i) & 0xff]) +#define TD41(i) ((u32) Td4s[((i) >> 24) & 0xff] << 24) +#define TD42(i) ((u32) Td4s[((i) >> 16) & 0xff] << 16) +#define TD43(i) ((u32) Td4s[((i) >> 8) & 0xff] << 8) +#define TD44(i) ((u32) Td4s[(i) & 0xff]) #define TD0_(i) Td0[(i) & 0xff] #define TD1_(i) rotr(Td0[(i) & 0xff], 8) #define TD2_(i) rotr(Td0[(i) & 0xff], 16) diff --git a/contrib/wpa/src/crypto/crypto.h b/contrib/wpa/src/crypto/crypto.h index 12109ce83a9a..15f8ad04cea4 100644 --- a/contrib/wpa/src/crypto/crypto.h +++ b/contrib/wpa/src/crypto/crypto.h @@ -644,13 +644,6 @@ int crypto_bignum_rshift(const struct crypto_bignum *a, int n, int crypto_bignum_cmp(const struct crypto_bignum *a, const struct crypto_bignum *b); -/** - * crypto_bignum_bits - Get size of a bignum in bits - * @a: Bignum - * Returns: Number of bits in the bignum - */ -int crypto_bignum_bits(const struct crypto_bignum *a); - /** * crypto_bignum_is_zero - Is the given bignum zero * @a: Bignum diff --git a/contrib/wpa/src/crypto/crypto_openssl.c b/contrib/wpa/src/crypto/crypto_openssl.c index 1b0c1ec96b36..bab33a537293 100644 --- a/contrib/wpa/src/crypto/crypto_openssl.c +++ b/contrib/wpa/src/crypto/crypto_openssl.c @@ -570,8 +570,8 @@ int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, failed = !q || !ctx || !tmp || !BN_mod_exp(tmp, pub, q, p, ctx) || !BN_is_one(tmp); - BN_clear(q); - BN_clear(tmp); + BN_clear_free(q); + BN_clear_free(tmp); BN_CTX_free(ctx); if (failed) goto fail; @@ -580,8 +580,8 @@ int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, prime, prime_len, secret, len); fail: - BN_clear(pub); - BN_clear(p); + BN_clear_free(pub); + BN_clear_free(p); return res; } @@ -1303,6 +1303,18 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a, if (padlen > buflen) return -1; + if (padlen) { +#ifdef OPENSSL_IS_BORINGSSL + if (BN_bn2bin_padded(buf, padlen, (const BIGNUM *) a) == 0) + return -1; + return padlen; +#else /* OPENSSL_IS_BORINGSSL */ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) + return BN_bn2binpad((const BIGNUM *) a, buf, padlen); +#endif +#endif + } + num_bytes = BN_num_bytes((const BIGNUM *) a); if ((size_t) num_bytes > buflen) return -1; @@ -1476,12 +1488,6 @@ int crypto_bignum_cmp(const struct crypto_bignum *a, } -int crypto_bignum_bits(const struct crypto_bignum *a) -{ - return BN_num_bits((const BIGNUM *) a); -} - - int crypto_bignum_is_zero(const struct crypto_bignum *a) { return BN_is_zero((const BIGNUM *) a); @@ -1870,7 +1876,7 @@ struct crypto_ecdh * crypto_ecdh_init(int group) { struct crypto_ecdh *ecdh; EVP_PKEY *params = NULL; - EC_KEY *ec_params; + EC_KEY *ec_params = NULL; EVP_PKEY_CTX *kctx = NULL; ecdh = os_zalloc(sizeof(*ecdh)); @@ -1913,6 +1919,7 @@ struct crypto_ecdh * crypto_ecdh_init(int group) } done: + EC_KEY_free(ec_params); EVP_PKEY_free(params); EVP_PKEY_CTX_free(kctx); @@ -2052,13 +2059,17 @@ struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, secret = wpabuf_alloc(secret_len); if (!secret) goto fail; - if (EVP_PKEY_derive(ctx, wpabuf_put(secret, secret_len), - &secret_len) != 1) { + if (EVP_PKEY_derive(ctx, wpabuf_put(secret, 0), &secret_len) != 1) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_derive(2) failed: %s", ERR_error_string(ERR_get_error(), NULL)); goto fail; } + if (secret->size != secret_len) + wpa_printf(MSG_DEBUG, + "OpenSSL: EVP_PKEY_derive(2) changed secret_len %d -> %d", + (int) secret->size, (int) secret_len); + wpabuf_put(secret, secret_len); done: BN_free(x); diff --git a/contrib/wpa/src/crypto/crypto_wolfssl.c b/contrib/wpa/src/crypto/crypto_wolfssl.c index 976a008651b7..4cedab4367cd 100644 --- a/contrib/wpa/src/crypto/crypto_wolfssl.c +++ b/contrib/wpa/src/crypto/crypto_wolfssl.c @@ -1198,12 +1198,6 @@ int crypto_bignum_cmp(const struct crypto_bignum *a, } -int crypto_bignum_bits(const struct crypto_bignum *a) -{ - return mp_count_bits((mp_int *) a); -} - - int crypto_bignum_is_zero(const struct crypto_bignum *a) { return mp_iszero((mp_int *) a); diff --git a/contrib/wpa/src/crypto/sha1-internal.c b/contrib/wpa/src/crypto/sha1-internal.c index a4917070f196..ffa04df017e3 100644 --- a/contrib/wpa/src/crypto/sha1-internal.c +++ b/contrib/wpa/src/crypto/sha1-internal.c @@ -224,7 +224,7 @@ void SHA1Transform(u32 state[5], const unsigned char buffer[64]) /* Wipe variables */ a = b = c = d = e = 0; #ifdef SHA1HANDSOFF - os_memset(block, 0, 64); + forced_memzero(block, 64); #endif } @@ -300,7 +300,7 @@ void SHA1Final(unsigned char digest[20], SHA1_CTX* context) os_memset(context->buffer, 0, 64); os_memset(context->state, 0, 20); os_memset(context->count, 0, 8); - os_memset(finalcount, 0, 8); + forced_memzero(finalcount, sizeof(finalcount)); } /* ===== end - public domain SHA1 implementation ===== */ diff --git a/contrib/wpa/src/crypto/sha1-prf.c b/contrib/wpa/src/crypto/sha1-prf.c index 4b2d1373067f..13851494fb92 100644 --- a/contrib/wpa/src/crypto/sha1-prf.c +++ b/contrib/wpa/src/crypto/sha1-prf.c @@ -61,7 +61,7 @@ int sha1_prf(const u8 *key, size_t key_len, const char *label, } counter++; } - os_memset(hash, 0, sizeof(hash)); + forced_memzero(hash, sizeof(hash)); return 0; } diff --git a/contrib/wpa/src/crypto/sha1-tlsprf.c b/contrib/wpa/src/crypto/sha1-tlsprf.c index a11649a933eb..5e8d15920c3d 100644 --- a/contrib/wpa/src/crypto/sha1-tlsprf.c +++ b/contrib/wpa/src/crypto/sha1-tlsprf.c @@ -92,10 +92,10 @@ int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, SHA1_pos++; } - os_memset(A_MD5, 0, MD5_MAC_LEN); - os_memset(P_MD5, 0, MD5_MAC_LEN); - os_memset(A_SHA1, 0, SHA1_MAC_LEN); - os_memset(P_SHA1, 0, SHA1_MAC_LEN); + forced_memzero(A_MD5, MD5_MAC_LEN); + forced_memzero(P_MD5, MD5_MAC_LEN); + forced_memzero(A_SHA1, SHA1_MAC_LEN); + forced_memzero(P_SHA1, SHA1_MAC_LEN); return 0; } diff --git a/contrib/wpa/src/crypto/sha1-tprf.c b/contrib/wpa/src/crypto/sha1-tprf.c index 562510f8937d..c3acf19750d5 100644 --- a/contrib/wpa/src/crypto/sha1-tprf.c +++ b/contrib/wpa/src/crypto/sha1-tprf.c @@ -66,7 +66,7 @@ int sha1_t_prf(const u8 *key, size_t key_len, const char *label, len[0] = SHA1_MAC_LEN; } - os_memset(hash, 0, SHA1_MAC_LEN); + forced_memzero(hash, SHA1_MAC_LEN); return 0; } diff --git a/contrib/wpa/src/crypto/sha1.c b/contrib/wpa/src/crypto/sha1.c index 8fce139408f1..76d7a68f2610 100644 --- a/contrib/wpa/src/crypto/sha1.c +++ b/contrib/wpa/src/crypto/sha1.c @@ -86,7 +86,8 @@ int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, _addr[1] = mac; _len[1] = SHA1_MAC_LEN; ret = sha1_vector(2, _addr, _len, mac); - os_memset(k_pad, 0, sizeof(k_pad)); + forced_memzero(k_pad, sizeof(k_pad)); + forced_memzero(tk, sizeof(tk)); return ret; } diff --git a/contrib/wpa/src/crypto/sha256-kdf.c b/contrib/wpa/src/crypto/sha256-kdf.c index af7d954d8a44..5a6b744552d6 100644 --- a/contrib/wpa/src/crypto/sha256-kdf.c +++ b/contrib/wpa/src/crypto/sha256-kdf.c @@ -69,7 +69,7 @@ int hmac_sha256_kdf(const u8 *secret, size_t secret_len, if (iter == 255) { os_memset(out, 0, outlen); - os_memset(T, 0, SHA256_MAC_LEN); + forced_memzero(T, SHA256_MAC_LEN); return -1; } iter++; @@ -77,11 +77,11 @@ int hmac_sha256_kdf(const u8 *secret, size_t secret_len, if (hmac_sha256_vector(secret, secret_len, 4, addr, len, T) < 0) { os_memset(out, 0, outlen); - os_memset(T, 0, SHA256_MAC_LEN); + forced_memzero(T, SHA256_MAC_LEN); return -1; } } - os_memset(T, 0, SHA256_MAC_LEN); + forced_memzero(T, SHA256_MAC_LEN); return 0; } diff --git a/contrib/wpa/src/crypto/sha256-prf.c b/contrib/wpa/src/crypto/sha256-prf.c index 722cad6bdeb4..d665a9983cf8 100644 --- a/contrib/wpa/src/crypto/sha256-prf.c +++ b/contrib/wpa/src/crypto/sha256-prf.c @@ -102,7 +102,7 @@ int sha256_prf_bits(const u8 *key, size_t key_len, const char *label, buf[pos - 1] &= mask; } - os_memset(hash, 0, sizeof(hash)); + forced_memzero(hash, sizeof(hash)); return 0; } diff --git a/contrib/wpa/src/crypto/sha256-tlsprf.c b/contrib/wpa/src/crypto/sha256-tlsprf.c index 0528dadfdca6..9045cd36b489 100644 --- a/contrib/wpa/src/crypto/sha256-tlsprf.c +++ b/contrib/wpa/src/crypto/sha256-tlsprf.c @@ -26,8 +26,8 @@ * This function is used to derive new, cryptographically separate keys from a * given key in TLS. This PRF is defined in RFC 2246, Chapter 5. */ -void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, - const u8 *seed, size_t seed_len, u8 *out, size_t outlen) +int tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen) { size_t clen; u8 A[SHA256_MAC_LEN]; @@ -50,12 +50,15 @@ void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, * PRF(secret, label, seed) = P_SHA256(secret, label + seed) */ - hmac_sha256_vector(secret, secret_len, 2, &addr[1], &len[1], A); + if (hmac_sha256_vector(secret, secret_len, 2, &addr[1], &len[1], A) < 0) + return -1; pos = 0; while (pos < outlen) { - hmac_sha256_vector(secret, secret_len, 3, addr, len, P); - hmac_sha256(secret, secret_len, A, SHA256_MAC_LEN, A); + if (hmac_sha256_vector(secret, secret_len, 3, addr, len, P) < + 0 || + hmac_sha256(secret, secret_len, A, SHA256_MAC_LEN, A) < 0) + return -1; clen = outlen - pos; if (clen > SHA256_MAC_LEN) @@ -63,4 +66,6 @@ void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, os_memcpy(out + pos, P, clen); pos += clen; } + + return 0; } diff --git a/contrib/wpa/src/crypto/sha256.h b/contrib/wpa/src/crypto/sha256.h index 5219022edd7d..8054bbe5c514 100644 --- a/contrib/wpa/src/crypto/sha256.h +++ b/contrib/wpa/src/crypto/sha256.h @@ -20,9 +20,9 @@ int sha256_prf(const u8 *key, size_t key_len, const char *label, int sha256_prf_bits(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len_bits); -void tls_prf_sha256(const u8 *secret, size_t secret_len, - const char *label, const u8 *seed, size_t seed_len, - u8 *out, size_t outlen); +int tls_prf_sha256(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); int hmac_sha256_kdf(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); diff --git a/contrib/wpa/src/crypto/sha384-kdf.c b/contrib/wpa/src/crypto/sha384-kdf.c index 1d196279086e..babcb9ed04c9 100644 --- a/contrib/wpa/src/crypto/sha384-kdf.c +++ b/contrib/wpa/src/crypto/sha384-kdf.c @@ -69,7 +69,7 @@ int hmac_sha384_kdf(const u8 *secret, size_t secret_len, if (iter == 255) { os_memset(out, 0, outlen); - os_memset(T, 0, SHA384_MAC_LEN); + forced_memzero(T, SHA384_MAC_LEN); return -1; } iter++; @@ -77,11 +77,11 @@ int hmac_sha384_kdf(const u8 *secret, size_t secret_len, if (hmac_sha384_vector(secret, secret_len, 4, addr, len, T) < 0) { os_memset(out, 0, outlen); - os_memset(T, 0, SHA384_MAC_LEN); + forced_memzero(T, SHA384_MAC_LEN); return -1; } } - os_memset(T, 0, SHA384_MAC_LEN); + forced_memzero(T, SHA384_MAC_LEN); return 0; } diff --git a/contrib/wpa/src/crypto/sha384-prf.c b/contrib/wpa/src/crypto/sha384-prf.c index 03e3cb353a3d..420e78c380cd 100644 --- a/contrib/wpa/src/crypto/sha384-prf.c +++ b/contrib/wpa/src/crypto/sha384-prf.c @@ -102,7 +102,7 @@ int sha384_prf_bits(const u8 *key, size_t key_len, const char *label, buf[pos - 1] &= mask; } - os_memset(hash, 0, sizeof(hash)); + forced_memzero(hash, sizeof(hash)); return 0; } diff --git a/contrib/wpa/src/crypto/sha512-kdf.c b/contrib/wpa/src/crypto/sha512-kdf.c index 8b71f9b0e4f9..5bde66485e6d 100644 --- a/contrib/wpa/src/crypto/sha512-kdf.c +++ b/contrib/wpa/src/crypto/sha512-kdf.c @@ -69,7 +69,7 @@ int hmac_sha512_kdf(const u8 *secret, size_t secret_len, if (iter == 255) { os_memset(out, 0, outlen); - os_memset(T, 0, SHA512_MAC_LEN); + forced_memzero(T, SHA512_MAC_LEN); return -1; } iter++; @@ -77,11 +77,11 @@ int hmac_sha512_kdf(const u8 *secret, size_t secret_len, if (hmac_sha512_vector(secret, secret_len, 4, addr, len, T) < 0) { os_memset(out, 0, outlen); - os_memset(T, 0, SHA512_MAC_LEN); + forced_memzero(T, SHA512_MAC_LEN); return -1; } } - os_memset(T, 0, SHA512_MAC_LEN); + forced_memzero(T, SHA512_MAC_LEN); return 0; } diff --git a/contrib/wpa/src/crypto/sha512-prf.c b/contrib/wpa/src/crypto/sha512-prf.c index 3b2ad889d6ef..e48cf5f05662 100644 --- a/contrib/wpa/src/crypto/sha512-prf.c +++ b/contrib/wpa/src/crypto/sha512-prf.c @@ -102,7 +102,7 @@ int sha512_prf_bits(const u8 *key, size_t key_len, const char *label, buf[pos - 1] &= mask; } - os_memset(hash, 0, sizeof(hash)); + forced_memzero(hash, sizeof(hash)); return 0; } diff --git a/contrib/wpa/src/crypto/tls.h b/contrib/wpa/src/crypto/tls.h index 8bdb91ff2469..c8b1a824ed54 100644 --- a/contrib/wpa/src/crypto/tls.h +++ b/contrib/wpa/src/crypto/tls.h @@ -48,6 +48,18 @@ enum tls_fail_reason { #define TLS_MAX_ALT_SUBJECT 10 +struct tls_cert_data { + int depth; + const char *subject; + const struct wpabuf *cert; + const u8 *hash; + size_t hash_len; + const char *altsubject[TLS_MAX_ALT_SUBJECT]; + int num_altsubject; + const char *serial_num; + int tod; +}; + union tls_event_data { struct { int depth; @@ -57,16 +69,7 @@ union tls_event_data { const struct wpabuf *cert; } cert_fail; - struct { - int depth; - const char *subject; - const struct wpabuf *cert; - const u8 *hash; - size_t hash_len; - const char *altsubject[TLS_MAX_ALT_SUBJECT]; - int num_altsubject; - const char *serial_num; - } peer_cert; + struct tls_cert_data peer_cert; struct { int is_local; @@ -108,6 +111,7 @@ struct tls_config { #define TLS_CONN_ENABLE_TLSv1_0 BIT(14) #define TLS_CONN_ENABLE_TLSv1_1 BIT(15) #define TLS_CONN_ENABLE_TLSv1_2 BIT(16) +#define TLS_CONN_TEAP_ANON_DH BIT(17) /** * struct tls_connection_params - Parameters for TLS connection @@ -184,12 +188,15 @@ struct tls_connection_params { const char *suffix_match; const char *domain_match; const char *client_cert; + const char *client_cert2; const u8 *client_cert_blob; size_t client_cert_blob_len; const char *private_key; + const char *private_key2; const u8 *private_key_blob; size_t private_key_blob_len; const char *private_key_passwd; + const char *private_key_passwd2; const char *dh_file; const u8 *dh_blob; size_t dh_blob_len; @@ -643,4 +650,24 @@ tls_connection_get_success_data(struct tls_connection *conn); void tls_connection_remove_session(struct tls_connection *conn); +/** + * tls_get_tls_unique - Fetch "tls-unique" for channel binding + * @conn: Connection context data from tls_connection_init() + * @buf: Buffer for returning the value + * @max_len: Maximum length of the buffer in bytes + * Returns: Number of bytes written to buf or -1 on error + * + * This function can be used to fetch "tls-unique" (RFC 5929, Section 3) which + * is the first TLS Finished message sent in the most recent TLS handshake of + * the TLS connection. + */ +int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len); + +/** + * tls_connection_get_cipher_suite - Get current TLS cipher suite + * @conn: Connection context data from tls_connection_init() + * Returns: TLS cipher suite of the current connection or 0 on error + */ +u16 tls_connection_get_cipher_suite(struct tls_connection *conn); + #endif /* TLS_H */ diff --git a/contrib/wpa/src/crypto/tls_openssl.c b/contrib/wpa/src/crypto/tls_openssl.c index b0c23ae6c9b1..07d38e47b917 100644 --- a/contrib/wpa/src/crypto/tls_openssl.c +++ b/contrib/wpa/src/crypto/tls_openssl.c @@ -44,6 +44,13 @@ #define OPENSSL_NEED_EAP_FAST_PRF #endif +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \ + defined(EAP_SERVER_FAST) || defined(EAP_TEAP) || \ + defined(EAP_SERVER_TEAP) +#define EAP_FAST_OR_TEAP +#endif + + #if defined(OPENSSL_IS_BORINGSSL) /* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */ typedef size_t stack_index_t; @@ -1071,11 +1078,8 @@ void * tls_init(const struct tls_config *conf) } #ifndef OPENSSL_NO_ENGINE - wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); -#if OPENSSL_VERSION_NUMBER < 0x10100000L - ERR_load_ENGINE_strings(); - ENGINE_load_dynamic(); -#endif /* OPENSSL_VERSION_NUMBER */ + wpa_printf(MSG_DEBUG, "ENGINE: Loading builtin engines"); + ENGINE_load_builtin_engines(); if (conf && (conf->opensc_engine_path || conf->pkcs11_engine_path || @@ -1331,6 +1335,8 @@ static const char * openssl_content_type(int content_type) return "heartbeat"; case 256: return "TLS header info"; /* pseudo content type */ + case 257: + return "inner content type"; /* pseudo content type */ default: return "?"; } @@ -1340,6 +1346,8 @@ static const char * openssl_content_type(int content_type) static const char * openssl_handshake_type(int content_type, const u8 *buf, size_t len) { + if (content_type == 257 && buf && len == 1) + return openssl_content_type(buf[0]); if (content_type != 22 || !buf || len == 0) return ""; switch (buf[0]) { @@ -1570,6 +1578,11 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) options |= SSL_OP_NO_COMPRESSION; #endif /* SSL_OP_NO_COMPRESSION */ SSL_set_options(conn->ssl, options); +#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT + /* Hopefully there is no need for middlebox compatibility mechanisms + * when going through EAP authentication. */ + SSL_clear_options(conn->ssl, SSL_OP_ENABLE_MIDDLEBOX_COMPAT); +#endif conn->ssl_in = BIO_new(BIO_s_mem()); if (!conn->ssl_in) { @@ -2152,6 +2165,34 @@ static void openssl_tls_fail_event(struct tls_connection *conn, } +static int openssl_cert_tod(X509 *cert) +{ + CERTIFICATEPOLICIES *ext; + stack_index_t i; + char buf[100]; + int res; + int tod = 0; + + ext = X509_get_ext_d2i(cert, NID_certificate_policies, NULL, NULL); + if (!ext) + return 0; + + for (i = 0; i < sk_POLICYINFO_num(ext); i++) { + POLICYINFO *policy; + + policy = sk_POLICYINFO_value(ext, i); + res = OBJ_obj2txt(buf, sizeof(buf), policy->policyid, 0); + if (res < 0 || (size_t) res >= sizeof(buf)) + continue; + wpa_printf(MSG_DEBUG, "OpenSSL: Certificate Policy %s", buf); + if (os_strcmp(buf, "1.3.6.1.4.1.40808.1.3.1") == 0) + tod = 1; + } + + return tod; +} + + static void openssl_tls_cert_event(struct tls_connection *conn, X509 *err_cert, int depth, const char *subject) @@ -2244,6 +2285,8 @@ static void openssl_tls_cert_event(struct tls_connection *conn, ev.peer_cert.altsubject[alt] = altsubject[alt]; ev.peer_cert.num_altsubject = num_altsubject; + ev.peer_cert.tod = openssl_cert_tod(err_cert); + context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev); wpabuf_free(cert); for (alt = 0; alt < num_altsubject; alt++) @@ -2348,7 +2391,30 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) } #endif /* CONFIG_SHA256 */ + openssl_tls_cert_event(conn, err_cert, depth, buf); + if (!preverify_ok) { + if (depth > 0) { + /* Send cert event for the peer certificate so that + * the upper layers get information about it even if + * validation of a CA certificate fails. */ + STACK_OF(X509) *chain; + + chain = X509_STORE_CTX_get1_chain(x509_ctx); + if (chain && sk_X509_num(chain) > 0) { + char buf2[256]; + X509 *cert; + + cert = sk_X509_value(chain, 0); + X509_NAME_oneline(X509_get_subject_name(cert), + buf2, sizeof(buf2)); + + openssl_tls_cert_event(conn, cert, 0, buf2); + } + if (chain) + sk_X509_pop_free(chain, X509_free); + } + wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," " error %d (%s) depth %d for '%s'", err, err_str, depth, buf); @@ -2404,8 +2470,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) openssl_tls_fail_event(conn, err_cert, err, depth, buf, "Domain mismatch", TLS_FAIL_DOMAIN_MISMATCH); - } else - openssl_tls_cert_event(conn, err_cert, depth, buf); + } if (conn->cert_probe && preverify_ok && depth == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate " @@ -2580,9 +2645,23 @@ static int tls_connection_ca_cert(struct tls_data *data, (const unsigned char **) &ca_cert_blob, ca_cert_blob_len); if (cert == NULL) { - tls_show_errors(MSG_WARNING, __func__, - "Failed to parse ca_cert_blob"); - return -1; + BIO *bio = BIO_new_mem_buf(ca_cert_blob, + ca_cert_blob_len); + + if (bio) { + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + } + + if (!cert) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to parse ca_cert_blob"); + return -1; + } + + while (ERR_get_error()) { + /* Ignore errors from DER conversion. */ + } } if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), @@ -3016,6 +3095,40 @@ static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags, } #endif /* CONFIG_SUITEB */ + if (flags & TLS_CONN_TEAP_ANON_DH) { +#ifndef TEAP_DH_ANON_CS +#define TEAP_DH_ANON_CS \ + "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:" \ + "ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:" \ + "ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:" \ + "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:" \ + "DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:" \ + "DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:" \ + "ADH-AES256-GCM-SHA384:ADH-AES128-GCM-SHA256:" \ + "ADH-AES256-SHA256:ADH-AES128-SHA256:ADH-AES256-SHA:ADH-AES128-SHA" +#endif + static const char *cs = TEAP_DH_ANON_CS; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + !defined(OPENSSL_IS_BORINGSSL) + /* + * Need to drop to security level 0 to allow anonymous + * cipher suites for EAP-TEAP. + */ + SSL_set_security_level(conn->ssl, 0); +#endif + + wpa_printf(MSG_DEBUG, + "OpenSSL: Enable cipher suites for anonymous EAP-TEAP provisioning: %s", + cs); + if (SSL_set_cipher_list(conn->ssl, cs) != 1) { + tls_show_errors(MSG_INFO, __func__, + "Cipher suite configuration failed"); + return -1; + } + } + return 0; } @@ -4002,7 +4115,7 @@ int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, _out, skip + out_len) == 0) { ret = 0; } - os_memset(master_key, 0, sizeof(master_key)); + forced_memzero(master_key, sizeof(master_key)); os_free(rnd); if (ret == 0) os_memcpy(out, _out + skip, out_len); @@ -4192,6 +4305,22 @@ openssl_connection_handshake(struct tls_connection *conn, wpa_printf(MSG_DEBUG, "OpenSSL: Handshake finished - resumed=%d", tls_connection_resumed(conn->ssl_ctx, conn)); + if (conn->server) { + char *buf; + size_t buflen = 2000; + + buf = os_malloc(buflen); + if (buf) { + if (SSL_get_shared_ciphers(conn->ssl, buf, + buflen)) { + buf[buflen - 1] = '\0'; + wpa_printf(MSG_DEBUG, + "OpenSSL: Shared ciphers: %s", + buf); + } + os_free(buf); + } + } if (appl_data && in_data) *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data)); @@ -4374,11 +4503,15 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, c++; } + if (!buf[0]) { + wpa_printf(MSG_DEBUG, "OpenSSL: No ciphers listed"); + return -1; + } wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1); #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) -#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#ifdef EAP_FAST_OR_TEAP if (os_strstr(buf, ":ADH-")) { /* * Need to drop to security level 0 to allow anonymous @@ -4389,7 +4522,7 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, /* Force at least security level 1 */ SSL_set_security_level(conn->ssl, 1); } -#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +#endif /* EAP_FAST_OR_TEAP */ #endif if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) { @@ -4443,7 +4576,7 @@ int tls_connection_enable_workaround(void *ssl_ctx, } -#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#ifdef EAP_FAST_OR_TEAP /* ClientHello TLS extensions require a patch to openssl, so this function is * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ @@ -4460,7 +4593,7 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, return 0; } -#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +#endif /* EAP_FAST_OR_TEAP */ int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) @@ -4669,6 +4802,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, &this_update, &next_update); if (!res) { + OCSP_CERTID_free(id); id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer); if (!id) { wpa_printf(MSG_DEBUG, @@ -4979,6 +5113,114 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } +static void openssl_debug_dump_cipher_list(SSL_CTX *ssl_ctx) +{ + SSL *ssl; + int i; + + ssl = SSL_new(ssl_ctx); + if (!ssl) + return; + + wpa_printf(MSG_DEBUG, + "OpenSSL: Enabled cipher suites in priority order"); + for (i = 0; ; i++) { + const char *cipher; + + cipher = SSL_get_cipher_list(ssl, i); + if (!cipher) + break; + wpa_printf(MSG_DEBUG, "Cipher %d: %s", i, cipher); + } + + SSL_free(ssl); +} + + +#if !defined(LIBRESSL_VERSION_NUMBER) && !defined(BORINGSSL_API_VERSION) + +static const char * openssl_pkey_type_str(const EVP_PKEY *pkey) +{ + if (!pkey) + return "NULL"; + switch (EVP_PKEY_type(EVP_PKEY_id(pkey))) { + case EVP_PKEY_RSA: + return "RSA"; + case EVP_PKEY_DSA: + return "DSA"; + case EVP_PKEY_DH: + return "DH"; + case EVP_PKEY_EC: + return "EC"; + } + return "?"; +} + + +static void openssl_debug_dump_certificate(int i, X509 *cert) +{ + char buf[256]; + EVP_PKEY *pkey; + ASN1_INTEGER *ser; + char serial_num[128]; + + X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); + + ser = X509_get_serialNumber(cert); + if (ser) + wpa_snprintf_hex_uppercase(serial_num, sizeof(serial_num), + ASN1_STRING_get0_data(ser), + ASN1_STRING_length(ser)); + else + serial_num[0] = '\0'; + + pkey = X509_get_pubkey(cert); + wpa_printf(MSG_DEBUG, "%d: %s (%s) %s", i, buf, + openssl_pkey_type_str(pkey), serial_num); + EVP_PKEY_free(pkey); +} + + +static void openssl_debug_dump_certificates(SSL_CTX *ssl_ctx) +{ + STACK_OF(X509) *certs; + + wpa_printf(MSG_DEBUG, "OpenSSL: Configured certificate chain"); + if (SSL_CTX_get0_chain_certs(ssl_ctx, &certs) == 1) { + int i; + + for (i = sk_X509_num(certs); i > 0; i--) + openssl_debug_dump_certificate(i, sk_X509_value(certs, + i - 1)); + } + openssl_debug_dump_certificate(0, SSL_CTX_get0_certificate(ssl_ctx)); +} + +#endif + + +static void openssl_debug_dump_certificate_chains(SSL_CTX *ssl_ctx) +{ +#if !defined(LIBRESSL_VERSION_NUMBER) && !defined(BORINGSSL_API_VERSION) + int res; + + for (res = SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_FIRST); + res == 1; + res = SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_NEXT)) + openssl_debug_dump_certificates(ssl_ctx); + + SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_FIRST); +#endif +} + + +static void openssl_debug_dump_ctx(SSL_CTX *ssl_ctx) +{ + openssl_debug_dump_cipher_list(ssl_ctx); + openssl_debug_dump_certificate_chains(ssl_ctx); +} + + int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { @@ -5004,6 +5246,9 @@ int tls_global_set_params(void *tls_ctx, tls_global_client_cert(data, params->client_cert) || tls_global_private_key(data, params->private_key, params->private_key_passwd) || + tls_global_client_cert(data, params->client_cert2) || + tls_global_private_key(data, params->private_key2, + params->private_key_passwd2) || tls_global_dh(data, params->dh_file)) { wpa_printf(MSG_INFO, "TLS: Failed to set global parameters"); return -1; @@ -5073,11 +5318,13 @@ int tls_global_set_params(void *tls_ctx, tls_global->ocsp_stapling_response = NULL; #endif /* HAVE_OCSP */ + openssl_debug_dump_ctx(ssl_ctx); + return 0; } -#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#ifdef EAP_FAST_OR_TEAP /* Pre-shared secred requires a patch to openssl, so this function is * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ @@ -5158,7 +5405,7 @@ static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, return 1; } -#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +#endif /* EAP_FAST_OR_TEAP */ int tls_connection_set_session_ticket_cb(void *tls_ctx, @@ -5166,7 +5413,7 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, tls_session_ticket_cb cb, void *ctx) { -#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#ifdef EAP_FAST_OR_TEAP conn->session_ticket_cb = cb; conn->session_ticket_cb_ctx = ctx; @@ -5183,9 +5430,9 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, } return 0; -#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +#else /* EAP_FAST_OR_TEAP */ return -1; -#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +#endif /* EAP_FAST_OR_TEAP */ } @@ -5268,3 +5515,36 @@ void tls_connection_remove_session(struct tls_connection *conn) wpa_printf(MSG_DEBUG, "OpenSSL: Removed cached session to disable session resumption"); } + + +int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len) +{ + size_t len; + int reused; + + reused = SSL_session_reused(conn->ssl); + if ((conn->server && !reused) || (!conn->server && reused)) + len = SSL_get_peer_finished(conn->ssl, buf, max_len); + else + len = SSL_get_finished(conn->ssl, buf, max_len); + + if (len == 0 || len > max_len) + return -1; + + return len; +} + + +u16 tls_connection_get_cipher_suite(struct tls_connection *conn) +{ + const SSL_CIPHER *cipher; + + cipher = SSL_get_current_cipher(conn->ssl); + if (!cipher) + return 0; +#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER) + return SSL_CIPHER_get_protocol_id(cipher); +#else + return SSL_CIPHER_get_id(cipher) & 0xFFFF; +#endif +} diff --git a/contrib/wpa/src/crypto/tls_wolfssl.c b/contrib/wpa/src/crypto/tls_wolfssl.c index e9cb425c115a..d222d142767d 100644 --- a/contrib/wpa/src/crypto/tls_wolfssl.c +++ b/contrib/wpa/src/crypto/tls_wolfssl.c @@ -141,7 +141,7 @@ static int wolfssl_receive_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx) if (get > (wpabuf_len(data->in_data) - data->consumed)) get = wpabuf_len(data->in_data) - data->consumed; - os_memcpy(buf, wpabuf_head(data->in_data) + data->consumed, get); + os_memcpy(buf, wpabuf_head_u8(data->in_data) + data->consumed, get); data->consumed += get; if (get == 0) @@ -2044,7 +2044,7 @@ int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, _out, skip + out_len); } - os_memset(master_key, 0, master_key_len); + forced_memzero(master_key, master_key_len); if (ret == 0) os_memcpy(out, _out + skip, out_len); bin_clear_free(tmp_out, skip + out_len); diff --git a/contrib/wpa/src/drivers/driver.h b/contrib/wpa/src/drivers/driver.h index e7c8f318f35d..2a8459ae3f2d 100644 --- a/contrib/wpa/src/drivers/driver.h +++ b/contrib/wpa/src/drivers/driver.h @@ -101,6 +101,20 @@ enum reg_type { REGDOM_TYPE_INTERSECTION, }; +/** + * struct hostapd_wmm_rule - WMM regulatory rule + * @min_cwmin: Lower bound of CW_min value + * @min_cwmax: Lower bound of CW_max value + * @min_aifs: Lower bound of AIFS value + * @max_txop: Upper bound of TXOP, value in units of 32 usec + */ +struct hostapd_wmm_rule { + int min_cwmin; + int min_cwmax; + int min_aifs; + int max_txop; +}; + /** * struct hostapd_channel_data - Channel information */ @@ -156,34 +170,48 @@ struct hostapd_channel_data { * dfs_cac_ms - DFS CAC time in milliseconds */ unsigned int dfs_cac_ms; + + /** + * wmm_rules_valid - Indicates wmm_rules state + */ + int wmm_rules_valid; + + /** + * wmm_rules - WMM regulatory rules + */ + struct hostapd_wmm_rule wmm_rules[WMM_AC_NUM]; }; -#define HE_MAX_NUM_SS 8 -#define HE_MAX_PHY_CAPAB_SIZE 3 - -/** - * struct he_ppe_threshold - IEEE 802.11ax HE PPE Threshold - */ -struct he_ppe_threshold { - u32 numss_m1; - u32 ru_count; - u32 ppet16_ppet8_ru3_ru0[HE_MAX_NUM_SS]; -}; +#define HE_MAX_MAC_CAPAB_SIZE 6 +#define HE_MAX_PHY_CAPAB_SIZE 11 +#define HE_MAX_MCS_CAPAB_SIZE 12 +#define HE_MAX_PPET_CAPAB_SIZE 25 /** * struct he_capabilities - IEEE 802.11ax HE capabilities */ struct he_capabilities { u8 he_supported; - u32 phy_cap[HE_MAX_PHY_CAPAB_SIZE]; - u32 mac_cap; - u32 mcs; - struct he_ppe_threshold ppet; + u8 phy_cap[HE_MAX_PHY_CAPAB_SIZE]; + u8 mac_cap[HE_MAX_MAC_CAPAB_SIZE]; + u8 mcs[HE_MAX_MCS_CAPAB_SIZE]; + u8 ppet[HE_MAX_PPET_CAPAB_SIZE]; }; #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0) #define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1) + +enum ieee80211_op_mode { + IEEE80211_MODE_INFRA = 0, + IEEE80211_MODE_IBSS = 1, + IEEE80211_MODE_AP = 2, + IEEE80211_MODE_MESH = 5, + + /* only add new entries before IEEE80211_MODE_NUM */ + IEEE80211_MODE_NUM +}; + /** * struct hostapd_hw_modes - Supported hardware mode information */ @@ -243,15 +271,10 @@ struct hostapd_hw_modes { /** * he_capab - HE (IEEE 802.11ax) capabilities */ - struct he_capabilities he_capab; + struct he_capabilities he_capab[IEEE80211_MODE_NUM]; }; -#define IEEE80211_MODE_INFRA 0 -#define IEEE80211_MODE_IBSS 1 -#define IEEE80211_MODE_AP 2 -#define IEEE80211_MODE_MESH 5 - #define IEEE80211_CAP_ESS 0x0001 #define IEEE80211_CAP_IBSS 0x0002 #define IEEE80211_CAP_PRIVACY 0x0010 @@ -698,6 +721,11 @@ struct hostapd_freq_params { */ int vht_enabled; + /** + * he_enabled - Whether HE is enabled + */ + int he_enabled; + /** * center_freq1 - Segment 0 center frequency in MHz * @@ -1045,6 +1073,14 @@ struct wpa_driver_associate_params { */ int req_key_mgmt_offload; + /** + * req_handshake_offload - Request EAPOL handshake offload + * + * Request EAPOL handshake offload for this connection if the device + * supports it. + */ + int req_handshake_offload; + /** * Flag for indicating whether this association includes support for * RRM (Radio Resource Measurements) @@ -1122,6 +1158,11 @@ enum hide_ssid { HIDDEN_SSID_ZERO_CONTENTS }; +enum ch_switch_state { + CH_SW_STARTED, + CH_SW_FINISHED +}; + struct wowlan_triggers { u8 any; u8 disconnect; @@ -1752,6 +1793,7 @@ struct hostapd_data; struct hostap_sta_driver_data { unsigned long rx_packets, tx_packets; unsigned long long rx_bytes, tx_bytes; + unsigned long long rx_airtime, tx_airtime; int bytes_64bit; /* whether 64-bit byte counters are supported */ unsigned long current_tx_rate; unsigned long current_rx_rate; @@ -1761,6 +1803,8 @@ struct hostap_sta_driver_data { unsigned long tx_retry_failed; unsigned long tx_retry_count; s8 last_ack_rssi; + unsigned long backlog_packets; + unsigned long backlog_bytes; s8 signal; u8 rx_vhtmcs; u8 tx_vhtmcs; @@ -1781,6 +1825,8 @@ struct hostapd_sta_add_params { const struct ieee80211_vht_capabilities *vht_capabilities; int vht_opmode_enabled; u8 vht_opmode; + const struct ieee80211_he_capabilities *he_capab; + size_t he_capab_len; u32 flags; /* bitmask of WPA_STA_* flags */ u32 flags_mask; /* unset bits in flags */ #ifdef CONFIG_MESH @@ -2337,7 +2383,7 @@ struct wpa_driver_ops { * * Returns: 0 on success, -1 on failure */ - int (*deauthenticate)(void *priv, const u8 *addr, int reason_code); + int (*deauthenticate)(void *priv, const u8 *addr, u16 reason_code); /** * associate - Request driver to associate @@ -2806,7 +2852,7 @@ struct wpa_driver_ops { * a Deauthentication frame to be sent to it. */ int (*sta_deauth)(void *priv, const u8 *own_addr, const u8 *addr, - int reason); + u16 reason); /** * sta_disassoc - Disassociate a station (AP only) @@ -2820,7 +2866,7 @@ struct wpa_driver_ops { * a Disassociation frame to be sent to it. */ int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr, - int reason); + u16 reason); /** * sta_remove - Remove a station entry (AP only) @@ -2937,6 +2983,16 @@ struct wpa_driver_ops { unsigned int total_flags, unsigned int flags_or, unsigned int flags_and); + /** + * sta_set_airtime_weight - Set station airtime weight (AP only) + * @priv: Private driver interface data + * @addr: Station address + * @weight: New weight for station airtime assignment + * Returns: 0 on success, -1 on failure + */ + int (*sta_set_airtime_weight)(void *priv, const u8 *addr, + unsigned int weight); + /** * set_tx_queue_params - Set TX queue parameters * @priv: Private driver interface data @@ -3974,6 +4030,18 @@ struct wpa_driver_ops { */ int (*leave_mesh)(void *priv); + /** + * probe_mesh_link - Inject a frame over direct mesh link to a given + * peer skipping the next_hop lookup from mpath table. + * @priv: Private driver interface data + * @addr: Peer MAC address + * @eth: Ethernet frame to be sent + * @len: Ethernet frame lengtn in bytes + * Returns 0 on success, -1 on failure + */ + int (*probe_mesh_link)(void *priv, const u8 *addr, const u8 *eth, + size_t len); + /** * do_acs - Automatically select channel * @priv: Private driver interface data @@ -4167,6 +4235,21 @@ struct wpa_driver_ops { * Returns: 0 on success, < 0 on failure */ int (*set_4addr_mode)(void *priv, const char *bridge_ifname, int val); + + /** + * update_dh_ie - Update DH IE + * @priv: Private driver interface data + * @peer_mac: Peer MAC address + * @reason_code: Reacon code + * @ie: DH IE + * @ie_len: DH IE length in bytes + * Returns: 0 on success, -1 on failure + * + * This callback is used to let the driver know the DH processing result + * and DH IE for a pending association. + */ + int (*update_dh_ie)(void *priv, const u8 *peer_mac, u16 reason_code, + const u8 *ie, size_t ie_len); }; /** @@ -4540,6 +4623,15 @@ enum wpa_event_type { * */ EVENT_CH_SWITCH, + /** + * EVENT_CH_SWITCH_STARTED - AP or GO started to switch channels + * + * This is a pre-switch event indicating the shortly following switch + * of operating channels. + * + * Described in wpa_event_data.ch_switch + */ + EVENT_CH_SWITCH_STARTED, /** * EVENT_WNM - Request WNM operation * @@ -4703,6 +4795,11 @@ enum wpa_event_type { * This event is emitted when an interface is added/removed for WDS STA. */ EVENT_WDS_STA_INTERFACE_STATUS, + + /** + * EVENT_UPDATE_DH - Notification of updated DH information + */ + EVENT_UPDATE_DH, }; @@ -5536,6 +5633,15 @@ union wpa_event_data { INTERFACE_REMOVED } istatus; } wds_sta_interface; + + /** + * struct update_dh - Data for EVENT_UPDATE_DH + */ + struct update_dh { + const u8 *peer; + const u8 *ie; + size_t ie_len; + } update_dh; }; /** diff --git a/contrib/wpa/src/drivers/driver_atheros.c b/contrib/wpa/src/drivers/driver_atheros.c new file mode 100644 index 000000000000..840d4ff4057e --- /dev/null +++ b/contrib/wpa/src/drivers/driver_atheros.c @@ -0,0 +1,2300 @@ +/* + * hostapd / Driver interaction with Atheros driver + * Copyright (c) 2004, Sam Leffler + * Copyright (c) 2004, Video54 Technologies + * Copyright (c) 2005-2007, Jouni Malinen + * Copyright (c) 2009, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include +#include + +#include "common.h" +#include "eloop.h" +#include "common/ieee802_11_defs.h" +#include "l2_packet/l2_packet.h" +#include "p2p/p2p.h" + +#include "common.h" +#ifndef _BYTE_ORDER +#ifdef WORDS_BIGENDIAN +#define _BYTE_ORDER _BIG_ENDIAN +#else +#define _BYTE_ORDER _LITTLE_ENDIAN +#endif +#endif /* _BYTE_ORDER */ + +/* + * Note, the ATH_WPS_IE setting must match with the driver build.. If the + * driver does not include this, the IEEE80211_IOCTL_GETWPAIE ioctl will fail. + */ +#define ATH_WPS_IE + +#include "ieee80211_external.h" + +/* Avoid conflicting definition from the driver header files with + * common/wpa_common.h */ +#undef WPA_OUI_TYPE + + +#ifdef CONFIG_WPS +#include +#endif /* CONFIG_WPS */ + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW 0x0019 +#endif + +#include "linux_wext.h" + +#include "driver.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "l2_packet/l2_packet.h" +#include "common/ieee802_11_defs.h" +#include "netlink.h" +#include "linux_ioctl.h" + +#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS) || defined(CONFIG_FILS) +#define ATHEROS_USE_RAW_RECEIVE +#endif + + +struct atheros_driver_data { + struct hostapd_data *hapd; /* back pointer */ + + char iface[IFNAMSIZ + 1]; + int ifindex; + struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ + struct l2_packet_data *sock_recv; /* raw packet recv socket */ + int ioctl_sock; /* socket for ioctl() use */ + struct netlink_data *netlink; + int we_version; + int fils_en; /* FILS enable/disable in driver */ + u8 acct_mac[ETH_ALEN]; + struct hostap_sta_driver_data acct_data; + + struct l2_packet_data *sock_raw; /* raw 802.11 management frames */ + struct wpabuf *wpa_ie; + struct wpabuf *wps_beacon_ie; + struct wpabuf *wps_probe_resp_ie; + u8 own_addr[ETH_ALEN]; +}; + +static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + u16 reason_code); +static int atheros_set_privacy(void *priv, int enabled); + +static const char * athr_get_ioctl_name(int op) +{ + switch (op) { + case IEEE80211_IOCTL_SETPARAM: + return "SETPARAM"; + case IEEE80211_IOCTL_GETPARAM: + return "GETPARAM"; + case IEEE80211_IOCTL_SETKEY: + return "SETKEY"; + case IEEE80211_IOCTL_SETWMMPARAMS: + return "SETWMMPARAMS"; + case IEEE80211_IOCTL_DELKEY: + return "DELKEY"; + case IEEE80211_IOCTL_GETWMMPARAMS: + return "GETWMMPARAMS"; + case IEEE80211_IOCTL_SETMLME: + return "SETMLME"; + case IEEE80211_IOCTL_GETCHANINFO: + return "GETCHANINFO"; + case IEEE80211_IOCTL_SETOPTIE: + return "SETOPTIE"; + case IEEE80211_IOCTL_GETOPTIE: + return "GETOPTIE"; + case IEEE80211_IOCTL_ADDMAC: + return "ADDMAC"; + case IEEE80211_IOCTL_DELMAC: + return "DELMAC"; + case IEEE80211_IOCTL_GETCHANLIST: + return "GETCHANLIST"; + case IEEE80211_IOCTL_SETCHANLIST: + return "SETCHANLIST"; + case IEEE80211_IOCTL_KICKMAC: + return "KICKMAC"; + case IEEE80211_IOCTL_CHANSWITCH: + return "CHANSWITCH"; + case IEEE80211_IOCTL_GETMODE: + return "GETMODE"; + case IEEE80211_IOCTL_SETMODE: + return "SETMODE"; + case IEEE80211_IOCTL_GET_APPIEBUF: + return "GET_APPIEBUF"; + case IEEE80211_IOCTL_SET_APPIEBUF: + return "SET_APPIEBUF"; + case IEEE80211_IOCTL_SET_ACPARAMS: + return "SET_ACPARAMS"; + case IEEE80211_IOCTL_FILTERFRAME: + return "FILTERFRAME"; + case IEEE80211_IOCTL_SET_RTPARAMS: + return "SET_RTPARAMS"; + case IEEE80211_IOCTL_SET_MEDENYENTRY: + return "SET_MEDENYENTRY"; + case IEEE80211_IOCTL_GET_MACADDR: + return "GET_MACADDR"; + case IEEE80211_IOCTL_SET_HBRPARAMS: + return "SET_HBRPARAMS"; + case IEEE80211_IOCTL_SET_RXTIMEOUT: + return "SET_RXTIMEOUT"; + case IEEE80211_IOCTL_STA_STATS: + return "STA_STATS"; + case IEEE80211_IOCTL_GETWPAIE: + return "GETWPAIE"; + default: + return "??"; + } +} + + +static const char * athr_get_param_name(int op) +{ + switch (op) { + case IEEE80211_IOC_MCASTCIPHER: + return "MCASTCIPHER"; + case IEEE80211_PARAM_MCASTKEYLEN: + return "MCASTKEYLEN"; + case IEEE80211_PARAM_UCASTCIPHERS: + return "UCASTCIPHERS"; + case IEEE80211_PARAM_KEYMGTALGS: + return "KEYMGTALGS"; + case IEEE80211_PARAM_RSNCAPS: + return "RSNCAPS"; + case IEEE80211_PARAM_WPA: + return "WPA"; + case IEEE80211_PARAM_AUTHMODE: + return "AUTHMODE"; + case IEEE80211_PARAM_PRIVACY: + return "PRIVACY"; + case IEEE80211_PARAM_COUNTERMEASURES: + return "COUNTERMEASURES"; + default: + return "??"; + } +} + + +#ifdef CONFIG_FILS +static int +get80211param(struct atheros_driver_data *drv, int op, int *data) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.mode = op; + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_GETPARAM, &iwr) < 0) + return -1; + + *data = iwr.u.mode; + return 0; +} +#endif /* CONFIG_FILS */ + + +static int +set80211priv(struct atheros_driver_data *drv, int op, void *data, int len) +{ + struct iwreq iwr; + int do_inline = len < IFNAMSIZ; + + /* Certain ioctls must use the non-inlined method */ + if (op == IEEE80211_IOCTL_SET_APPIEBUF || + op == IEEE80211_IOCTL_FILTERFRAME) + do_inline = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + if (do_inline) { + /* + * Argument data fits inline; put it there. + */ + os_memcpy(iwr.u.name, data, len); + } else { + /* + * Argument data too big for inline transfer; setup a + * parameter block instead; the kernel will transfer + * the data for the driver. + */ + iwr.u.data.pointer = data; + iwr.u.data.length = len; + } + + if (ioctl(drv->ioctl_sock, op, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "atheros: %s: %s: ioctl op=0x%x " + "(%s) len=%d failed: %d (%s)", + __func__, drv->iface, op, + athr_get_ioctl_name(op), + len, errno, strerror(errno)); + return -1; + } + return 0; +} + +static int +set80211param(struct atheros_driver_data *drv, int op, int arg) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.mode = op; + os_memcpy(iwr.u.name + sizeof(__u32), &arg, sizeof(arg)); + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { + wpa_printf(MSG_INFO, + "%s: %s: Failed to set parameter (op %d (%s) arg %d): ioctl[IEEE80211_IOCTL_SETPARAM]: %s", + __func__, drv->iface, op, athr_get_param_name(op), + arg, strerror(errno)); + return -1; + } + return 0; +} + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * +ether_sprintf(const u8 *addr) +{ + static char buf[sizeof(MACSTR)]; + + if (addr != NULL) + os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + else + os_snprintf(buf, sizeof(buf), MACSTR, 0, 0, 0, 0, 0, 0); + return buf; +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + +/* + * Configure WPA parameters. + */ +static int +atheros_configure_wpa(struct atheros_driver_data *drv, + struct wpa_bss_params *params) +{ + int v; + + switch (params->wpa_group) { + case WPA_CIPHER_CCMP: + v = IEEE80211_CIPHER_AES_CCM; + break; +#ifdef ATH_GCM_SUPPORT + case WPA_CIPHER_CCMP_256: + v = IEEE80211_CIPHER_AES_CCM_256; + break; + case WPA_CIPHER_GCMP: + v = IEEE80211_CIPHER_AES_GCM; + break; + case WPA_CIPHER_GCMP_256: + v = IEEE80211_CIPHER_AES_GCM_256; + break; +#endif /* ATH_GCM_SUPPORT */ + case WPA_CIPHER_TKIP: + v = IEEE80211_CIPHER_TKIP; + break; + case WPA_CIPHER_WEP104: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_WEP40: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_NONE: + v = IEEE80211_CIPHER_NONE; + break; + default: + wpa_printf(MSG_ERROR, "Unknown group key cipher %u", + params->wpa_group); + return -1; + } + wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { + wpa_printf(MSG_INFO, "Unable to set group key cipher to %u", v); + return -1; + } + if (v == IEEE80211_CIPHER_WEP) { + /* key length is done only for specific ciphers */ + v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); + if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { + wpa_printf(MSG_INFO, + "Unable to set group key length to %u", v); + return -1; + } + } + + v = 0; + if (params->wpa_pairwise & WPA_CIPHER_CCMP) + v |= 1<wpa_pairwise & WPA_CIPHER_CCMP_256) + v |= 1<wpa_pairwise & WPA_CIPHER_GCMP) + v |= 1<wpa_pairwise & WPA_CIPHER_GCMP_256) + v |= 1<wpa_pairwise & WPA_CIPHER_TKIP) + v |= 1<wpa_pairwise & WPA_CIPHER_NONE) + v |= 1<wpa_key_mgmt); + if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, + params->wpa_key_mgmt)) { + wpa_printf(MSG_INFO, + "Unable to set key management algorithms to 0x%x", + params->wpa_key_mgmt); + return -1; + } + + v = 0; + if (params->rsn_preauth) + v |= BIT(0); +#ifdef CONFIG_IEEE80211W + if (params->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + v |= BIT(7); + if (params->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) + v |= BIT(6); + } +#endif /* CONFIG_IEEE80211W */ + + wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { + wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x", + v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa); + if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) { + wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa); + return -1; + } + return 0; +} + +static int +atheros_set_ieee8021x(void *priv, struct wpa_bss_params *params) +{ + struct atheros_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); + + if (!params->enabled) { + /* XXX restore state */ + if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, + IEEE80211_AUTH_AUTO) < 0) + return -1; + /* IEEE80211_AUTH_AUTO ends up enabling Privacy; clear that */ + return atheros_set_privacy(drv, 0); + } + if (!params->wpa && !params->ieee802_1x) { + wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!"); + return -1; + } + if (params->wpa && atheros_configure_wpa(drv, params) != 0) { + wpa_printf(MSG_WARNING, "Error configuring WPA state!"); + return -1; + } + if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, + (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { + wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!"); + return -1; + } + + return 0; +} + +static int +atheros_set_privacy(void *priv, int enabled) +{ + struct atheros_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled); +} + +static int +atheros_set_sta_authorized(void *priv, const u8 *addr, int authorized) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d", + __func__, ether_sprintf(addr), authorized); + + if (authorized) + mlme.im_op = IEEE80211_MLME_AUTHORIZE; + else + mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; + mlme.im_reason = 0; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR, + __func__, authorized ? "" : "un", MAC2STR(addr)); + } + + return ret; +} + +static int +atheros_sta_set_flags(void *priv, const u8 *addr, + unsigned int total_flags, unsigned int flags_or, + unsigned int flags_and) +{ + /* For now, only support setting Authorized flag */ + if (flags_or & WPA_STA_AUTHORIZED) + return atheros_set_sta_authorized(priv, addr, 1); + if (!(flags_and & WPA_STA_AUTHORIZED)) + return atheros_set_sta_authorized(priv, addr, 0); + return 0; +} + +static int +atheros_del_key(void *priv, const u8 *addr, int key_idx) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_del_key wk; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", + __func__, ether_sprintf(addr), key_idx); + + os_memset(&wk, 0, sizeof(wk)); + if (addr != NULL) { + os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; + } else { + wk.idk_keyix = key_idx; + } + + ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s" + " key_idx %d)", __func__, ether_sprintf(addr), + key_idx); + } + + return ret; +} + +static int +atheros_set_key(const char *ifname, void *priv, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, const u8 *seq, + size_t seq_len, const u8 *key, size_t key_len) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_key wk; + u_int8_t cipher; + int ret; + + if (alg == WPA_ALG_NONE) + return atheros_del_key(drv, addr, key_idx); + + wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d", + __func__, alg, ether_sprintf(addr), key_idx); + + switch (alg) { + case WPA_ALG_WEP: + cipher = IEEE80211_CIPHER_WEP; + break; + case WPA_ALG_TKIP: + cipher = IEEE80211_CIPHER_TKIP; + break; + case WPA_ALG_CCMP: + cipher = IEEE80211_CIPHER_AES_CCM; + break; +#ifdef ATH_GCM_SUPPORT + case WPA_ALG_CCMP_256: + cipher = IEEE80211_CIPHER_AES_CCM_256; + break; + case WPA_ALG_GCMP: + cipher = IEEE80211_CIPHER_AES_GCM; + break; + case WPA_ALG_GCMP_256: + cipher = IEEE80211_CIPHER_AES_GCM_256; + break; +#endif /* ATH_GCM_SUPPORT */ +#ifdef CONFIG_IEEE80211W + case WPA_ALG_IGTK: + cipher = IEEE80211_CIPHER_AES_CMAC; + break; +#ifdef ATH_GCM_SUPPORT + case WPA_ALG_BIP_CMAC_256: + cipher = IEEE80211_CIPHER_AES_CMAC_256; + break; + case WPA_ALG_BIP_GMAC_128: + cipher = IEEE80211_CIPHER_AES_GMAC; + break; + case WPA_ALG_BIP_GMAC_256: + cipher = IEEE80211_CIPHER_AES_GMAC_256; + break; +#endif /* ATH_GCM_SUPPORT */ +#endif /* CONFIG_IEEE80211W */ + default: + wpa_printf(MSG_INFO, "%s: unknown/unsupported algorithm %d", + __func__, alg); + return -1; + } + + if (key_len > sizeof(wk.ik_keydata)) { + wpa_printf(MSG_INFO, "%s: key length %lu too big", __func__, + (unsigned long) key_len); + return -3; + } + + os_memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; + if (addr == NULL || is_broadcast_ether_addr(addr)) { + os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + wk.ik_keyix = key_idx; + if (set_tx) + wk.ik_flags |= IEEE80211_KEY_DEFAULT; + } else { + os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = IEEE80211_KEYIX_NONE; + } + wk.ik_keylen = key_len; + os_memcpy(wk.ik_keydata, key, key_len); + + ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s" + " key_idx %d alg %d key_len %lu set_tx %d)", + __func__, ether_sprintf(wk.ik_macaddr), key_idx, + alg, (unsigned long) key_len, set_tx); + } + + return ret; +} + + +static int +atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, + u8 *seq) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_key wk; + + wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", + __func__, ether_sprintf(addr), idx); + + os_memset(&wk, 0, sizeof(wk)); + if (addr == NULL) + os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + else + os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = idx; + + if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) { + wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data " + "(addr " MACSTR " key_idx %d)", + __func__, MAC2STR(wk.ik_macaddr), idx); + return -1; + } + +#ifdef WORDS_BIGENDIAN + { + /* + * wk.ik_keytsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; +#ifndef WPA_KEY_RSC_LEN +#define WPA_KEY_RSC_LEN 8 +#endif + u8 tmp[WPA_KEY_RSC_LEN]; + os_memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); + for (i = 0; i < WPA_KEY_RSC_LEN; i++) { + seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; + } + } +#else /* WORDS_BIGENDIAN */ + os_memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); +#endif /* WORDS_BIGENDIAN */ + return 0; +} + + +static int +atheros_flush(void *priv) +{ + u8 allsta[IEEE80211_ADDR_LEN]; + os_memset(allsta, 0xff, IEEE80211_ADDR_LEN); + return atheros_sta_deauth(priv, NULL, allsta, + IEEE80211_REASON_AUTH_LEAVE); +} + + +static int +atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_sta_stats stats; + + os_memset(data, 0, sizeof(*data)); + + /* + * Fetch statistics for station from the system. + */ + os_memset(&stats, 0, sizeof(stats)); + os_memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_STA_STATS, + &stats, sizeof(stats))) { + wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + if (os_memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + os_memcpy(data, &drv->acct_data, sizeof(*data)); + return 0; + } + + wpa_printf(MSG_INFO, + "Failed to get station stats information element"); + return -1; + } + + data->rx_packets = stats.is_stats.ns_rx_data; + data->rx_bytes = stats.is_stats.ns_rx_bytes; + data->tx_packets = stats.is_stats.ns_tx_data; + data->tx_bytes = stats.is_stats.ns_tx_bytes; + return 0; +} + + +static int +atheros_sta_clear_stats(void *priv, const u8 *addr) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr)); + + mlme.im_op = IEEE80211_MLME_CLEAR_STATS; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, + sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + } + + return ret; +} + + +static int +atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) +{ + struct atheros_driver_data *drv = priv; + u8 buf[512]; + struct ieee80211req_getset_appiebuf *app_ie; + + wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, + (unsigned long) ie_len); + wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len); + + wpabuf_free(drv->wpa_ie); + if (ie) + drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len); + else + drv->wpa_ie = NULL; + + app_ie = (struct ieee80211req_getset_appiebuf *) buf; + if (ie) + os_memcpy(&(app_ie->app_buf[0]), ie, ie_len); + app_ie->app_buflen = ie_len; + + app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON; + + /* append WPS IE for Beacon */ + if (drv->wps_beacon_ie != NULL) { + os_memcpy(&(app_ie->app_buf[ie_len]), + wpabuf_head(drv->wps_beacon_ie), + wpabuf_len(drv->wps_beacon_ie)); + app_ie->app_buflen = ie_len + wpabuf_len(drv->wps_beacon_ie); + } + wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(Beacon)", + app_ie->app_buf, app_ie->app_buflen); + set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie, + sizeof(struct ieee80211req_getset_appiebuf) + + app_ie->app_buflen); + + /* append WPS IE for Probe Response */ + app_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_RESP; + if (drv->wps_probe_resp_ie != NULL) { + os_memcpy(&(app_ie->app_buf[ie_len]), + wpabuf_head(drv->wps_probe_resp_ie), + wpabuf_len(drv->wps_probe_resp_ie)); + app_ie->app_buflen = ie_len + + wpabuf_len(drv->wps_probe_resp_ie); + } else + app_ie->app_buflen = ie_len; + wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(ProbeResp)", + app_ie->app_buf, app_ie->app_buflen); + set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie, + sizeof(struct ieee80211req_getset_appiebuf) + + app_ie->app_buflen); + return 0; +} + +static int +atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + u16 reason_code) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +static int +atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + u16 reason_code) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr " + MACSTR " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set, + u8 qos_map_set_len) +{ +#ifdef CONFIG_ATHEROS_QOS_MAP + struct atheros_driver_data *drv = ctx; + struct ieee80211req_athdbg req; + struct ieee80211_qos_map *qos_map = &req.data.qos_map; + struct iwreq iwr; + int i, up_start; + + if (qos_map_set_len < 16 || qos_map_set_len > 58 || + qos_map_set_len & 1) { + wpa_printf(MSG_ERROR, "Invalid QoS Map"); + return -1; + } else { + os_memset(&req, 0, sizeof(struct ieee80211req_athdbg)); + req.cmd = IEEE80211_DBGREQ_SETQOSMAPCONF; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, sizeof(iwr.ifr_name)); + iwr.u.data.pointer = (void *) &req; + iwr.u.data.length = sizeof(struct ieee80211req_athdbg); + } + + qos_map->valid = 1; + qos_map->num_dscp_except = (qos_map_set_len - 16) / 2; + if (qos_map->num_dscp_except) { + for (i = 0; i < qos_map->num_dscp_except; i++) { + qos_map->dscp_exception[i].dscp = qos_map_set[i * 2]; + qos_map->dscp_exception[i].up = qos_map_set[i * 2 + 1]; + } + } + + up_start = qos_map_set_len - 16; + for (i = 0; i < IEEE80211_MAX_QOS_UP_RANGE; i++) { + qos_map->up[i].low = qos_map_set[up_start + (i * 2)]; + qos_map->up[i].high = qos_map_set[up_start + (i * 2) + 1]; + } + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_DBGREQ, &iwr) < 0) { + wpa_printf(MSG_ERROR, + "%s: %s: Failed to set QoS Map: ioctl[IEEE80211_IOCTL_DBGREQ]: %s", + __func__, drv->iface, strerror(errno)); + return -1; + } +#endif /* CONFIG_ATHEROS_QOS_MAP */ + + return 0; +} + +#ifdef ATHEROS_USE_RAW_RECEIVE +static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct atheros_driver_data *drv = ctx; + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 fc, stype; + int ielen; + const u8 *iebuf; + + if (len < IEEE80211_HDRLEN) + return; + + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) + return; + + stype = WLAN_FC_GET_STYPE(fc); + + wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, + (int) len); + + if (stype == WLAN_FC_STYPE_PROBE_REQ) { + if (len < IEEE80211_HDRLEN) + return; + + os_memset(&event, 0, sizeof(event)); + event.rx_probe_req.sa = mgmt->sa; + event.rx_probe_req.da = mgmt->da; + event.rx_probe_req.bssid = mgmt->bssid; + event.rx_probe_req.ie = buf + IEEE80211_HDRLEN; + event.rx_probe_req.ie_len = len - IEEE80211_HDRLEN; + wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event); + return; + } + + if (stype == WLAN_FC_STYPE_ACTION && + (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) == 0 || + is_broadcast_ether_addr(mgmt->bssid))) { + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); + return; + } + + if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", + __func__); + return; + } + + switch (stype) { + case WLAN_FC_STYPE_ASSOC_REQ: + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)) + break; + ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); + iebuf = mgmt->u.assoc_req.variable; + drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0); + break; + case WLAN_FC_STYPE_REASSOC_REQ: + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)) + break; + ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); + iebuf = mgmt->u.reassoc_req.variable; + drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1); + break; + case WLAN_FC_STYPE_AUTH: + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) + break; + os_memset(&event, 0, sizeof(event)); + if (le_to_host16(mgmt->u.auth.auth_alg) == WLAN_AUTH_SAE) { + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); + break; + } + os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); + os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN); + event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); + event.auth.status_code = + le_to_host16(mgmt->u.auth.status_code); + event.auth.auth_transaction = + le_to_host16(mgmt->u.auth.auth_transaction); + event.auth.ies = mgmt->u.auth.variable; + event.auth.ies_len = len - IEEE80211_HDRLEN - + sizeof(mgmt->u.auth); + wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event); + break; + default: + break; + } +} +#endif /* ATHEROS_USE_RAW_RECEIVE */ + +static int atheros_receive_pkt(struct atheros_driver_data *drv) +{ + int ret = 0; + struct ieee80211req_set_filter filt; + + wpa_printf(MSG_DEBUG, "%s Enter", __func__); + filt.app_filterype = 0; +#ifdef CONFIG_WPS + filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ; +#endif /* CONFIG_WPS */ +#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS) + filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ | + IEEE80211_FILTER_TYPE_AUTH | + IEEE80211_FILTER_TYPE_ACTION); +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */ +#ifdef CONFIG_WNM + filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; +#endif /* CONFIG_WNM */ +#ifdef CONFIG_HS20 + filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; +#endif /* CONFIG_HS20 */ + if (filt.app_filterype) { + ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); + if (ret) + return ret; + } + +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS) + drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, + atheros_raw_receive, drv, 1); + if (drv->sock_raw == NULL) + return -1; +#endif /* CONFIG_WPS || CONFIG_IEEE80211R || CONFIG_FILS */ + return ret; +} + +static int atheros_reset_appfilter(struct atheros_driver_data *drv) +{ + struct ieee80211req_set_filter filt; + filt.app_filterype = 0; + return set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); +} + +#ifdef CONFIG_WPS +static int +atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) +{ + struct atheros_driver_data *drv = priv; + u8 buf[512]; + struct ieee80211req_getset_appiebuf *beac_ie; + + wpa_printf(MSG_DEBUG, "%s buflen = %lu frametype=%u", __func__, + (unsigned long) len, frametype); + wpa_hexdump(MSG_DEBUG, "atheros: IE", ie, len); + + beac_ie = (struct ieee80211req_getset_appiebuf *) buf; + beac_ie->app_frmtype = frametype; + beac_ie->app_buflen = len; + if (ie) + os_memcpy(&(beac_ie->app_buf[0]), ie, len); + + /* append the WPA/RSN IE if it is set already */ + if (((frametype == IEEE80211_APPIE_FRAME_BEACON) || + (frametype == IEEE80211_APPIE_FRAME_PROBE_RESP)) && + (drv->wpa_ie != NULL)) { + wpa_hexdump_buf(MSG_DEBUG, "atheros: Append WPA/RSN IE", + drv->wpa_ie); + os_memcpy(&(beac_ie->app_buf[len]), wpabuf_head(drv->wpa_ie), + wpabuf_len(drv->wpa_ie)); + beac_ie->app_buflen += wpabuf_len(drv->wpa_ie); + } + + wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF", + beac_ie->app_buf, beac_ie->app_buflen); + return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, + sizeof(struct ieee80211req_getset_appiebuf) + + beac_ie->app_buflen); +} + +static int +atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) +{ + struct atheros_driver_data *drv = priv; + + wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - beacon", beacon); + wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - proberesp", + proberesp); + wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - assocresp", + assocresp); + wpabuf_free(drv->wps_beacon_ie); + drv->wps_beacon_ie = beacon ? wpabuf_dup(beacon) : NULL; + wpabuf_free(drv->wps_probe_resp_ie); + drv->wps_probe_resp_ie = proberesp ? wpabuf_dup(proberesp) : NULL; + + atheros_set_wps_ie(priv, assocresp ? wpabuf_head(assocresp) : NULL, + assocresp ? wpabuf_len(assocresp) : 0, + IEEE80211_APPIE_FRAME_ASSOC_RESP); + if (atheros_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, + beacon ? wpabuf_len(beacon) : 0, + IEEE80211_APPIE_FRAME_BEACON)) + return -1; + return atheros_set_wps_ie(priv, + proberesp ? wpabuf_head(proberesp) : NULL, + proberesp ? wpabuf_len(proberesp): 0, + IEEE80211_APPIE_FRAME_PROBE_RESP); +} +#else /* CONFIG_WPS */ +#define atheros_set_ap_wps_ie NULL +#endif /* CONFIG_WPS */ + +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) +static int +atheros_sta_auth(void *priv, struct wpa_driver_sta_auth_params *params) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d", + __func__, ether_sprintf(params->addr), params->status); + +#ifdef CONFIG_FILS + /* Copy FILS AAD parameters if the driver supports FILS */ + if (params->fils_auth && drv->fils_en) { + wpa_printf(MSG_DEBUG, "%s: im_op IEEE80211_MLME_AUTH_FILS", + __func__); + os_memcpy(mlme.fils_aad.ANonce, params->fils_anonce, + IEEE80211_FILS_NONCE_LEN); + os_memcpy(mlme.fils_aad.SNonce, params->fils_snonce, + IEEE80211_FILS_NONCE_LEN); + os_memcpy(mlme.fils_aad.kek, params->fils_kek, + IEEE80211_MAX_WPA_KEK_LEN); + mlme.fils_aad.kek_len = params->fils_kek_len; + mlme.im_op = IEEE80211_MLME_AUTH_FILS; + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", + mlme.fils_aad.ANonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", + mlme.fils_aad.SNonce, FILS_NONCE_LEN); + wpa_hexdump_key(MSG_DEBUG, "FILS: KEK", + mlme.fils_aad.kek, mlme.fils_aad.kek_len); + } else { + mlme.im_op = IEEE80211_MLME_AUTH; + } +#else /* CONFIG_FILS */ + mlme.im_op = IEEE80211_MLME_AUTH; +#endif /* CONFIG_FILS */ + + mlme.im_reason = params->status; + mlme.im_seq = params->seq; + os_memcpy(mlme.im_macaddr, params->addr, IEEE80211_ADDR_LEN); + mlme.im_optie_len = params->len; + if (params->len) { + if (params->len < IEEE80211_MAX_OPT_IE) { + os_memcpy(mlme.im_optie, params->ie, params->len); + } else { + wpa_printf(MSG_DEBUG, "%s: Not enough space to copy " + "opt_ie STA (addr " MACSTR " reason %d, " + "ie_len %d)", + __func__, MAC2STR(params->addr), + params->status, (int) params->len); + return -1; + } + } + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(params->addr), params->status); + } + return ret; +} + +static int +atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr, + int reassoc, u16 status_code, const u8 *ie, size_t len) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d reassoc %d", + __func__, ether_sprintf(addr), status_code, reassoc); + + if (reassoc) + mlme.im_op = IEEE80211_MLME_REASSOC; + else + mlme.im_op = IEEE80211_MLME_ASSOC; + mlme.im_reason = status_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + mlme.im_optie_len = len; + if (len) { + if (len < IEEE80211_MAX_OPT_IE) { + os_memcpy(mlme.im_optie, ie, len); + } else { + wpa_printf(MSG_DEBUG, "%s: Not enough space to copy " + "opt_ie STA (addr " MACSTR " reason %d, " + "ie_len %d)", + __func__, MAC2STR(addr), status_code, + (int) len); + return -1; + } + } + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to assoc STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), status_code); + } + return ret; +} +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */ + +static void +atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct hostapd_data *hapd = drv->hapd; + struct ieee80211req_wpaie ie; + int ielen = 0; + u8 *iebuf = NULL; + + /* + * Fetch negotiated WPA/RSN parameters from the system. + */ + os_memset(&ie, 0, sizeof(ie)); + os_memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { + /* + * See ATH_WPS_IE comment in the beginning of the file for a + * possible cause for the failure.. + */ + wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE: %s", + __func__, strerror(errno)); + goto no_ie; + } + wpa_hexdump(MSG_MSGDUMP, "atheros req WPA IE", + ie.wpa_ie, IEEE80211_MAX_OPT_IE); + wpa_hexdump(MSG_MSGDUMP, "atheros req RSN IE", + ie.rsn_ie, IEEE80211_MAX_OPT_IE); +#ifdef ATH_WPS_IE + wpa_hexdump(MSG_MSGDUMP, "atheros req WPS IE", + ie.wps_ie, IEEE80211_MAX_OPT_IE); +#endif /* ATH_WPS_IE */ + iebuf = ie.wpa_ie; + /* atheros seems to return some random data if WPA/RSN IE is not set. + * Assume the IE was not included if the IE type is unknown. */ + if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) + iebuf[1] = 0; + if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { + /* atheros-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not + * set. This is needed for WPA2. */ + iebuf = ie.rsn_ie; + if (iebuf[0] != WLAN_EID_RSN) + iebuf[1] = 0; + } + + ielen = iebuf[1]; + +#ifdef ATH_WPS_IE + /* if WPS IE is present, preference is given to WPS */ + if (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie.wps_ie[1] > 0) { + iebuf = ie.wps_ie; + ielen = ie.wps_ie[1]; + } +#endif /* ATH_WPS_IE */ + + if (ielen == 0) + iebuf = NULL; + else + ielen += 2; + +no_ie: + drv_event_assoc(hapd, addr, iebuf, ielen, 0); + + if (os_memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + /* Cached accounting data is not valid anymore. */ + os_memset(drv->acct_mac, 0, ETH_ALEN); + os_memset(&drv->acct_data, 0, sizeof(drv->acct_data)); + } +} + +static void +atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, + char *custom, char *end) +{ +#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = os_strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + union wpa_event_data data; + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = 1; + data.michael_mic_failure.src = addr; + wpa_supplicant_event(drv->hapd, + EVENT_MICHAEL_MIC_FAILURE, &data); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) { + char *key, *value; + u32 val; + key = custom; + while ((key = os_strchr(key, '\n')) != NULL) { + key++; + value = os_strchr(key, '='); + if (value == NULL) + continue; + *value++ = '\0'; + val = strtoul(value, NULL, 10); + if (os_strcmp(key, "mac") == 0) + hwaddr_aton(value, drv->acct_mac); + else if (os_strcmp(key, "rx_packets") == 0) + drv->acct_data.rx_packets = val; + else if (os_strcmp(key, "tx_packets") == 0) + drv->acct_data.tx_packets = val; + else if (os_strcmp(key, "rx_bytes") == 0) + drv->acct_data.rx_bytes = val; + else if (os_strcmp(key, "tx_bytes") == 0) + drv->acct_data.tx_bytes = val; + key = value; + } +#ifdef CONFIG_WPS + } else if (os_strncmp(custom, "PUSH-BUTTON.indication", 22) == 0) { + /* Some atheros kernels send push button as a wireless event */ + /* PROBLEM! this event is received for ALL BSSs ... + * so all are enabled for WPS... ugh. + */ + wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL); + } else if (os_strncmp(custom, "Manage.prob_req ", 16) == 0) { + /* + * Atheros driver uses a hack to pass Probe Request frames as a + * binary data in the custom wireless event. The old way (using + * packet sniffing) didn't work when bridging. + * Format: "Manage.prob_req " | zero padding | frame + */ + int len = atoi(custom + 16); + if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) { + wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event " + "length %d", len); + return; + } + atheros_raw_receive(drv, NULL, + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); +#endif /* CONFIG_WPS */ +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) + } else if (os_strncmp(custom, "Manage.assoc_req ", 17) == 0) { + /* Format: "Manage.assoc_req " | zero padding | + * frame */ + int len = atoi(custom + 17); + if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) { + wpa_printf(MSG_DEBUG, + "Invalid Manage.assoc_req event length %d", + len); + return; + } + atheros_raw_receive(drv, NULL, + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); + } else if (os_strncmp(custom, "Manage.auth ", 12) == 0) { + /* Format: "Manage.auth " | zero padding | frame */ + int len = atoi(custom + 12); + if (len < 0 || + MGMT_FRAM_TAG_SIZE + len > end - custom) { + wpa_printf(MSG_DEBUG, + "Invalid Manage.auth event length %d", len); + return; + } + atheros_raw_receive(drv, NULL, + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); +#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R || CONFIG_FILS */ +#ifdef ATHEROS_USE_RAW_RECEIVE + } else if (os_strncmp(custom, "Manage.action ", 14) == 0) { + /* Format: "Manage.assoc_req " | zero padding | frame + */ + int len = atoi(custom + 14); + if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) { + wpa_printf(MSG_DEBUG, + "Invalid Manage.action event length %d", + len); + return; + } + atheros_raw_receive(drv, NULL, + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); +#endif /* ATHEROS_USE_RAW_RECEIVE */ + } +} + + +static void send_action_cb_event(struct atheros_driver_data *drv, + char *data, size_t data_len) +{ + union wpa_event_data event; + struct ieee80211_send_action_cb *sa; + const struct ieee80211_hdr *hdr; + u16 fc; + + if (data_len < sizeof(*sa) + 24) { + wpa_printf(MSG_DEBUG, + "athr: Too short event message (data_len=%d sizeof(*sa)=%d)", + (int) data_len, (int) sizeof(*sa)); + wpa_hexdump(MSG_DEBUG, "athr: Short event message", + data, data_len); + return; + } + + sa = (struct ieee80211_send_action_cb *) data; + + hdr = (const struct ieee80211_hdr *) (sa + 1); + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_GET_TYPE(fc); + event.tx_status.stype = WLAN_FC_GET_STYPE(fc); + event.tx_status.dst = sa->dst_addr; + event.tx_status.data = (const u8 *) hdr; + event.tx_status.data_len = data_len - sizeof(*sa); + event.tx_status.ack = sa->ack; + wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event); +} + + +/* +* Handle size of data problem. WEXT only allows data of 256 bytes for custom +* events, and p2p data can be much bigger. So the athr driver sends a small +* event telling me to collect the big data with an ioctl. +* On the first event, send all pending events to supplicant. +*/ +static void fetch_pending_big_events(struct atheros_driver_data *drv) +{ + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u8 tbuf[IW_PRIV_SIZE_MASK]; /* max size is 2047 bytes */ + u16 fc, stype; + struct iwreq iwr; + size_t data_len; + u32 freq, frame_type; + + while (1) { + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.data.pointer = (void *) tbuf; + iwr.u.data.length = sizeof(tbuf); + iwr.u.data.flags = IEEE80211_IOC_P2P_FETCH_FRAME; + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) + < 0) { + if (errno == ENOSPC) { + wpa_printf(MSG_DEBUG, "%s:%d exit", + __func__, __LINE__); + return; + } + wpa_printf(MSG_DEBUG, "athr: %s: P2P_BIG_PARAM[" + "P2P_FETCH_FRAME] failed: %s", + __func__, strerror(errno)); + return; + } + data_len = iwr.u.data.length; + wpa_hexdump(MSG_DEBUG, "athr: P2P_FETCH_FRAME data", + (u8 *) tbuf, data_len); + if (data_len < sizeof(freq) + sizeof(frame_type) + 24) { + wpa_printf(MSG_DEBUG, "athr: frame too short"); + continue; + } + os_memcpy(&freq, tbuf, sizeof(freq)); + os_memcpy(&frame_type, &tbuf[sizeof(freq)], + sizeof(frame_type)); + mgmt = (void *) &tbuf[sizeof(freq) + sizeof(frame_type)]; + data_len -= sizeof(freq) + sizeof(frame_type); + + if (frame_type == IEEE80211_EV_RX_MGMT) { + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + + wpa_printf(MSG_DEBUG, "athr: EV_RX_MGMT stype=%u " + "freq=%u len=%u", stype, freq, (int) data_len); + + if (stype == WLAN_FC_STYPE_ACTION) { + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = (const u8 *) mgmt; + event.rx_mgmt.frame_len = data_len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, + &event); + continue; + } + } else if (frame_type == IEEE80211_EV_P2P_SEND_ACTION_CB) { + wpa_printf(MSG_DEBUG, + "%s: ACTION_CB frame_type=%u len=%zu", + __func__, frame_type, data_len); + send_action_cb_event(drv, (void *) mgmt, data_len); + } else { + wpa_printf(MSG_DEBUG, "athr: %s unknown type %d", + __func__, frame_type); + continue; + } + } +} + +static void +atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv, + int opcode, char *buf, int len) +{ + switch (opcode) { + case IEEE80211_EV_P2P_SEND_ACTION_CB: + wpa_printf(MSG_DEBUG, "WEXT: EV_P2P_SEND_ACTION_CB"); + fetch_pending_big_events(drv); + break; + case IEEE80211_EV_RX_MGMT: + wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT"); + fetch_pending_big_events(drv); + break; + default: + break; + } +} + +static void +atheros_wireless_event_wireless(struct atheros_driver_data *drv, + char *data, unsigned int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while ((size_t) (end - pos) >= IW_EV_LCP_LEN) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVASSOCREQIE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVEXPIRED: + drv_event_disassoc(drv->hapd, + (u8 *) iwe->u.addr.sa_data); + break; + case IWEVREGISTERED: + atheros_new_sta(drv, (u8 *) iwe->u.addr.sa_data); + break; + case IWEVASSOCREQIE: + /* Driver hack.. Use IWEVASSOCREQIE to bypass + * IWEVCUSTOM size limitations. Need to handle this + * just like IWEVCUSTOM. + */ + case IWEVCUSTOM: + if (iwe->u.data.length > end - custom) + return; + buf = os_malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; /* XXX */ + os_memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + + if (iwe->u.data.flags != 0) { + atheros_wireless_event_atheros_custom( + drv, (int) iwe->u.data.flags, + buf, len); + } else { + atheros_wireless_event_wireless_custom( + drv, buf, buf + iwe->u.data.length); + } + os_free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void +atheros_wireless_event_rtm_newlink(void *ctx, + struct ifinfomsg *ifi, u8 *buf, size_t len) +{ + struct atheros_driver_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + + if (ifi->ifi_index != drv->ifindex) + return; + + attrlen = len; + attr = (struct rtattr *) buf; + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + atheros_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static int +atheros_get_we_version(struct atheros_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s", + strerror(errno)); + os_free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + os_free(range); + return 0; +} + + +static int +atheros_wireless_event_init(struct atheros_driver_data *drv) +{ + struct netlink_config *cfg; + + atheros_get_we_version(drv); + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + return -1; + cfg->ctx = drv; + cfg->newlink_cb = atheros_wireless_event_rtm_newlink; + drv->netlink = netlink_init(cfg); + if (drv->netlink == NULL) { + os_free(cfg); + return -1; + } + + return 0; +} + + +static int +atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr, u32 flags) +{ + struct atheros_driver_data *drv = priv; + unsigned char buf[3000]; + unsigned char *bp = buf; + struct l2_ethhdr *eth; + size_t len; + int status; + + /* + * Prepend the Ethernet header. If the caller left us + * space at the front we could just insert it but since + * we don't know we copy to a local buffer. Given the frequency + * and size of frames this probably doesn't matter. + */ + len = data_len + sizeof(struct l2_ethhdr); + if (len > sizeof(buf)) { + bp = os_malloc(len); + if (bp == NULL) { + wpa_printf(MSG_INFO, + "EAPOL frame discarded, cannot malloc temp buffer of size %lu!", + (unsigned long) len); + return -1; + } + } + eth = (struct l2_ethhdr *) bp; + os_memcpy(eth->h_dest, addr, ETH_ALEN); + os_memcpy(eth->h_source, own_addr, ETH_ALEN); + eth->h_proto = host_to_be16(ETH_P_EAPOL); + os_memcpy(eth + 1, data, data_len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); + + status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); + + if (bp != buf) + os_free(bp); + return status; +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct atheros_driver_data *drv = ctx; + drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr), + len - sizeof(struct l2_ethhdr)); +} + + +static void atheros_read_fils_cap(struct atheros_driver_data *drv) +{ + int fils = 0; + +#ifdef CONFIG_FILS + /* TODO: Would be better to have #ifdef on the IEEE80211_PARAM_* value + * to automatically check this against the driver header files. */ + if (get80211param(drv, IEEE80211_PARAM_ENABLE_FILS, &fils) < 0) { + wpa_printf(MSG_DEBUG, + "%s: Failed to get FILS capability from driver", + __func__); + /* Assume driver does not support FILS */ + fils = 0; + } +#endif /* CONFIG_FILS */ + drv->fils_en = fils; + wpa_printf(MSG_DEBUG, "atheros: fils_en=%d", drv->fils_en); +} + + +static void * +atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) +{ + struct atheros_driver_data *drv; + struct ifreq ifr; + struct iwreq iwr; + char brname[IFNAMSIZ]; + + drv = os_zalloc(sizeof(struct atheros_driver_data)); + if (drv == NULL) { + wpa_printf(MSG_INFO, + "Could not allocate memory for atheros driver data"); + return NULL; + } + + drv->hapd = hapd; + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); + goto bad; + } + os_memcpy(drv->iface, params->ifname, sizeof(drv->iface)); + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", + strerror(errno)); + goto bad; + } + drv->ifindex = ifr.ifr_ifindex; + + drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_xmit == NULL) + goto bad; + if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) + goto bad; + os_memcpy(drv->own_addr, params->own_addr, ETH_ALEN); + if (params->bridge[0]) { + wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", + params->bridge[0]); + drv->sock_recv = l2_packet_init(params->bridge[0], NULL, + ETH_P_EAPOL, handle_read, drv, + 1); + if (drv->sock_recv == NULL) + goto bad; + } else if (linux_br_get(brname, drv->iface) == 0) { + wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for " + "EAPOL receive", brname); + drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_recv == NULL) + goto bad; + } else + drv->sock_recv = drv->sock_xmit; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.mode = IW_MODE_MASTER; + + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { + wpa_printf(MSG_ERROR, + "Could not set interface to master mode! ioctl[SIOCSIWMODE]: %s", + strerror(errno)); + goto bad; + } + + /* mark down during setup */ + linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); + atheros_set_privacy(drv, 0); /* default to no privacy */ + + if (atheros_receive_pkt(drv)) + goto bad; + + if (atheros_wireless_event_init(drv)) + goto bad; + + /* Read FILS capability from the driver */ + atheros_read_fils_cap(drv); + + return drv; +bad: + atheros_reset_appfilter(drv); + if (drv->sock_raw) + l2_packet_deinit(drv->sock_raw); + if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) + l2_packet_deinit(drv->sock_recv); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + os_free(drv); + return NULL; +} + + +static void +atheros_deinit(void *priv) +{ + struct atheros_driver_data *drv = priv; + + atheros_reset_appfilter(drv); + + if (drv->wpa_ie || drv->wps_beacon_ie || drv->wps_probe_resp_ie) { + atheros_set_opt_ie(priv, NULL, 0); + wpabuf_free(drv->wpa_ie); + wpabuf_free(drv->wps_beacon_ie); + wpabuf_free(drv->wps_probe_resp_ie); + } + netlink_deinit(drv->netlink); + (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) + l2_packet_deinit(drv->sock_recv); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->sock_raw) + l2_packet_deinit(drv->sock_raw); + os_free(drv); +} + +static int +atheros_set_ssid(void *priv, const u8 *buf, int len) +{ + struct atheros_driver_data *drv = priv; + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s", + len, strerror(errno)); + return -1; + } + return 0; +} + +static int +atheros_get_ssid(void *priv, u8 *buf, int len) +{ + struct atheros_driver_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ? + IW_ESSID_MAX_SIZE : len; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s", + strerror(errno)); + ret = -1; + } else + ret = iwr.u.essid.length; + + return ret; +} + +static int +atheros_set_countermeasures(void *priv, int enabled) +{ + struct atheros_driver_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); +} + +static int +atheros_commit(void *priv) +{ + struct atheros_driver_data *drv = priv; + return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); +} + +static int atheros_set_authmode(void *priv, int auth_algs) +{ + int authmode; + + if ((auth_algs & WPA_AUTH_ALG_OPEN) && + (auth_algs & WPA_AUTH_ALG_SHARED)) + authmode = IEEE80211_AUTH_AUTO; + else if (auth_algs & WPA_AUTH_ALG_OPEN) + authmode = IEEE80211_AUTH_OPEN; + else if (auth_algs & WPA_AUTH_ALG_SHARED) + authmode = IEEE80211_AUTH_SHARED; + else + return -1; + + return set80211param(priv, IEEE80211_PARAM_AUTHMODE, authmode); +} + +static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params) +{ + /* + * TODO: Use this to replace set_authmode, set_privacy, set_ieee8021x, + * set_generic_elem, and hapd_set_ssid. + */ + + wpa_printf(MSG_DEBUG, "atheros: set_ap - pairwise_ciphers=0x%x " + "group_cipher=0x%x key_mgmt_suites=0x%x auth_algs=0x%x " + "wpa_version=0x%x privacy=%d interworking=%d", + params->pairwise_ciphers, params->group_cipher, + params->key_mgmt_suites, params->auth_algs, + params->wpa_version, params->privacy, params->interworking); + wpa_hexdump_ascii(MSG_DEBUG, "atheros: SSID", + params->ssid, params->ssid_len); + if (params->hessid) + wpa_printf(MSG_DEBUG, "atheros: HESSID " MACSTR, + MAC2STR(params->hessid)); + wpa_hexdump_buf(MSG_DEBUG, "atheros: beacon_ies", + params->beacon_ies); + wpa_hexdump_buf(MSG_DEBUG, "atheros: proberesp_ies", + params->proberesp_ies); + wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies", + params->assocresp_ies); + +#if defined(CONFIG_HS20) && (defined(IEEE80211_PARAM_OSEN) || defined(CONFIG_ATHEROS_OSEN)) + if (params->osen) { + struct wpa_bss_params bss_params; + + os_memset(&bss_params, 0, sizeof(struct wpa_bss_params)); + bss_params.enabled = 1; + bss_params.wpa = 2; + bss_params.wpa_pairwise = WPA_CIPHER_CCMP; + bss_params.wpa_group = WPA_CIPHER_CCMP; + bss_params.ieee802_1x = 1; + + if (atheros_set_privacy(priv, 1) || + set80211param(priv, IEEE80211_PARAM_OSEN, 1)) + return -1; + + return atheros_set_ieee8021x(priv, &bss_params); + } +#endif /* CONFIG_HS20 && IEEE80211_PARAM_OSEN */ + + return 0; +} + + +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) + +static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len, + int noack, unsigned int freq, + const u16 *csa_offs, size_t csa_offs_len) +{ + struct atheros_driver_data *drv = priv; + u8 buf[1510]; + const struct ieee80211_mgmt *mgmt; + struct ieee80211req_mgmtbuf *mgmt_frm; + + mgmt = (const struct ieee80211_mgmt *) frm; + wpa_printf(MSG_DEBUG, "%s frmlen = %lu " MACSTR, __func__, + (unsigned long) data_len, MAC2STR(mgmt->da)); + mgmt_frm = (struct ieee80211req_mgmtbuf *) buf; + os_memcpy(mgmt_frm->macaddr, (u8 *)mgmt->da, IEEE80211_ADDR_LEN); + mgmt_frm->buflen = data_len; + if (&mgmt_frm->buf[0] + data_len > buf + sizeof(buf)) { + wpa_printf(MSG_INFO, "atheros: Too long frame for " + "atheros_send_mgmt (%u)", (unsigned int) data_len); + return -1; + } + os_memcpy(&mgmt_frm->buf[0], frm, data_len); + return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm, + sizeof(struct ieee80211req_mgmtbuf) + data_len); +} +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */ + + +#ifdef CONFIG_IEEE80211R + +static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie, + size_t tspec_ielen) +{ + struct atheros_driver_data *drv = priv; + int retv; + struct ieee80211req_res req; + struct ieee80211req_res_addts *addts = &req.u.addts; + + wpa_printf(MSG_DEBUG, "%s", __func__); + req.type = IEEE80211_RESREQ_ADDTS; + os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN); + os_memcpy(addts->tspecie, tspec_ie, tspec_ielen); + retv = set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req, + sizeof(struct ieee80211req_res)); + if (retv < 0) { + wpa_printf(MSG_DEBUG, "%s IEEE80211_IOCTL_RES_REQ FAILED " + "retv = %d", __func__, retv); + return -1; + } + os_memcpy(tspec_ie, addts->tspecie, tspec_ielen); + return addts->status; +} + + +static int atheros_add_sta_node(void *priv, const u8 *addr, u16 auth_alg) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_res req; + struct ieee80211req_res_addnode *addnode = &req.u.addnode; + + wpa_printf(MSG_DEBUG, "%s", __func__); + req.type = IEEE80211_RESREQ_ADDNODE; + os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN); + addnode->auth_alg = auth_alg; + return set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req, + sizeof(struct ieee80211req_res)); +} + +#endif /* CONFIG_IEEE80211R */ + + +/* Use only to set a big param, get will not work. */ +static int +set80211big(struct atheros_driver_data *drv, int op, const void *data, int len) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.data.pointer = (void *) data; + iwr.u.data.length = len; + iwr.u.data.flags = op; + wpa_printf(MSG_DEBUG, "%s: op=0x%x=%d (%s) len=0x%x", + __func__, op, op, athr_get_param_name(op), len); + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "%s: op=0x%x (%s) subop=0x%x=%d " + "value=0x%x,0x%x failed: %d (%s)", + __func__, op, athr_get_ioctl_name(op), iwr.u.mode, + iwr.u.mode, iwr.u.data.length, + iwr.u.data.flags, errno, strerror(errno)); + return -1; + } + return 0; +} + + +static int atheros_send_action(void *priv, unsigned int freq, + unsigned int wait, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, int no_cck) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211_p2p_send_action *act; + int res; + + act = os_zalloc(sizeof(*act) + data_len); + if (act == NULL) + return -1; + act->freq = freq; + os_memcpy(act->dst_addr, dst, ETH_ALEN); + os_memcpy(act->src_addr, src, ETH_ALEN); + os_memcpy(act->bssid, bssid, ETH_ALEN); + os_memcpy(act + 1, data, data_len); + wpa_printf(MSG_DEBUG, "%s: freq=%d, wait=%u, dst=" MACSTR ", src=" + MACSTR ", bssid=" MACSTR, + __func__, act->freq, wait, MAC2STR(act->dst_addr), + MAC2STR(act->src_addr), MAC2STR(act->bssid)); + wpa_hexdump(MSG_MSGDUMP, "athr: act", (u8 *) act, sizeof(*act)); + wpa_hexdump(MSG_MSGDUMP, "athr: data", data, data_len); + + res = set80211big(drv, IEEE80211_IOC_P2P_SEND_ACTION, + act, sizeof(*act) + data_len); + os_free(act); + return res; +} + + +#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM) +static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer, + u8 *ie, u16 *len, enum wnm_oper oper) +{ +#define IEEE80211_APPIE_MAX 1024 /* max appie buffer size */ + u8 buf[IEEE80211_APPIE_MAX]; + struct ieee80211req_getset_appiebuf *tfs_ie; + u16 val; + + wpa_printf(MSG_DEBUG, "atheros: ifname=%s, WNM TFS IE oper=%d " MACSTR, + drv->iface, oper, MAC2STR(peer)); + + switch (oper) { + case WNM_SLEEP_TFS_REQ_IE_SET: + if (*len > IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf)) { + wpa_printf(MSG_DEBUG, "TFS Req IE(s) too large"); + return -1; + } + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = ETH_ALEN + 2 + 2 + *len; + + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = *len; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + /* copy the ie */ + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2 + 2, ie, *len); + + if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + break; + case WNM_SLEEP_TFS_RESP_IE_ADD: + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf); + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = 0; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + if (set80211priv(drv, IEEE80211_IOCTL_GET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to get WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + + *len = tfs_ie->app_buflen; + os_memcpy(ie, &(tfs_ie->app_buf[0]), *len); + wpa_printf(MSG_DEBUG, "atheros: %c len=%d", tfs_ie->app_buf[0], + *len); + break; + case WNM_SLEEP_TFS_RESP_IE_NONE: + *len = 0; + break; + case WNM_SLEEP_TFS_IE_DEL: + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf); + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = 0; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + break; + default: + wpa_printf(MSG_DEBUG, "Unsupported TFS oper %d", oper); + break; + } + + return 0; +} + + +static int atheros_wnm_sleep(struct atheros_driver_data *drv, + const u8 *peer, enum wnm_oper oper) +{ + u8 *data, *pos; + size_t dlen; + int ret; + u16 val; + + wpa_printf(MSG_DEBUG, "atheros: WNM-Sleep Oper %d, " MACSTR, + oper, MAC2STR(peer)); + + dlen = ETH_ALEN + 2 + 2; + data = os_malloc(dlen); + if (data == NULL) + return -1; + + /* Command header for driver */ + pos = data; + os_memcpy(pos, peer, ETH_ALEN); + pos += ETH_ALEN; + + val = oper; + os_memcpy(pos, &val, 2); + pos += 2; + + val = 0; + os_memcpy(pos, &val, 2); + + ret = atheros_set_wps_ie(drv, data, dlen, IEEE80211_APPIE_FRAME_WNM); + + os_free(data); + + return ret; +} + + +static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len) +{ + struct atheros_driver_data *drv = priv; + + switch (oper) { + case WNM_SLEEP_ENTER_CONFIRM: + case WNM_SLEEP_ENTER_FAIL: + case WNM_SLEEP_EXIT_CONFIRM: + case WNM_SLEEP_EXIT_FAIL: + return atheros_wnm_sleep(drv, peer, oper); + case WNM_SLEEP_TFS_REQ_IE_SET: + case WNM_SLEEP_TFS_RESP_IE_ADD: + case WNM_SLEEP_TFS_RESP_IE_NONE: + case WNM_SLEEP_TFS_IE_DEL: + return athr_wnm_tfs(drv, peer, buf, buf_len, oper); + default: + wpa_printf(MSG_DEBUG, "atheros: Unsupported WNM operation %d", + oper); + return -1; + } +} +#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */ + + +const struct wpa_driver_ops wpa_driver_atheros_ops = { + .name = "atheros", + .hapd_init = atheros_init, + .hapd_deinit = atheros_deinit, + .set_ieee8021x = atheros_set_ieee8021x, + .set_privacy = atheros_set_privacy, + .set_key = atheros_set_key, + .get_seqnum = atheros_get_seqnum, + .flush = atheros_flush, + .set_generic_elem = atheros_set_opt_ie, + .sta_set_flags = atheros_sta_set_flags, + .read_sta_data = atheros_read_sta_driver_data, + .hapd_send_eapol = atheros_send_eapol, + .sta_disassoc = atheros_sta_disassoc, + .sta_deauth = atheros_sta_deauth, + .hapd_set_ssid = atheros_set_ssid, + .hapd_get_ssid = atheros_get_ssid, + .set_countermeasures = atheros_set_countermeasures, + .sta_clear_stats = atheros_sta_clear_stats, + .commit = atheros_commit, + .set_ap_wps_ie = atheros_set_ap_wps_ie, + .set_authmode = atheros_set_authmode, + .set_ap = atheros_set_ap, +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) + .sta_assoc = atheros_sta_assoc, + .sta_auth = atheros_sta_auth, + .send_mlme = atheros_send_mgmt, +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R + .add_tspec = atheros_add_tspec, + .add_sta_node = atheros_add_sta_node, +#endif /* CONFIG_IEEE80211R */ + .send_action = atheros_send_action, +#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM) + .wnm_oper = atheros_wnm_oper, +#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */ + .set_qos_map = atheros_set_qos_map, +}; diff --git a/contrib/wpa/src/drivers/driver_bsd.c b/contrib/wpa/src/drivers/driver_bsd.c index 808fc7da55af..77d336f24ffa 100644 --- a/contrib/wpa/src/drivers/driver_bsd.c +++ b/contrib/wpa/src/drivers/driver_bsd.c @@ -662,7 +662,7 @@ rtbuf_len(void) #undef WPA_OUI_TYPE static int bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, - int reason_code); + u16 reason_code); static const char * ether_sprintf(const u8 *addr) @@ -754,7 +754,7 @@ bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, } static int -bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code) +bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, u16 reason_code) { return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code, addr); @@ -762,7 +762,7 @@ bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code) static int bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, - int reason_code) + u16 reason_code) { return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code, addr); @@ -1025,7 +1025,7 @@ wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled) } static int -wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code) +wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, u16 reason_code) { return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code, addr); diff --git a/contrib/wpa/src/drivers/driver_common.c b/contrib/wpa/src/drivers/driver_common.c index e55e6cd2b795..731c6a3b16bd 100644 --- a/contrib/wpa/src/drivers/driver_common.c +++ b/contrib/wpa/src/drivers/driver_common.c @@ -67,6 +67,7 @@ const char * event_to_string(enum wpa_event_type event) E2S(DRIVER_CLIENT_POLL_OK); E2S(EAPOL_TX_STATUS); E2S(CH_SWITCH); + E2S(CH_SWITCH_STARTED); E2S(WNM); E2S(CONNECT_FAILED_REASON); E2S(DFS_RADAR_DETECTED); @@ -87,6 +88,7 @@ const char * event_to_string(enum wpa_event_type event) E2S(STATION_OPMODE_CHANGED); E2S(INTERFACE_MAC_CHANGED); E2S(WDS_STA_INTERFACE_STATUS); + E2S(UPDATE_DH); } return "UNKNOWN"; diff --git a/contrib/wpa/src/drivers/driver_hostap.c b/contrib/wpa/src/drivers/driver_hostap.c new file mode 100644 index 000000000000..186eccbf2181 --- /dev/null +++ b/contrib/wpa/src/drivers/driver_hostap.c @@ -0,0 +1,1202 @@ +/* + * Driver interaction with Linux Host AP driver + * Copyright (c) 2003-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#include "linux_wext.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" +#include "eloop.h" +#include "driver_hostap.h" + + +#include +#include + +#include "priv_netlink.h" +#include "netlink.h" +#include "linux_ioctl.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" + + +/* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X + * frames that might be longer than normal default MTU and they are not + * fragmented */ +#define HOSTAPD_MTU 2290 + +static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + +struct hostap_driver_data { + struct hostapd_data *hapd; + + char iface[IFNAMSIZ + 1]; + int sock; /* raw packet socket for driver access */ + int ioctl_sock; /* socket for ioctl() use */ + struct netlink_data *netlink; + + int we_version; + + u8 *generic_ie; + size_t generic_ie_len; + u8 *wps_ie; + size_t wps_ie_len; +}; + + +static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, + int len); +static int hostap_set_iface_flags(void *priv, int dev_up); + +static void handle_data(struct hostap_driver_data *drv, u8 *buf, size_t len, + u16 stype) +{ + struct ieee80211_hdr *hdr; + u16 fc, ethertype; + u8 *pos, *sa; + size_t left; + union wpa_event_data event; + + if (len < sizeof(struct ieee80211_hdr)) + return; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) { + printf("Not ToDS data frame (fc=0x%04x)\n", fc); + return; + } + + sa = hdr->addr2; + os_memset(&event, 0, sizeof(event)); + event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); + event.rx_from_unknown.addr = sa; + wpa_supplicant_event(drv->hapd, EVENT_RX_FROM_UNKNOWN, &event); + + pos = (u8 *) (hdr + 1); + left = len - sizeof(*hdr); + + if (left < sizeof(rfc1042_header)) { + printf("Too short data frame\n"); + return; + } + + if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) { + printf("Data frame with no RFC1042 header\n"); + return; + } + pos += sizeof(rfc1042_header); + left -= sizeof(rfc1042_header); + + if (left < 2) { + printf("No ethertype in data frame\n"); + return; + } + + ethertype = WPA_GET_BE16(pos); + pos += 2; + left -= 2; + switch (ethertype) { + case ETH_P_PAE: + drv_event_eapol_rx(drv->hapd, sa, pos, left); + break; + + default: + printf("Unknown ethertype 0x%04x in data frame\n", ethertype); + break; + } +} + + +static void handle_tx_callback(struct hostap_driver_data *drv, u8 *buf, + size_t len, int ok) +{ + struct ieee80211_hdr *hdr; + u16 fc; + union wpa_event_data event; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_GET_TYPE(fc); + event.tx_status.stype = WLAN_FC_GET_STYPE(fc); + event.tx_status.dst = hdr->addr1; + event.tx_status.data = buf; + event.tx_status.data_len = len; + event.tx_status.ack = ok; + wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event); +} + + +static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len) +{ + struct ieee80211_hdr *hdr; + u16 fc, type, stype; + size_t data_len = len; + int ver; + union wpa_event_data event; + + /* PSPOLL is only 16 bytes, but driver does not (at least yet) pass + * these to user space */ + if (len < 24) { + wpa_printf(MSG_MSGDUMP, "handle_frame: too short (%lu)", + (unsigned long) len); + return; + } + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + if (type != WLAN_FC_TYPE_MGMT || stype != WLAN_FC_STYPE_BEACON) { + wpa_hexdump(MSG_MSGDUMP, "Received management frame", + buf, len); + } + + ver = fc & WLAN_FC_PVER; + + /* protocol version 2 is reserved for indicating ACKed frame (TX + * callbacks), and version 1 for indicating failed frame (no ACK, TX + * callbacks) */ + if (ver == 1 || ver == 2) { + handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0); + return; + } else if (ver != 0) { + printf("unknown protocol version %d\n", ver); + return; + } + + switch (type) { + case WLAN_FC_TYPE_MGMT: + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = data_len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); + break; + case WLAN_FC_TYPE_CTRL: + wpa_printf(MSG_DEBUG, "CTRL"); + break; + case WLAN_FC_TYPE_DATA: + wpa_printf(MSG_DEBUG, "DATA"); + handle_data(drv, buf, data_len, stype); + break; + default: + wpa_printf(MSG_DEBUG, "unknown frame type %d", type); + break; + } +} + + +static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct hostap_driver_data *drv = eloop_ctx; + int len; + unsigned char buf[3000]; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + wpa_printf(MSG_ERROR, "recv: %s", strerror(errno)); + return; + } + + handle_frame(drv, buf, len); +} + + +static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr) +{ + struct ifreq ifr; + struct sockaddr_ll addr; + + drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (drv->sock < 0) { + wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s", + strerror(errno)); + return -1; + } + + if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) { + wpa_printf(MSG_ERROR, "Could not register read socket"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + if (os_snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", + drv->iface) >= (int) sizeof(ifr.ifr_name)) { + wpa_printf(MSG_ERROR, "hostap: AP interface name truncated"); + return -1; + } + if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", + strerror(errno)); + return -1; + } + + if (hostap_set_iface_flags(drv, 1)) { + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); + return -1; + } + + return linux_get_ifhwaddr(drv->sock, drv->iface, own_addr); +} + + +static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack, + unsigned int freq, + const u16 *csa_offs, size_t csa_offs_len) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg; + int res; + + /* Request TX callback */ + hdr->frame_control |= host_to_le16(BIT(1)); + res = send(drv->sock, msg, len, 0); + hdr->frame_control &= ~host_to_le16(BIT(1)); + + return res; +} + + +static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, const u8 *own_addr, + u32 flags) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + sizeof(rfc1042_header) + 2 + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + printf("malloc() failed for hostapd_send_data(len=%lu)\n", + (unsigned long) len); + return -1; + } + + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); + hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); + if (encrypt) + hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); + memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); + memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); + + pos = (u8 *) (hdr + 1); + memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); + pos += sizeof(rfc1042_header); + *((u16 *) pos) = htons(ETH_P_PAE); + pos += 2; + memcpy(pos, data, data_len); + + res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0, NULL, 0); + if (res < 0) { + wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - " + "failed: %d (%s)", + (unsigned long) len, errno, strerror(errno)); + } + os_free(hdr); + + return res; +} + + +static int hostap_sta_set_flags(void *priv, const u8 *addr, + unsigned int total_flags, unsigned int flags_or, + unsigned int flags_and) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + if (flags_or & WPA_STA_AUTHORIZED) + flags_or = BIT(5); /* WLAN_STA_AUTHORIZED */ + if (!(flags_and & WPA_STA_AUTHORIZED)) + flags_and = ~BIT(5); + else + flags_and = ~0; + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_SET_FLAGS_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + param.u.set_flags_sta.flags_or = flags_or; + param.u.set_flags_sta.flags_and = flags_and; + return hostapd_ioctl(drv, ¶m, sizeof(param)); +} + + +static int hostap_set_iface_flags(void *priv, int dev_up) +{ + struct hostap_driver_data *drv = priv; + struct ifreq ifr; + char ifname[IFNAMSIZ]; + + if (os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface) >= IFNAMSIZ) { + wpa_printf(MSG_ERROR, "hostap: AP interface name truncated"); + return -1; + } + if (linux_set_iface_flags(drv->ioctl_sock, ifname, dev_up) < 0) + return -1; + + if (dev_up) { + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_mtu = HOSTAPD_MTU; + if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { + wpa_printf(MSG_INFO, + "Setting MTU failed - trying to survive with current value: ioctl[SIOCSIFMTU]: %s", + strerror(errno)); + } + } + + return 0; +} + + +static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, + int len) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_HOSTAPD]: %s", + strerror(errno)); + return -1; + } + + return 0; +} + + +static int wpa_driver_hostap_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_SET_ENCRYPTION; + if (addr == NULL) + memset(param->sta_addr, 0xff, ETH_ALEN); + else + memcpy(param->sta_addr, addr, ETH_ALEN); + switch (alg) { + case WPA_ALG_NONE: + os_strlcpy((char *) param->u.crypt.alg, "NONE", + HOSTAP_CRYPT_ALG_NAME_LEN); + break; + case WPA_ALG_WEP: + os_strlcpy((char *) param->u.crypt.alg, "WEP", + HOSTAP_CRYPT_ALG_NAME_LEN); + break; + case WPA_ALG_TKIP: + os_strlcpy((char *) param->u.crypt.alg, "TKIP", + HOSTAP_CRYPT_ALG_NAME_LEN); + break; + case WPA_ALG_CCMP: + os_strlcpy((char *) param->u.crypt.alg, "CCMP", + HOSTAP_CRYPT_ALG_NAME_LEN); + break; + default: + os_free(buf); + return -1; + } + param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; + param->u.crypt.idx = key_idx; + param->u.crypt.key_len = key_len; + memcpy((u8 *) (param + 1), key, key_len); + + if (hostapd_ioctl(drv, param, blen)) { + printf("Failed to set encryption.\n"); + ret = -1; + } + free(buf); + + return ret; +} + + +static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr, + int idx, u8 *seq) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + + blen = sizeof(*param) + 32; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_GET_ENCRYPTION; + if (addr == NULL) + os_memset(param->sta_addr, 0xff, ETH_ALEN); + else + os_memcpy(param->sta_addr, addr, ETH_ALEN); + param->u.crypt.idx = idx; + + if (hostapd_ioctl(drv, param, blen)) { + printf("Failed to get encryption.\n"); + ret = -1; + } else { + os_memcpy(seq, param->u.crypt.seq, 8); + } + os_free(buf); + + return ret; +} + + +static int hostap_ioctl_prism2param(void *priv, int param, int value) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + int *i; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = param; + *i++ = value; + + if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_PRISM2_PARAM]: %s", + strerror(errno)); + return -1; + } + + return 0; +} + + +static int hostap_set_ieee8021x(void *priv, struct wpa_bss_params *params) +{ + struct hostap_driver_data *drv = priv; + int enabled = params->enabled; + + /* enable kernel driver support for IEEE 802.1X */ + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_IEEE_802_1X, enabled)) { + printf("Could not setup IEEE 802.1X support in kernel driver." + "\n"); + return -1; + } + + if (!enabled) + return 0; + + /* use host driver implementation of encryption to allow + * individual keys and passing plaintext EAPOL frames */ + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_DECRYPT, 1) || + hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_ENCRYPT, 1)) { + printf("Could not setup host-based encryption in kernel " + "driver.\n"); + return -1; + } + + return 0; +} + + +static int hostap_set_privacy(void *priv, int enabled) +{ + struct hostap_drvier_data *drv = priv; + + return hostap_ioctl_prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED, + enabled); +} + + +static int hostap_set_ssid(void *priv, const u8 *buf, int len) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len + 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s", + len, strerror(errno)); + return -1; + } + + return 0; +} + + +static int hostap_flush(void *priv) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_FLUSH; + return hostapd_ioctl(drv, ¶m, sizeof(param)); +} + + +static int hostap_read_sta_data(void *priv, + struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + char buf[1024], line[128], *pos; + FILE *f; + unsigned long val; + + memset(data, 0, sizeof(*data)); + snprintf(buf, sizeof(buf), "/proc/net/hostap/%s/" MACSTR, + drv->iface, MAC2STR(addr)); + + f = fopen(buf, "r"); + if (!f) + return -1; + /* Need to read proc file with in one piece, so use large enough + * buffer. */ + setbuffer(f, buf, sizeof(buf)); + + while (fgets(line, sizeof(line), f)) { + pos = strchr(line, '='); + if (!pos) + continue; + *pos++ = '\0'; + val = strtoul(pos, NULL, 10); + if (strcmp(line, "rx_packets") == 0) + data->rx_packets = val; + else if (strcmp(line, "tx_packets") == 0) + data->tx_packets = val; + else if (strcmp(line, "rx_bytes") == 0) + data->rx_bytes = val; + else if (strcmp(line, "tx_bytes") == 0) + data->tx_bytes = val; + } + + fclose(f); + + return 0; +} + + +static int hostap_sta_add(void *priv, struct hostapd_sta_add_params *params) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + int tx_supp_rates = 0; + size_t i; + +#define WLAN_RATE_1M BIT(0) +#define WLAN_RATE_2M BIT(1) +#define WLAN_RATE_5M5 BIT(2) +#define WLAN_RATE_11M BIT(3) + + for (i = 0; i < params->supp_rates_len; i++) { + if ((params->supp_rates[i] & 0x7f) == 2) + tx_supp_rates |= WLAN_RATE_1M; + if ((params->supp_rates[i] & 0x7f) == 4) + tx_supp_rates |= WLAN_RATE_2M; + if ((params->supp_rates[i] & 0x7f) == 11) + tx_supp_rates |= WLAN_RATE_5M5; + if ((params->supp_rates[i] & 0x7f) == 22) + tx_supp_rates |= WLAN_RATE_11M; + } + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_ADD_STA; + memcpy(param.sta_addr, params->addr, ETH_ALEN); + param.u.add_sta.aid = params->aid; + param.u.add_sta.capability = params->capability; + param.u.add_sta.tx_supp_rates = tx_supp_rates; + return hostapd_ioctl(drv, ¶m, sizeof(param)); +} + + +static int hostap_sta_remove(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + hostap_sta_set_flags(drv, addr, 0, 0, ~WPA_STA_AUTHORIZED); + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_REMOVE_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + printf("Could not remove station from kernel driver.\n"); + return -1; + } + return 0; +} + + +static int hostap_get_inact_sec(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_GET_INFO_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + return -1; + } + + return param.u.get_info_sta.inactive_sec; +} + + +static int hostap_sta_clear_stats(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_STA_CLEAR_STATS; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + return -1; + } + + return 0; +} + + +static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv) +{ + struct prism2_hostapd_param *param; + int res; + size_t blen, elem_len; + + elem_len = drv->generic_ie_len + drv->wps_ie_len; + blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + elem_len; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; + param->u.generic_elem.len = elem_len; + if (drv->generic_ie) { + os_memcpy(param->u.generic_elem.data, drv->generic_ie, + drv->generic_ie_len); + } + if (drv->wps_ie) { + os_memcpy(¶m->u.generic_elem.data[drv->generic_ie_len], + drv->wps_ie, drv->wps_ie_len); + } + wpa_hexdump(MSG_DEBUG, "hostap: Set generic IE", + param->u.generic_elem.data, elem_len); + res = hostapd_ioctl(drv, param, blen); + + os_free(param); + + return res; +} + + +static int hostap_set_generic_elem(void *priv, + const u8 *elem, size_t elem_len) +{ + struct hostap_driver_data *drv = priv; + + os_free(drv->generic_ie); + drv->generic_ie = NULL; + drv->generic_ie_len = 0; + if (elem) { + drv->generic_ie = os_memdup(elem, elem_len); + if (drv->generic_ie == NULL) + return -1; + drv->generic_ie_len = elem_len; + } + + return hostapd_ioctl_set_generic_elem(drv); +} + + +static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) +{ + struct hostap_driver_data *drv = priv; + + /* + * Host AP driver supports only one set of extra IEs, so we need to + * use the Probe Response IEs also for Beacon frames since they include + * more information. + */ + + os_free(drv->wps_ie); + drv->wps_ie = NULL; + drv->wps_ie_len = 0; + if (proberesp) { + drv->wps_ie = os_memdup(wpabuf_head(proberesp), + wpabuf_len(proberesp)); + if (drv->wps_ie == NULL) + return -1; + drv->wps_ie_len = wpabuf_len(proberesp); + } + + return hostapd_ioctl_set_generic_elem(drv); +} + + +static void +hostapd_wireless_event_wireless_custom(struct hostap_driver_data *drv, + char *custom) +{ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + union wpa_event_data data; + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = 1; + data.michael_mic_failure.src = addr; + wpa_supplicant_event(drv->hapd, + EVENT_MICHAEL_MIC_FAILURE, &data); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } +} + + +static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv, + char *data, unsigned int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while ((size_t) (end - pos) >= IW_EV_LCP_LEN) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVCUSTOM: + if (iwe->u.data.length > end - custom) + return; + buf = malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + hostapd_wireless_event_wireless_custom(drv, buf); + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void hostapd_wireless_event_rtm_newlink(void *ctx, + struct ifinfomsg *ifi, + u8 *buf, size_t len) +{ + struct hostap_driver_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + + /* TODO: use ifi->ifi_index to filter out wireless events from other + * interfaces */ + + attrlen = len; + attr = (struct rtattr *) buf; + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + hostapd_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static int hostap_get_we_version(struct hostap_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s", + strerror(errno)); + os_free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + free(range); + return 0; +} + + +static int hostap_wireless_event_init(struct hostap_driver_data *drv) +{ + struct netlink_config *cfg; + + hostap_get_we_version(drv); + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + return -1; + cfg->ctx = drv; + cfg->newlink_cb = hostapd_wireless_event_rtm_newlink; + drv->netlink = netlink_init(cfg); + if (drv->netlink == NULL) { + os_free(cfg); + return -1; + } + + return 0; +} + + +static void * hostap_init(struct hostapd_data *hapd, + struct wpa_init_params *params) +{ + struct hostap_driver_data *drv; + + drv = os_zalloc(sizeof(struct hostap_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for hostapd driver data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->ioctl_sock = drv->sock = -1; + memcpy(drv->iface, params->ifname, sizeof(drv->iface)); + + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); + os_free(drv); + return NULL; + } + + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) { + wpa_printf(MSG_ERROR, + "Could not enable hostapd mode for interface %s", + drv->iface); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + if (hostap_init_sockets(drv, params->own_addr) || + hostap_wireless_event_init(drv)) { + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + return drv; +} + + +static void hostap_driver_deinit(void *priv) +{ + struct hostap_driver_data *drv = priv; + + netlink_deinit(drv->netlink); + (void) hostap_set_iface_flags(drv, 0); + (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 0); + (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD_STA, 0); + + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + + if (drv->sock >= 0) + close(drv->sock); + + os_free(drv->generic_ie); + os_free(drv->wps_ie); + + free(drv); +} + + +static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + u16 reason) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_mgmt mgmt; + + if (is_broadcast_ether_addr(addr)) { + /* + * New Prism2.5/3 STA firmware versions seem to have issues + * with this broadcast deauth frame. This gets the firmware in + * odd state where nothing works correctly, so let's skip + * sending this for the hostap driver. + */ + return 0; + } + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DEAUTH); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, own_addr, ETH_ALEN); + memcpy(mgmt.bssid, own_addr, ETH_ALEN); + mgmt.u.deauth.reason_code = host_to_le16(reason); + return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth), 0, 0, NULL, 0); +} + + +static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.freq.m = freq->channel; + iwr.u.freq.e = 0; + + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s", + strerror(errno)); + return -1; + } + + return 0; +} + + +static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + u16 reason) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_mgmt mgmt; + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DISASSOC); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, own_addr, ETH_ALEN); + memcpy(mgmt.bssid, own_addr, ETH_ALEN); + mgmt.u.disassoc.reason_code = host_to_le16(reason); + return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.disassoc), 0, 0, NULL, 0); +} + + +static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, + u16 *num_modes, + u16 *flags, u8 *dfs) +{ + struct hostapd_hw_modes *mode; + int i, clen, rlen; + const short chan2freq[14] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 + }; + + mode = os_zalloc(sizeof(struct hostapd_hw_modes)); + if (mode == NULL) + return NULL; + + *num_modes = 1; + *flags = 0; + *dfs = 0; + + mode->mode = HOSTAPD_MODE_IEEE80211B; + mode->num_channels = 14; + mode->num_rates = 4; + + clen = mode->num_channels * sizeof(struct hostapd_channel_data); + rlen = mode->num_rates * sizeof(int); + + mode->channels = os_zalloc(clen); + mode->rates = os_zalloc(rlen); + if (mode->channels == NULL || mode->rates == NULL) { + os_free(mode->channels); + os_free(mode->rates); + os_free(mode); + return NULL; + } + + for (i = 0; i < 14; i++) { + mode->channels[i].chan = i + 1; + mode->channels[i].freq = chan2freq[i]; + mode->channels[i].allowed_bw = HOSTAPD_CHAN_WIDTH_20; + /* TODO: Get allowed channel list from the driver */ + if (i >= 11) + mode->channels[i].flag = HOSTAPD_CHAN_DISABLED; + } + + mode->rates[0] = 10; + mode->rates[1] = 20; + mode->rates[2] = 55; + mode->rates[3] = 110; + + return mode; +} + + +static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr, + const u8 *addr, int qos) +{ + struct ieee80211_hdr hdr; + + os_memset(&hdr, 0, sizeof(hdr)); + + /* + * WLAN_FC_STYPE_NULLFUNC would be more appropriate, + * but it is apparently not retried so TX Exc events + * are not received for it. + * This is the reason the driver overrides the default + * handling. + */ + hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_DATA); + + hdr.frame_control |= + host_to_le16(WLAN_FC_FROMDS); + os_memcpy(hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); + os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); + + hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0, NULL, 0); +} + + +const struct wpa_driver_ops wpa_driver_hostap_ops = { + .name = "hostap", + .desc = "Host AP driver (Intersil Prism2/2.5/3)", + .set_key = wpa_driver_hostap_set_key, + .hapd_init = hostap_init, + .hapd_deinit = hostap_driver_deinit, + .set_ieee8021x = hostap_set_ieee8021x, + .set_privacy = hostap_set_privacy, + .get_seqnum = hostap_get_seqnum, + .flush = hostap_flush, + .set_generic_elem = hostap_set_generic_elem, + .read_sta_data = hostap_read_sta_data, + .hapd_send_eapol = hostap_send_eapol, + .sta_set_flags = hostap_sta_set_flags, + .sta_deauth = hostap_sta_deauth, + .sta_disassoc = hostap_sta_disassoc, + .sta_remove = hostap_sta_remove, + .hapd_set_ssid = hostap_set_ssid, + .send_mlme = hostap_send_mlme, + .sta_add = hostap_sta_add, + .get_inact_sec = hostap_get_inact_sec, + .sta_clear_stats = hostap_sta_clear_stats, + .get_hw_feature_data = hostap_get_hw_feature_data, + .set_ap_wps_ie = hostap_set_ap_wps_ie, + .set_freq = hostap_set_freq, + .poll_client = wpa_driver_hostap_poll_client, +}; diff --git a/contrib/wpa/src/drivers/driver_macsec_linux.c b/contrib/wpa/src/drivers/driver_macsec_linux.c index 9d981bb70f78..e922503fccd0 100644 --- a/contrib/wpa/src/drivers/driver_macsec_linux.c +++ b/contrib/wpa/src/drivers/driver_macsec_linux.c @@ -1,6 +1,7 @@ /* * Driver interaction with Linux MACsec kernel module * Copyright (c) 2016, Sabrina Dubroca and Red Hat, Inc. + * Copyright (c) 2019, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,6 +23,7 @@ #include "utils/common.h" #include "utils/eloop.h" +#include "common/eapol_common.h" #include "pae/ieee802_1x_kay.h" #include "driver.h" #include "driver_wired_common.h" @@ -57,6 +59,7 @@ struct macsec_drv_data { char ifname[IFNAMSIZ + 1]; int ifi; int parent_ifi; + int use_pae_group_addr; Boolean created_link; @@ -1399,6 +1402,214 @@ static int macsec_drv_status(void *priv, char *buf, size_t buflen) } +#ifdef __linux__ + +static void macsec_drv_handle_data(void *ctx, unsigned char *buf, size_t len) +{ +#ifdef HOSTAPD + struct ieee8023_hdr *hdr; + u8 *pos, *sa; + size_t left; + union wpa_event_data event; + + /* must contain at least ieee8023_hdr 6 byte source, 6 byte dest, + * 2 byte ethertype */ + if (len < 14) { + wpa_printf(MSG_MSGDUMP, "%s: too short (%lu)", + __func__, (unsigned long) len); + return; + } + + hdr = (struct ieee8023_hdr *) buf; + + switch (ntohs(hdr->ethertype)) { + case ETH_P_PAE: + wpa_printf(MSG_MSGDUMP, "Received EAPOL packet"); + sa = hdr->src; + os_memset(&event, 0, sizeof(event)); + event.new_sta.addr = sa; + wpa_supplicant_event(ctx, EVENT_NEW_STA, &event); + + pos = (u8 *) (hdr + 1); + left = len - sizeof(*hdr); + drv_event_eapol_rx(ctx, sa, pos, left); + break; + + default: + wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame", + ntohs(hdr->ethertype)); + break; + } +#endif /* HOSTAPD */ +} + + +static void macsec_drv_handle_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + int len; + unsigned char buf[3000]; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + wpa_printf(MSG_ERROR, "macsec_linux: recv: %s", + strerror(errno)); + return; + } + + macsec_drv_handle_data(eloop_ctx, buf, len); +} + +#endif /* __linux__ */ + + +static int macsec_drv_init_sockets(struct macsec_drv_data *drv, u8 *own_addr) +{ +#ifdef __linux__ + struct ifreq ifr; + struct sockaddr_ll addr; + + drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); + if (drv->common.sock < 0) { + wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s", + strerror(errno)); + return -1; + } + + if (eloop_register_read_sock(drv->common.sock, macsec_drv_handle_read, + drv->common.ctx, NULL)) { + wpa_printf(MSG_INFO, "Could not register read socket"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) { + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", + strerror(errno)); + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->common.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) + { + wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); + return -1; + } + + /* filter multicast address */ + if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex, + pae_group_addr, 1) < 0) { + wpa_printf(MSG_ERROR, "wired: Failed to add multicast group " + "membership"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) { + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s", + strerror(errno)); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x", + ifr.ifr_hwaddr.sa_family); + return -1; + } + os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + return 0; +#else /* __linux__ */ + return -1; +#endif /* __linux__ */ +} + + +static void * macsec_drv_hapd_init(struct hostapd_data *hapd, + struct wpa_init_params *params) +{ + struct macsec_drv_data *drv; + + drv = os_zalloc(sizeof(struct macsec_drv_data)); + if (drv == NULL) { + wpa_printf(MSG_INFO, + "Could not allocate memory for wired driver data"); + return NULL; + } + + drv->common.ctx = hapd; + os_strlcpy(drv->common.ifname, params->ifname, + sizeof(drv->common.ifname)); + drv->use_pae_group_addr = params->use_pae_group_addr; + + if (macsec_drv_init_sockets(drv, params->own_addr)) { + os_free(drv); + return NULL; + } + + return drv; +} + + +static void macsec_drv_hapd_deinit(void *priv) +{ + struct macsec_drv_data *drv = priv; + + if (drv->common.sock >= 0) { + eloop_unregister_read_sock(drv->common.sock); + close(drv->common.sock); + } + + os_free(drv); +} + + +static int macsec_drv_send_eapol(void *priv, const u8 *addr, + const u8 *data, size_t data_len, int encrypt, + const u8 *own_addr, u32 flags) +{ + struct macsec_drv_data *drv = priv; + struct ieee8023_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + wpa_printf(MSG_INFO, + "%s: malloc() failed (len=%lu)", + __func__, (unsigned long) len); + return -1; + } + + os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr, + ETH_ALEN); + os_memcpy(hdr->src, own_addr, ETH_ALEN); + hdr->ethertype = htons(ETH_P_PAE); + + pos = (u8 *) (hdr + 1); + os_memcpy(pos, data, data_len); + + res = send(drv->common.sock, (u8 *) hdr, len, 0); + os_free(hdr); + + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s: packet len: %lu - failed: send: %s", + __func__, (unsigned long) len, strerror(errno)); + } + + return res; +} + + const struct wpa_driver_ops wpa_driver_macsec_linux_ops = { .name = "macsec_linux", .desc = "MACsec Ethernet driver for Linux", @@ -1407,6 +1618,9 @@ const struct wpa_driver_ops wpa_driver_macsec_linux_ops = { .get_capa = driver_wired_get_capa, .init = macsec_drv_wpa_init, .deinit = macsec_drv_wpa_deinit, + .hapd_init = macsec_drv_hapd_init, + .hapd_deinit = macsec_drv_hapd_deinit, + .hapd_send_eapol = macsec_drv_send_eapol, .macsec_init = macsec_drv_macsec_init, .macsec_deinit = macsec_drv_macsec_deinit, diff --git a/contrib/wpa/src/drivers/driver_macsec_qca.c b/contrib/wpa/src/drivers/driver_macsec_qca.c index 8372393f26c2..f4e55d5d99a1 100644 --- a/contrib/wpa/src/drivers/driver_macsec_qca.c +++ b/contrib/wpa/src/drivers/driver_macsec_qca.c @@ -3,6 +3,7 @@ * Copyright (c) 2005-2009, Jouni Malinen * Copyright (c) 2004, Gunter Burchardt * Copyright (c) 2013-2014, Qualcomm Atheros, Inc. + * Copyright (c) 2019, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -29,6 +30,7 @@ #include "utils/eloop.h" #include "common/defs.h" #include "common/ieee802_1x_defs.h" +#include "common/eapol_common.h" #include "pae/ieee802_1x_kay.h" #include "driver.h" #include "driver_wired_common.h" @@ -64,6 +66,7 @@ struct channel_map { struct macsec_qca_data { struct driver_wired_common_data common; + int use_pae_group_addr; u32 secy_id; /* shadow */ @@ -126,6 +129,134 @@ static void __macsec_drv_deinit(struct macsec_qca_data *drv) } +#ifdef __linux__ + +static void macsec_qca_handle_data(void *ctx, unsigned char *buf, size_t len) +{ +#ifdef HOSTAPD + struct ieee8023_hdr *hdr; + u8 *pos, *sa; + size_t left; + union wpa_event_data event; + + /* at least 6 bytes src macaddress, 6 bytes dst macaddress + * and 2 bytes ethertype + */ + if (len < 14) { + wpa_printf(MSG_MSGDUMP, + "macsec_qca_handle_data: too short (%lu)", + (unsigned long) len); + return; + } + hdr = (struct ieee8023_hdr *) buf; + + switch (ntohs(hdr->ethertype)) { + case ETH_P_PAE: + wpa_printf(MSG_MSGDUMP, "Received EAPOL packet"); + sa = hdr->src; + os_memset(&event, 0, sizeof(event)); + event.new_sta.addr = sa; + wpa_supplicant_event(ctx, EVENT_NEW_STA, &event); + + pos = (u8 *) (hdr + 1); + left = len - sizeof(*hdr); + drv_event_eapol_rx(ctx, sa, pos, left); + break; + default: + wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame", + ntohs(hdr->ethertype)); + break; + } +#endif /* HOSTAPD */ +} + + +static void macsec_qca_handle_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + int len; + unsigned char buf[3000]; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + wpa_printf(MSG_ERROR, "macsec_qca: recv: %s", strerror(errno)); + return; + } + + macsec_qca_handle_data(eloop_ctx, buf, len); +} + +#endif /* __linux__ */ + + +static int macsec_qca_init_sockets(struct macsec_qca_data *drv, u8 *own_addr) +{ +#ifdef __linux__ + struct ifreq ifr; + struct sockaddr_ll addr; + + drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); + if (drv->common.sock < 0) { + wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s", + strerror(errno)); + return -1; + } + + if (eloop_register_read_sock(drv->common.sock, macsec_qca_handle_read, + drv->common.ctx, NULL)) { + wpa_printf(MSG_INFO, "Could not register read socket"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) { + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", + strerror(errno)); + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->common.sock, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + wpa_printf(MSG_ERROR, "macsec_qca: bind: %s", strerror(errno)); + return -1; + } + + /* filter multicast address */ + if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex, + pae_group_addr, 1) < 0) { + wpa_printf(MSG_ERROR, + "macsec_qca_init_sockets: Failed to add multicast group membership"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) { + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s", + strerror(errno)); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x", + ifr.ifr_hwaddr.sa_family); + return -1; + } + os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + return 0; +#else /* __linux__ */ + return -1; +#endif /* __linux__ */ +} + + static void * macsec_qca_init(void *ctx, const char *ifname) { struct macsec_qca_data *drv; @@ -160,6 +291,97 @@ static void macsec_qca_deinit(void *priv) } +static void * macsec_qca_hapd_init(struct hostapd_data *hapd, + struct wpa_init_params *params) +{ + struct macsec_qca_data *drv; + + drv = os_zalloc(sizeof(struct macsec_qca_data)); + if (!drv) { + wpa_printf(MSG_INFO, + "Could not allocate memory for macsec_qca driver data"); + return NULL; + } + + /* Board specific settings */ + if (os_memcmp("eth2", params->ifname, 4) == 0) + drv->secy_id = 1; + else if (os_memcmp("eth3", params->ifname, 4) == 0) + drv->secy_id = 2; + else if (os_memcmp("eth4", params->ifname, 4) == 0) + drv->secy_id = 0; + else if (os_memcmp("eth5", params->ifname, 4) == 0) + drv->secy_id = 1; + else + drv->secy_id = -1; + + drv->common.ctx = hapd; + os_strlcpy(drv->common.ifname, params->ifname, + sizeof(drv->common.ifname)); + drv->use_pae_group_addr = params->use_pae_group_addr; + + if (macsec_qca_init_sockets(drv, params->own_addr)) { + os_free(drv); + return NULL; + } + + return drv; +} + + +static void macsec_qca_hapd_deinit(void *priv) +{ + struct macsec_qca_data *drv = priv; + + if (drv->common.sock >= 0) { + eloop_unregister_read_sock(drv->common.sock); + close(drv->common.sock); + } + + os_free(drv); +} + + +static int macsec_qca_send_eapol(void *priv, const u8 *addr, + const u8 *data, size_t data_len, int encrypt, + const u8 *own_addr, u32 flags) +{ + struct macsec_qca_data *drv = priv; + struct ieee8023_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + data_len; + hdr = os_zalloc(len); + if (!hdr) { + wpa_printf(MSG_INFO, + "malloc() failed for macsec_qca_send_eapol(len=%lu)", + (unsigned long) len); + return -1; + } + + os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr, + ETH_ALEN); + os_memcpy(hdr->src, own_addr, ETH_ALEN); + hdr->ethertype = htons(ETH_P_PAE); + + pos = (u8 *) (hdr + 1); + os_memcpy(pos, data, data_len); + + res = send(drv->common.sock, (u8 *) hdr, len, 0); + os_free(hdr); + + if (res < 0) { + wpa_printf(MSG_ERROR, + "macsec_qca_send_eapol - packet len: %lu - failed: send: %s", + (unsigned long) len, strerror(errno)); + } + + return res; +} + + static int macsec_qca_macsec_init(void *priv, struct macsec_init_params *params) { struct macsec_qca_data *drv = priv; @@ -800,6 +1022,9 @@ const struct wpa_driver_ops wpa_driver_macsec_qca_ops = { .get_capa = driver_wired_get_capa, .init = macsec_qca_init, .deinit = macsec_qca_deinit, + .hapd_init = macsec_qca_hapd_init, + .hapd_deinit = macsec_qca_hapd_deinit, + .hapd_send_eapol = macsec_qca_send_eapol, .macsec_init = macsec_qca_macsec_init, .macsec_deinit = macsec_qca_macsec_deinit, diff --git a/contrib/wpa/src/drivers/driver_ndis.c b/contrib/wpa/src/drivers/driver_ndis.c index 9a08e5772328..120aa57b108b 100644 --- a/contrib/wpa/src/drivers/driver_ndis.c +++ b/contrib/wpa/src/drivers/driver_ndis.c @@ -720,7 +720,7 @@ static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv) static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr, - int reason_code) + u16 reason_code) { struct wpa_driver_ndis_data *drv = priv; return wpa_driver_ndis_disconnect(drv); diff --git a/contrib/wpa/src/drivers/driver_nl80211.h b/contrib/wpa/src/drivers/driver_nl80211.h index 1e7fe7a98fff..74982694561e 100644 --- a/contrib/wpa/src/drivers/driver_nl80211.h +++ b/contrib/wpa/src/drivers/driver_nl80211.h @@ -83,6 +83,12 @@ struct i802_bss { u8 rand_addr[ETH_ALEN]; }; +struct drv_nl80211_if_info { + int ifindex; + /* the AP/AP_VLAN iface that is in this bridge */ + int reason; +}; + struct wpa_driver_nl80211_data { struct nl80211_global *global; struct dl_list list; @@ -163,7 +169,6 @@ struct wpa_driver_nl80211_data { unsigned int scan_vendor_cmd_avail:1; unsigned int connect_reassoc:1; unsigned int set_wifi_conf_vendor_cmd_avail:1; - unsigned int he_capab_vendor_cmd_avail:1; unsigned int fetch_bss_trans_status:1; unsigned int roam_vendor_cmd_avail:1; unsigned int get_supported_akm_suites_avail:1; @@ -188,11 +193,8 @@ struct wpa_driver_nl80211_data { struct nl_handle *rtnl_sk; /* nl_sock for NETLINK_ROUTE */ - int default_if_indices[16]; - /* the AP/AP_VLAN iface that is in this bridge */ - int default_if_indices_reason[16]; - int *if_indices; - int *if_indices_reason; + struct drv_nl80211_if_info default_if_indices[16]; + struct drv_nl80211_if_info *if_indices; int num_if_indices; /* From failed authentication command */ @@ -215,8 +217,6 @@ struct wpa_driver_nl80211_data { * (NL80211_CMD_VENDOR). 0 if no pending scan request. */ int last_scan_cmd; - - struct he_capabilities he_capab; }; struct nl_msg; diff --git a/contrib/wpa/src/drivers/driver_nl80211_capa.c b/contrib/wpa/src/drivers/driver_nl80211_capa.c index 37eeb5e6686d..8318b10ab9e7 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_capa.c +++ b/contrib/wpa/src/drivers/driver_nl80211_capa.c @@ -778,9 +778,6 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) case QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION: drv->set_wifi_conf_vendor_cmd_avail = 1; break; - case QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES: - drv->he_capab_vendor_cmd_avail = 1; - break; case QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS: drv->fetch_bss_trans_status = 1; break; @@ -1082,100 +1079,6 @@ static int qca_nl80211_get_akm_suites(struct wpa_driver_nl80211_data *drv) } -static int qca_nl80211_he_capab_handler(struct nl_msg *msg, void *arg) -{ - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct he_capabilities *he_capab = arg; - struct nlattr *nl_vend; - struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX + 1]; - size_t len; - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (!tb[NL80211_ATTR_VENDOR_DATA]) - return NL_SKIP; - - nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; - nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX, - nla_data(nl_vend), nla_len(nl_vend), NULL); - - if (tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED]) { - u8 he_supported; - - he_supported = nla_get_u8( - tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED]); - wpa_printf(MSG_DEBUG, "nl80211: HE capabilities supported: %u", - he_supported); - he_capab->he_supported = he_supported; - if (!he_supported) - return NL_SKIP; - } - - if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]) { - len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]); - - if (len > sizeof(he_capab->phy_cap)) - len = sizeof(he_capab->phy_cap); - os_memcpy(he_capab->phy_cap, - nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]), - len); - } - - if (tb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_CAPAB]) - he_capab->mac_cap = - nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_CAPAB]); - - if (tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_MCS]) - he_capab->mcs = - nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_MCS]); - - if (tb_vendor[QCA_WLAN_VENDOR_ATTR_NUM_SS]) - he_capab->ppet.numss_m1 = - nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_NUM_SS]); - - if (tb_vendor[QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK]) - he_capab->ppet.ru_count = - nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK]); - - if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]) { - len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]); - - if (len > sizeof(he_capab->ppet.ppet16_ppet8_ru3_ru0)) - len = sizeof(he_capab->ppet.ppet16_ppet8_ru3_ru0); - os_memcpy(he_capab->ppet.ppet16_ppet8_ru3_ru0, - nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]), - len); - } - - return NL_SKIP; -} - - -static void qca_nl80211_check_he_capab(struct wpa_driver_nl80211_data *drv) -{ - struct nl_msg *msg; - int ret; - - if (!drv->he_capab_vendor_cmd_avail) - return; - - if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || - nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || - nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, - QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES)) { - nlmsg_free(msg); - return; - } - - ret = send_and_recv_msgs(drv, msg, qca_nl80211_he_capab_handler, - &drv->he_capab); - if (!ret && drv->he_capab.he_supported) - drv->capa.flags |= WPA_DRIVER_FLAGS_HE_CAPABILITIES; -} - - struct features_info { u8 *flags; size_t flags_len; @@ -1373,7 +1276,6 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) if (!(info.capa->flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) qca_nl80211_check_dfs_capa(drv); qca_nl80211_get_features(drv); - qca_nl80211_check_he_capab(drv); /* * To enable offchannel simultaneous support in wpa_supplicant, the @@ -1492,6 +1394,57 @@ static void phy_info_freq(struct hostapd_hw_modes *mode, chan->dfs_cac_ms = nla_get_u32( tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]); } + + chan->wmm_rules_valid = 0; + if (tb_freq[NL80211_FREQUENCY_ATTR_WMM]) { + static struct nla_policy wmm_policy[NL80211_WMMR_MAX + 1] = { + [NL80211_WMMR_CW_MIN] = { .type = NLA_U16 }, + [NL80211_WMMR_CW_MAX] = { .type = NLA_U16 }, + [NL80211_WMMR_AIFSN] = { .type = NLA_U8 }, + [NL80211_WMMR_TXOP] = { .type = NLA_U16 }, + }; + struct nlattr *nl_wmm; + struct nlattr *tb_wmm[NL80211_WMMR_MAX + 1]; + int rem_wmm, ac, count = 0; + + nla_for_each_nested(nl_wmm, tb_freq[NL80211_FREQUENCY_ATTR_WMM], + rem_wmm) { + if (nla_parse_nested(tb_wmm, NL80211_WMMR_MAX, nl_wmm, + wmm_policy)) { + wpa_printf(MSG_DEBUG, + "nl80211: Failed to parse WMM rules attribute"); + return; + } + if (!tb_wmm[NL80211_WMMR_CW_MIN] || + !tb_wmm[NL80211_WMMR_CW_MAX] || + !tb_wmm[NL80211_WMMR_AIFSN] || + !tb_wmm[NL80211_WMMR_TXOP]) { + wpa_printf(MSG_DEBUG, + "nl80211: Channel is missing WMM rule attribute"); + return; + } + ac = nl_wmm->nla_type; + if (ac < 0 || ac >= WMM_AC_NUM) { + wpa_printf(MSG_DEBUG, + "nl80211: Invalid AC value %d", ac); + return; + } + + chan->wmm_rules[ac].min_cwmin = + nla_get_u16(tb_wmm[NL80211_WMMR_CW_MIN]); + chan->wmm_rules[ac].min_cwmax = + nla_get_u16(tb_wmm[NL80211_WMMR_CW_MAX]); + chan->wmm_rules[ac].min_aifs = + nla_get_u8(tb_wmm[NL80211_WMMR_AIFSN]); + chan->wmm_rules[ac].max_txop = + nla_get_u16(tb_wmm[NL80211_WMMR_TXOP]) / 32; + count++; + } + + /* Set valid flag if all the AC rules are present */ + if (count == WMM_AC_NUM) + chan->wmm_rules_valid = 1; + } } @@ -1598,6 +1551,101 @@ static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb) } +static void phy_info_iftype_copy(struct he_capabilities *he_capab, + enum ieee80211_op_mode opmode, + struct nlattr **tb, struct nlattr **tb_flags) +{ + enum nl80211_iftype iftype; + size_t len; + + switch (opmode) { + case IEEE80211_MODE_INFRA: + iftype = NL80211_IFTYPE_STATION; + break; + case IEEE80211_MODE_IBSS: + iftype = NL80211_IFTYPE_ADHOC; + break; + case IEEE80211_MODE_AP: + iftype = NL80211_IFTYPE_AP; + break; + case IEEE80211_MODE_MESH: + iftype = NL80211_IFTYPE_MESH_POINT; + break; + default: + return; + } + + if (!nla_get_flag(tb_flags[iftype])) + return; + + he_capab->he_supported = 1; + + if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]) { + len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]); + + if (len > sizeof(he_capab->phy_cap)) + len = sizeof(he_capab->phy_cap); + os_memcpy(he_capab->phy_cap, + nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]), + len); + } + + if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]) { + len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]); + + if (len > sizeof(he_capab->mac_cap)) + len = sizeof(he_capab->mac_cap); + os_memcpy(he_capab->mac_cap, + nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]), + len); + } + + if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]) { + len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]); + + if (len > sizeof(he_capab->mcs)) + len = sizeof(he_capab->mcs); + os_memcpy(he_capab->mcs, + nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]), + len); + } + + if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]) { + len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]); + + if (len > sizeof(he_capab->ppet)) + len = sizeof(he_capab->ppet); + os_memcpy(&he_capab->ppet, + nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]), + len); + } +} + + +static int phy_info_iftype(struct hostapd_hw_modes *mode, + struct nlattr *nl_iftype) +{ + struct nlattr *tb[NL80211_BAND_IFTYPE_ATTR_MAX + 1]; + struct nlattr *tb_flags[NL80211_IFTYPE_MAX + 1]; + unsigned int i; + + nla_parse(tb, NL80211_BAND_IFTYPE_ATTR_MAX, + nla_data(nl_iftype), nla_len(nl_iftype), NULL); + + if (!tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES]) + return NL_STOP; + + if (nla_parse_nested(tb_flags, NL80211_IFTYPE_MAX, + tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES], NULL)) + return NL_STOP; + + for (i = 0; i < IEEE80211_MODE_NUM; i++) + phy_info_iftype_copy(&mode->he_capab[i], i, tb, tb_flags); + + return NL_OK; +} + + static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) { struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; @@ -1654,6 +1702,19 @@ static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) return ret; } + if (tb_band[NL80211_BAND_ATTR_IFTYPE_DATA]) { + struct nlattr *nl_iftype; + int rem_band; + + nla_for_each_nested(nl_iftype, + tb_band[NL80211_BAND_ATTR_IFTYPE_DATA], + rem_band) { + ret = phy_info_iftype(mode, nl_iftype); + if (ret != NL_OK) + return ret; + } + } + return NL_OK; } diff --git a/contrib/wpa/src/drivers/driver_nl80211_event.c b/contrib/wpa/src/drivers/driver_nl80211_event.c index ee7b4da38d87..7c16330662eb 100644 --- a/contrib/wpa/src/drivers/driver_nl80211_event.c +++ b/contrib/wpa/src/drivers/driver_nl80211_event.c @@ -136,6 +136,7 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd) C2S(NL80211_CMD_EXTERNAL_AUTH) C2S(NL80211_CMD_STA_OPMODE_CHANGED) C2S(NL80211_CMD_CONTROL_PORT_FRAME) + C2S(NL80211_CMD_UPDATE_OWE_INFO) default: return "NL80211_CMD_UNKNOWN"; } @@ -534,7 +535,8 @@ static int calculate_chan_offset(int width, int freq, int cf1, int cf2) static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, struct nlattr *ifindex, struct nlattr *freq, struct nlattr *type, struct nlattr *bw, - struct nlattr *cf1, struct nlattr *cf2) + struct nlattr *cf1, struct nlattr *cf2, + int finished) { struct i802_bss *bss; union wpa_event_data data; @@ -542,7 +544,8 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, int chan_offset = 0; int ifidx; - wpa_printf(MSG_DEBUG, "nl80211: Channel switch event"); + wpa_printf(MSG_DEBUG, "nl80211: Channel switch%s event", + finished ? "" : " started"); if (!freq) return; @@ -593,10 +596,12 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, if (cf2) data.ch_switch.cf2 = nla_get_u32(cf2); - bss->freq = data.ch_switch.freq; + if (finished) + bss->freq = data.ch_switch.freq; drv->assoc_freq = data.ch_switch.freq; - wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data); + wpa_supplicant_event(bss->ctx, finished ? + EVENT_CH_SWITCH : EVENT_CH_SWITCH_STARTED, &data); } @@ -1101,6 +1106,29 @@ static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv, } +static void mlme_event_dh_event(struct wpa_driver_nl80211_data *drv, + struct i802_bss *bss, + struct nlattr *tb[]) +{ + union wpa_event_data data; + + if (!is_ap_interface(drv->nlmode)) + return; + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE]) + return; + + os_memset(&data, 0, sizeof(data)); + data.update_dh.peer = nla_data(tb[NL80211_ATTR_MAC]); + data.update_dh.ie = nla_data(tb[NL80211_ATTR_IE]); + data.update_dh.ie_len = nla_len(tb[NL80211_ATTR_IE]); + + wpa_printf(MSG_DEBUG, "nl80211: DH event - peer " MACSTR, + MAC2STR(data.update_dh.peer)); + + wpa_supplicant_event(bss->ctx, EVENT_UPDATE_DH, &data); +} + + static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, struct nlattr *tb[], int external_scan) { @@ -2508,6 +2536,16 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, tb[NL80211_ATTR_PMK], tb[NL80211_ATTR_PMKID]); break; + case NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: + mlme_event_ch_switch(drv, + tb[NL80211_ATTR_IFINDEX], + tb[NL80211_ATTR_WIPHY_FREQ], + tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE], + tb[NL80211_ATTR_CHANNEL_WIDTH], + tb[NL80211_ATTR_CENTER_FREQ1], + tb[NL80211_ATTR_CENTER_FREQ2], + 0); + break; case NL80211_CMD_CH_SWITCH_NOTIFY: mlme_event_ch_switch(drv, tb[NL80211_ATTR_IFINDEX], @@ -2515,7 +2553,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE], tb[NL80211_ATTR_CHANNEL_WIDTH], tb[NL80211_ATTR_CENTER_FREQ1], - tb[NL80211_ATTR_CENTER_FREQ2]); + tb[NL80211_ATTR_CENTER_FREQ2], + 1); break; case NL80211_CMD_DISCONNECT: mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], @@ -2586,6 +2625,9 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, case NL80211_CMD_STA_OPMODE_CHANGED: nl80211_sta_opmode_change_event(drv, tb); break; + case NL80211_CMD_UPDATE_OWE_INFO: + mlme_event_dh_event(drv, bss, tb); + break; default: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", cmd); @@ -2634,8 +2676,9 @@ int process_global_event(struct nl_msg *msg, void *arg) } } wpa_printf(MSG_DEBUG, - "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d wdev 0x%llx)", - gnlh->cmd, ifidx, (long long unsigned int) wdev_id); + "nl80211: Ignored event %d (%s) for foreign interface (ifindex %d wdev 0x%llx)", + gnlh->cmd, nl80211_command_to_string(gnlh->cmd), + ifidx, (long long unsigned int) wdev_id); } return NL_SKIP; diff --git a/contrib/wpa/src/drivers/driver_privsep.c b/contrib/wpa/src/drivers/driver_privsep.c index a3f0837e1569..55cf61885741 100644 --- a/contrib/wpa/src/drivers/driver_privsep.c +++ b/contrib/wpa/src/drivers/driver_privsep.c @@ -368,7 +368,7 @@ static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid) static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr, - int reason_code) + u16 reason_code) { //struct wpa_driver_privsep_data *drv = priv; wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", diff --git a/contrib/wpa/src/drivers/nl80211_copy.h b/contrib/wpa/src/drivers/nl80211_copy.h new file mode 100644 index 000000000000..6f09d1500960 --- /dev/null +++ b/contrib/wpa/src/drivers/nl80211_copy.h @@ -0,0 +1,6467 @@ +#ifndef __LINUX_NL80211_H +#define __LINUX_NL80211_H +/* + * 802.11 netlink interface public header + * + * Copyright 2006-2010 Johannes Berg + * Copyright 2008 Michael Wu + * Copyright 2008 Luis Carlos Cobo + * Copyright 2008 Michael Buesch + * Copyright 2008, 2009 Luis R. Rodriguez + * Copyright 2008 Jouni Malinen + * Copyright 2008 Colin McCabe + * Copyright 2015-2017 Intel Deutschland GmbH + * Copyright (C) 2018-2019 Intel Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/* + * This header file defines the userspace API to the wireless stack. Please + * be careful not to break things - i.e. don't move anything around or so + * unless you can demonstrate that it breaks neither API nor ABI. + * + * Additions to the API should be accompanied by actual implementations in + * an upstream driver, so that example implementations exist in case there + * are ever concerns about the precise semantics of the API or changes are + * needed, and to ensure that code for dead (no longer implemented) API + * can actually be identified and removed. + * Nonetheless, semantics should also be documented carefully in this file. + */ + +#include + +#define NL80211_GENL_NAME "nl80211" + +#define NL80211_MULTICAST_GROUP_CONFIG "config" +#define NL80211_MULTICAST_GROUP_SCAN "scan" +#define NL80211_MULTICAST_GROUP_REG "regulatory" +#define NL80211_MULTICAST_GROUP_MLME "mlme" +#define NL80211_MULTICAST_GROUP_VENDOR "vendor" +#define NL80211_MULTICAST_GROUP_NAN "nan" +#define NL80211_MULTICAST_GROUP_TESTMODE "testmode" + +/** + * DOC: Station handling + * + * Stations are added per interface, but a special case exists with VLAN + * interfaces. When a station is bound to an AP interface, it may be moved + * into a VLAN identified by a VLAN interface index (%NL80211_ATTR_STA_VLAN). + * The station is still assumed to belong to the AP interface it was added + * to. + * + * Station handling varies per interface type and depending on the driver's + * capabilities. + * + * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS + * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows: + * - a setup station entry is added, not yet authorized, without any rate + * or capability information, this just exists to avoid race conditions + * - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid + * to add rate and capability information to the station and at the same + * time mark it authorized. + * - %NL80211_TDLS_ENABLE_LINK is then used + * - after this, the only valid operation is to remove it by tearing down + * the TDLS link (%NL80211_TDLS_DISABLE_LINK) + * + * TODO: need more info for other interface types + */ + +/** + * DOC: Frame transmission/registration support + * + * Frame transmission and registration support exists to allow userspace + * management entities such as wpa_supplicant react to management frames + * that are not being handled by the kernel. This includes, for example, + * certain classes of action frames that cannot be handled in the kernel + * for various reasons. + * + * Frame registration is done on a per-interface basis and registrations + * cannot be removed other than by closing the socket. It is possible to + * specify a registration filter to register, for example, only for a + * certain type of action frame. In particular with action frames, those + * that userspace registers for will not be returned as unhandled by the + * driver, so that the registered application has to take responsibility + * for doing that. + * + * The type of frame that can be registered for is also dependent on the + * driver and interface type. The frame types are advertised in wiphy + * attributes so applications know what to expect. + * + * NOTE: When an interface changes type while registrations are active, + * these registrations are ignored until the interface type is + * changed again. This means that changing the interface type can + * lead to a situation that couldn't otherwise be produced, but + * any such registrations will be dormant in the sense that they + * will not be serviced, i.e. they will not receive any frames. + * + * Frame transmission allows userspace to send for example the required + * responses to action frames. It is subject to some sanity checking, + * but many frames can be transmitted. When a frame was transmitted, its + * status is indicated to the sending socket. + * + * For more technical details, see the corresponding command descriptions + * below. + */ + +/** + * DOC: Virtual interface / concurrency capabilities + * + * Some devices are able to operate with virtual MACs, they can have + * more than one virtual interface. The capability handling for this + * is a bit complex though, as there may be a number of restrictions + * on the types of concurrency that are supported. + * + * To start with, each device supports the interface types listed in + * the %NL80211_ATTR_SUPPORTED_IFTYPES attribute, but by listing the + * types there no concurrency is implied. + * + * Once concurrency is desired, more attributes must be observed: + * To start with, since some interface types are purely managed in + * software, like the AP-VLAN type in mac80211 for example, there's + * an additional list of these, they can be added at any time and + * are only restricted by some semantic restrictions (e.g. AP-VLAN + * cannot be added without a corresponding AP interface). This list + * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute. + * + * Further, the list of supported combinations is exported. This is + * in the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute. Basically, + * it exports a list of "groups", and at any point in time the + * interfaces that are currently active must fall into any one of + * the advertised groups. Within each group, there are restrictions + * on the number of interfaces of different types that are supported + * and also the number of different channels, along with potentially + * some other restrictions. See &enum nl80211_if_combination_attrs. + * + * All together, these attributes define the concurrency of virtual + * interfaces that a given device supports. + */ + +/** + * DOC: packet coalesce support + * + * In most cases, host that receives IPv4 and IPv6 multicast/broadcast + * packets does not do anything with these packets. Therefore the + * reception of these unwanted packets causes unnecessary processing + * and power consumption. + * + * Packet coalesce feature helps to reduce number of received interrupts + * to host by buffering these packets in firmware/hardware for some + * predefined time. Received interrupt will be generated when one of the + * following events occur. + * a) Expiration of hardware timer whose expiration time is set to maximum + * coalescing delay of matching coalesce rule. + * b) Coalescing buffer in hardware reaches it's limit. + * c) Packet doesn't match any of the configured coalesce rules. + * + * User needs to configure following parameters for creating a coalesce + * rule. + * a) Maximum coalescing delay + * b) List of packet patterns which needs to be matched + * c) Condition for coalescence. pattern 'match' or 'no match' + * Multiple such rules can be created. + */ + +/** + * DOC: WPA/WPA2 EAPOL handshake offload + * + * By setting @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK flag drivers + * can indicate they support offloading EAPOL handshakes for WPA/WPA2 + * preshared key authentication. In %NL80211_CMD_CONNECT the preshared + * key should be specified using %NL80211_ATTR_PMK. Drivers supporting + * this offload may reject the %NL80211_CMD_CONNECT when no preshared + * key material is provided, for example when that driver does not + * support setting the temporal keys through %CMD_NEW_KEY. + * + * Similarly @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X flag can be + * set by drivers indicating offload support of the PTK/GTK EAPOL + * handshakes during 802.1X authentication. In order to use the offload + * the %NL80211_CMD_CONNECT should have %NL80211_ATTR_WANT_1X_4WAY_HS + * attribute flag. Drivers supporting this offload may reject the + * %NL80211_CMD_CONNECT when the attribute flag is not present. + * + * For 802.1X the PMK or PMK-R0 are set by providing %NL80211_ATTR_PMK + * using %NL80211_CMD_SET_PMK. For offloaded FT support also + * %NL80211_ATTR_PMKR0_NAME must be provided. + */ + +/** + * DOC: FILS shared key authentication offload + * + * FILS shared key authentication offload can be advertized by drivers by + * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support + * FILS shared key authentication offload should be able to construct the + * authentication and association frames for FILS shared key authentication and + * eventually do a key derivation as per IEEE 802.11ai. The below additional + * parameters should be given to driver in %NL80211_CMD_CONNECT and/or in + * %NL80211_CMD_UPDATE_CONNECT_PARAMS. + * %NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai + * %NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai + * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message + * %NL80211_ATTR_FILS_ERP_RRK - used to generate the rIK and rMSK + * rIK should be used to generate an authentication tag on the ERP message and + * rMSK should be used to derive a PMKSA. + * rIK, rMSK should be generated and keyname_nai, sequence number should be used + * as specified in IETF RFC 6696. + * + * When FILS shared key authentication is completed, driver needs to provide the + * below additional parameters to userspace, which can be either after setting + * up a connection or after roaming. + * %NL80211_ATTR_FILS_KEK - used for key renewal + * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges + * %NL80211_ATTR_PMKID - used to identify the PMKSA used/generated + * %Nl80211_ATTR_PMK - used to update PMKSA cache in userspace + * The PMKSA can be maintained in userspace persistently so that it can be used + * later after reboots or wifi turn off/on also. + * + * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS + * capable AP supporting PMK caching. It specifies the scope within which the + * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and + * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based + * on FILS cache identifier. Additionally %NL80211_ATTR_PMK is used with + * %NL80211_SET_PMKSA to specify the PMK corresponding to a PMKSA for driver to + * use in a FILS shared key connection with PMKSA caching. + */ + +/** + * enum nl80211_commands - supported nl80211 commands + * + * @NL80211_CMD_UNSPEC: unspecified command to catch errors + * + * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request + * to get a list of all present wiphys. + * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or + * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, + * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ (and the + * attributes determining the channel width; this is used for setting + * monitor mode channel), %NL80211_ATTR_WIPHY_RETRY_SHORT, + * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. + * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL + * instead, the support here is for backward compatibility only. + * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request + * or rename notification. Has attributes %NL80211_ATTR_WIPHY and + * %NL80211_ATTR_WIPHY_NAME. + * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME. + * + * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration; + * either a dump request for all interfaces or a specific get with a + * single %NL80211_ATTR_IFINDEX is supported. + * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE. + * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response + * to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX, + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also + * be sent from userspace to request creation of a new virtual interface, + * then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and + * %NL80211_ATTR_IFNAME. + * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from + * userspace to request deletion of a virtual interface, then requires + * attribute %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified + * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. + * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT, + * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD. + * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA, + * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER, + * and %NL80211_ATTR_KEY_SEQ attributes. + * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX + * or %NL80211_ATTR_MAC. + * + * @NL80211_CMD_GET_BEACON: (not used) + * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface + * using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL + * attributes. For drivers that generate the beacon and probe responses + * internally, the following attributes must be provided: %NL80211_ATTR_IE, + * %NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP. + * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters + * are like for %NL80211_CMD_SET_BEACON, and additionally parameters that + * do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL, + * %NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID, + * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, + * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, + * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, + * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT, + * %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS. + * The channel to use can be set on the interface or be given using the + * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width. + * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP + * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface + * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP + * + * @NL80211_CMD_GET_STATION: Get station attributes for station identified by + * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_STATION: Set station attributes for station identified by + * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_STATION: Add a station with given attributes to the + * the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC + * or, if no MAC address given, all stations, on the interface identified + * by %NL80211_ATTR_IFINDEX. %NL80211_ATTR_MGMT_SUBTYPE and + * %NL80211_ATTR_REASON_CODE can optionally be used to specify which type + * of disconnection indication should be sent to the station + * (Deauthentication or Disassociation frame and reason code for that + * frame). + * + * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by + * %NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP. + * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by + * %NL80211_ATTR_MAC. + * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the + * the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC + * or, if no MAC address given, all mesh paths, on the interface identified + * by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by + * %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set + * regulatory domain. If %NL80211_ATTR_WIPHY is specified and the device + * has a private regulatory domain, it will be returned. Otherwise, the + * global regdomain will be returned. + * A device will have a private regulatory domain if it uses the + * regulatory_hint() API. Even when a private regdomain is used the channel + * information will still be mended according to further hints from + * the regulatory core to help with compliance. A dump version of this API + * is now available which will returns the global regdomain as well as + * all private regdomains of present wiphys (for those that have it). + * If a wiphy is self-managed (%NL80211_ATTR_WIPHY_SELF_MANAGED_REG), then + * its private regdomain is the only valid one for it. The regulatory + * core is not used to help with compliance in this case. + * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command + * after being queried by the kernel. CRDA replies by sending a regulatory + * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our + * current alpha2 if it found a match. It also provides + * NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each + * regulatory rule is a nested set of attributes given by + * %NL80211_ATTR_REG_RULE_FREQ_[START|END] and + * %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by + * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and + * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP. + * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain + * to the specified ISO/IEC 3166-1 alpha2 country code. The core will + * store this as a valid request and then query userspace for it. + * + * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MESH_CONFIG: Set mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The + * interface is identified with %NL80211_ATTR_IFINDEX and the management + * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be + * added to the end of the specified management frame is specified with + * %NL80211_ATTR_IE. If the command succeeds, the requested data will be + * added to all specified management frames generated by + * kernel/firmware/driver. + * Note: This command has been removed and it is only reserved at this + * point to avoid re-using existing command number. The functionality this + * command was planned for has been provided with cleaner design with the + * option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN, + * NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, + * NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE. + * + * @NL80211_CMD_GET_SCAN: get scan results + * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters + * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the + * probe requests at CCK rate or not. %NL80211_ATTR_BSSID can be used to + * specify a BSSID to scan for; if not included, the wildcard BSSID will + * be used. + * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to + * NL80211_CMD_GET_SCAN and on the "scan" multicast group) + * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, + * partial scan results may be available + * + * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain + * intervals and certain number of cycles, as specified by + * %NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is + * not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified, + * scheduled scan will run in an infinite loop with the specified interval. + * These attributes are mutually exculsive, + * i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if + * NL80211_ATTR_SCHED_SCAN_PLANS is defined. + * If for some reason scheduled scan is aborted by the driver, all scan + * plans are canceled (including scan plans that did not start yet). + * Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS) + * are passed, they are used in the probe requests. For + * broadcast, a broadcast SSID must be passed (ie. an empty + * string). If no SSID is passed, no probe requests are sent and + * a passive scan is performed. %NL80211_ATTR_SCAN_FREQUENCIES, + * if passed, define which channels should be scanned; if not + * passed, all channels allowed for the current regulatory domain + * are used. Extra IEs can also be passed from the userspace by + * using the %NL80211_ATTR_IE attribute. The first cycle of the + * scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY + * is supplied. If the device supports multiple concurrent scheduled + * scans, it will allow such when the caller provides the flag attribute + * %NL80211_ATTR_SCHED_SCAN_MULTI to indicate user-space support for it. + * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if + * scheduled scan is not running. The caller may assume that as soon + * as the call returns, it is safe to start a new scheduled scan again. + * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan + * results available. + * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has + * stopped. The driver may issue this event at any time during a + * scheduled scan. One reason for stopping the scan is if the hardware + * does not support starting an association or a normal scan while running + * a scheduled scan. This event is also sent when the + * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface + * is brought down while a scheduled scan was running. + * + * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation + * or noise level + * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to + * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) + * + * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry using %NL80211_ATTR_MAC + * (for the BSSID), %NL80211_ATTR_PMKID, and optionally %NL80211_ATTR_PMK + * (PMK is used for PTKSA derivation in case of FILS shared key offload) or + * using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID, + * %NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS + * authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier + * advertized by a FILS capable AP identifying the scope of PMKSA in an + * ESS. + * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC + * (for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID, + * %NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS + * authentication. + * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries. + * + * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain + * has been changed and provides details of the request information + * that caused the change such as who initiated the regulatory request + * (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx + * (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if + * the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or + * %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain + * set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is + * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on + * to (%NL80211_ATTR_REG_ALPHA2). + * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon + * has been found while world roaming thus enabling active scan or + * any mode of operation that initiates TX (beacons) on a channel + * where we would not have been able to do either before. As an example + * if you are world roaming (regulatory domain set to world or if your + * driver is using a custom world roaming regulatory domain) and while + * doing a passive scan on the 5 GHz band you find an AP there (if not + * on a DFS channel) you will now be able to actively scan for that AP + * or use AP mode on your card on that same channel. Note that this will + * never be used for channels 1-11 on the 2 GHz band as they are always + * enabled world wide. This beacon hint is only sent if your device had + * either disabled active scanning or beaconing on a channel. We send to + * userspace the wiphy on which we removed a restriction from + * (%NL80211_ATTR_WIPHY) and the channel on which this occurred + * before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER) + * the beacon hint was processed. + * + * @NL80211_CMD_AUTHENTICATE: authentication request and notification. + * This command is used both as a command (request to authenticate) and + * as an event on the "mlme" multicast group indicating completion of the + * authentication process. + * When used as a command, %NL80211_ATTR_IFINDEX is used to identify the + * interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and + * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify + * the SSID (mainly for association, but is included in authentication + * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used + * to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE + * is used to specify the authentication type. %NL80211_ATTR_IE is used to + * define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs) + * to be added to the frame. + * When used as an event, this reports reception of an Authentication + * frame in station and IBSS modes when the local MLME processed the + * frame, i.e., it was for the local STA and was received in correct + * state. This is similar to MLME-AUTHENTICATE.confirm primitive in the + * MLME SAP interface (kernel providing MLME, userspace SME). The + * included %NL80211_ATTR_FRAME attribute contains the management frame + * (including both the header and frame body, but not FCS). This event is + * also used to indicate if the authentication attempt timed out. In that + * case the %NL80211_ATTR_FRAME attribute is replaced with a + * %NL80211_ATTR_TIMED_OUT flag (and %NL80211_ATTR_MAC to indicate which + * pending authentication timed out). + * @NL80211_CMD_ASSOCIATE: association request and notification; like + * NL80211_CMD_AUTHENTICATE but for Association and Reassociation + * (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request, + * MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). The + * %NL80211_ATTR_PREV_BSSID attribute is used to specify whether the + * request is for the initial association to an ESS (that attribute not + * included) or for reassociation within the ESS (that attribute is + * included). + * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like + * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to + * MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication + * primitives). + * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like + * NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to + * MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives). + * + * @NL80211_CMD_MICHAEL_MIC_FAILURE: notification of a locally detected Michael + * MIC (part of TKIP) failure; sent on the "mlme" multicast group; the + * event includes %NL80211_ATTR_MAC to describe the source MAC address of + * the frame with invalid MIC, %NL80211_ATTR_KEY_TYPE to show the key + * type, %NL80211_ATTR_KEY_IDX to indicate the key identifier, and + * %NL80211_ATTR_KEY_SEQ to indicate the TSC value of the frame; this + * event matches with MLME-MICHAELMICFAILURE.indication() primitive + * + * @NL80211_CMD_JOIN_IBSS: Join a new IBSS -- given at least an SSID and a + * FREQ attribute (for the initial frequency if no peer can be found) + * and optionally a MAC (as BSSID) and FREQ_FIXED attribute if those + * should be fixed rather than automatically determined. Can only be + * executed on a network interface that is UP, and fixed BSSID/FREQ + * may be rejected. Another optional parameter is the beacon interval, + * given in the %NL80211_ATTR_BEACON_INTERVAL attribute, which if not + * given defaults to 100 TU (102.4ms). + * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is + * determined by the network interface. + * + * @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute + * to identify the device, and the TESTDATA blob attribute to pass through + * to the driver. + * + * @NL80211_CMD_CONNECT: connection request and notification; this command + * requests to connect to a specified network but without separating + * auth and assoc steps. For this, you need to specify the SSID in a + * %NL80211_ATTR_SSID attribute, and can optionally specify the association + * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP, + * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, + * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, + * %NL80211_ATTR_CONTROL_PORT_OVER_NL80211, %NL80211_ATTR_MAC_HINT, and + * %NL80211_ATTR_WIPHY_FREQ_HINT. + * If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are + * restrictions on BSS selection, i.e., they effectively prevent roaming + * within the ESS. %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT + * can be included to provide a recommendation of the initial BSS while + * allowing the driver to roam to other BSSes within the ESS and also to + * ignore this recommendation if the indicated BSS is not ideal. Only one + * set of BSSID,frequency parameters is used (i.e., either the enforcing + * %NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict + * %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT). + * %NL80211_ATTR_PREV_BSSID can be used to request a reassociation within + * the ESS in case the device is already associated and an association with + * a different BSS is desired. + * Background scan period can optionally be + * specified in %NL80211_ATTR_BG_SCAN_PERIOD, + * if not specified default background scan configuration + * in driver is used and if period value is 0, bg scan will be disabled. + * This attribute is ignored if driver does not support roam scan. + * It is also sent as an event, with the BSSID and response IEs when the + * connection is established or failed to be established. This can be + * determined by the %NL80211_ATTR_STATUS_CODE attribute (0 = success, + * non-zero = failure). If %NL80211_ATTR_TIMED_OUT is included in the + * event, the connection attempt failed due to not being able to initiate + * authentication/association or not receiving a response from the AP. + * Non-zero %NL80211_ATTR_STATUS_CODE value is indicated in that case as + * well to remain backwards compatible. + * When establishing a security association, drivers that support 4 way + * handshake offload should send %NL80211_CMD_PORT_AUTHORIZED event when + * the 4 way handshake is completed successfully. + * @NL80211_CMD_ROAM: Notification indicating the card/driver roamed by itself. + * When a security association was established with the new AP (e.g. if + * the FT protocol was used for roaming or the driver completed the 4 way + * handshake), this event should be followed by an + * %NL80211_CMD_PORT_AUTHORIZED event. + * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify + * userspace that a connection was dropped by the AP or due to other + * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and + * %NL80211_ATTR_REASON_CODE attributes are used. + * + * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices + * associated with this wiphy must be down and will follow. + * + * @NL80211_CMD_REMAIN_ON_CHANNEL: Request to remain awake on the specified + * channel for the specified amount of time. This can be used to do + * off-channel operations like transmit a Public Action frame and wait for + * a response while being associated to an AP on another channel. + * %NL80211_ATTR_IFINDEX is used to specify which interface (and thus + * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the + * frequency for the operation. + * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds + * to remain on the channel. This command is also used as an event to + * notify when the requested duration starts (it may take a while for the + * driver to schedule this time due to other concurrent needs for the + * radio). + * When called, this operation returns a cookie (%NL80211_ATTR_COOKIE) + * that will be included with any events pertaining to this request; + * the cookie is also used to cancel the request. + * @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a + * pending remain-on-channel duration if the desired operation has been + * completed prior to expiration of the originally requested duration. + * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the + * radio. The %NL80211_ATTR_COOKIE attribute must be given as well to + * uniquely identify the request. + * This command is also used as an event to notify when a requested + * remain-on-channel duration has expired. + * + * @NL80211_CMD_SET_TX_BITRATE_MASK: Set the mask of rates to be used in TX + * rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface + * and @NL80211_ATTR_TX_RATES the set of allowed rates. + * + * @NL80211_CMD_REGISTER_FRAME: Register for receiving certain mgmt frames + * (via @NL80211_CMD_FRAME) for processing in userspace. This command + * requires an interface index, a frame type attribute (optional for + * backward compatibility reasons, if not given assumes action frames) + * and a match attribute containing the first few bytes of the frame + * that should match, e.g. a single byte for only a category match or + * four bytes for vendor frames including the OUI. The registration + * cannot be dropped, but is removed automatically when the netlink + * socket is closed. Multiple registrations can be made. + * @NL80211_CMD_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for + * backward compatibility + * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This + * command is used both as a request to transmit a management frame and + * as an event indicating reception of a frame that was not processed in + * kernel code, but is for us (i.e., which may need to be processed in a + * user space application). %NL80211_ATTR_FRAME is used to specify the + * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ is used + * to indicate on which channel the frame is to be transmitted or was + * received. If this channel is not the current channel (remain-on-channel + * or the operational channel) the device will switch to the given channel + * and transmit the frame, optionally waiting for a response for the time + * specified using %NL80211_ATTR_DURATION. When called, this operation + * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the + * TX status event pertaining to the TX request. + * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the + * management frames at CCK rate or not in 2GHz band. + * %NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA + * counters which will be updated to the current value. This attribute + * is used during CSA period. + * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this + * command may be used with the corresponding cookie to cancel the wait + * time if it is known that it is no longer necessary. + * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility. + * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame + * transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies + * the TX command and %NL80211_ATTR_FRAME includes the contents of the + * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged + * the frame. + * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for + * backward compatibility. + * + * @NL80211_CMD_SET_POWER_SAVE: Set powersave, using %NL80211_ATTR_PS_STATE + * @NL80211_CMD_GET_POWER_SAVE: Get powersave status in %NL80211_ATTR_PS_STATE + * + * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command + * is used to configure connection quality monitoring notification trigger + * levels. + * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This + * command is used as an event to indicate the that a trigger level was + * reached. + * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ + * and the attributes determining channel width) the given interface + * (identifed by %NL80211_ATTR_IFINDEX) shall operate on. + * In case multiple channels are supported by the device, the mechanism + * with which it switches channels is implementation-defined. + * When a monitor interface is given, it can only switch channel while + * no other interfaces are operating to avoid disturbing the operation + * of any other interfaces, and other interfaces will again take + * precedence when they are used. + * + * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface. + * + * @NL80211_CMD_SET_MULTICAST_TO_UNICAST: Configure if this AP should perform + * multicast to unicast conversion. When enabled, all multicast packets + * with ethertype ARP, IPv4 or IPv6 (possibly within an 802.1Q header) + * will be sent out to each station once with the destination (multicast) + * MAC address replaced by the station's MAC address. Note that this may + * break certain expectations of the receiver, e.g. the ability to drop + * unicast IP packets encapsulated in multicast L2 frames, or the ability + * to not send destination unreachable messages in such cases. + * This can only be toggled per BSS. Configure this on an interface of + * type %NL80211_IFTYPE_AP. It applies to all its VLAN interfaces + * (%NL80211_IFTYPE_AP_VLAN), except for those in 4addr (WDS) mode. + * If %NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED is not present with this + * command, the feature is disabled. + * + * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial + * mesh config parameters may be given. + * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the + * network is determined by the network interface. + * + * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame + * notification. This event is used to indicate that an unprotected + * deauthentication frame was dropped when MFP is in use. + * @NL80211_CMD_UNPROT_DISASSOCIATE: Unprotected disassociation frame + * notification. This event is used to indicate that an unprotected + * disassociation frame was dropped when MFP is in use. + * + * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a + * beacon or probe response from a compatible mesh peer. This is only + * sent while no station information (sta_info) exists for the new peer + * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH, + * @NL80211_MESH_SETUP_USERSPACE_AMPE, or + * @NL80211_MESH_SETUP_USERSPACE_MPM is set. On reception of this + * notification, userspace may decide to create a new station + * (@NL80211_CMD_NEW_STATION). To stop this notification from + * reoccurring, the userspace authentication daemon may want to create the + * new station with the AUTHENTICATED flag unset and maybe change it later + * depending on the authentication result. + * + * @NL80211_CMD_GET_WOWLAN: get Wake-on-Wireless-LAN (WoWLAN) settings. + * @NL80211_CMD_SET_WOWLAN: set Wake-on-Wireless-LAN (WoWLAN) settings. + * Since wireless is more complex than wired ethernet, it supports + * various triggers. These triggers can be configured through this + * command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For + * more background information, see + * http://wireless.kernel.org/en/users/Documentation/WoWLAN. + * The @NL80211_CMD_SET_WOWLAN command can also be used as a notification + * from the driver reporting the wakeup reason. In this case, the + * @NL80211_ATTR_WOWLAN_TRIGGERS attribute will contain the reason + * for the wakeup, if it was caused by wireless. If it is not present + * in the wakeup notification, the wireless device didn't cause the + * wakeup but reports that it was woken up. + * + * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver + * the necessary information for supporting GTK rekey offload. This + * feature is typically used during WoWLAN. The configuration data + * is contained in %NL80211_ATTR_REKEY_DATA (which is nested and + * contains the data in sub-attributes). After rekeying happened, + * this command may also be sent by the driver as an MLME event to + * inform userspace of the new replay counter. + * + * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace + * of PMKSA caching dandidates. + * + * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). + * In addition, this can be used as an event to request userspace to take + * actions on TDLS links (set up a new link or tear down an existing one). + * In such events, %NL80211_ATTR_TDLS_OPERATION indicates the requested + * operation, %NL80211_ATTR_MAC contains the peer MAC address, and + * %NL80211_ATTR_REASON_CODE the reason code to be used (only with + * %NL80211_TDLS_TEARDOWN). + * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. The + * %NL80211_ATTR_TDLS_ACTION attribute determines the type of frame to be + * sent. Public Action codes (802.11-2012 8.1.5.1) will be sent as + * 802.11 management frames, while TDLS action codes (802.11-2012 + * 8.5.13.1) will be encapsulated and sent as data frames. The currently + * supported Public Action code is %WLAN_PUB_ACTION_TDLS_DISCOVER_RES + * and the currently supported TDLS actions codes are given in + * &enum ieee80211_tdls_actioncode. + * + * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP + * (or GO) interface (i.e. hostapd) to ask for unexpected frames to + * implement sending deauth to stations that send unexpected class 3 + * frames. Also used as the event sent by the kernel when such a frame + * is received. + * For the event, the %NL80211_ATTR_MAC attribute carries the TA and + * other attributes like the interface index are present. + * If used as the command it must have an interface index and you can + * only unsubscribe from the event by closing the socket. Subscription + * is also for %NL80211_CMD_UNEXPECTED_4ADDR_FRAME events. + * + * @NL80211_CMD_UNEXPECTED_4ADDR_FRAME: Sent as an event indicating that the + * associated station identified by %NL80211_ATTR_MAC sent a 4addr frame + * and wasn't already in a 4-addr VLAN. The event will be sent similarly + * to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener. + * + * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface + * by sending a null data frame to it and reporting when the frame is + * acknowleged. This is used to allow timing out inactive clients. Uses + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a + * direct reply with an %NL80211_ATTR_COOKIE that is later used to match + * up the event with the request. The event includes the same data and + * has %NL80211_ATTR_ACK set if the frame was ACKed. + * + * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from + * other BSSes when any interfaces are in AP mode. This helps implement + * OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME + * messages. Note that per PHY only one application may register. + * + * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether + * No Acknowledgement Policy should be applied. + * + * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels + * independently of the userspace SME, send this event indicating + * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the + * attributes determining channel width. This indication may also be + * sent when a remotely-initiated switch (e.g., when a STA receives a CSA + * from the remote AP) is completed; + * + * @NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: Notify that a channel switch + * has been started on an interface, regardless of the initiator + * (ie. whether it was requested from a remote device or + * initiated on our own). It indicates that + * %NL80211_ATTR_IFINDEX will be on %NL80211_ATTR_WIPHY_FREQ + * after %NL80211_ATTR_CH_SWITCH_COUNT TBTT's. The userspace may + * decide to react to this indication by requesting other + * interfaces to change channel as well. + * + * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by + * its %NL80211_ATTR_WDEV identifier. It must have been created with + * %NL80211_CMD_NEW_INTERFACE previously. After it has been started, the + * P2P Device can be used for P2P operations, e.g. remain-on-channel and + * public action frame TX. + * @NL80211_CMD_STOP_P2P_DEVICE: Stop the given P2P Device, identified by + * its %NL80211_ATTR_WDEV identifier. + * + * @NL80211_CMD_CONN_FAILED: connection request to an AP failed; used to + * notify userspace that AP has rejected the connection request from a + * station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON + * is used for this. + * + * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames + * for IBSS or MESH vif. + * + * @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control. + * This is to be used with the drivers advertising the support of MAC + * address based access control. List of MAC addresses is passed in + * %NL80211_ATTR_MAC_ADDRS and ACL policy is passed in + * %NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it + * is not already done. The new list will replace any existing list. Driver + * will clear its ACL when the list of MAC addresses passed is empty. This + * command is used in AP/P2P GO mode. Driver has to make sure to clear its + * ACL list during %NL80211_CMD_STOP_AP. + * + * @NL80211_CMD_RADAR_DETECT: Start a Channel availability check (CAC). Once + * a radar is detected or the channel availability scan (CAC) has finished + * or was aborted, or a radar was detected, usermode will be notified with + * this event. This command is also used to notify userspace about radars + * while operating on this channel. + * %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the + * event. + * + * @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features, + * i.e. features for the nl80211 protocol rather than device features. + * Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap. + * + * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition + * Information Element to the WLAN driver + * + * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver + * to the supplicant. This will carry the target AP's MAC address along + * with the relevant Information Elements. This event is used to report + * received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE). + * + * @NL80211_CMD_CRIT_PROTOCOL_START: Indicates user-space will start running + * a critical protocol that needs more reliability in the connection to + * complete. + * + * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can + * return back to normal. + * + * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules. + * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules. + * + * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the + * the new channel information (Channel Switch Announcement - CSA) + * in the beacon for some time (as defined in the + * %NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the + * new channel. Userspace provides the new channel information (using + * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel + * width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform + * other station that transmission must be blocked until the channel + * switch is complete. + * + * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified + * by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in + * %NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in + * %NL80211_ATTR_VENDOR_DATA. + * For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is + * used in the wiphy data as a nested attribute containing descriptions + * (&struct nl80211_vendor_cmd_info) of the supported vendor commands. + * This may also be sent as an event with the same attributes. + * + * @NL80211_CMD_SET_QOS_MAP: Set Interworking QoS mapping for IP DSCP values. + * The QoS mapping information is included in %NL80211_ATTR_QOS_MAP. If + * that attribute is not included, QoS mapping is disabled. Since this + * QoS mapping is relevant for IP packets, it is only valid during an + * association. This is cleared on disassociation and AP restart. + * + * @NL80211_CMD_ADD_TX_TS: Ask the kernel to add a traffic stream for the given + * %NL80211_ATTR_TSID and %NL80211_ATTR_MAC with %NL80211_ATTR_USER_PRIO + * and %NL80211_ATTR_ADMITTED_TIME parameters. + * Note that the action frame handshake with the AP shall be handled by + * userspace via the normal management RX/TX framework, this only sets + * up the TX TS in the driver/device. + * If the admitted time attribute is not added then the request just checks + * if a subsequent setup could be successful, the intent is to use this to + * avoid setting up a session with the AP when local restrictions would + * make that impossible. However, the subsequent "real" setup may still + * fail even if the check was successful. + * @NL80211_CMD_DEL_TX_TS: Remove an existing TS with the %NL80211_ATTR_TSID + * and %NL80211_ATTR_MAC parameters. It isn't necessary to call this + * before removing a station entry entirely, or before disassociating + * or similar, cleanup will happen in the driver/device in this case. + * + * @NL80211_CMD_GET_MPP: Get mesh path attributes for mesh proxy path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_JOIN_OCB: Join the OCB network. The center frequency and + * bandwidth of a channel must be given. + * @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the + * network is determined by the network interface. + * + * @NL80211_CMD_TDLS_CHANNEL_SWITCH: Start channel-switching with a TDLS peer, + * identified by the %NL80211_ATTR_MAC parameter. A target channel is + * provided via %NL80211_ATTR_WIPHY_FREQ and other attributes determining + * channel width/type. The target operating class is given via + * %NL80211_ATTR_OPER_CLASS. + * The driver is responsible for continually initiating channel-switching + * operations and returning to the base channel for communication with the + * AP. + * @NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH: Stop channel-switching with a TDLS + * peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel + * when this command completes. + * + * @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used + * as an event to indicate changes for devices with wiphy-specific regdom + * management. + * + * @NL80211_CMD_ABORT_SCAN: Stop an ongoing scan. Returns -ENOENT if a scan is + * not running. The driver indicates the status of the scan through + * cfg80211_scan_done(). + * + * @NL80211_CMD_START_NAN: Start NAN operation, identified by its + * %NL80211_ATTR_WDEV interface. This interface must have been + * previously created with %NL80211_CMD_NEW_INTERFACE. After it + * has been started, the NAN interface will create or join a + * cluster. This command must have a valid + * %NL80211_ATTR_NAN_MASTER_PREF attribute and optional + * %NL80211_ATTR_BANDS attributes. If %NL80211_ATTR_BANDS is + * omitted or set to 0, it means don't-care and the device will + * decide what to use. After this command NAN functions can be + * added. + * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by + * its %NL80211_ATTR_WDEV interface. + * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined + * with %NL80211_ATTR_NAN_FUNC nested attribute. When called, this + * operation returns the strictly positive and unique instance id + * (%NL80211_ATTR_NAN_FUNC_INST_ID) and a cookie (%NL80211_ATTR_COOKIE) + * of the function upon success. + * Since instance ID's can be re-used, this cookie is the right + * way to identify the function. This will avoid races when a termination + * event is handled by the user space after it has already added a new + * function that got the same instance id from the kernel as the one + * which just terminated. + * This cookie may be used in NAN events even before the command + * returns, so userspace shouldn't process NAN events until it processes + * the response to this command. + * Look at %NL80211_ATTR_SOCKET_OWNER as well. + * @NL80211_CMD_DEL_NAN_FUNCTION: Delete a NAN function by cookie. + * This command is also used as a notification sent when a NAN function is + * terminated. This will contain a %NL80211_ATTR_NAN_FUNC_INST_ID + * and %NL80211_ATTR_COOKIE attributes. + * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN + * configuration. NAN must be operational (%NL80211_CMD_START_NAN + * was executed). It must contain at least one of the following + * attributes: %NL80211_ATTR_NAN_MASTER_PREF, + * %NL80211_ATTR_BANDS. If %NL80211_ATTR_BANDS is omitted, the + * current configuration is not changed. If it is present but + * set to zero, the configuration is changed to don't-care + * (i.e. the device can decide what to do). + * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported. + * This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and + * %NL80211_ATTR_COOKIE. + * + * @NL80211_CMD_UPDATE_CONNECT_PARAMS: Update one or more connect parameters + * for subsequent roaming cases if the driver or firmware uses internal + * BSS selection. This command can be issued only while connected and it + * does not result in a change for the current association. Currently, + * only the %NL80211_ATTR_IE data is used and updated with this command. + * + * @NL80211_CMD_SET_PMK: For offloaded 4-Way handshake, set the PMK or PMK-R0 + * for the given authenticator address (specified with %NL80211_ATTR_MAC). + * When %NL80211_ATTR_PMKR0_NAME is set, %NL80211_ATTR_PMK specifies the + * PMK-R0, otherwise it specifies the PMK. + * @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously + * configured PMK for the authenticator address identified by + * %NL80211_ATTR_MAC. + * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates that the 4 way + * handshake was completed successfully by the driver. The BSSID is + * specified with %NL80211_ATTR_MAC. Drivers that support 4 way handshake + * offload should send this event after indicating 802.11 association with + * %NL80211_CMD_CONNECT or %NL80211_CMD_ROAM. If the 4 way handshake failed + * %NL80211_CMD_DISCONNECT should be indicated instead. + * + * @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request + * and RX notification. This command is used both as a request to transmit + * a control port frame and as a notification that a control port frame + * has been received. %NL80211_ATTR_FRAME is used to specify the + * frame contents. The frame is the raw EAPoL data, without ethernet or + * 802.11 headers. + * When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added + * indicating the protocol type of the received frame; whether the frame + * was received unencrypted and the MAC address of the peer respectively. + * + * @NL80211_CMD_RELOAD_REGDB: Request that the regdb firmware file is reloaded. + * + * @NL80211_CMD_EXTERNAL_AUTH: This interface is exclusively defined for host + * drivers that do not define separate commands for authentication and + * association, but rely on user space for the authentication to happen. + * This interface acts both as the event request (driver to user space) + * to trigger the authentication and command response (userspace to + * driver) to indicate the authentication status. + * + * User space uses the %NL80211_CMD_CONNECT command to the host driver to + * trigger a connection. The host driver selects a BSS and further uses + * this interface to offload only the authentication part to the user + * space. Authentication frames are passed between the driver and user + * space through the %NL80211_CMD_FRAME interface. Host driver proceeds + * further with the association after getting successful authentication + * status. User space indicates the authentication status through + * %NL80211_ATTR_STATUS_CODE attribute in %NL80211_CMD_EXTERNAL_AUTH + * command interface. + * + * Host driver reports this status on an authentication failure to the + * user space through the connect result as the user space would have + * initiated the connection through the connect request. + * + * @NL80211_CMD_STA_OPMODE_CHANGED: An event that notify station's + * ht opmode or vht opmode changes using any of %NL80211_ATTR_SMPS_MODE, + * %NL80211_ATTR_CHANNEL_WIDTH,%NL80211_ATTR_NSS attributes with its + * address(specified in %NL80211_ATTR_MAC). + * + * @NL80211_CMD_GET_FTM_RESPONDER_STATS: Retrieve FTM responder statistics, in + * the %NL80211_ATTR_FTM_RESPONDER_STATS attribute. + * + * @NL80211_CMD_PEER_MEASUREMENT_START: start a (set of) peer measurement(s) + * with the given parameters, which are encapsulated in the nested + * %NL80211_ATTR_PEER_MEASUREMENTS attribute. Optionally, MAC address + * randomization may be enabled and configured by specifying the + * %NL80211_ATTR_MAC and %NL80211_ATTR_MAC_MASK attributes. + * If a timeout is requested, use the %NL80211_ATTR_TIMEOUT attribute. + * A u64 cookie for further %NL80211_ATTR_COOKIE use is is returned in + * the netlink extended ack message. + * + * To cancel a measurement, close the socket that requested it. + * + * Measurement results are reported to the socket that requested the + * measurement using @NL80211_CMD_PEER_MEASUREMENT_RESULT when they + * become available, so applications must ensure a large enough socket + * buffer size. + * + * Depending on driver support it may or may not be possible to start + * multiple concurrent measurements. + * @NL80211_CMD_PEER_MEASUREMENT_RESULT: This command number is used for the + * result notification from the driver to the requesting socket. + * @NL80211_CMD_PEER_MEASUREMENT_COMPLETE: Notification only, indicating that + * the measurement completed, using the measurement cookie + * (%NL80211_ATTR_COOKIE). + * + * @NL80211_CMD_NOTIFY_RADAR: Notify the kernel that a radar signal was + * detected and reported by a neighboring device on the channel + * indicated by %NL80211_ATTR_WIPHY_FREQ and other attributes + * determining the width and type. + * + * @NL80211_CMD_UPDATE_OWE_INFO: This interface allows the host driver to + * offload OWE processing to user space. This intends to support + * OWE AKM by the host drivers that implement SME but rely + * on the user space for the cryptographic/DH IE processing in AP mode. + * + * @NL80211_CMD_PROBE_MESH_LINK: The requirement for mesh link metric + * refreshing, is that from one mesh point we be able to send some data + * frames to other mesh points which are not currently selected as a + * primary traffic path, but which are only 1 hop away. The absence of + * the primary path to the chosen node makes it necessary to apply some + * form of marking on a chosen packet stream so that the packets can be + * properly steered to the selected node for testing, and not by the + * regular mesh path lookup. Further, the packets must be of type data + * so that the rate control (often embedded in firmware) is used for + * rate selection. + * + * Here attribute %NL80211_ATTR_MAC is used to specify connected mesh + * peer MAC address and %NL80211_ATTR_FRAME is used to specify the frame + * content. The frame is ethernet data. + * + * @NL80211_CMD_MAX: highest used command number + * @__NL80211_CMD_AFTER_LAST: internal use + */ +enum nl80211_commands { +/* don't change the order or add anything between, this is ABI! */ + NL80211_CMD_UNSPEC, + + NL80211_CMD_GET_WIPHY, /* can dump */ + NL80211_CMD_SET_WIPHY, + NL80211_CMD_NEW_WIPHY, + NL80211_CMD_DEL_WIPHY, + + NL80211_CMD_GET_INTERFACE, /* can dump */ + NL80211_CMD_SET_INTERFACE, + NL80211_CMD_NEW_INTERFACE, + NL80211_CMD_DEL_INTERFACE, + + NL80211_CMD_GET_KEY, + NL80211_CMD_SET_KEY, + NL80211_CMD_NEW_KEY, + NL80211_CMD_DEL_KEY, + + NL80211_CMD_GET_BEACON, + NL80211_CMD_SET_BEACON, + NL80211_CMD_START_AP, + NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP, + NL80211_CMD_STOP_AP, + NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP, + + NL80211_CMD_GET_STATION, + NL80211_CMD_SET_STATION, + NL80211_CMD_NEW_STATION, + NL80211_CMD_DEL_STATION, + + NL80211_CMD_GET_MPATH, + NL80211_CMD_SET_MPATH, + NL80211_CMD_NEW_MPATH, + NL80211_CMD_DEL_MPATH, + + NL80211_CMD_SET_BSS, + + NL80211_CMD_SET_REG, + NL80211_CMD_REQ_SET_REG, + + NL80211_CMD_GET_MESH_CONFIG, + NL80211_CMD_SET_MESH_CONFIG, + + NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */, + + NL80211_CMD_GET_REG, + + NL80211_CMD_GET_SCAN, + NL80211_CMD_TRIGGER_SCAN, + NL80211_CMD_NEW_SCAN_RESULTS, + NL80211_CMD_SCAN_ABORTED, + + NL80211_CMD_REG_CHANGE, + + NL80211_CMD_AUTHENTICATE, + NL80211_CMD_ASSOCIATE, + NL80211_CMD_DEAUTHENTICATE, + NL80211_CMD_DISASSOCIATE, + + NL80211_CMD_MICHAEL_MIC_FAILURE, + + NL80211_CMD_REG_BEACON_HINT, + + NL80211_CMD_JOIN_IBSS, + NL80211_CMD_LEAVE_IBSS, + + NL80211_CMD_TESTMODE, + + NL80211_CMD_CONNECT, + NL80211_CMD_ROAM, + NL80211_CMD_DISCONNECT, + + NL80211_CMD_SET_WIPHY_NETNS, + + NL80211_CMD_GET_SURVEY, + NL80211_CMD_NEW_SURVEY_RESULTS, + + NL80211_CMD_SET_PMKSA, + NL80211_CMD_DEL_PMKSA, + NL80211_CMD_FLUSH_PMKSA, + + NL80211_CMD_REMAIN_ON_CHANNEL, + NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, + + NL80211_CMD_SET_TX_BITRATE_MASK, + + NL80211_CMD_REGISTER_FRAME, + NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME, + NL80211_CMD_FRAME, + NL80211_CMD_ACTION = NL80211_CMD_FRAME, + NL80211_CMD_FRAME_TX_STATUS, + NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS, + + NL80211_CMD_SET_POWER_SAVE, + NL80211_CMD_GET_POWER_SAVE, + + NL80211_CMD_SET_CQM, + NL80211_CMD_NOTIFY_CQM, + + NL80211_CMD_SET_CHANNEL, + NL80211_CMD_SET_WDS_PEER, + + NL80211_CMD_FRAME_WAIT_CANCEL, + + NL80211_CMD_JOIN_MESH, + NL80211_CMD_LEAVE_MESH, + + NL80211_CMD_UNPROT_DEAUTHENTICATE, + NL80211_CMD_UNPROT_DISASSOCIATE, + + NL80211_CMD_NEW_PEER_CANDIDATE, + + NL80211_CMD_GET_WOWLAN, + NL80211_CMD_SET_WOWLAN, + + NL80211_CMD_START_SCHED_SCAN, + NL80211_CMD_STOP_SCHED_SCAN, + NL80211_CMD_SCHED_SCAN_RESULTS, + NL80211_CMD_SCHED_SCAN_STOPPED, + + NL80211_CMD_SET_REKEY_OFFLOAD, + + NL80211_CMD_PMKSA_CANDIDATE, + + NL80211_CMD_TDLS_OPER, + NL80211_CMD_TDLS_MGMT, + + NL80211_CMD_UNEXPECTED_FRAME, + + NL80211_CMD_PROBE_CLIENT, + + NL80211_CMD_REGISTER_BEACONS, + + NL80211_CMD_UNEXPECTED_4ADDR_FRAME, + + NL80211_CMD_SET_NOACK_MAP, + + NL80211_CMD_CH_SWITCH_NOTIFY, + + NL80211_CMD_START_P2P_DEVICE, + NL80211_CMD_STOP_P2P_DEVICE, + + NL80211_CMD_CONN_FAILED, + + NL80211_CMD_SET_MCAST_RATE, + + NL80211_CMD_SET_MAC_ACL, + + NL80211_CMD_RADAR_DETECT, + + NL80211_CMD_GET_PROTOCOL_FEATURES, + + NL80211_CMD_UPDATE_FT_IES, + NL80211_CMD_FT_EVENT, + + NL80211_CMD_CRIT_PROTOCOL_START, + NL80211_CMD_CRIT_PROTOCOL_STOP, + + NL80211_CMD_GET_COALESCE, + NL80211_CMD_SET_COALESCE, + + NL80211_CMD_CHANNEL_SWITCH, + + NL80211_CMD_VENDOR, + + NL80211_CMD_SET_QOS_MAP, + + NL80211_CMD_ADD_TX_TS, + NL80211_CMD_DEL_TX_TS, + + NL80211_CMD_GET_MPP, + + NL80211_CMD_JOIN_OCB, + NL80211_CMD_LEAVE_OCB, + + NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, + + NL80211_CMD_TDLS_CHANNEL_SWITCH, + NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH, + + NL80211_CMD_WIPHY_REG_CHANGE, + + NL80211_CMD_ABORT_SCAN, + + NL80211_CMD_START_NAN, + NL80211_CMD_STOP_NAN, + NL80211_CMD_ADD_NAN_FUNCTION, + NL80211_CMD_DEL_NAN_FUNCTION, + NL80211_CMD_CHANGE_NAN_CONFIG, + NL80211_CMD_NAN_MATCH, + + NL80211_CMD_SET_MULTICAST_TO_UNICAST, + + NL80211_CMD_UPDATE_CONNECT_PARAMS, + + NL80211_CMD_SET_PMK, + NL80211_CMD_DEL_PMK, + + NL80211_CMD_PORT_AUTHORIZED, + + NL80211_CMD_RELOAD_REGDB, + + NL80211_CMD_EXTERNAL_AUTH, + + NL80211_CMD_STA_OPMODE_CHANGED, + + NL80211_CMD_CONTROL_PORT_FRAME, + + NL80211_CMD_GET_FTM_RESPONDER_STATS, + + NL80211_CMD_PEER_MEASUREMENT_START, + NL80211_CMD_PEER_MEASUREMENT_RESULT, + NL80211_CMD_PEER_MEASUREMENT_COMPLETE, + + NL80211_CMD_NOTIFY_RADAR, + + NL80211_CMD_UPDATE_OWE_INFO, + + NL80211_CMD_PROBE_MESH_LINK, + + /* add new commands above here */ + + /* used to define NL80211_CMD_MAX below */ + __NL80211_CMD_AFTER_LAST, + NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 +}; + +/* + * Allow user space programs to use #ifdef on new commands by defining them + * here + */ +#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS +#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE +#define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE +#define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE +#define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE +#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE +#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE +#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT + +#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS + +/* source-level API compatibility */ +#define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG +#define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG +#define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE + +/** + * enum nl80211_attrs - nl80211 netlink attributes + * + * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors + * + * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf. + * /sys/class/ieee80211//index + * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) + * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters + * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz, + * defines the channel together with the (deprecated) + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE attribute or the attributes + * %NL80211_ATTR_CHANNEL_WIDTH and if needed %NL80211_ATTR_CENTER_FREQ1 + * and %NL80211_ATTR_CENTER_FREQ2 + * @NL80211_ATTR_CHANNEL_WIDTH: u32 attribute containing one of the values + * of &enum nl80211_chan_width, describing the channel width. See the + * documentation of the enum for more information. + * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the + * channel, used for anything but 20 MHz bandwidth + * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the + * channel, used only for 80+80 MHz bandwidth + * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ + * if HT20 or HT40 are to be used (i.e., HT disabled if not included): + * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including + * this attribute) + * NL80211_CHAN_HT20 = HT20 only + * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel + * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel + * This attribute is now deprecated. + * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is + * less than or equal to the RTS threshold; allowed range: 1..255; + * dot11ShortRetryLimit; u8 + * @NL80211_ATTR_WIPHY_RETRY_LONG: TX retry limit for frames whose length is + * greater than the RTS threshold; allowed range: 1..255; + * dot11ShortLongLimit; u8 + * @NL80211_ATTR_WIPHY_FRAG_THRESHOLD: fragmentation threshold, i.e., maximum + * length in octets for frames; allowed range: 256..8000, disable + * fragmentation with (u32)-1; dot11FragmentationThreshold; u32 + * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length + * larger than or equal to this use RTS/CTS handshake); allowed range: + * 0..65536, disable with (u32)-1; dot11RTSThreshold; u32 + * @NL80211_ATTR_WIPHY_COVERAGE_CLASS: Coverage Class as defined by IEEE 802.11 + * section 7.3.2.9; dot11CoverageClass; u8 + * + * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on + * @NL80211_ATTR_IFNAME: network interface name + * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype + * + * @NL80211_ATTR_WDEV: wireless device identifier, used for pseudo-devices + * that don't have a netdev (u64) + * + * @NL80211_ATTR_MAC: MAC address (various uses) + * + * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of + * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC + * keys + * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3) + * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 + * section 7.3.2.25.1, e.g. 0x000FAC04) + * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and + * CCMP keys, each six bytes in little endian + * @NL80211_ATTR_KEY_DEFAULT: Flag attribute indicating the key is default key + * @NL80211_ATTR_KEY_DEFAULT_MGMT: Flag attribute indicating the key is the + * default management key + * @NL80211_ATTR_CIPHER_SUITES_PAIRWISE: For crypto settings for connect or + * other commands, indicates which pairwise cipher suites are used + * @NL80211_ATTR_CIPHER_SUITE_GROUP: For crypto settings for connect or + * other commands, indicates which group cipher suite is used + * + * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU + * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing + * @NL80211_ATTR_BEACON_HEAD: portion of the beacon before the TIM IE + * @NL80211_ATTR_BEACON_TAIL: portion of the beacon after the TIM IE + * + * @NL80211_ATTR_STA_AID: Association ID for the station (u16) + * @NL80211_ATTR_STA_FLAGS: flags, nested element with NLA_FLAG attributes of + * &enum nl80211_sta_flags (deprecated, use %NL80211_ATTR_STA_FLAGS2) + * @NL80211_ATTR_STA_LISTEN_INTERVAL: listen interval as defined by + * IEEE 802.11 7.3.1.6 (u16). + * @NL80211_ATTR_STA_SUPPORTED_RATES: supported rates, array of supported + * rates as defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). + * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station + * to, or the AP interface the station was originally added to to. + * @NL80211_ATTR_STA_INFO: information about a station, part of station info + * given for %NL80211_CMD_GET_STATION, nested attribute containing + * info as possible, see &enum nl80211_sta_info. + * + * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands, + * consisting of a nested array. + * + * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes). + * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link + * (see &enum nl80211_plink_action). + * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. + * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path + * info given for %NL80211_CMD_GET_MPATH, nested attribute described at + * &enum nl80211_mpath_info. + * + * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of + * &enum nl80211_mntr_flags. + * + * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the + * current regulatory domain should be set to or is already set to. + * For example, 'CR', for Costa Rica. This attribute is used by the kernel + * to query the CRDA to retrieve one regulatory domain. This attribute can + * also be used by userspace to query the kernel for the currently set + * regulatory domain. We chose an alpha2 as that is also used by the + * IEEE-802.11 country information element to identify a country. + * Users can also simply ask the wireless core to set regulatory domain + * to a specific alpha2. + * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory + * rules. + * + * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic + * rates in format defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). + * + * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION) + * + * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all + * supported interface types, each a flag attribute with the number + * of the interface mode. + * + * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for + * %NL80211_CMD_SET_MGMT_EXTRA_IE. + * + * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with + * %NL80211_CMD_SET_MGMT_EXTRA_IE). + * + * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with + * a single scan request, a wiphy attribute. + * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: number of SSIDs you can + * scan with a single scheduled scan request, a wiphy attribute. + * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements + * that can be added to a scan request + * @NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: maximum length of information + * elements that can be added to a scheduled scan request + * @NL80211_ATTR_MAX_MATCH_SETS: maximum number of sets that can be + * used with @NL80211_ATTR_SCHED_SCAN_MATCH, a wiphy attribute. + * + * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) + * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive + * scanning and include a zero-length SSID (wildcard) for wildcard scan + * @NL80211_ATTR_BSS: scan result BSS + * + * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain + * currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_* + * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently + * set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*) + * + * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies + * an array of command numbers (i.e. a mapping index to command number) + * that the driver for the given wiphy supports. + * + * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header + * and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and + * NL80211_CMD_ASSOCIATE events + * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets) + * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type, + * represented as a u32 + * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and + * %NL80211_CMD_DISASSOCIATE, u16 + * + * @NL80211_ATTR_KEY_TYPE: Key Type, see &enum nl80211_key_type, represented as + * a u32 + * + * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change + * due to considerations from a beacon hint. This attribute reflects + * the state of the channel _before_ the beacon hint processing. This + * attributes consists of a nested attribute containing + * NL80211_FREQUENCY_ATTR_* + * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change + * due to considerations from a beacon hint. This attribute reflects + * the state of the channel _after_ the beacon hint processing. This + * attributes consists of a nested attribute containing + * NL80211_FREQUENCY_ATTR_* + * + * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported + * cipher suites + * + * @NL80211_ATTR_FREQ_FIXED: a flag indicating the IBSS should not try to look + * for other networks on different channels + * + * @NL80211_ATTR_TIMED_OUT: a flag indicating than an operation timed out; this + * is used, e.g., with %NL80211_CMD_AUTHENTICATE event + * + * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is + * used for the association (&enum nl80211_mfp, represented as a u32); + * this attribute can be used with %NL80211_CMD_ASSOCIATE and + * %NL80211_CMD_CONNECT requests. %NL80211_MFP_OPTIONAL is not allowed for + * %NL80211_CMD_ASSOCIATE since user space SME is expected and hence, it + * must have decided whether to use management frame protection or not. + * Setting %NL80211_MFP_OPTIONAL with a %NL80211_CMD_CONNECT request will + * let the driver (or the firmware) decide whether to use MFP or not. + * + * @NL80211_ATTR_STA_FLAGS2: Attribute containing a + * &struct nl80211_sta_flag_update. + * + * @NL80211_ATTR_CONTROL_PORT: A flag indicating whether user space controls + * IEEE 802.1X port, i.e., sets/clears %NL80211_STA_FLAG_AUTHORIZED, in + * station mode. If the flag is included in %NL80211_CMD_ASSOCIATE + * request, the driver will assume that the port is unauthorized until + * authorized by user space. Otherwise, port is marked authorized by + * default in station mode. + * @NL80211_ATTR_CONTROL_PORT_ETHERTYPE: A 16-bit value indicating the + * ethertype that will be used for key negotiation. It can be + * specified with the associate and connect commands. If it is not + * specified, the value defaults to 0x888E (PAE, 802.1X). This + * attribute is also used as a flag in the wiphy information to + * indicate that protocols other than PAE are supported. + * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with + * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom + * ethertype frames used for key negotiation must not be encrypted. + * @NL80211_ATTR_CONTROL_PORT_OVER_NL80211: A flag indicating whether control + * port frames (e.g. of type given in %NL80211_ATTR_CONTROL_PORT_ETHERTYPE) + * will be sent directly to the network interface or sent via the NL80211 + * socket. If this attribute is missing, then legacy behavior of sending + * control port frames directly to the network interface is used. If the + * flag is included, then control port frames are sent over NL80211 instead + * using %CMD_CONTROL_PORT_FRAME. If control port routing over NL80211 is + * to be used then userspace must also use the %NL80211_ATTR_SOCKET_OWNER + * flag. + * + * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. + * We recommend using nested, driver-specific attributes within this. + * + * @NL80211_ATTR_DISCONNECTED_BY_AP: A flag indicating that the DISCONNECT + * event was due to the AP disconnecting the station, and not due to + * a local disconnect request. + * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT + * event (u16) + * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating + * that protected APs should be used. This is also used with NEW_BEACON to + * indicate that the BSS is to use protection. + * + * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON + * to indicate which unicast key ciphers will be used with the connection + * (an array of u32). + * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to + * indicate which group key cipher will be used with the connection (a + * u32). + * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to + * indicate which WPA version(s) the AP we want to associate with is using + * (a u32 with flags from &enum nl80211_wpa_versions). + * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to + * indicate which key management algorithm(s) to use (an array of u32). + * This attribute is also sent in response to @NL80211_CMD_GET_WIPHY, + * indicating the supported AKM suites, intended for specific drivers which + * implement SME and have constraints on which AKMs are supported and also + * the cases where an AKM support is offloaded to the driver/firmware. + * If there is no such notification from the driver, user space should + * assume the driver supports all the AKM suites. + * + * @NL80211_ATTR_REQ_IE: (Re)association request information elements as + * sent out by the card, for ROAM and successful CONNECT events. + * @NL80211_ATTR_RESP_IE: (Re)association response information elements as + * sent by peer, for ROAM and successful CONNECT events. + * + * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used in ASSOCIATE and CONNECT + * commands to specify a request to reassociate within an ESS, i.e., to use + * Reassociate Request frame (with the value of this attribute in the + * Current AP address field) instead of Association Request frame which is + * used for the initial association to an ESS. + * + * @NL80211_ATTR_KEY: key information in a nested attribute with + * %NL80211_KEY_* sub-attributes + * @NL80211_ATTR_KEYS: array of keys for static WEP keys for connect() + * and join_ibss(), key information is in a nested attribute each + * with %NL80211_KEY_* sub-attributes + * + * @NL80211_ATTR_PID: Process ID of a network namespace. + * + * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for + * dumps. This number increases whenever the object list being + * dumped changes, and as such userspace can verify that it has + * obtained a complete and consistent snapshot by verifying that + * all dump messages contain the same generation number. If it + * changed then the list changed and the dump should be repeated + * completely from scratch. + * + * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface + * + * @NL80211_ATTR_SURVEY_INFO: survey information about a channel, part of + * the survey response for %NL80211_CMD_GET_SURVEY, nested attribute + * containing info as possible, see &enum survey_info. + * + * @NL80211_ATTR_PMKID: PMK material for PMKSA caching. + * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can + * cache, a wiphy attribute. + * + * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32. + * @NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION: Device attribute that + * specifies the maximum duration that can be requested with the + * remain-on-channel operation, in milliseconds, u32. + * + * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects. + * + * @NL80211_ATTR_TX_RATES: Nested set of attributes + * (enum nl80211_tx_rate_attributes) describing TX rates per band. The + * enum nl80211_band value is used as the index (nla_type() of the nested + * data. If a band is not included, it will be configured to allow all + * rates based on negotiated supported rates information. This attribute + * is used with %NL80211_CMD_SET_TX_BITRATE_MASK and with starting AP, + * and joining mesh networks (not IBSS yet). In the later case, it must + * specify just a single bitrate, which is to be used for the beacon. + * The driver must also specify support for this with the extended + * features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY, + * NL80211_EXT_FEATURE_BEACON_RATE_HT and + * NL80211_EXT_FEATURE_BEACON_RATE_VHT. + * + * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain + * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME. + * @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the + * @NL80211_CMD_REGISTER_FRAME command. + * @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a + * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing + * information about which frame types can be transmitted with + * %NL80211_CMD_FRAME. + * @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a + * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing + * information about which frame types can be registered for RX. + * + * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was + * acknowledged by the recipient. + * + * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values. + * + * @NL80211_ATTR_CQM: connection quality monitor configuration in a + * nested attribute with %NL80211_ATTR_CQM_* sub-attributes. + * + * @NL80211_ATTR_LOCAL_STATE_CHANGE: Flag attribute to indicate that a command + * is requesting a local authentication/association state change without + * invoking actual management frame exchange. This can be used with + * NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE, + * NL80211_CMD_DISASSOCIATE. + * + * @NL80211_ATTR_AP_ISOLATE: (AP mode) Do not forward traffic between stations + * connected to this BSS. + * + * @NL80211_ATTR_WIPHY_TX_POWER_SETTING: Transmit power setting type. See + * &enum nl80211_tx_power_setting for possible values. + * @NL80211_ATTR_WIPHY_TX_POWER_LEVEL: Transmit power level in signed mBm units. + * This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING + * for non-automatic settings. + * + * @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly + * means support for per-station GTKs. + * + * @NL80211_ATTR_WIPHY_ANTENNA_TX: Bitmap of allowed antennas for transmitting. + * This can be used to mask out antennas which are not attached or should + * not be used for transmitting. If an antenna is not selected in this + * bitmap the hardware is not allowed to transmit on this antenna. + * + * Each bit represents one antenna, starting with antenna 1 at the first + * bit. Depending on which antennas are selected in the bitmap, 802.11n + * drivers can derive which chainmasks to use (if all antennas belonging to + * a particular chain are disabled this chain should be disabled) and if + * a chain has diversity antennas wether diversity should be used or not. + * HT capabilities (STBC, TX Beamforming, Antenna selection) can be + * derived from the available chains after applying the antenna mask. + * Non-802.11n drivers can derive wether to use diversity or not. + * Drivers may reject configurations or RX/TX mask combinations they cannot + * support by returning -EINVAL. + * + * @NL80211_ATTR_WIPHY_ANTENNA_RX: Bitmap of allowed antennas for receiving. + * This can be used to mask out antennas which are not attached or should + * not be used for receiving. If an antenna is not selected in this bitmap + * the hardware should not be configured to receive on this antenna. + * For a more detailed description see @NL80211_ATTR_WIPHY_ANTENNA_TX. + * + * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: Bitmap of antennas which are available + * for configuration as TX antennas via the above parameters. + * + * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available + * for configuration as RX antennas via the above parameters. + * + * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS + * + * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be + * transmitted on another channel when the channel given doesn't match + * the current channel. If the current channel doesn't match and this + * flag isn't set, the frame will be rejected. This is also used as an + * nl80211 capability flag. + * + * @NL80211_ATTR_BSS_HT_OPMODE: HT operation mode (u16) + * + * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags + * attributes, specifying what a key should be set as default as. + * See &enum nl80211_key_default_types. + * + * @NL80211_ATTR_MESH_SETUP: Optional mesh setup parameters. These cannot be + * changed once the mesh is active. + * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute + * containing attributes from &enum nl80211_meshconf_params. + * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver + * allows auth frames in a mesh to be passed to userspace for processing via + * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag. + * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as defined in + * &enum nl80211_plink_state. Used when userspace is driving the peer link + * management state machine. @NL80211_MESH_SETUP_USERSPACE_AMPE or + * @NL80211_MESH_SETUP_USERSPACE_MPM must be enabled. + * + * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy + * capabilities, the supported WoWLAN triggers + * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to + * indicate which WoW triggers should be enabled. This is also + * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN + * triggers. + * + * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan + * cycles, in msecs. + * + * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more + * sets of attributes to match during scheduled scans. Only BSSs + * that match any of the sets will be reported. These are + * pass-thru filter rules. + * For a match to succeed, the BSS must match all attributes of a + * set. Since not every hardware supports matching all types of + * attributes, there is no guarantee that the reported BSSs are + * fully complying with the match sets and userspace needs to be + * able to ignore them by itself. + * Thus, the implementation is somewhat hardware-dependent, but + * this is only an optimization and the userspace application + * needs to handle all the non-filtered results anyway. + * If the match attributes don't make sense when combined with + * the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID + * is included in the probe request, but the match attributes + * will never let it go through), -EINVAL may be returned. + * If omitted, no filtering is done. + * + * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported + * interface combinations. In each nested item, it contains attributes + * defined in &enum nl80211_if_combination_attrs. + * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like + * %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that + * are managed in software: interfaces of these types aren't subject to + * any restrictions in their number or combinations. + * + * @NL80211_ATTR_REKEY_DATA: nested attribute containing the information + * necessary for GTK rekeying in the device, see &enum nl80211_rekey_data. + * + * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan, + * nested array attribute containing an entry for each band, with the entry + * being a list of supported rates as defined by IEEE 802.11 7.3.2.2 but + * without the length restriction (at most %NL80211_MAX_SUPP_RATES). + * + * @NL80211_ATTR_HIDDEN_SSID: indicates whether SSID is to be hidden from Beacon + * and Probe Response (when response to wildcard Probe Request); see + * &enum nl80211_hidden_ssid, represented as a u32 + * + * @NL80211_ATTR_IE_PROBE_RESP: Information element(s) for Probe Response frame. + * This is used with %NL80211_CMD_NEW_BEACON and %NL80211_CMD_SET_BEACON to + * provide extra IEs (e.g., WPS/P2P IE) into Probe Response frames when the + * driver (or firmware) replies to Probe Request frames. + * @NL80211_ATTR_IE_ASSOC_RESP: Information element(s) for (Re)Association + * Response frames. This is used with %NL80211_CMD_NEW_BEACON and + * %NL80211_CMD_SET_BEACON to provide extra IEs (e.g., WPS/P2P IE) into + * (Re)Association Response frames when the driver (or firmware) replies to + * (Re)Association Request frames. + * + * @NL80211_ATTR_STA_WME: Nested attribute containing the wme configuration + * of the station, see &enum nl80211_sta_wme_attr. + * @NL80211_ATTR_SUPPORT_AP_UAPSD: the device supports uapsd when working + * as AP. + * + * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of + * roaming to another AP in the same ESS if the signal lever is low. + * + * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching + * candidate information, see &enum nl80211_pmksa_candidate_attr. + * + * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not + * for management frames transmission. In order to avoid p2p probe/action + * frames are being transmitted at CCK rate in 2GHz band, the user space + * applications use this attribute. + * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and + * %NL80211_CMD_FRAME commands. + * + * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup + * request, link setup confirm, link teardown, etc.). Values are + * described in the TDLS (802.11z) specification. + * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a + * TDLS conversation between two devices. + * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see + * &enum nl80211_tdls_operation, represented as a u8. + * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate + * as a TDLS peer sta. + * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown + * procedures should be performed by sending TDLS packets via + * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be + * used for asking the driver to perform a TDLS operation. + * + * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices + * that have AP support to indicate that they have the AP SME integrated + * with support for the features listed in this attribute, see + * &enum nl80211_ap_sme_features. + * + * @NL80211_ATTR_DONT_WAIT_FOR_ACK: Used with %NL80211_CMD_FRAME, this tells + * the driver to not wait for an acknowledgement. Note that due to this, + * it will also not give a status callback nor return a cookie. This is + * mostly useful for probe responses to save airtime. + * + * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from + * &enum nl80211_feature_flags and is advertised in wiphy information. + * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe + * requests while operating in AP-mode. + * This attribute holds a bitmap of the supported protocols for + * offloading (see &enum nl80211_probe_resp_offload_support_attr). + * + * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire + * probe-response frame. The DA field in the 802.11 header is zero-ed out, + * to be filled by the FW. + * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable + * this feature. Currently, only supported in mac80211 drivers. + * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the + * ATTR_HT_CAPABILITY to which attention should be paid. + * Currently, only mac80211 NICs support this feature. + * The values that may be configured are: + * MCS rates, MAX-AMSDU, HT-20-40 and HT_CAP_SGI_40 + * AMPDU density and AMPDU factor. + * All values are treated as suggestions and may be ignored + * by the driver as required. The actual values may be seen in + * the station debugfs ht_caps file. + * + * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country + * abides to when initiating radiation on DFS channels. A country maps + * to one DFS region. + * + * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of + * up to 16 TIDs. + * + * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be + * used by the drivers which has MLME in firmware and does not have support + * to report per station tx/rx activity to free up the station entry from + * the list. This needs to be used when the driver advertises the + * capability to timeout the stations. + * + * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int); + * this attribute is (depending on the driver capabilities) added to + * received frames indicated with %NL80211_CMD_FRAME. + * + * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds + * or 0 to disable background scan. + * + * @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from + * userspace. If unset it is assumed the hint comes directly from + * a user. If set code could specify exactly what type of source + * was used to provide the hint. For the different types of + * allowed user regulatory hints see nl80211_user_reg_hint_type. + * + * @NL80211_ATTR_CONN_FAILED_REASON: The reason for which AP has rejected + * the connection request from a station. nl80211_connect_failed_reason + * enum has different reasons of connection failure. + * + * @NL80211_ATTR_AUTH_DATA: Fields and elements in Authentication frames. + * This contains the authentication frame body (non-IE and IE data), + * excluding the Authentication algorithm number, i.e., starting at the + * Authentication transaction sequence number field. It is used with + * authentication algorithms that need special fields to be added into + * the frames (SAE and FILS). Currently, only the SAE cases use the + * initial two fields (Authentication transaction sequence number and + * Status code). However, those fields are included in the attribute data + * for all authentication algorithms to keep the attribute definition + * consistent. + * + * @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION) + * + * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32) + * + * @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with + * the START_AP and SET_BSS commands + * @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the + * START_AP and SET_BSS commands. This can have the values 0 or 1; + * if not given in START_AP 0 is assumed, if not given in SET_BSS + * no change is made. + * + * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode + * defined in &enum nl80211_mesh_power_mode. + * + * @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy, + * carried in a u32 attribute + * + * @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for + * MAC ACL. + * + * @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum + * number of MAC addresses that a device can support for MAC + * ACL. + * + * @NL80211_ATTR_RADAR_EVENT: Type of radar event for notification to userspace, + * contains a value of enum nl80211_radar_event (u32). + * + * @NL80211_ATTR_EXT_CAPA: 802.11 extended capabilities that the kernel driver + * has and handles. The format is the same as the IE contents. See + * 802.11-2012 8.4.2.29 for more information. + * @NL80211_ATTR_EXT_CAPA_MASK: Extended capabilities that the kernel driver + * has set in the %NL80211_ATTR_EXT_CAPA value, for multibit fields. + * + * @NL80211_ATTR_STA_CAPABILITY: Station capabilities (u16) are advertised to + * the driver, e.g., to enable TDLS power save (PU-APSD). + * + * @NL80211_ATTR_STA_EXT_CAPABILITY: Station extended capabilities are + * advertised to the driver, e.g., to enable TDLS off channel operations + * and PU-APSD. + * + * @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see + * &enum nl80211_protocol_features, the attribute is a u32. + * + * @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports + * receiving the data for a single wiphy split across multiple + * messages, given with wiphy dump message + * + * @NL80211_ATTR_MDID: Mobility Domain Identifier + * + * @NL80211_ATTR_IE_RIC: Resource Information Container Information + * Element + * + * @NL80211_ATTR_CRIT_PROT_ID: critical protocol identifier requiring increased + * reliability, see &enum nl80211_crit_proto_id (u16). + * @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which + * the connection should have increased reliability (u16). + * + * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16). + * This is similar to @NL80211_ATTR_STA_AID but with a difference of being + * allowed to be used with the first @NL80211_CMD_SET_STATION command to + * update a TDLS peer STA entry. + * + * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information. + * + * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's + * until the channel switch event. + * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission + * must be blocked on the current channel (before the channel switch + * operation). + * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information + * for the time while performing a channel switch. + * @NL80211_ATTR_CSA_C_OFF_BEACON: An array of offsets (u16) to the channel + * switch counters in the beacons tail (%NL80211_ATTR_BEACON_TAIL). + * @NL80211_ATTR_CSA_C_OFF_PRESP: An array of offsets (u16) to the channel + * switch counters in the probe response (%NL80211_ATTR_PROBE_RESP). + * + * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32. + * As specified in the &enum nl80211_rxmgmt_flags. + * + * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels. + * + * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported + * supported operating classes. + * + * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space + * controls DFS operation in IBSS mode. If the flag is included in + * %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS + * channels and reports radar events to userspace. Userspace is required + * to react to radar events, e.g. initiate a channel switch or leave the + * IBSS network. + * + * @NL80211_ATTR_SUPPORT_5_MHZ: A flag indicating that the device supports + * 5 MHz channel bandwidth. + * @NL80211_ATTR_SUPPORT_10_MHZ: A flag indicating that the device supports + * 10 MHz channel bandwidth. + * + * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode + * Notification Element based on association request when used with + * %NL80211_CMD_NEW_STATION or %NL80211_CMD_SET_STATION (only when + * %NL80211_FEATURE_FULL_AP_CLIENT_STATE is supported, or with TDLS); + * u8 attribute. + * + * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if + * %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet) + * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command + * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this + * attribute is also used for vendor command feature advertisement + * @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy + * info, containing a nested array of possible events + * + * @NL80211_ATTR_QOS_MAP: IP DSCP mapping for Interworking QoS mapping. This + * data is in the format defined for the payload of the QoS Map Set element + * in IEEE Std 802.11-2012, 8.4.2.97. + * + * @NL80211_ATTR_MAC_HINT: MAC address recommendation as initial BSS + * @NL80211_ATTR_WIPHY_FREQ_HINT: frequency of the recommended initial BSS + * + * @NL80211_ATTR_MAX_AP_ASSOC_STA: Device attribute that indicates how many + * associated stations are supported in AP mode (including P2P GO); u32. + * Since drivers may not have a fixed limit on the maximum number (e.g., + * other concurrent operations may affect this), drivers are allowed to + * advertise values that cannot always be met. In such cases, an attempt + * to add a new station entry with @NL80211_CMD_NEW_STATION may fail. + * + * @NL80211_ATTR_CSA_C_OFFSETS_TX: An array of csa counter offsets (u16) which + * should be updated when the frame is transmitted. + * @NL80211_ATTR_MAX_CSA_COUNTERS: U8 attribute used to advertise the maximum + * supported number of csa counters. + * + * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32. + * As specified in the &enum nl80211_tdls_peer_capability. + * + * @NL80211_ATTR_SOCKET_OWNER: Flag attribute, if set during interface + * creation then the new interface will be owned by the netlink socket + * that created it and will be destroyed when the socket is closed. + * If set during scheduled scan start then the new scan req will be + * owned by the netlink socket that created it and the scheduled scan will + * be stopped when the socket is closed. + * If set during configuration of regulatory indoor operation then the + * regulatory indoor configuration would be owned by the netlink socket + * that configured the indoor setting, and the indoor operation would be + * cleared when the socket is closed. + * If set during NAN interface creation, the interface will be destroyed + * if the socket is closed just like any other interface. Moreover, NAN + * notifications will be sent in unicast to that socket. Without this + * attribute, the notifications will be sent to the %NL80211_MCGRP_NAN + * multicast group. + * If set during %NL80211_CMD_ASSOCIATE or %NL80211_CMD_CONNECT the + * station will deauthenticate when the socket is closed. + * If set during %NL80211_CMD_JOIN_IBSS the IBSS will be automatically + * torn down when the socket is closed. + * If set during %NL80211_CMD_JOIN_MESH the mesh setup will be + * automatically torn down when the socket is closed. + * If set during %NL80211_CMD_START_AP the AP will be automatically + * disabled when the socket is closed. + * + * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is + * the TDLS link initiator. + * + * @NL80211_ATTR_USE_RRM: flag for indicating whether the current connection + * shall support Radio Resource Measurements (11k). This attribute can be + * used with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests. + * User space applications are expected to use this flag only if the + * underlying device supports these minimal RRM features: + * %NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES, + * %NL80211_FEATURE_QUIET, + * Or, if global RRM is supported, see: + * %NL80211_EXT_FEATURE_RRM + * If this flag is used, driver must add the Power Capabilities IE to the + * association request. In addition, it must also set the RRM capability + * flag in the association request's Capability Info field. + * + * @NL80211_ATTR_WIPHY_DYN_ACK: flag attribute used to enable ACK timeout + * estimation algorithm (dynack). In order to activate dynack + * %NL80211_FEATURE_ACKTO_ESTIMATION feature flag must be set by lower + * drivers to indicate dynack capability. Dynack is automatically disabled + * setting valid value for coverage class. + * + * @NL80211_ATTR_TSID: a TSID value (u8 attribute) + * @NL80211_ATTR_USER_PRIO: user priority value (u8 attribute) + * @NL80211_ATTR_ADMITTED_TIME: admitted time in units of 32 microseconds + * (per second) (u16 attribute) + * + * @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see + * &enum nl80211_smps_mode. + * + * @NL80211_ATTR_OPER_CLASS: operating class + * + * @NL80211_ATTR_MAC_MASK: MAC address mask + * + * @NL80211_ATTR_WIPHY_SELF_MANAGED_REG: flag attribute indicating this device + * is self-managing its regulatory information and any regulatory domain + * obtained from it is coming from the device's wiphy and not the global + * cfg80211 regdomain. + * + * @NL80211_ATTR_EXT_FEATURES: extended feature flags contained in a byte + * array. The feature flags are identified by their bit index (see &enum + * nl80211_ext_feature_index). The bit index is ordered starting at the + * least-significant bit of the first byte in the array, ie. bit index 0 + * is located at bit 0 of byte 0. bit index 25 would be located at bit 1 + * of byte 3 (u8 array). + * + * @NL80211_ATTR_SURVEY_RADIO_STATS: Request overall radio statistics to be + * returned along with other survey data. If set, @NL80211_CMD_GET_SURVEY + * may return a survey entry without a channel indicating global radio + * statistics (only some values are valid and make sense.) + * For devices that don't return such an entry even then, the information + * should be contained in the result as the sum of the respective counters + * over all channels. + * + * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a + * scheduled scan is started. Or the delay before a WoWLAN + * net-detect scan is started, counting from the moment the + * system is suspended. This value is a u32, in seconds. + + * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device + * is operating in an indoor environment. + * + * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS: maximum number of scan plans for + * scheduled scan supported by the device (u32), a wiphy attribute. + * @NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL: maximum interval (in seconds) for + * a scan plan (u32), a wiphy attribute. + * @NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS: maximum number of iterations in + * a scan plan (u32), a wiphy attribute. + * @NL80211_ATTR_SCHED_SCAN_PLANS: a list of scan plans for scheduled scan. + * Each scan plan defines the number of scan iterations and the interval + * between scans. The last scan plan will always run infinitely, + * thus it must not specify the number of iterations, only the interval + * between scans. The scan plans are executed sequentially. + * Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan. + * @NL80211_ATTR_PBSS: flag attribute. If set it means operate + * in a PBSS. Specified in %NL80211_CMD_CONNECT to request + * connecting to a PCP, and in %NL80211_CMD_START_AP to start + * a PCP instead of AP. Relevant for DMG networks only. + * @NL80211_ATTR_BSS_SELECT: nested attribute for driver supporting the + * BSS selection feature. When used with %NL80211_CMD_GET_WIPHY it contains + * attributes according &enum nl80211_bss_select_attr to indicate what + * BSS selection behaviours are supported. When used with %NL80211_CMD_CONNECT + * it contains the behaviour-specific attribute containing the parameters for + * BSS selection to be done by driver and/or firmware. + * + * @NL80211_ATTR_STA_SUPPORT_P2P_PS: whether P2P PS mechanism supported + * or not. u8, one of the values of &enum nl80211_sta_p2p_ps_status + * + * @NL80211_ATTR_PAD: attribute used for padding for 64-bit alignment + * + * @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes: + * %NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA, + * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities per + * interface type. + * + * @NL80211_ATTR_MU_MIMO_GROUP_DATA: array of 24 bytes that defines a MU-MIMO + * groupID for monitor mode. + * The first 8 bytes are a mask that defines the membership in each + * group (there are 64 groups, group 0 and 63 are reserved), + * each bit represents a group and set to 1 for being a member in + * that group and 0 for not being a member. + * The remaining 16 bytes define the position in each group: 2 bits for + * each group. + * (smaller group numbers represented on most significant bits and bigger + * group numbers on least significant bits.) + * This attribute is used only if all interfaces are in monitor mode. + * Set this attribute in order to monitor packets using the given MU-MIMO + * groupID data. + * to turn off that feature set all the bits of the groupID to zero. + * @NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR: mac address for the sniffer to follow + * when using MU-MIMO air sniffer. + * to turn that feature off set an invalid mac address + * (e.g. FF:FF:FF:FF:FF:FF) + * + * @NL80211_ATTR_SCAN_START_TIME_TSF: The time at which the scan was actually + * started (u64). The time is the TSF of the BSS the interface that + * requested the scan is connected to (if available, otherwise this + * attribute must not be included). + * @NL80211_ATTR_SCAN_START_TIME_TSF_BSSID: The BSS according to which + * %NL80211_ATTR_SCAN_START_TIME_TSF is set. + * @NL80211_ATTR_MEASUREMENT_DURATION: measurement duration in TUs (u16). If + * %NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY is not set, this is the + * maximum measurement duration allowed. This attribute is used with + * measurement requests. It can also be used with %NL80211_CMD_TRIGGER_SCAN + * if the scan is used for beacon report radio measurement. + * @NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY: flag attribute that indicates + * that the duration specified with %NL80211_ATTR_MEASUREMENT_DURATION is + * mandatory. If this flag is not set, the duration is the maximum duration + * and the actual measurement duration may be shorter. + * + * @NL80211_ATTR_MESH_PEER_AID: Association ID for the mesh peer (u16). This is + * used to pull the stored data for mesh peer in power save state. + * + * @NL80211_ATTR_NAN_MASTER_PREF: the master preference to be used by + * %NL80211_CMD_START_NAN and optionally with + * %NL80211_CMD_CHANGE_NAN_CONFIG. Its type is u8 and it can't be 0. + * Also, values 1 and 255 are reserved for certification purposes and + * should not be used during a normal device operation. + * @NL80211_ATTR_BANDS: operating bands configuration. This is a u32 + * bitmask of BIT(NL80211_BAND_*) as described in %enum + * nl80211_band. For instance, for NL80211_BAND_2GHZ, bit 0 + * would be set. This attribute is used with + * %NL80211_CMD_START_NAN and %NL80211_CMD_CHANGE_NAN_CONFIG, and + * it is optional. If no bands are set, it means don't-care and + * the device will decide what to use. + * @NL80211_ATTR_NAN_FUNC: a function that can be added to NAN. See + * &enum nl80211_nan_func_attributes for description of this nested + * attribute. + * @NL80211_ATTR_NAN_MATCH: used to report a match. This is a nested attribute. + * See &enum nl80211_nan_match_attributes. + * @NL80211_ATTR_FILS_KEK: KEK for FILS (Re)Association Request/Response frame + * protection. + * @NL80211_ATTR_FILS_NONCES: Nonces (part of AAD) for FILS (Re)Association + * Request/Response frame protection. This attribute contains the 16 octet + * STA Nonce followed by 16 octets of AP Nonce. + * + * @NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED: Indicates whether or not multicast + * packets should be send out as unicast to all stations (flag attribute). + * + * @NL80211_ATTR_BSSID: The BSSID of the AP. Note that %NL80211_ATTR_MAC is also + * used in various commands/events for specifying the BSSID. + * + * @NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI: Relative RSSI threshold by which + * other BSSs has to be better or slightly worse than the current + * connected BSS so that they get reported to user space. + * This will give an opportunity to userspace to consider connecting to + * other matching BSSs which have better or slightly worse RSSI than + * the current connected BSS by using an offloaded operation to avoid + * unnecessary wakeups. + * + * @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in + * the specified band is to be adjusted before doing + * %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparison to figure out + * better BSSs. The attribute value is a packed structure + * value as specified by &struct nl80211_bss_select_rssi_adjust. + * + * @NL80211_ATTR_TIMEOUT_REASON: The reason for which an operation timed out. + * u32 attribute with an &enum nl80211_timeout_reason value. This is used, + * e.g., with %NL80211_CMD_CONNECT event. + * + * @NL80211_ATTR_FILS_ERP_USERNAME: EAP Re-authentication Protocol (ERP) + * username part of NAI used to refer keys rRK and rIK. This is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_REALM: EAP Re-authentication Protocol (ERP) realm part + * of NAI specifying the domain name of the ER server. This is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM: Unsigned 16-bit ERP next sequence number + * to use in ERP messages. This is used in generating the FILS wrapped data + * for FILS authentication and is used with %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_RRK: ERP re-authentication Root Key (rRK) for the + * NAI specified by %NL80211_ATTR_FILS_ERP_USERNAME and + * %NL80211_ATTR_FILS_ERP_REALM. This is used for generating rIK and rMSK + * from successful FILS authentication and is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP + * identifying the scope of PMKSAs. This is used with + * @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA. + * + * @NL80211_ATTR_PMK: attribute for passing PMK key material. Used with + * %NL80211_CMD_SET_PMKSA for the PMKSA identified by %NL80211_ATTR_PMKID. + * For %NL80211_CMD_CONNECT it is used to provide PSK for offloading 4-way + * handshake for WPA/WPA2-PSK networks. For 802.1X authentication it is + * used with %NL80211_CMD_SET_PMK. For offloaded FT support this attribute + * specifies the PMK-R0 if NL80211_ATTR_PMKR0_NAME is included as well. + * + * @NL80211_ATTR_SCHED_SCAN_MULTI: flag attribute which user-space shall use to + * indicate that it supports multiple active scheduled scan requests. + * @NL80211_ATTR_SCHED_SCAN_MAX_REQS: indicates maximum number of scheduled + * scan request that may be active for the device (u32). + * + * @NL80211_ATTR_WANT_1X_4WAY_HS: flag attribute which user-space can include + * in %NL80211_CMD_CONNECT to indicate that for 802.1X authentication it + * wants to use the supported offload of the 4-way handshake. + * @NL80211_ATTR_PMKR0_NAME: PMK-R0 Name for offloaded FT. + * @NL80211_ATTR_PORT_AUTHORIZED: (reserved) + * + * @NL80211_ATTR_EXTERNAL_AUTH_ACTION: Identify the requested external + * authentication operation (u32 attribute with an + * &enum nl80211_external_auth_action value). This is used with the + * %NL80211_CMD_EXTERNAL_AUTH request event. + * @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user + * space supports external authentication. This attribute shall be used + * with %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP request. The driver + * may offload authentication processing to user space if this capability + * is indicated in the respective requests from the user space. + * + * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this + * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED. + * + * @NL80211_ATTR_TXQ_STATS: TXQ statistics (nested attribute, see &enum + * nl80211_txq_stats) + * @NL80211_ATTR_TXQ_LIMIT: Total packet limit for the TXQ queues for this phy. + * The smaller of this and the memory limit is enforced. + * @NL80211_ATTR_TXQ_MEMORY_LIMIT: Total memory memory limit (in bytes) for the + * TXQ queues for this phy. The smaller of this and the packet limit is + * enforced. + * @NL80211_ATTR_TXQ_QUANTUM: TXQ scheduler quantum (bytes). Number of bytes + * a flow is assigned on each round of the DRR scheduler. + * @NL80211_ATTR_HE_CAPABILITY: HE Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION). Can be set + * only if %NL80211_STA_FLAG_WME is set. + * + * @NL80211_ATTR_FTM_RESPONDER: nested attribute which user-space can include + * in %NL80211_CMD_START_AP or %NL80211_CMD_SET_BEACON for fine timing + * measurement (FTM) responder functionality and containing parameters as + * possible, see &enum nl80211_ftm_responder_attr + * + * @NL80211_ATTR_FTM_RESPONDER_STATS: Nested attribute with FTM responder + * statistics, see &enum nl80211_ftm_responder_stats. + * + * @NL80211_ATTR_TIMEOUT: Timeout for the given operation in milliseconds (u32), + * if the attribute is not given no timeout is requested. Note that 0 is an + * invalid value. + * + * @NL80211_ATTR_PEER_MEASUREMENTS: peer measurements request (and result) + * data, uses nested attributes specified in + * &enum nl80211_peer_measurement_attrs. + * This is also used for capability advertisement in the wiphy information, + * with the appropriate sub-attributes. + * + * @NL80211_ATTR_AIRTIME_WEIGHT: Station's weight when scheduled by the airtime + * scheduler. + * + * @NL80211_ATTR_STA_TX_POWER_SETTING: Transmit power setting type (u8) for + * station associated with the AP. See &enum nl80211_tx_power_setting for + * possible values. + * @NL80211_ATTR_STA_TX_POWER: Transmit power level (s16) in dBm units. This + * allows to set Tx power for a station. If this attribute is not included, + * the default per-interface tx power setting will be overriding. Driver + * should be picking up the lowest tx power, either tx power per-interface + * or per-station. + * + * @NUM_NL80211_ATTR: total number of nl80211_attrs available + * @NL80211_ATTR_MAX: highest attribute number currently defined + * @__NL80211_ATTR_AFTER_LAST: internal use + */ +enum nl80211_attrs { +/* don't change the order or add anything between, this is ABI! */ + NL80211_ATTR_UNSPEC, + + NL80211_ATTR_WIPHY, + NL80211_ATTR_WIPHY_NAME, + + NL80211_ATTR_IFINDEX, + NL80211_ATTR_IFNAME, + NL80211_ATTR_IFTYPE, + + NL80211_ATTR_MAC, + + NL80211_ATTR_KEY_DATA, + NL80211_ATTR_KEY_IDX, + NL80211_ATTR_KEY_CIPHER, + NL80211_ATTR_KEY_SEQ, + NL80211_ATTR_KEY_DEFAULT, + + NL80211_ATTR_BEACON_INTERVAL, + NL80211_ATTR_DTIM_PERIOD, + NL80211_ATTR_BEACON_HEAD, + NL80211_ATTR_BEACON_TAIL, + + NL80211_ATTR_STA_AID, + NL80211_ATTR_STA_FLAGS, + NL80211_ATTR_STA_LISTEN_INTERVAL, + NL80211_ATTR_STA_SUPPORTED_RATES, + NL80211_ATTR_STA_VLAN, + NL80211_ATTR_STA_INFO, + + NL80211_ATTR_WIPHY_BANDS, + + NL80211_ATTR_MNTR_FLAGS, + + NL80211_ATTR_MESH_ID, + NL80211_ATTR_STA_PLINK_ACTION, + NL80211_ATTR_MPATH_NEXT_HOP, + NL80211_ATTR_MPATH_INFO, + + NL80211_ATTR_BSS_CTS_PROT, + NL80211_ATTR_BSS_SHORT_PREAMBLE, + NL80211_ATTR_BSS_SHORT_SLOT_TIME, + + NL80211_ATTR_HT_CAPABILITY, + + NL80211_ATTR_SUPPORTED_IFTYPES, + + NL80211_ATTR_REG_ALPHA2, + NL80211_ATTR_REG_RULES, + + NL80211_ATTR_MESH_CONFIG, + + NL80211_ATTR_BSS_BASIC_RATES, + + NL80211_ATTR_WIPHY_TXQ_PARAMS, + NL80211_ATTR_WIPHY_FREQ, + NL80211_ATTR_WIPHY_CHANNEL_TYPE, + + NL80211_ATTR_KEY_DEFAULT_MGMT, + + NL80211_ATTR_MGMT_SUBTYPE, + NL80211_ATTR_IE, + + NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + + NL80211_ATTR_SCAN_FREQUENCIES, + NL80211_ATTR_SCAN_SSIDS, + NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */ + NL80211_ATTR_BSS, + + NL80211_ATTR_REG_INITIATOR, + NL80211_ATTR_REG_TYPE, + + NL80211_ATTR_SUPPORTED_COMMANDS, + + NL80211_ATTR_FRAME, + NL80211_ATTR_SSID, + NL80211_ATTR_AUTH_TYPE, + NL80211_ATTR_REASON_CODE, + + NL80211_ATTR_KEY_TYPE, + + NL80211_ATTR_MAX_SCAN_IE_LEN, + NL80211_ATTR_CIPHER_SUITES, + + NL80211_ATTR_FREQ_BEFORE, + NL80211_ATTR_FREQ_AFTER, + + NL80211_ATTR_FREQ_FIXED, + + + NL80211_ATTR_WIPHY_RETRY_SHORT, + NL80211_ATTR_WIPHY_RETRY_LONG, + NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + NL80211_ATTR_WIPHY_RTS_THRESHOLD, + + NL80211_ATTR_TIMED_OUT, + + NL80211_ATTR_USE_MFP, + + NL80211_ATTR_STA_FLAGS2, + + NL80211_ATTR_CONTROL_PORT, + + NL80211_ATTR_TESTDATA, + + NL80211_ATTR_PRIVACY, + + NL80211_ATTR_DISCONNECTED_BY_AP, + NL80211_ATTR_STATUS_CODE, + + NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + NL80211_ATTR_CIPHER_SUITE_GROUP, + NL80211_ATTR_WPA_VERSIONS, + NL80211_ATTR_AKM_SUITES, + + NL80211_ATTR_REQ_IE, + NL80211_ATTR_RESP_IE, + + NL80211_ATTR_PREV_BSSID, + + NL80211_ATTR_KEY, + NL80211_ATTR_KEYS, + + NL80211_ATTR_PID, + + NL80211_ATTR_4ADDR, + + NL80211_ATTR_SURVEY_INFO, + + NL80211_ATTR_PMKID, + NL80211_ATTR_MAX_NUM_PMKIDS, + + NL80211_ATTR_DURATION, + + NL80211_ATTR_COOKIE, + + NL80211_ATTR_WIPHY_COVERAGE_CLASS, + + NL80211_ATTR_TX_RATES, + + NL80211_ATTR_FRAME_MATCH, + + NL80211_ATTR_ACK, + + NL80211_ATTR_PS_STATE, + + NL80211_ATTR_CQM, + + NL80211_ATTR_LOCAL_STATE_CHANGE, + + NL80211_ATTR_AP_ISOLATE, + + NL80211_ATTR_WIPHY_TX_POWER_SETTING, + NL80211_ATTR_WIPHY_TX_POWER_LEVEL, + + NL80211_ATTR_TX_FRAME_TYPES, + NL80211_ATTR_RX_FRAME_TYPES, + NL80211_ATTR_FRAME_TYPE, + + NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, + + NL80211_ATTR_SUPPORT_IBSS_RSN, + + NL80211_ATTR_WIPHY_ANTENNA_TX, + NL80211_ATTR_WIPHY_ANTENNA_RX, + + NL80211_ATTR_MCAST_RATE, + + NL80211_ATTR_OFFCHANNEL_TX_OK, + + NL80211_ATTR_BSS_HT_OPMODE, + + NL80211_ATTR_KEY_DEFAULT_TYPES, + + NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, + + NL80211_ATTR_MESH_SETUP, + + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, + + NL80211_ATTR_SUPPORT_MESH_AUTH, + NL80211_ATTR_STA_PLINK_STATE, + + NL80211_ATTR_WOWLAN_TRIGGERS, + NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, + + NL80211_ATTR_SCHED_SCAN_INTERVAL, + + NL80211_ATTR_INTERFACE_COMBINATIONS, + NL80211_ATTR_SOFTWARE_IFTYPES, + + NL80211_ATTR_REKEY_DATA, + + NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, + NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, + + NL80211_ATTR_SCAN_SUPP_RATES, + + NL80211_ATTR_HIDDEN_SSID, + + NL80211_ATTR_IE_PROBE_RESP, + NL80211_ATTR_IE_ASSOC_RESP, + + NL80211_ATTR_STA_WME, + NL80211_ATTR_SUPPORT_AP_UAPSD, + + NL80211_ATTR_ROAM_SUPPORT, + + NL80211_ATTR_SCHED_SCAN_MATCH, + NL80211_ATTR_MAX_MATCH_SETS, + + NL80211_ATTR_PMKSA_CANDIDATE, + + NL80211_ATTR_TX_NO_CCK_RATE, + + NL80211_ATTR_TDLS_ACTION, + NL80211_ATTR_TDLS_DIALOG_TOKEN, + NL80211_ATTR_TDLS_OPERATION, + NL80211_ATTR_TDLS_SUPPORT, + NL80211_ATTR_TDLS_EXTERNAL_SETUP, + + NL80211_ATTR_DEVICE_AP_SME, + + NL80211_ATTR_DONT_WAIT_FOR_ACK, + + NL80211_ATTR_FEATURE_FLAGS, + + NL80211_ATTR_PROBE_RESP_OFFLOAD, + + NL80211_ATTR_PROBE_RESP, + + NL80211_ATTR_DFS_REGION, + + NL80211_ATTR_DISABLE_HT, + NL80211_ATTR_HT_CAPABILITY_MASK, + + NL80211_ATTR_NOACK_MAP, + + NL80211_ATTR_INACTIVITY_TIMEOUT, + + NL80211_ATTR_RX_SIGNAL_DBM, + + NL80211_ATTR_BG_SCAN_PERIOD, + + NL80211_ATTR_WDEV, + + NL80211_ATTR_USER_REG_HINT_TYPE, + + NL80211_ATTR_CONN_FAILED_REASON, + + NL80211_ATTR_AUTH_DATA, + + NL80211_ATTR_VHT_CAPABILITY, + + NL80211_ATTR_SCAN_FLAGS, + + NL80211_ATTR_CHANNEL_WIDTH, + NL80211_ATTR_CENTER_FREQ1, + NL80211_ATTR_CENTER_FREQ2, + + NL80211_ATTR_P2P_CTWINDOW, + NL80211_ATTR_P2P_OPPPS, + + NL80211_ATTR_LOCAL_MESH_POWER_MODE, + + NL80211_ATTR_ACL_POLICY, + + NL80211_ATTR_MAC_ADDRS, + + NL80211_ATTR_MAC_ACL_MAX, + + NL80211_ATTR_RADAR_EVENT, + + NL80211_ATTR_EXT_CAPA, + NL80211_ATTR_EXT_CAPA_MASK, + + NL80211_ATTR_STA_CAPABILITY, + NL80211_ATTR_STA_EXT_CAPABILITY, + + NL80211_ATTR_PROTOCOL_FEATURES, + NL80211_ATTR_SPLIT_WIPHY_DUMP, + + NL80211_ATTR_DISABLE_VHT, + NL80211_ATTR_VHT_CAPABILITY_MASK, + + NL80211_ATTR_MDID, + NL80211_ATTR_IE_RIC, + + NL80211_ATTR_CRIT_PROT_ID, + NL80211_ATTR_MAX_CRIT_PROT_DURATION, + + NL80211_ATTR_PEER_AID, + + NL80211_ATTR_COALESCE_RULE, + + NL80211_ATTR_CH_SWITCH_COUNT, + NL80211_ATTR_CH_SWITCH_BLOCK_TX, + NL80211_ATTR_CSA_IES, + NL80211_ATTR_CSA_C_OFF_BEACON, + NL80211_ATTR_CSA_C_OFF_PRESP, + + NL80211_ATTR_RXMGMT_FLAGS, + + NL80211_ATTR_STA_SUPPORTED_CHANNELS, + + NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, + + NL80211_ATTR_HANDLE_DFS, + + NL80211_ATTR_SUPPORT_5_MHZ, + NL80211_ATTR_SUPPORT_10_MHZ, + + NL80211_ATTR_OPMODE_NOTIF, + + NL80211_ATTR_VENDOR_ID, + NL80211_ATTR_VENDOR_SUBCMD, + NL80211_ATTR_VENDOR_DATA, + NL80211_ATTR_VENDOR_EVENTS, + + NL80211_ATTR_QOS_MAP, + + NL80211_ATTR_MAC_HINT, + NL80211_ATTR_WIPHY_FREQ_HINT, + + NL80211_ATTR_MAX_AP_ASSOC_STA, + + NL80211_ATTR_TDLS_PEER_CAPABILITY, + + NL80211_ATTR_SOCKET_OWNER, + + NL80211_ATTR_CSA_C_OFFSETS_TX, + NL80211_ATTR_MAX_CSA_COUNTERS, + + NL80211_ATTR_TDLS_INITIATOR, + + NL80211_ATTR_USE_RRM, + + NL80211_ATTR_WIPHY_DYN_ACK, + + NL80211_ATTR_TSID, + NL80211_ATTR_USER_PRIO, + NL80211_ATTR_ADMITTED_TIME, + + NL80211_ATTR_SMPS_MODE, + + NL80211_ATTR_OPER_CLASS, + + NL80211_ATTR_MAC_MASK, + + NL80211_ATTR_WIPHY_SELF_MANAGED_REG, + + NL80211_ATTR_EXT_FEATURES, + + NL80211_ATTR_SURVEY_RADIO_STATS, + + NL80211_ATTR_NETNS_FD, + + NL80211_ATTR_SCHED_SCAN_DELAY, + + NL80211_ATTR_REG_INDOOR, + + NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS, + NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL, + NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS, + NL80211_ATTR_SCHED_SCAN_PLANS, + + NL80211_ATTR_PBSS, + + NL80211_ATTR_BSS_SELECT, + + NL80211_ATTR_STA_SUPPORT_P2P_PS, + + NL80211_ATTR_PAD, + + NL80211_ATTR_IFTYPE_EXT_CAPA, + + NL80211_ATTR_MU_MIMO_GROUP_DATA, + NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR, + + NL80211_ATTR_SCAN_START_TIME_TSF, + NL80211_ATTR_SCAN_START_TIME_TSF_BSSID, + NL80211_ATTR_MEASUREMENT_DURATION, + NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY, + + NL80211_ATTR_MESH_PEER_AID, + + NL80211_ATTR_NAN_MASTER_PREF, + NL80211_ATTR_BANDS, + NL80211_ATTR_NAN_FUNC, + NL80211_ATTR_NAN_MATCH, + + NL80211_ATTR_FILS_KEK, + NL80211_ATTR_FILS_NONCES, + + NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED, + + NL80211_ATTR_BSSID, + + NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI, + NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST, + + NL80211_ATTR_TIMEOUT_REASON, + + NL80211_ATTR_FILS_ERP_USERNAME, + NL80211_ATTR_FILS_ERP_REALM, + NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, + NL80211_ATTR_FILS_ERP_RRK, + NL80211_ATTR_FILS_CACHE_ID, + + NL80211_ATTR_PMK, + + NL80211_ATTR_SCHED_SCAN_MULTI, + NL80211_ATTR_SCHED_SCAN_MAX_REQS, + + NL80211_ATTR_WANT_1X_4WAY_HS, + NL80211_ATTR_PMKR0_NAME, + NL80211_ATTR_PORT_AUTHORIZED, + + NL80211_ATTR_EXTERNAL_AUTH_ACTION, + NL80211_ATTR_EXTERNAL_AUTH_SUPPORT, + + NL80211_ATTR_NSS, + NL80211_ATTR_ACK_SIGNAL, + + NL80211_ATTR_CONTROL_PORT_OVER_NL80211, + + NL80211_ATTR_TXQ_STATS, + NL80211_ATTR_TXQ_LIMIT, + NL80211_ATTR_TXQ_MEMORY_LIMIT, + NL80211_ATTR_TXQ_QUANTUM, + + NL80211_ATTR_HE_CAPABILITY, + + NL80211_ATTR_FTM_RESPONDER, + + NL80211_ATTR_FTM_RESPONDER_STATS, + + NL80211_ATTR_TIMEOUT, + + NL80211_ATTR_PEER_MEASUREMENTS, + + NL80211_ATTR_AIRTIME_WEIGHT, + NL80211_ATTR_STA_TX_POWER_SETTING, + NL80211_ATTR_STA_TX_POWER, + + /* add attributes here, update the policy in nl80211.c */ + + __NL80211_ATTR_AFTER_LAST, + NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST, + NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 +}; + +/* source-level API compatibility */ +#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION +#define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG +#define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER +#define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA + +/* + * Allow user space programs to use #ifdef on new attributes by defining them + * here + */ +#define NL80211_CMD_CONNECT NL80211_CMD_CONNECT +#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY +#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES +#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS +#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ +#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE +#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE +#define NL80211_ATTR_IE NL80211_ATTR_IE +#define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR +#define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE +#define NL80211_ATTR_FRAME NL80211_ATTR_FRAME +#define NL80211_ATTR_SSID NL80211_ATTR_SSID +#define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE +#define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE +#define NL80211_ATTR_CIPHER_SUITES_PAIRWISE NL80211_ATTR_CIPHER_SUITES_PAIRWISE +#define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP +#define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS +#define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES +#define NL80211_ATTR_KEY NL80211_ATTR_KEY +#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS +#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS + +#define NL80211_WIPHY_NAME_MAXLEN 64 + +#define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_HT_RATES 77 +#define NL80211_MAX_SUPP_REG_RULES 128 +#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 +#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 +#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 +#define NL80211_HT_CAPABILITY_LEN 26 +#define NL80211_VHT_CAPABILITY_LEN 12 +#define NL80211_HE_MIN_CAPABILITY_LEN 16 +#define NL80211_HE_MAX_CAPABILITY_LEN 51 +#define NL80211_MAX_NR_CIPHER_SUITES 5 +#define NL80211_MAX_NR_AKM_SUITES 2 + +#define NL80211_MIN_REMAIN_ON_CHANNEL_TIME 10 + +/* default RSSI threshold for scan results if none specified. */ +#define NL80211_SCAN_RSSI_THOLD_OFF -300 + +#define NL80211_CQM_TXE_MAX_INTVL 1800 + +/** + * enum nl80211_iftype - (virtual) interface types + * + * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides + * @NL80211_IFTYPE_ADHOC: independent BSS member + * @NL80211_IFTYPE_STATION: managed BSS member + * @NL80211_IFTYPE_AP: access point + * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points; VLAN interfaces + * are a bit special in that they must always be tied to a pre-existing + * AP type interface. + * @NL80211_IFTYPE_WDS: wireless distribution interface + * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames + * @NL80211_IFTYPE_MESH_POINT: mesh point + * @NL80211_IFTYPE_P2P_CLIENT: P2P client + * @NL80211_IFTYPE_P2P_GO: P2P group owner + * @NL80211_IFTYPE_P2P_DEVICE: P2P device interface type, this is not a netdev + * and therefore can't be created in the normal ways, use the + * %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE + * commands to create and destroy one + * @NL80211_IF_TYPE_OCB: Outside Context of a BSS + * This mode corresponds to the MIB variable dot11OCBActivated=true + * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev) + * @NL80211_IFTYPE_MAX: highest interface type number currently defined + * @NUM_NL80211_IFTYPES: number of defined interface types + * + * These values are used with the %NL80211_ATTR_IFTYPE + * to set the type of an interface. + * + */ +enum nl80211_iftype { + NL80211_IFTYPE_UNSPECIFIED, + NL80211_IFTYPE_ADHOC, + NL80211_IFTYPE_STATION, + NL80211_IFTYPE_AP, + NL80211_IFTYPE_AP_VLAN, + NL80211_IFTYPE_WDS, + NL80211_IFTYPE_MONITOR, + NL80211_IFTYPE_MESH_POINT, + NL80211_IFTYPE_P2P_CLIENT, + NL80211_IFTYPE_P2P_GO, + NL80211_IFTYPE_P2P_DEVICE, + NL80211_IFTYPE_OCB, + NL80211_IFTYPE_NAN, + + /* keep last */ + NUM_NL80211_IFTYPES, + NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1 +}; + +/** + * enum nl80211_sta_flags - station flags + * + * Station flags. When a station is added to an AP interface, it is + * assumed to be already associated (and hence authenticated.) + * + * @__NL80211_STA_FLAG_INVALID: attribute number 0 is reserved + * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X) + * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames + * with short barker preamble + * @NL80211_STA_FLAG_WME: station is WME/QoS capable + * @NL80211_STA_FLAG_MFP: station uses management frame protection + * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated + * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer -- this flag should + * only be used in managed mode (even in the flags mask). Note that the + * flag can't be changed, it is only valid while adding a station, and + * attempts to change it will silently be ignored (rather than rejected + * as errors.) + * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers + * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a + * previously added station into associated state + * @NL80211_STA_FLAG_MAX: highest station flag number currently defined + * @__NL80211_STA_FLAG_AFTER_LAST: internal use + */ +enum nl80211_sta_flags { + __NL80211_STA_FLAG_INVALID, + NL80211_STA_FLAG_AUTHORIZED, + NL80211_STA_FLAG_SHORT_PREAMBLE, + NL80211_STA_FLAG_WME, + NL80211_STA_FLAG_MFP, + NL80211_STA_FLAG_AUTHENTICATED, + NL80211_STA_FLAG_TDLS_PEER, + NL80211_STA_FLAG_ASSOCIATED, + + /* keep last */ + __NL80211_STA_FLAG_AFTER_LAST, + NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_p2p_ps_status - station support of P2P PS + * + * @NL80211_P2P_PS_UNSUPPORTED: station doesn't support P2P PS mechanism + * @@NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism + * @NUM_NL80211_P2P_PS_STATUS: number of values + */ +enum nl80211_sta_p2p_ps_status { + NL80211_P2P_PS_UNSUPPORTED = 0, + NL80211_P2P_PS_SUPPORTED, + + NUM_NL80211_P2P_PS_STATUS, +}; + +#define NL80211_STA_FLAG_MAX_OLD_API NL80211_STA_FLAG_TDLS_PEER + +/** + * struct nl80211_sta_flag_update - station flags mask/set + * @mask: mask of station flags to set + * @set: which values to set them to + * + * Both mask and set contain bits as per &enum nl80211_sta_flags. + */ +struct nl80211_sta_flag_update { + __u32 mask; + __u32 set; +} __attribute__((packed)); + +/** + * enum nl80211_he_gi - HE guard interval + * @NL80211_RATE_INFO_HE_GI_0_8: 0.8 usec + * @NL80211_RATE_INFO_HE_GI_1_6: 1.6 usec + * @NL80211_RATE_INFO_HE_GI_3_2: 3.2 usec + */ +enum nl80211_he_gi { + NL80211_RATE_INFO_HE_GI_0_8, + NL80211_RATE_INFO_HE_GI_1_6, + NL80211_RATE_INFO_HE_GI_3_2, +}; + +/** + * enum nl80211_he_ru_alloc - HE RU allocation values + * @NL80211_RATE_INFO_HE_RU_ALLOC_26: 26-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_52: 52-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_106: 106-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_242: 242-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_484: 484-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_996: 996-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_2x996: 2x996-tone RU allocation + */ +enum nl80211_he_ru_alloc { + NL80211_RATE_INFO_HE_RU_ALLOC_26, + NL80211_RATE_INFO_HE_RU_ALLOC_52, + NL80211_RATE_INFO_HE_RU_ALLOC_106, + NL80211_RATE_INFO_HE_RU_ALLOC_242, + NL80211_RATE_INFO_HE_RU_ALLOC_484, + NL80211_RATE_INFO_HE_RU_ALLOC_996, + NL80211_RATE_INFO_HE_RU_ALLOC_2x996, +}; + +/** + * enum nl80211_rate_info - bitrate information + * + * These attribute types are used with %NL80211_STA_INFO_TXRATE + * when getting information about the bitrate of a station. + * There are 2 attributes for bitrate, a legacy one that represents + * a 16-bit value, and new one that represents a 32-bit value. + * If the rate value fits into 16 bit, both attributes are reported + * with the same value. If the rate is too high to fit into 16 bits + * (>6.5535Gbps) only 32-bit attribute is included. + * User space tools encouraged to use the 32-bit attribute and fall + * back to the 16-bit one for compatibility with older kernels. + * + * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved + * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) + * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) + * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 MHz dualchannel bitrate + * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval + * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s) + * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined + * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8) + * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8) + * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate + * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: unused - 80+80 is treated the + * same as 160 for purposes of the bitrates + * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate + * @NL80211_RATE_INFO_10_MHZ_WIDTH: 10 MHz width - note that this is + * a legacy rate and will be reported as the actual bitrate, i.e. + * half the base (20 MHz) rate + * @NL80211_RATE_INFO_5_MHZ_WIDTH: 5 MHz width - note that this is + * a legacy rate and will be reported as the actual bitrate, i.e. + * a quarter of the base (20 MHz) rate + * @NL80211_RATE_INFO_HE_MCS: HE MCS index (u8, 0-11) + * @NL80211_RATE_INFO_HE_NSS: HE NSS value (u8, 1-8) + * @NL80211_RATE_INFO_HE_GI: HE guard interval identifier + * (u8, see &enum nl80211_he_gi) + * @NL80211_RATE_INFO_HE_DCM: HE DCM value (u8, 0/1) + * @NL80211_RATE_INFO_RU_ALLOC: HE RU allocation, if not present then + * non-OFDMA was used (u8, see &enum nl80211_he_ru_alloc) + * @__NL80211_RATE_INFO_AFTER_LAST: internal use + */ +enum nl80211_rate_info { + __NL80211_RATE_INFO_INVALID, + NL80211_RATE_INFO_BITRATE, + NL80211_RATE_INFO_MCS, + NL80211_RATE_INFO_40_MHZ_WIDTH, + NL80211_RATE_INFO_SHORT_GI, + NL80211_RATE_INFO_BITRATE32, + NL80211_RATE_INFO_VHT_MCS, + NL80211_RATE_INFO_VHT_NSS, + NL80211_RATE_INFO_80_MHZ_WIDTH, + NL80211_RATE_INFO_80P80_MHZ_WIDTH, + NL80211_RATE_INFO_160_MHZ_WIDTH, + NL80211_RATE_INFO_10_MHZ_WIDTH, + NL80211_RATE_INFO_5_MHZ_WIDTH, + NL80211_RATE_INFO_HE_MCS, + NL80211_RATE_INFO_HE_NSS, + NL80211_RATE_INFO_HE_GI, + NL80211_RATE_INFO_HE_DCM, + NL80211_RATE_INFO_HE_RU_ALLOC, + + /* keep last */ + __NL80211_RATE_INFO_AFTER_LAST, + NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_bss_param - BSS information collected by STA + * + * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM + * when getting information about the bitrate of a station. + * + * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved + * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag) + * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE: whether short preamble is enabled + * (flag) + * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME: whether short slot time is enabled + * (flag) + * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8) + * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16) + * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined + * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use + */ +enum nl80211_sta_bss_param { + __NL80211_STA_BSS_PARAM_INVALID, + NL80211_STA_BSS_PARAM_CTS_PROT, + NL80211_STA_BSS_PARAM_SHORT_PREAMBLE, + NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME, + NL80211_STA_BSS_PARAM_DTIM_PERIOD, + NL80211_STA_BSS_PARAM_BEACON_INTERVAL, + + /* keep last */ + __NL80211_STA_BSS_PARAM_AFTER_LAST, + NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_info - station information + * + * These attribute types are used with %NL80211_ATTR_STA_INFO + * when getting information about a station. + * + * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved + * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) + * @NL80211_STA_INFO_RX_BYTES: total received bytes (MPDU length) + * (u32, from this station) + * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (MPDU length) + * (u32, to this station) + * @NL80211_STA_INFO_RX_BYTES64: total received bytes (MPDU length) + * (u64, from this station) + * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (MPDU length) + * (u64, to this station) + * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) + * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute + * containing info as possible, see &enum nl80211_rate_info + * @NL80211_STA_INFO_RX_PACKETS: total received packet (MSDUs and MMPDUs) + * (u32, from this station) + * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (MSDUs and MMPDUs) + * (u32, to this station) + * @NL80211_STA_INFO_TX_RETRIES: total retries (MPDUs) (u32, to this station) + * @NL80211_STA_INFO_TX_FAILED: total failed packets (MPDUs) + * (u32, to this station) + * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm) + * @NL80211_STA_INFO_LLID: the station's mesh LLID + * @NL80211_STA_INFO_PLID: the station's mesh PLID + * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station + * (see %enum nl80211_plink_state) + * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested + * attribute, like NL80211_STA_INFO_TX_BITRATE. + * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute + * containing info as possible, see &enum nl80211_sta_bss_param + * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected + * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update. + * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32) + * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64) + * @NL80211_STA_INFO_LOCAL_PM: local mesh STA link-specific power mode + * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode + * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards + * non-peer STA + * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU + * Contains a nested array of signal strength attributes (u8, dBm) + * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average + * Same format as NL80211_STA_INFO_CHAIN_SIGNAL. + * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the + * 802.11 header (u32, kbps) + * @NL80211_STA_INFO_RX_DROP_MISC: RX packets dropped for unspecified reasons + * (u64) + * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64) + * @NL80211_STA_INFO_BEACON_SIGNAL_AVG: signal strength average + * for beacons only (u8, dBm) + * @NL80211_STA_INFO_TID_STATS: per-TID statistics (see &enum nl80211_tid_stats) + * This is a nested attribute where each the inner attribute number is the + * TID+1 and the special TID 16 (i.e. value 17) is used for non-QoS frames; + * each one of those is again nested with &enum nl80211_tid_stats + * attributes carrying the actual values. + * @NL80211_STA_INFO_RX_DURATION: aggregate PPDU duration for all frames + * received from the station (u64, usec) + * @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment + * @NL80211_STA_INFO_ACK_SIGNAL: signal strength of the last ACK frame(u8, dBm) + * @NL80211_STA_INFO_ACK_SIGNAL_AVG: avg signal strength of ACK frames (s8, dBm) + * @NL80211_STA_INFO_RX_MPDUS: total number of received packets (MPDUs) + * (u32, from this station) + * @NL80211_STA_INFO_FCS_ERROR_COUNT: total number of packets (MPDUs) received + * with an FCS error (u32, from this station). This count may not include + * some packets with an FCS error due to TA corruption. Hence this counter + * might not be fully accurate. + * @NL80211_STA_INFO_CONNECTED_TO_GATE: set to true if STA has a path to a + * mesh gate (u8, 0 or 1) + * @NL80211_STA_INFO_TX_DURATION: aggregate PPDU duration for all frames + * sent to the station (u64, usec) + * @NL80211_STA_INFO_AIRTIME_WEIGHT: current airtime weight for station (u16) + * @NL80211_STA_INFO_AIRTIME_LINK_METRIC: airtime link metric for mesh station + * @__NL80211_STA_INFO_AFTER_LAST: internal + * @NL80211_STA_INFO_MAX: highest possible station info attribute + */ +enum nl80211_sta_info { + __NL80211_STA_INFO_INVALID, + NL80211_STA_INFO_INACTIVE_TIME, + NL80211_STA_INFO_RX_BYTES, + NL80211_STA_INFO_TX_BYTES, + NL80211_STA_INFO_LLID, + NL80211_STA_INFO_PLID, + NL80211_STA_INFO_PLINK_STATE, + NL80211_STA_INFO_SIGNAL, + NL80211_STA_INFO_TX_BITRATE, + NL80211_STA_INFO_RX_PACKETS, + NL80211_STA_INFO_TX_PACKETS, + NL80211_STA_INFO_TX_RETRIES, + NL80211_STA_INFO_TX_FAILED, + NL80211_STA_INFO_SIGNAL_AVG, + NL80211_STA_INFO_RX_BITRATE, + NL80211_STA_INFO_BSS_PARAM, + NL80211_STA_INFO_CONNECTED_TIME, + NL80211_STA_INFO_STA_FLAGS, + NL80211_STA_INFO_BEACON_LOSS, + NL80211_STA_INFO_T_OFFSET, + NL80211_STA_INFO_LOCAL_PM, + NL80211_STA_INFO_PEER_PM, + NL80211_STA_INFO_NONPEER_PM, + NL80211_STA_INFO_RX_BYTES64, + NL80211_STA_INFO_TX_BYTES64, + NL80211_STA_INFO_CHAIN_SIGNAL, + NL80211_STA_INFO_CHAIN_SIGNAL_AVG, + NL80211_STA_INFO_EXPECTED_THROUGHPUT, + NL80211_STA_INFO_RX_DROP_MISC, + NL80211_STA_INFO_BEACON_RX, + NL80211_STA_INFO_BEACON_SIGNAL_AVG, + NL80211_STA_INFO_TID_STATS, + NL80211_STA_INFO_RX_DURATION, + NL80211_STA_INFO_PAD, + NL80211_STA_INFO_ACK_SIGNAL, + NL80211_STA_INFO_ACK_SIGNAL_AVG, + NL80211_STA_INFO_RX_MPDUS, + NL80211_STA_INFO_FCS_ERROR_COUNT, + NL80211_STA_INFO_CONNECTED_TO_GATE, + NL80211_STA_INFO_TX_DURATION, + NL80211_STA_INFO_AIRTIME_WEIGHT, + NL80211_STA_INFO_AIRTIME_LINK_METRIC, + + /* keep last */ + __NL80211_STA_INFO_AFTER_LAST, + NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1 +}; + +/* we renamed this - stay compatible */ +#define NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG NL80211_STA_INFO_ACK_SIGNAL_AVG + + +/** + * enum nl80211_tid_stats - per TID statistics attributes + * @__NL80211_TID_STATS_INVALID: attribute number 0 is reserved + * @NL80211_TID_STATS_RX_MSDU: number of MSDUs received (u64) + * @NL80211_TID_STATS_TX_MSDU: number of MSDUs transmitted (or + * attempted to transmit; u64) + * @NL80211_TID_STATS_TX_MSDU_RETRIES: number of retries for + * transmitted MSDUs (not counting the first attempt; u64) + * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted + * MSDUs (u64) + * @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment + * @NL80211_TID_STATS_TXQ_STATS: TXQ stats (nested attribute) + * @NUM_NL80211_TID_STATS: number of attributes here + * @NL80211_TID_STATS_MAX: highest numbered attribute here + */ +enum nl80211_tid_stats { + __NL80211_TID_STATS_INVALID, + NL80211_TID_STATS_RX_MSDU, + NL80211_TID_STATS_TX_MSDU, + NL80211_TID_STATS_TX_MSDU_RETRIES, + NL80211_TID_STATS_TX_MSDU_FAILED, + NL80211_TID_STATS_PAD, + NL80211_TID_STATS_TXQ_STATS, + + /* keep last */ + NUM_NL80211_TID_STATS, + NL80211_TID_STATS_MAX = NUM_NL80211_TID_STATS - 1 +}; + +/** + * enum nl80211_txq_stats - per TXQ statistics attributes + * @__NL80211_TXQ_STATS_INVALID: attribute number 0 is reserved + * @NUM_NL80211_TXQ_STATS: number of attributes here + * @NL80211_TXQ_STATS_BACKLOG_BYTES: number of bytes currently backlogged + * @NL80211_TXQ_STATS_BACKLOG_PACKETS: number of packets currently + * backlogged + * @NL80211_TXQ_STATS_FLOWS: total number of new flows seen + * @NL80211_TXQ_STATS_DROPS: total number of packet drops + * @NL80211_TXQ_STATS_ECN_MARKS: total number of packet ECN marks + * @NL80211_TXQ_STATS_OVERLIMIT: number of drops due to queue space overflow + * @NL80211_TXQ_STATS_OVERMEMORY: number of drops due to memory limit overflow + * (only for per-phy stats) + * @NL80211_TXQ_STATS_COLLISIONS: number of hash collisions + * @NL80211_TXQ_STATS_TX_BYTES: total number of bytes dequeued from TXQ + * @NL80211_TXQ_STATS_TX_PACKETS: total number of packets dequeued from TXQ + * @NL80211_TXQ_STATS_MAX_FLOWS: number of flow buckets for PHY + * @NL80211_TXQ_STATS_MAX: highest numbered attribute here + */ +enum nl80211_txq_stats { + __NL80211_TXQ_STATS_INVALID, + NL80211_TXQ_STATS_BACKLOG_BYTES, + NL80211_TXQ_STATS_BACKLOG_PACKETS, + NL80211_TXQ_STATS_FLOWS, + NL80211_TXQ_STATS_DROPS, + NL80211_TXQ_STATS_ECN_MARKS, + NL80211_TXQ_STATS_OVERLIMIT, + NL80211_TXQ_STATS_OVERMEMORY, + NL80211_TXQ_STATS_COLLISIONS, + NL80211_TXQ_STATS_TX_BYTES, + NL80211_TXQ_STATS_TX_PACKETS, + NL80211_TXQ_STATS_MAX_FLOWS, + + /* keep last */ + NUM_NL80211_TXQ_STATS, + NL80211_TXQ_STATS_MAX = NUM_NL80211_TXQ_STATS - 1 +}; + +/** + * enum nl80211_mpath_flags - nl80211 mesh path flags + * + * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active + * @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running + * @NL80211_MPATH_FLAG_SN_VALID: the mesh path contains a valid SN + * @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set + * @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded + */ +enum nl80211_mpath_flags { + NL80211_MPATH_FLAG_ACTIVE = 1<<0, + NL80211_MPATH_FLAG_RESOLVING = 1<<1, + NL80211_MPATH_FLAG_SN_VALID = 1<<2, + NL80211_MPATH_FLAG_FIXED = 1<<3, + NL80211_MPATH_FLAG_RESOLVED = 1<<4, +}; + +/** + * enum nl80211_mpath_info - mesh path information + * + * These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting + * information about a mesh path. + * + * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved + * @NL80211_MPATH_INFO_FRAME_QLEN: number of queued frames for this destination + * @NL80211_MPATH_INFO_SN: destination sequence number + * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path + * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now + * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in + * &enum nl80211_mpath_flags; + * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec + * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries + * @NL80211_MPATH_INFO_HOP_COUNT: hop count to destination + * @NL80211_MPATH_INFO_PATH_CHANGE: total number of path changes to destination + * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number + * currently defined + * @__NL80211_MPATH_INFO_AFTER_LAST: internal use + */ +enum nl80211_mpath_info { + __NL80211_MPATH_INFO_INVALID, + NL80211_MPATH_INFO_FRAME_QLEN, + NL80211_MPATH_INFO_SN, + NL80211_MPATH_INFO_METRIC, + NL80211_MPATH_INFO_EXPTIME, + NL80211_MPATH_INFO_FLAGS, + NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, + NL80211_MPATH_INFO_DISCOVERY_RETRIES, + NL80211_MPATH_INFO_HOP_COUNT, + NL80211_MPATH_INFO_PATH_CHANGE, + + /* keep last */ + __NL80211_MPATH_INFO_AFTER_LAST, + NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_band_iftype_attr - Interface type data attributes + * + * @__NL80211_BAND_IFTYPE_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_BAND_IFTYPE_ATTR_IFTYPES: nested attribute containing a flag attribute + * for each interface type that supports the band data + * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC: HE MAC capabilities as in HE + * capabilities IE + * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY: HE PHY capabilities as in HE + * capabilities IE + * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET: HE supported NSS/MCS as in HE + * capabilities IE + * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE: HE PPE thresholds information as + * defined in HE capabilities IE + * @NL80211_BAND_IFTYPE_ATTR_MAX: highest band HE capability attribute currently + * defined + * @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use + */ +enum nl80211_band_iftype_attr { + __NL80211_BAND_IFTYPE_ATTR_INVALID, + + NL80211_BAND_IFTYPE_ATTR_IFTYPES, + NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC, + NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY, + NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET, + NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE, + + /* keep last */ + __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST, + NL80211_BAND_IFTYPE_ATTR_MAX = __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_band_attr - band attributes + * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band, + * an array of nested frequency attributes + * @NL80211_BAND_ATTR_RATES: supported bitrates in this band, + * an array of nested bitrate attributes + * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as + * defined in 802.11n + * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE + * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n + * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n + * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as + * defined in 802.11ac + * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE + * @NL80211_BAND_ATTR_IFTYPE_DATA: nested array attribute, with each entry using + * attributes from &enum nl80211_band_iftype_attr + * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined + * @__NL80211_BAND_ATTR_AFTER_LAST: internal use + */ +enum nl80211_band_attr { + __NL80211_BAND_ATTR_INVALID, + NL80211_BAND_ATTR_FREQS, + NL80211_BAND_ATTR_RATES, + + NL80211_BAND_ATTR_HT_MCS_SET, + NL80211_BAND_ATTR_HT_CAPA, + NL80211_BAND_ATTR_HT_AMPDU_FACTOR, + NL80211_BAND_ATTR_HT_AMPDU_DENSITY, + + NL80211_BAND_ATTR_VHT_MCS_SET, + NL80211_BAND_ATTR_VHT_CAPA, + NL80211_BAND_ATTR_IFTYPE_DATA, + + /* keep last */ + __NL80211_BAND_ATTR_AFTER_LAST, + NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 +}; + +#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA + +/** + * enum nl80211_wmm_rule - regulatory wmm rule + * + * @__NL80211_WMMR_INVALID: attribute number 0 is reserved + * @NL80211_WMMR_CW_MIN: Minimum contention window slot. + * @NL80211_WMMR_CW_MAX: Maximum contention window slot. + * @NL80211_WMMR_AIFSN: Arbitration Inter Frame Space. + * @NL80211_WMMR_TXOP: Maximum allowed tx operation time. + * @nl80211_WMMR_MAX: highest possible wmm rule. + * @__NL80211_WMMR_LAST: Internal use. + */ +enum nl80211_wmm_rule { + __NL80211_WMMR_INVALID, + NL80211_WMMR_CW_MIN, + NL80211_WMMR_CW_MAX, + NL80211_WMMR_AIFSN, + NL80211_WMMR_TXOP, + + /* keep last */ + __NL80211_WMMR_LAST, + NL80211_WMMR_MAX = __NL80211_WMMR_LAST - 1 +}; + +/** + * enum nl80211_frequency_attr - frequency attributes + * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz + * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current + * regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_IR: no mechanisms that initiate radiation + * are permitted on this channel, this includes sending probe + * requests, or modes of operation that require beaconing. + * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm + * (100 * dBm). + * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS + * (enum nl80211_dfs_state) + * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long + * this channel is in this DFS state. + * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this + * channel as the control channel + * @NL80211_FREQUENCY_ATTR_NO_HT40_PLUS: HT40+ isn't possible with this + * channel as the control channel + * @NL80211_FREQUENCY_ATTR_NO_80MHZ: any 80 MHz channel using this channel + * as the primary or any of the secondary channels isn't possible, + * this includes 80+80 channels + * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel + * using this channel as the primary or any of the secondary channels + * isn't possible + * @NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds. + * @NL80211_FREQUENCY_ATTR_INDOOR_ONLY: Only indoor use is permitted on this + * channel. A channel that has the INDOOR_ONLY attribute can only be + * used when there is a clear assessment that the device is operating in + * an indoor surroundings, i.e., it is connected to AC power (and not + * through portable DC inverters) or is under the control of a master + * that is acting as an AP and is connected to AC power. + * @NL80211_FREQUENCY_ATTR_IR_CONCURRENT: IR operation is allowed on this + * channel if it's connected concurrently to a BSS on the same channel on + * the 2 GHz band or to a channel in the same UNII band (on the 5 GHz + * band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO or TDLS + * off-channel on a channel that has the IR_CONCURRENT attribute set can be + * done when there is a clear assessment that the device is operating under + * the guidance of an authorized master, i.e., setting up a GO or TDLS + * off-channel while the device is also connected to an AP with DFS and + * radar detection on the UNII band (it is up to user-space, i.e., + * wpa_supplicant to perform the required verifications). Using this + * attribute for IR is disallowed for master interfaces (IBSS, AP). + * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_WMM: this channel has wmm limitations. + * This is a nested attribute that contains the wmm limitation per AC. + * (see &enum nl80211_wmm_rule) + * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number + * currently defined + * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use + * + * See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122 + * for more information on the FCC description of the relaxations allowed + * by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and + * NL80211_FREQUENCY_ATTR_IR_CONCURRENT. + */ +enum nl80211_frequency_attr { + __NL80211_FREQUENCY_ATTR_INVALID, + NL80211_FREQUENCY_ATTR_FREQ, + NL80211_FREQUENCY_ATTR_DISABLED, + NL80211_FREQUENCY_ATTR_NO_IR, + __NL80211_FREQUENCY_ATTR_NO_IBSS, + NL80211_FREQUENCY_ATTR_RADAR, + NL80211_FREQUENCY_ATTR_MAX_TX_POWER, + NL80211_FREQUENCY_ATTR_DFS_STATE, + NL80211_FREQUENCY_ATTR_DFS_TIME, + NL80211_FREQUENCY_ATTR_NO_HT40_MINUS, + NL80211_FREQUENCY_ATTR_NO_HT40_PLUS, + NL80211_FREQUENCY_ATTR_NO_80MHZ, + NL80211_FREQUENCY_ATTR_NO_160MHZ, + NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, + NL80211_FREQUENCY_ATTR_INDOOR_ONLY, + NL80211_FREQUENCY_ATTR_IR_CONCURRENT, + NL80211_FREQUENCY_ATTR_NO_20MHZ, + NL80211_FREQUENCY_ATTR_NO_10MHZ, + NL80211_FREQUENCY_ATTR_WMM, + + /* keep last */ + __NL80211_FREQUENCY_ATTR_AFTER_LAST, + NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1 +}; + +#define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER +#define NL80211_FREQUENCY_ATTR_PASSIVE_SCAN NL80211_FREQUENCY_ATTR_NO_IR +#define NL80211_FREQUENCY_ATTR_NO_IBSS NL80211_FREQUENCY_ATTR_NO_IR +#define NL80211_FREQUENCY_ATTR_NO_IR NL80211_FREQUENCY_ATTR_NO_IR +#define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \ + NL80211_FREQUENCY_ATTR_IR_CONCURRENT + +/** + * enum nl80211_bitrate_attr - bitrate attributes + * @__NL80211_BITRATE_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps + * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported + * in 2.4 GHz band. + * @NL80211_BITRATE_ATTR_MAX: highest bitrate attribute number + * currently defined + * @__NL80211_BITRATE_ATTR_AFTER_LAST: internal use + */ +enum nl80211_bitrate_attr { + __NL80211_BITRATE_ATTR_INVALID, + NL80211_BITRATE_ATTR_RATE, + NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE, + + /* keep last */ + __NL80211_BITRATE_ATTR_AFTER_LAST, + NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_initiator - Indicates the initiator of a reg domain request + * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world + * regulatory domain. + * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the + * regulatory domain. + * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the + * wireless core it thinks its knows the regulatory domain we should be in. + * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an + * 802.11 country information element with regulatory information it + * thinks we should consider. cfg80211 only processes the country + * code from the IE, and relies on the regulatory domain information + * structure passed by userspace (CRDA) from our wireless-regdb. + * If a channel is enabled but the country code indicates it should + * be disabled we disable the channel and re-enable it upon disassociation. + */ +enum nl80211_reg_initiator { + NL80211_REGDOM_SET_BY_CORE, + NL80211_REGDOM_SET_BY_USER, + NL80211_REGDOM_SET_BY_DRIVER, + NL80211_REGDOM_SET_BY_COUNTRY_IE, +}; + +/** + * enum nl80211_reg_type - specifies the type of regulatory domain + * @NL80211_REGDOM_TYPE_COUNTRY: the regulatory domain set is one that pertains + * to a specific country. When this is set you can count on the + * ISO / IEC 3166 alpha2 country code being valid. + * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory + * domain. + * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom + * driver specific world regulatory domain. These do not apply system-wide + * and are only applicable to the individual devices which have requested + * them to be applied. + * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product + * of an intersection between two regulatory domains -- the previously + * set regulatory domain on the system and the last accepted regulatory + * domain request to be processed. + */ +enum nl80211_reg_type { + NL80211_REGDOM_TYPE_COUNTRY, + NL80211_REGDOM_TYPE_WORLD, + NL80211_REGDOM_TYPE_CUSTOM_WORLD, + NL80211_REGDOM_TYPE_INTERSECTION, +}; + +/** + * enum nl80211_reg_rule_attr - regulatory rule attributes + * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional + * considerations for a given frequency range. These are the + * &enum nl80211_reg_rule_flags. + * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory + * rule in KHz. This is not a center of frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule + * in KHz. This is not a center a frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this + * frequency range, in KHz. + * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain + * for a given frequency range. The value is in mBi (100 * dBi). + * If you don't have one then don't send this. + * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for + * a given frequency range. The value is in mBm (100 * dBm). + * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds. + * If not present or 0 default CAC time will be used. + * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number + * currently defined + * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use + */ +enum nl80211_reg_rule_attr { + __NL80211_REG_RULE_ATTR_INVALID, + NL80211_ATTR_REG_RULE_FLAGS, + + NL80211_ATTR_FREQ_RANGE_START, + NL80211_ATTR_FREQ_RANGE_END, + NL80211_ATTR_FREQ_RANGE_MAX_BW, + + NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, + NL80211_ATTR_POWER_RULE_MAX_EIRP, + + NL80211_ATTR_DFS_CAC_TIME, + + /* keep last */ + __NL80211_REG_RULE_ATTR_AFTER_LAST, + NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sched_scan_match_attr - scheduled scan match attributes + * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, + * only report BSS with matching SSID. + * (This cannot be used together with BSSID.) + * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a + * BSS in scan results. Filtering is turned off if not specified. Note that + * if this attribute is in a match set of its own, then it is treated as + * the default value for all matchsets with an SSID, rather than being a + * matchset of its own without an RSSI filter. This is due to problems with + * how this API was implemented in the past. Also, due to the same problem, + * the only way to create a matchset with only an RSSI filter (with this + * attribute) is if there's only a single matchset with the RSSI attribute. + * @NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI: Flag indicating whether + * %NL80211_SCHED_SCAN_MATCH_ATTR_RSSI to be used as absolute RSSI or + * relative to current bss's RSSI. + * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST: When present the RSSI level for + * BSS-es in the specified band is to be adjusted before doing + * RSSI-based BSS selection. The attribute value is a packed structure + * value as specified by &struct nl80211_bss_select_rssi_adjust. + * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching + * (this cannot be used together with SSID). + * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Nested attribute that carries the + * band specific minimum rssi thresholds for the bands defined in + * enum nl80211_band. The minimum rssi threshold value(s32) specific to a + * band shall be encapsulated in attribute with type value equals to one + * of the NL80211_BAND_* defined in enum nl80211_band. For example, the + * minimum rssi threshold value for 2.4GHZ band shall be encapsulated + * within an attribute of type NL80211_BAND_2GHZ. And one or more of such + * attributes will be nested within this attribute. + * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter + * attribute number currently defined + * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use + */ +enum nl80211_sched_scan_match_attr { + __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID, + + NL80211_SCHED_SCAN_MATCH_ATTR_SSID, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST, + NL80211_SCHED_SCAN_MATCH_ATTR_BSSID, + NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI, + + /* keep last */ + __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, + NL80211_SCHED_SCAN_MATCH_ATTR_MAX = + __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1 +}; + +/* only for backward compatibility */ +#define NL80211_ATTR_SCHED_SCAN_MATCH_SSID NL80211_SCHED_SCAN_MATCH_ATTR_SSID + +/** + * enum nl80211_reg_rule_flags - regulatory rule flags + * + * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed + * @NL80211_RRF_NO_CCK: CCK modulation not allowed + * @NL80211_RRF_NO_INDOOR: indoor operation not allowed + * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed + * @NL80211_RRF_DFS: DFS support is required to be used + * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links + * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links + * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed, + * this includes probe requests or modes of operation that require + * beaconing. + * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated + * base on contiguous rules and wider channels will be allowed to cross + * multiple contiguous/overlapping frequency ranges. + * @NL80211_RRF_IR_CONCURRENT: See %NL80211_FREQUENCY_ATTR_IR_CONCURRENT + * @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation + * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation + * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed + * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed + */ +enum nl80211_reg_rule_flags { + NL80211_RRF_NO_OFDM = 1<<0, + NL80211_RRF_NO_CCK = 1<<1, + NL80211_RRF_NO_INDOOR = 1<<2, + NL80211_RRF_NO_OUTDOOR = 1<<3, + NL80211_RRF_DFS = 1<<4, + NL80211_RRF_PTP_ONLY = 1<<5, + NL80211_RRF_PTMP_ONLY = 1<<6, + NL80211_RRF_NO_IR = 1<<7, + __NL80211_RRF_NO_IBSS = 1<<8, + NL80211_RRF_AUTO_BW = 1<<11, + NL80211_RRF_IR_CONCURRENT = 1<<12, + NL80211_RRF_NO_HT40MINUS = 1<<13, + NL80211_RRF_NO_HT40PLUS = 1<<14, + NL80211_RRF_NO_80MHZ = 1<<15, + NL80211_RRF_NO_160MHZ = 1<<16, +}; + +#define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR +#define NL80211_RRF_NO_IBSS NL80211_RRF_NO_IR +#define NL80211_RRF_NO_IR NL80211_RRF_NO_IR +#define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS |\ + NL80211_RRF_NO_HT40PLUS) +#define NL80211_RRF_GO_CONCURRENT NL80211_RRF_IR_CONCURRENT + +/* For backport compatibility with older userspace */ +#define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS) + +/** + * enum nl80211_dfs_regions - regulatory DFS regions + * + * @NL80211_DFS_UNSET: Country has no DFS master region specified + * @NL80211_DFS_FCC: Country follows DFS master rules from FCC + * @NL80211_DFS_ETSI: Country follows DFS master rules from ETSI + * @NL80211_DFS_JP: Country follows DFS master rules from JP/MKK/Telec + */ +enum nl80211_dfs_regions { + NL80211_DFS_UNSET = 0, + NL80211_DFS_FCC = 1, + NL80211_DFS_ETSI = 2, + NL80211_DFS_JP = 3, +}; + +/** + * enum nl80211_user_reg_hint_type - type of user regulatory hint + * + * @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always + * assumed if the attribute is not set. + * @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular + * base station. Device drivers that have been tested to work + * properly to support this type of hint can enable these hints + * by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature + * capability on the struct wiphy. The wireless core will + * ignore all cell base station hints until at least one device + * present has been registered with the wireless core that + * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a + * supported feature. + * @NL80211_USER_REG_HINT_INDOOR: a user sent an hint indicating that the + * platform is operating in an indoor environment. + */ +enum nl80211_user_reg_hint_type { + NL80211_USER_REG_HINT_USER = 0, + NL80211_USER_REG_HINT_CELL_BASE = 1, + NL80211_USER_REG_HINT_INDOOR = 2, +}; + +/** + * enum nl80211_survey_info - survey information + * + * These attribute types are used with %NL80211_ATTR_SURVEY_INFO + * when getting information about a survey. + * + * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved + * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel + * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) + * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used + * @NL80211_SURVEY_INFO_TIME: amount of time (in ms) that the radio + * was turned on (on channel or globally) + * @NL80211_SURVEY_INFO_TIME_BUSY: amount of the time the primary + * channel was sensed busy (either due to activity or energy detect) + * @NL80211_SURVEY_INFO_TIME_EXT_BUSY: amount of time the extension + * channel was sensed busy + * @NL80211_SURVEY_INFO_TIME_RX: amount of time the radio spent + * receiving data (on channel or globally) + * @NL80211_SURVEY_INFO_TIME_TX: amount of time the radio spent + * transmitting data (on channel or globally) + * @NL80211_SURVEY_INFO_TIME_SCAN: time the radio spent for scan + * (on this channel or globally) + * @NL80211_SURVEY_INFO_PAD: attribute used for padding for 64-bit alignment + * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number + * currently defined + * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use + */ +enum nl80211_survey_info { + __NL80211_SURVEY_INFO_INVALID, + NL80211_SURVEY_INFO_FREQUENCY, + NL80211_SURVEY_INFO_NOISE, + NL80211_SURVEY_INFO_IN_USE, + NL80211_SURVEY_INFO_TIME, + NL80211_SURVEY_INFO_TIME_BUSY, + NL80211_SURVEY_INFO_TIME_EXT_BUSY, + NL80211_SURVEY_INFO_TIME_RX, + NL80211_SURVEY_INFO_TIME_TX, + NL80211_SURVEY_INFO_TIME_SCAN, + NL80211_SURVEY_INFO_PAD, + + /* keep last */ + __NL80211_SURVEY_INFO_AFTER_LAST, + NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1 +}; + +/* keep old names for compatibility */ +#define NL80211_SURVEY_INFO_CHANNEL_TIME NL80211_SURVEY_INFO_TIME +#define NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY NL80211_SURVEY_INFO_TIME_BUSY +#define NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY NL80211_SURVEY_INFO_TIME_EXT_BUSY +#define NL80211_SURVEY_INFO_CHANNEL_TIME_RX NL80211_SURVEY_INFO_TIME_RX +#define NL80211_SURVEY_INFO_CHANNEL_TIME_TX NL80211_SURVEY_INFO_TIME_TX + +/** + * enum nl80211_mntr_flags - monitor configuration flags + * + * Monitor configuration flags. + * + * @__NL80211_MNTR_FLAG_INVALID: reserved + * + * @NL80211_MNTR_FLAG_FCSFAIL: pass frames with bad FCS + * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP + * @NL80211_MNTR_FLAG_CONTROL: pass control frames + * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering + * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing. + * overrides all other flags. + * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address + * and ACK incoming unicast packets. + * + * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use + * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag + */ +enum nl80211_mntr_flags { + __NL80211_MNTR_FLAG_INVALID, + NL80211_MNTR_FLAG_FCSFAIL, + NL80211_MNTR_FLAG_PLCPFAIL, + NL80211_MNTR_FLAG_CONTROL, + NL80211_MNTR_FLAG_OTHER_BSS, + NL80211_MNTR_FLAG_COOK_FRAMES, + NL80211_MNTR_FLAG_ACTIVE, + + /* keep last */ + __NL80211_MNTR_FLAG_AFTER_LAST, + NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mesh_power_mode - mesh power save modes + * + * @NL80211_MESH_POWER_UNKNOWN: The mesh power mode of the mesh STA is + * not known or has not been set yet. + * @NL80211_MESH_POWER_ACTIVE: Active mesh power mode. The mesh STA is + * in Awake state all the time. + * @NL80211_MESH_POWER_LIGHT_SLEEP: Light sleep mode. The mesh STA will + * alternate between Active and Doze states, but will wake up for + * neighbor's beacons. + * @NL80211_MESH_POWER_DEEP_SLEEP: Deep sleep mode. The mesh STA will + * alternate between Active and Doze states, but may not wake up + * for neighbor's beacons. + * + * @__NL80211_MESH_POWER_AFTER_LAST - internal use + * @NL80211_MESH_POWER_MAX - highest possible power save level + */ + +enum nl80211_mesh_power_mode { + NL80211_MESH_POWER_UNKNOWN, + NL80211_MESH_POWER_ACTIVE, + NL80211_MESH_POWER_LIGHT_SLEEP, + NL80211_MESH_POWER_DEEP_SLEEP, + + __NL80211_MESH_POWER_AFTER_LAST, + NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1 +}; + +/** + * enum nl80211_meshconf_params - mesh configuration parameters + * + * Mesh configuration parameters. These can be changed while the mesh is + * active. + * + * @__NL80211_MESHCONF_INVALID: internal use + * + * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in + * millisecond units, used by the Peer Link Open message + * + * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in + * millisecond units, used by the peer link management to close a peer link + * + * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in + * millisecond units + * + * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed + * on this mesh interface + * + * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link + * open retries that can be sent to establish a new peer link instance in a + * mesh + * + * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh + * point. + * + * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically open + * peer links when we detect compatible mesh peers. Disabled if + * @NL80211_MESH_SETUP_USERSPACE_MPM or @NL80211_MESH_SETUP_USERSPACE_AMPE are + * set. + * + * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames + * containing a PREQ that an MP can send to a particular destination (path + * target) + * + * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths + * (in milliseconds) + * + * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait + * until giving up on a path discovery (in milliseconds) + * + * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh + * points receiving a PREQ shall consider the forwarding information from + * the root to be valid. (TU = time unit) + * + * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in + * TUs) during which an MP can send only one action frame containing a PREQ + * reference element + * + * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs) + * that it takes for an HWMP information element to propagate across the + * mesh + * + * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not + * + * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a + * source mesh point for path selection elements. + * + * @NL80211_MESHCONF_HWMP_RANN_INTERVAL: The interval of time (in TUs) between + * root announcements are transmitted. + * + * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has + * access to a broader network beyond the MBSS. This is done via Root + * Announcement frames. + * + * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in + * TUs) during which a mesh STA can send only one Action frame containing a + * PERR element. + * + * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding + * or forwarding entity (default is TRUE - forwarding entity) + * + * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the + * threshold for average signal strength of candidate station to establish + * a peer link. + * + * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors + * to synchronize to for 11s default synchronization method + * (see 11C.12.2.2) + * + * @NL80211_MESHCONF_HT_OPMODE: set mesh HT protection mode. + * + * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute + * + * @NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: The time (in TUs) for + * which mesh STAs receiving a proactive PREQ shall consider the forwarding + * information to the root mesh STA to be valid. + * + * @NL80211_MESHCONF_HWMP_ROOT_INTERVAL: The interval of time (in TUs) between + * proactive PREQs are transmitted. + * + * @NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: The minimum interval of time + * (in TUs) during which a mesh STA can send only one Action frame + * containing a PREQ element for root path confirmation. + * + * @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links. + * type &enum nl80211_mesh_power_mode (u32) + * + * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs) + * + * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've + * established peering with for longer than this time (in seconds), then + * remove it from the STA's list of peers. You may set this to 0 to disable + * the removal of the STA. Default is 30 minutes. + * + * @NL80211_MESHCONF_CONNECTED_TO_GATE: If set to true then this mesh STA + * will advertise that it is connected to a gate in the mesh formation + * field. If left unset then the mesh formation field will only + * advertise such if there is an active root mesh path. + * + * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use + */ +enum nl80211_meshconf_params { + __NL80211_MESHCONF_INVALID, + NL80211_MESHCONF_RETRY_TIMEOUT, + NL80211_MESHCONF_CONFIRM_TIMEOUT, + NL80211_MESHCONF_HOLDING_TIMEOUT, + NL80211_MESHCONF_MAX_PEER_LINKS, + NL80211_MESHCONF_MAX_RETRIES, + NL80211_MESHCONF_TTL, + NL80211_MESHCONF_AUTO_OPEN_PLINKS, + NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, + NL80211_MESHCONF_PATH_REFRESH_TIME, + NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, + NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, + NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, + NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, + NL80211_MESHCONF_HWMP_ROOTMODE, + NL80211_MESHCONF_ELEMENT_TTL, + NL80211_MESHCONF_HWMP_RANN_INTERVAL, + NL80211_MESHCONF_GATE_ANNOUNCEMENTS, + NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, + NL80211_MESHCONF_FORWARDING, + NL80211_MESHCONF_RSSI_THRESHOLD, + NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, + NL80211_MESHCONF_HT_OPMODE, + NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, + NL80211_MESHCONF_HWMP_ROOT_INTERVAL, + NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, + NL80211_MESHCONF_POWER_MODE, + NL80211_MESHCONF_AWAKE_WINDOW, + NL80211_MESHCONF_PLINK_TIMEOUT, + NL80211_MESHCONF_CONNECTED_TO_GATE, + + /* keep last */ + __NL80211_MESHCONF_ATTR_AFTER_LAST, + NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mesh_setup_params - mesh setup parameters + * + * Mesh setup parameters. These are used to start/join a mesh and cannot be + * changed while the mesh is active. + * + * @__NL80211_MESH_SETUP_INVALID: Internal use + * + * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a + * vendor specific path selection algorithm or disable it to use the + * default HWMP. + * + * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a + * vendor specific path metric or disable it to use the default Airtime + * metric. + * + * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a + * robust security network ie, or a vendor specific information element + * that vendors will use to identify the path selection methods and + * metrics in use. + * + * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication + * daemon will be authenticating mesh candidates. + * + * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication + * daemon will be securing peer link frames. AMPE is a secured version of + * Mesh Peering Management (MPM) and is implemented with the assistance of + * a userspace daemon. When this flag is set, the kernel will send peer + * management frames to a userspace daemon that will implement AMPE + * functionality (security capabilities selection, key confirmation, and + * key management). When the flag is unset (default), the kernel can + * autonomously complete (unsecured) mesh peering without the need of a + * userspace daemon. + * + * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a + * vendor specific synchronization method or disable it to use the default + * neighbor offset synchronization + * + * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will + * implement an MPM which handles peer allocation and state. + * + * @NL80211_MESH_SETUP_AUTH_PROTOCOL: Inform the kernel of the authentication + * method (u8, as defined in IEEE 8.4.2.100.6, e.g. 0x1 for SAE). + * Default is no authentication method required. + * + * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number + * + * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use + */ +enum nl80211_mesh_setup_params { + __NL80211_MESH_SETUP_INVALID, + NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL, + NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC, + NL80211_MESH_SETUP_IE, + NL80211_MESH_SETUP_USERSPACE_AUTH, + NL80211_MESH_SETUP_USERSPACE_AMPE, + NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, + NL80211_MESH_SETUP_USERSPACE_MPM, + NL80211_MESH_SETUP_AUTH_PROTOCOL, + + /* keep last */ + __NL80211_MESH_SETUP_ATTR_AFTER_LAST, + NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_txq_attr - TX queue parameter attributes + * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved + * @NL80211_TXQ_ATTR_AC: AC identifier (NL80211_AC_*) + * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning + * disabled + * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255] + * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal + * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number + */ +enum nl80211_txq_attr { + __NL80211_TXQ_ATTR_INVALID, + NL80211_TXQ_ATTR_AC, + NL80211_TXQ_ATTR_TXOP, + NL80211_TXQ_ATTR_CWMIN, + NL80211_TXQ_ATTR_CWMAX, + NL80211_TXQ_ATTR_AIFS, + + /* keep last */ + __NL80211_TXQ_ATTR_AFTER_LAST, + NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1 +}; + +enum nl80211_ac { + NL80211_AC_VO, + NL80211_AC_VI, + NL80211_AC_BE, + NL80211_AC_BK, + NL80211_NUM_ACS +}; + +/* backward compat */ +#define NL80211_TXQ_ATTR_QUEUE NL80211_TXQ_ATTR_AC +#define NL80211_TXQ_Q_VO NL80211_AC_VO +#define NL80211_TXQ_Q_VI NL80211_AC_VI +#define NL80211_TXQ_Q_BE NL80211_AC_BE +#define NL80211_TXQ_Q_BK NL80211_AC_BK + +/** + * enum nl80211_channel_type - channel type + * @NL80211_CHAN_NO_HT: 20 MHz, non-HT channel + * @NL80211_CHAN_HT20: 20 MHz HT channel + * @NL80211_CHAN_HT40MINUS: HT40 channel, secondary channel + * below the control channel + * @NL80211_CHAN_HT40PLUS: HT40 channel, secondary channel + * above the control channel + */ +enum nl80211_channel_type { + NL80211_CHAN_NO_HT, + NL80211_CHAN_HT20, + NL80211_CHAN_HT40MINUS, + NL80211_CHAN_HT40PLUS +}; + +/** + * enum nl80211_key_mode - Key mode + * + * @NL80211_KEY_RX_TX: (Default) + * Key can be used for Rx and Tx immediately + * + * The following modes can only be selected for unicast keys and when the + * driver supports @NL80211_EXT_FEATURE_EXT_KEY_ID: + * + * @NL80211_KEY_NO_TX: Only allowed in combination with @NL80211_CMD_NEW_KEY: + * Unicast key can only be used for Rx, Tx not allowed, yet + * @NL80211_KEY_SET_TX: Only allowed in combination with @NL80211_CMD_SET_KEY: + * The unicast key identified by idx and mac is cleared for Tx and becomes + * the preferred Tx key for the station. + */ +enum nl80211_key_mode { + NL80211_KEY_RX_TX, + NL80211_KEY_NO_TX, + NL80211_KEY_SET_TX +}; + +/** + * enum nl80211_chan_width - channel width definitions + * + * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH + * attribute. + * + * @NL80211_CHAN_WIDTH_20_NOHT: 20 MHz, non-HT channel + * @NL80211_CHAN_WIDTH_20: 20 MHz HT channel + * @NL80211_CHAN_WIDTH_40: 40 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_80: 80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_80P80: 80+80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well + * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_5: 5 MHz OFDM channel + * @NL80211_CHAN_WIDTH_10: 10 MHz OFDM channel + */ +enum nl80211_chan_width { + NL80211_CHAN_WIDTH_20_NOHT, + NL80211_CHAN_WIDTH_20, + NL80211_CHAN_WIDTH_40, + NL80211_CHAN_WIDTH_80, + NL80211_CHAN_WIDTH_80P80, + NL80211_CHAN_WIDTH_160, + NL80211_CHAN_WIDTH_5, + NL80211_CHAN_WIDTH_10, +}; + +/** + * enum nl80211_bss_scan_width - control channel width for a BSS + * + * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute. + * + * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible + * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide + * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide + */ +enum nl80211_bss_scan_width { + NL80211_BSS_CHAN_WIDTH_20, + NL80211_BSS_CHAN_WIDTH_10, + NL80211_BSS_CHAN_WIDTH_5, +}; + +/** + * enum nl80211_bss - netlink attributes for a BSS + * + * @__NL80211_BSS_INVALID: invalid + * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets) + * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) + * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) + * (if @NL80211_BSS_PRESP_DATA is present then this is known to be + * from a probe response, otherwise it may be from the same beacon + * that the NL80211_BSS_BEACON_TSF will be from) + * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) + * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) + * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the + * raw information elements from the probe response/beacon (bin); + * if the %NL80211_BSS_BEACON_IES attribute is present and the data is + * different then the IEs here are from a Probe Response frame; otherwise + * they are from a Beacon frame. + * However, if the driver does not indicate the source of the IEs, these + * IEs may be from either frame subtype. + * If present, the @NL80211_BSS_PRESP_DATA attribute indicates that the + * data here is known to be from a probe response, without any heuristics. + * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon + * in mBm (100 * dBm) (s32) + * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon + * in unspecified units, scaled to 0..100 (u8) + * @NL80211_BSS_STATUS: status, if this BSS is "used" + * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms + * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information + * elements from a Beacon frame (bin); not present if no Beacon frame has + * yet been received + * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel + * (u32, enum nl80211_bss_scan_width) + * @NL80211_BSS_BEACON_TSF: TSF of the last received beacon (u64) + * (not present if no beacon frame has been received yet) + * @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and + * @NL80211_BSS_TSF is known to be from a probe response (flag attribute) + * @NL80211_BSS_LAST_SEEN_BOOTTIME: CLOCK_BOOTTIME timestamp when this entry + * was last updated by a received frame. The value is expected to be + * accurate to about 10ms. (u64, nanoseconds) + * @NL80211_BSS_PAD: attribute used for padding for 64-bit alignment + * @NL80211_BSS_PARENT_TSF: the time at the start of reception of the first + * octet of the timestamp field of the last beacon/probe received for + * this BSS. The time is the TSF of the BSS specified by + * @NL80211_BSS_PARENT_BSSID. (u64). + * @NL80211_BSS_PARENT_BSSID: the BSS according to which @NL80211_BSS_PARENT_TSF + * is set. + * @NL80211_BSS_CHAIN_SIGNAL: per-chain signal strength of last BSS update. + * Contains a nested array of signal strength attributes (u8, dBm), + * using the nesting index as the antenna number. + * @__NL80211_BSS_AFTER_LAST: internal + * @NL80211_BSS_MAX: highest BSS attribute + */ +enum nl80211_bss { + __NL80211_BSS_INVALID, + NL80211_BSS_BSSID, + NL80211_BSS_FREQUENCY, + NL80211_BSS_TSF, + NL80211_BSS_BEACON_INTERVAL, + NL80211_BSS_CAPABILITY, + NL80211_BSS_INFORMATION_ELEMENTS, + NL80211_BSS_SIGNAL_MBM, + NL80211_BSS_SIGNAL_UNSPEC, + NL80211_BSS_STATUS, + NL80211_BSS_SEEN_MS_AGO, + NL80211_BSS_BEACON_IES, + NL80211_BSS_CHAN_WIDTH, + NL80211_BSS_BEACON_TSF, + NL80211_BSS_PRESP_DATA, + NL80211_BSS_LAST_SEEN_BOOTTIME, + NL80211_BSS_PAD, + NL80211_BSS_PARENT_TSF, + NL80211_BSS_PARENT_BSSID, + NL80211_BSS_CHAIN_SIGNAL, + + /* keep last */ + __NL80211_BSS_AFTER_LAST, + NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 +}; + +/** + * enum nl80211_bss_status - BSS "status" + * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS. + * Note that this is no longer used since cfg80211 no longer + * keeps track of whether or not authentication was done with + * a given BSS. + * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS. + * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS. + * + * The BSS status is a BSS attribute in scan dumps, which + * indicates the status the interface has wrt. this BSS. + */ +enum nl80211_bss_status { + NL80211_BSS_STATUS_AUTHENTICATED, + NL80211_BSS_STATUS_ASSOCIATED, + NL80211_BSS_STATUS_IBSS_JOINED, +}; + +/** + * enum nl80211_auth_type - AuthenticationType + * + * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication + * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only) + * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) + * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) + * @NL80211_AUTHTYPE_SAE: Simultaneous authentication of equals + * @NL80211_AUTHTYPE_FILS_SK: Fast Initial Link Setup shared key + * @NL80211_AUTHTYPE_FILS_SK_PFS: Fast Initial Link Setup shared key with PFS + * @NL80211_AUTHTYPE_FILS_PK: Fast Initial Link Setup public key + * @__NL80211_AUTHTYPE_NUM: internal + * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm + * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by + * trying multiple times); this is invalid in netlink -- leave out + * the attribute for this on CONNECT commands. + */ +enum nl80211_auth_type { + NL80211_AUTHTYPE_OPEN_SYSTEM, + NL80211_AUTHTYPE_SHARED_KEY, + NL80211_AUTHTYPE_FT, + NL80211_AUTHTYPE_NETWORK_EAP, + NL80211_AUTHTYPE_SAE, + NL80211_AUTHTYPE_FILS_SK, + NL80211_AUTHTYPE_FILS_SK_PFS, + NL80211_AUTHTYPE_FILS_PK, + + /* keep last */ + __NL80211_AUTHTYPE_NUM, + NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1, + NL80211_AUTHTYPE_AUTOMATIC +}; + +/** + * enum nl80211_key_type - Key Type + * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key + * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key + * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS) + * @NUM_NL80211_KEYTYPES: number of defined key types + */ +enum nl80211_key_type { + NL80211_KEYTYPE_GROUP, + NL80211_KEYTYPE_PAIRWISE, + NL80211_KEYTYPE_PEERKEY, + + NUM_NL80211_KEYTYPES +}; + +/** + * enum nl80211_mfp - Management frame protection state + * @NL80211_MFP_NO: Management frame protection not used + * @NL80211_MFP_REQUIRED: Management frame protection required + * @NL80211_MFP_OPTIONAL: Management frame protection is optional + */ +enum nl80211_mfp { + NL80211_MFP_NO, + NL80211_MFP_REQUIRED, + NL80211_MFP_OPTIONAL, +}; + +enum nl80211_wpa_versions { + NL80211_WPA_VERSION_1 = 1 << 0, + NL80211_WPA_VERSION_2 = 1 << 1, +}; + +/** + * enum nl80211_key_default_types - key default types + * @__NL80211_KEY_DEFAULT_TYPE_INVALID: invalid + * @NL80211_KEY_DEFAULT_TYPE_UNICAST: key should be used as default + * unicast key + * @NL80211_KEY_DEFAULT_TYPE_MULTICAST: key should be used as default + * multicast key + * @NUM_NL80211_KEY_DEFAULT_TYPES: number of default types + */ +enum nl80211_key_default_types { + __NL80211_KEY_DEFAULT_TYPE_INVALID, + NL80211_KEY_DEFAULT_TYPE_UNICAST, + NL80211_KEY_DEFAULT_TYPE_MULTICAST, + + NUM_NL80211_KEY_DEFAULT_TYPES +}; + +/** + * enum nl80211_key_attributes - key attributes + * @__NL80211_KEY_INVALID: invalid + * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of + * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC + * keys + * @NL80211_KEY_IDX: key ID (u8, 0-3) + * @NL80211_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 + * section 7.3.2.25.1, e.g. 0x000FAC04) + * @NL80211_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and + * CCMP keys, each six bytes in little endian + * @NL80211_KEY_DEFAULT: flag indicating default key + * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key + * @NL80211_KEY_TYPE: the key type from enum nl80211_key_type, if not + * specified the default depends on whether a MAC address was + * given with the command using the key or not (u32) + * @NL80211_KEY_DEFAULT_TYPES: A nested attribute containing flags + * attributes, specifying what a key should be set as default as. + * See &enum nl80211_key_default_types. + * @NL80211_KEY_MODE: the mode from enum nl80211_key_mode. + * Defaults to @NL80211_KEY_RX_TX. + * + * @__NL80211_KEY_AFTER_LAST: internal + * @NL80211_KEY_MAX: highest key attribute + */ +enum nl80211_key_attributes { + __NL80211_KEY_INVALID, + NL80211_KEY_DATA, + NL80211_KEY_IDX, + NL80211_KEY_CIPHER, + NL80211_KEY_SEQ, + NL80211_KEY_DEFAULT, + NL80211_KEY_DEFAULT_MGMT, + NL80211_KEY_TYPE, + NL80211_KEY_DEFAULT_TYPES, + NL80211_KEY_MODE, + + /* keep last */ + __NL80211_KEY_AFTER_LAST, + NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1 +}; + +/** + * enum nl80211_tx_rate_attributes - TX rate set attributes + * @__NL80211_TXRATE_INVALID: invalid + * @NL80211_TXRATE_LEGACY: Legacy (non-MCS) rates allowed for TX rate selection + * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with + * 1 = 500 kbps) but without the IE length restriction (at most + * %NL80211_MAX_SUPP_RATES in a single array). + * @NL80211_TXRATE_HT: HT (MCS) rates allowed for TX rate selection + * in an array of MCS numbers. + * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection, + * see &struct nl80211_txrate_vht + * @NL80211_TXRATE_GI: configure GI, see &enum nl80211_txrate_gi + * @__NL80211_TXRATE_AFTER_LAST: internal + * @NL80211_TXRATE_MAX: highest TX rate attribute + */ +enum nl80211_tx_rate_attributes { + __NL80211_TXRATE_INVALID, + NL80211_TXRATE_LEGACY, + NL80211_TXRATE_HT, + NL80211_TXRATE_VHT, + NL80211_TXRATE_GI, + + /* keep last */ + __NL80211_TXRATE_AFTER_LAST, + NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 +}; + +#define NL80211_TXRATE_MCS NL80211_TXRATE_HT +#define NL80211_VHT_NSS_MAX 8 + +/** + * struct nl80211_txrate_vht - VHT MCS/NSS txrate bitmap + * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.) + */ +struct nl80211_txrate_vht { + __u16 mcs[NL80211_VHT_NSS_MAX]; +}; + +enum nl80211_txrate_gi { + NL80211_TXRATE_DEFAULT_GI, + NL80211_TXRATE_FORCE_SGI, + NL80211_TXRATE_FORCE_LGI, +}; + +/** + * enum nl80211_band - Frequency band + * @NL80211_BAND_2GHZ: 2.4 GHz ISM band + * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) + * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz) + * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace + * since newer kernel versions may support more bands + */ +enum nl80211_band { + NL80211_BAND_2GHZ, + NL80211_BAND_5GHZ, + NL80211_BAND_60GHZ, + + NUM_NL80211_BANDS, +}; + +/** + * enum nl80211_ps_state - powersave state + * @NL80211_PS_DISABLED: powersave is disabled + * @NL80211_PS_ENABLED: powersave is enabled + */ +enum nl80211_ps_state { + NL80211_PS_DISABLED, + NL80211_PS_ENABLED, +}; + +/** + * enum nl80211_attr_cqm - connection quality monitor attributes + * @__NL80211_ATTR_CQM_INVALID: invalid + * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies + * the threshold for the RSSI level at which an event will be sent. Zero + * to disable. Alternatively, if %NL80211_EXT_FEATURE_CQM_RSSI_LIST is + * set, multiple values can be supplied as a low-to-high sorted array of + * threshold values in dBm. Events will be sent when the RSSI value + * crosses any of the thresholds. + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies + * the minimum amount the RSSI level must change after an event before a + * new event may be issued (to reduce effects of RSSI oscillation). + * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event + * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many + * consecutive packets were not acknowledged by the peer + * @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures + * during the given %NL80211_ATTR_CQM_TXE_INTVL before an + * %NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and + * %NL80211_ATTR_CQM_TXE_PKTS is generated. + * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given + * %NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is + * checked. + * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic + * interval in which %NL80211_ATTR_CQM_TXE_PKTS and + * %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an + * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting. + * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon + * loss event + * @NL80211_ATTR_CQM_RSSI_LEVEL: the RSSI value in dBm that triggered the + * RSSI threshold event. + * @__NL80211_ATTR_CQM_AFTER_LAST: internal + * @NL80211_ATTR_CQM_MAX: highest key attribute + */ +enum nl80211_attr_cqm { + __NL80211_ATTR_CQM_INVALID, + NL80211_ATTR_CQM_RSSI_THOLD, + NL80211_ATTR_CQM_RSSI_HYST, + NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + NL80211_ATTR_CQM_PKT_LOSS_EVENT, + NL80211_ATTR_CQM_TXE_RATE, + NL80211_ATTR_CQM_TXE_PKTS, + NL80211_ATTR_CQM_TXE_INTVL, + NL80211_ATTR_CQM_BEACON_LOSS_EVENT, + NL80211_ATTR_CQM_RSSI_LEVEL, + + /* keep last */ + __NL80211_ATTR_CQM_AFTER_LAST, + NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1 +}; + +/** + * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: The RSSI level is lower than the + * configured threshold + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the + * configured threshold + * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: (reserved, never sent) + */ +enum nl80211_cqm_rssi_threshold_event { + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + NL80211_CQM_RSSI_BEACON_LOSS_EVENT, +}; + + +/** + * enum nl80211_tx_power_setting - TX power adjustment + * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power + * @NL80211_TX_POWER_LIMITED: limit TX power by the mBm parameter + * @NL80211_TX_POWER_FIXED: fix TX power to the mBm parameter + */ +enum nl80211_tx_power_setting { + NL80211_TX_POWER_AUTOMATIC, + NL80211_TX_POWER_LIMITED, + NL80211_TX_POWER_FIXED, +}; + +/** + * enum nl80211_packet_pattern_attr - packet pattern attribute + * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute + * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has + * a zero bit are ignored + * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have + * a bit for each byte in the pattern. The lowest-order bit corresponds + * to the first byte of the pattern, but the bytes of the pattern are + * in a little-endian-like format, i.e. the 9th byte of the pattern + * corresponds to the lowest-order bit in the second byte of the mask. + * For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where + * xx indicates "don't care") would be represented by a pattern of + * twelve zero bytes, and a mask of "0xed,0x01". + * Note that the pattern matching is done as though frames were not + * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked + * first (including SNAP header unpacking) and then matched. + * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after + * these fixed number of bytes of received packet + * @NUM_NL80211_PKTPAT: number of attributes + * @MAX_NL80211_PKTPAT: max attribute number + */ +enum nl80211_packet_pattern_attr { + __NL80211_PKTPAT_INVALID, + NL80211_PKTPAT_MASK, + NL80211_PKTPAT_PATTERN, + NL80211_PKTPAT_OFFSET, + + NUM_NL80211_PKTPAT, + MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1, +}; + +/** + * struct nl80211_pattern_support - packet pattern support information + * @max_patterns: maximum number of patterns supported + * @min_pattern_len: minimum length of each pattern + * @max_pattern_len: maximum length of each pattern + * @max_pkt_offset: maximum Rx packet offset + * + * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when + * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in + * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of + * %NL80211_ATTR_COALESCE_RULE in the capability information given + * by the kernel to userspace. + */ +struct nl80211_pattern_support { + __u32 max_patterns; + __u32 min_pattern_len; + __u32 max_pattern_len; + __u32 max_pkt_offset; +} __attribute__((packed)); + +/* only for backward compatibility */ +#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID +#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK +#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN +#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET +#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT +#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT +#define nl80211_wowlan_pattern_support nl80211_pattern_support + +/** + * enum nl80211_wowlan_triggers - WoWLAN trigger definitions + * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes + * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put + * the chip into a special state -- works best with chips that have + * support for low-power operation already (flag) + * Note that this mode is incompatible with all of the others, if + * any others are even supported by the device. + * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect + * is detected is implementation-specific (flag) + * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed + * by 16 repetitions of MAC addr, anywhere in payload) (flag) + * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns + * which are passed in an array of nested attributes, each nested attribute + * defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern. + * Each pattern defines a wakeup packet. Packet offset is associated with + * each pattern which is used while matching the pattern. The matching is + * done on the MSDU, i.e. as though the packet was an 802.3 packet, so the + * pattern matching is done after the packet is converted to the MSDU. + * + * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute + * carrying a &struct nl80211_pattern_support. + * + * When reporting wakeup. it is a u32 attribute containing the 0-based + * index of the pattern that caused the wakeup, in the patterns passed + * to the kernel when configuring. + * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be + * used when setting, used only to indicate that GTK rekeying is supported + * by the device (flag) + * @NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: wake up on GTK rekey failure (if + * done by the device) (flag) + * @NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: wake up on EAP Identity Request + * packet (flag) + * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag) + * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released + * (on devices that have rfkill in the device) (flag) + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: For wakeup reporting only, contains + * the 802.11 packet that caused the wakeup, e.g. a deauth frame. The frame + * may be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN + * attribute contains the original length. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: Original length of the 802.11 + * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211 + * attribute if the packet was truncated somewhere. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: For wakeup reporting only, contains the + * 802.11 packet that caused the wakeup, e.g. a magic packet. The frame may + * be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN attribute + * contains the original length. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: Original length of the 802.3 + * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023 + * attribute if the packet was truncated somewhere. + * @NL80211_WOWLAN_TRIG_TCP_CONNECTION: TCP connection wake, see DOC section + * "TCP connection wakeup" for more details. This is a nested attribute + * containing the exact information for establishing and keeping alive + * the TCP connection. + * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the + * wakeup packet was received on the TCP connection + * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the + * TCP connection was lost or failed to be established + * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only, + * the TCP connection ran out of tokens to use for data to send to the + * service + * @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network + * is detected. This is a nested attribute that contains the + * same attributes used with @NL80211_CMD_START_SCHED_SCAN. It + * specifies how the scan is performed (e.g. the interval, the + * channels to scan and the initial delay) as well as the scan + * results that will trigger a wake (i.e. the matchsets). This + * attribute is also sent in a response to + * @NL80211_CMD_GET_WIPHY, indicating the number of match sets + * supported by the driver (u32). + * @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute + * containing an array with information about what triggered the + * wake up. If no elements are present in the array, it means + * that the information is not available. If more than one + * element is present, it means that more than one match + * occurred. + * Each element in the array is a nested attribute that contains + * one optional %NL80211_ATTR_SSID attribute and one optional + * %NL80211_ATTR_SCAN_FREQUENCIES attribute. At least one of + * these attributes must be present. If + * %NL80211_ATTR_SCAN_FREQUENCIES contains more than one + * frequency, it means that the match occurred in more than one + * channel. + * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers + * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number + * + * These nested attributes are used to configure the wakeup triggers and + * to report the wakeup reason(s). + */ +enum nl80211_wowlan_triggers { + __NL80211_WOWLAN_TRIG_INVALID, + NL80211_WOWLAN_TRIG_ANY, + NL80211_WOWLAN_TRIG_DISCONNECT, + NL80211_WOWLAN_TRIG_MAGIC_PKT, + NL80211_WOWLAN_TRIG_PKT_PATTERN, + NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED, + NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE, + NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, + NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, + NL80211_WOWLAN_TRIG_RFKILL_RELEASE, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN, + NL80211_WOWLAN_TRIG_TCP_CONNECTION, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS, + NL80211_WOWLAN_TRIG_NET_DETECT, + NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS, + + /* keep last */ + NUM_NL80211_WOWLAN_TRIG, + MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1 +}; + +/** + * DOC: TCP connection wakeup + * + * Some devices can establish a TCP connection in order to be woken up by a + * packet coming in from outside their network segment, or behind NAT. If + * configured, the device will establish a TCP connection to the given + * service, and periodically send data to that service. The first data + * packet is usually transmitted after SYN/ACK, also ACKing the SYN/ACK. + * The data packets can optionally include a (little endian) sequence + * number (in the TCP payload!) that is generated by the device, and, also + * optionally, a token from a list of tokens. This serves as a keep-alive + * with the service, and for NATed connections, etc. + * + * During this keep-alive period, the server doesn't send any data to the + * client. When receiving data, it is compared against the wakeup pattern + * (and mask) and if it matches, the host is woken up. Similarly, if the + * connection breaks or cannot be established to start with, the host is + * also woken up. + * + * Developer's note: ARP offload is required for this, otherwise TCP + * response packets might not go through correctly. + */ + +/** + * struct nl80211_wowlan_tcp_data_seq - WoWLAN TCP data sequence + * @start: starting value + * @offset: offset of sequence number in packet + * @len: length of the sequence value to write, 1 through 4 + * + * Note: don't confuse with the TCP sequence number(s), this is for the + * keepalive packet payload. The actual value is written into the packet + * in little endian. + */ +struct nl80211_wowlan_tcp_data_seq { + __u32 start, offset, len; +}; + +/** + * struct nl80211_wowlan_tcp_data_token - WoWLAN TCP data token config + * @offset: offset of token in packet + * @len: length of each token + * @token_stream: stream of data to be used for the tokens, the length must + * be a multiple of @len for this to make sense + */ +struct nl80211_wowlan_tcp_data_token { + __u32 offset, len; + __u8 token_stream[]; +}; + +/** + * struct nl80211_wowlan_tcp_data_token_feature - data token features + * @min_len: minimum token length + * @max_len: maximum token length + * @bufsize: total available token buffer size (max size of @token_stream) + */ +struct nl80211_wowlan_tcp_data_token_feature { + __u32 min_len, max_len, bufsize; +}; + +/** + * enum nl80211_wowlan_tcp_attrs - WoWLAN TCP connection parameters + * @__NL80211_WOWLAN_TCP_INVALID: invalid number for nested attributes + * @NL80211_WOWLAN_TCP_SRC_IPV4: source IPv4 address (in network byte order) + * @NL80211_WOWLAN_TCP_DST_IPV4: destination IPv4 address + * (in network byte order) + * @NL80211_WOWLAN_TCP_DST_MAC: destination MAC address, this is given because + * route lookup when configured might be invalid by the time we suspend, + * and doing a route lookup when suspending is no longer possible as it + * might require ARP querying. + * @NL80211_WOWLAN_TCP_SRC_PORT: source port (u16); optional, if not given a + * socket and port will be allocated + * @NL80211_WOWLAN_TCP_DST_PORT: destination port (u16) + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD: data packet payload, at least one byte. + * For feature advertising, a u32 attribute holding the maximum length + * of the data payload. + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ: data packet sequence configuration + * (if desired), a &struct nl80211_wowlan_tcp_data_seq. For feature + * advertising it is just a flag + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN: data packet token configuration, + * see &struct nl80211_wowlan_tcp_data_token and for advertising see + * &struct nl80211_wowlan_tcp_data_token_feature. + * @NL80211_WOWLAN_TCP_DATA_INTERVAL: data interval in seconds, maximum + * interval in feature advertising (u32) + * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a + * u32 attribute holding the maximum length + * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for + * feature advertising. The mask works like @NL80211_PKTPAT_MASK + * but on the TCP payload only. + * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes + * @MAX_NL80211_WOWLAN_TCP: highest attribute number + */ +enum nl80211_wowlan_tcp_attrs { + __NL80211_WOWLAN_TCP_INVALID, + NL80211_WOWLAN_TCP_SRC_IPV4, + NL80211_WOWLAN_TCP_DST_IPV4, + NL80211_WOWLAN_TCP_DST_MAC, + NL80211_WOWLAN_TCP_SRC_PORT, + NL80211_WOWLAN_TCP_DST_PORT, + NL80211_WOWLAN_TCP_DATA_PAYLOAD, + NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, + NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, + NL80211_WOWLAN_TCP_DATA_INTERVAL, + NL80211_WOWLAN_TCP_WAKE_PAYLOAD, + NL80211_WOWLAN_TCP_WAKE_MASK, + + /* keep last */ + NUM_NL80211_WOWLAN_TCP, + MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1 +}; + +/** + * struct nl80211_coalesce_rule_support - coalesce rule support information + * @max_rules: maximum number of rules supported + * @pat: packet pattern support information + * @max_delay: maximum supported coalescing delay in msecs + * + * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the + * capability information given by the kernel to userspace. + */ +struct nl80211_coalesce_rule_support { + __u32 max_rules; + struct nl80211_pattern_support pat; + __u32 max_delay; +} __attribute__((packed)); + +/** + * enum nl80211_attr_coalesce_rule - coalesce rule attribute + * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute + * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing + * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence, + * see &enum nl80211_coalesce_condition. + * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched + * after these fixed number of bytes of received packet + * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes + * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number + */ +enum nl80211_attr_coalesce_rule { + __NL80211_COALESCE_RULE_INVALID, + NL80211_ATTR_COALESCE_RULE_DELAY, + NL80211_ATTR_COALESCE_RULE_CONDITION, + NL80211_ATTR_COALESCE_RULE_PKT_PATTERN, + + /* keep last */ + NUM_NL80211_ATTR_COALESCE_RULE, + NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1 +}; + +/** + * enum nl80211_coalesce_condition - coalesce rule conditions + * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns + * in a rule are matched. + * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns + * in a rule are not matched. + */ +enum nl80211_coalesce_condition { + NL80211_COALESCE_CONDITION_MATCH, + NL80211_COALESCE_CONDITION_NO_MATCH +}; + +/** + * enum nl80211_iface_limit_attrs - limit attributes + * @NL80211_IFACE_LIMIT_UNSPEC: (reserved) + * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that + * can be chosen from this set of interface types (u32) + * @NL80211_IFACE_LIMIT_TYPES: nested attribute containing a + * flag attribute for each interface type in this set + * @NUM_NL80211_IFACE_LIMIT: number of attributes + * @MAX_NL80211_IFACE_LIMIT: highest attribute number + */ +enum nl80211_iface_limit_attrs { + NL80211_IFACE_LIMIT_UNSPEC, + NL80211_IFACE_LIMIT_MAX, + NL80211_IFACE_LIMIT_TYPES, + + /* keep last */ + NUM_NL80211_IFACE_LIMIT, + MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1 +}; + +/** + * enum nl80211_if_combination_attrs -- interface combination attributes + * + * @NL80211_IFACE_COMB_UNSPEC: (reserved) + * @NL80211_IFACE_COMB_LIMITS: Nested attributes containing the limits + * for given interface types, see &enum nl80211_iface_limit_attrs. + * @NL80211_IFACE_COMB_MAXNUM: u32 attribute giving the total number of + * interfaces that can be created in this group. This number doesn't + * apply to interfaces purely managed in software, which are listed + * in a separate attribute %NL80211_ATTR_INTERFACES_SOFTWARE. + * @NL80211_IFACE_COMB_STA_AP_BI_MATCH: flag attribute specifying that + * beacon intervals within this group must be all the same even for + * infrastructure and AP/GO combinations, i.e. the GO(s) must adopt + * the infrastructure network's beacon interval. + * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many + * different channels may be used within this group. + * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap + * of supported channel widths for radar detection. + * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap + * of supported regulatory regions for radar detection. + * @NL80211_IFACE_COMB_BI_MIN_GCD: u32 attribute specifying the minimum GCD of + * different beacon intervals supported by all the interface combinations + * in this group (if not present, all beacon intervals be identical). + * @NUM_NL80211_IFACE_COMB: number of attributes + * @MAX_NL80211_IFACE_COMB: highest attribute number + * + * Examples: + * limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2 + * => allows an AP and a STA that must match BIs + * + * numbers = [ #{AP, P2P-GO} <= 8 ], BI min gcd, channels = 1, max = 8, + * => allows 8 of AP/GO that can have BI gcd >= min gcd + * + * numbers = [ #{STA} <= 2 ], channels = 2, max = 2 + * => allows two STAs on different channels + * + * numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4 + * => allows a STA plus three P2P interfaces + * + * The list of these four possibilities could completely be contained + * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate + * that any of these groups must match. + * + * "Combinations" of just a single interface will not be listed here, + * a single interface of any valid interface type is assumed to always + * be possible by itself. This means that implicitly, for each valid + * interface type, the following group always exists: + * numbers = [ #{} <= 1 ], channels = 1, max = 1 + */ +enum nl80211_if_combination_attrs { + NL80211_IFACE_COMB_UNSPEC, + NL80211_IFACE_COMB_LIMITS, + NL80211_IFACE_COMB_MAXNUM, + NL80211_IFACE_COMB_STA_AP_BI_MATCH, + NL80211_IFACE_COMB_NUM_CHANNELS, + NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, + NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, + NL80211_IFACE_COMB_BI_MIN_GCD, + + /* keep last */ + NUM_NL80211_IFACE_COMB, + MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1 +}; + + +/** + * enum nl80211_plink_state - state of a mesh peer link finite state machine + * + * @NL80211_PLINK_LISTEN: initial state, considered the implicit + * state of non existent mesh peer links + * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to + * this mesh peer + * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received + * from this mesh peer + * @NL80211_PLINK_CNF_RCVD: mesh plink confirm frame has been + * received from this mesh peer + * @NL80211_PLINK_ESTAB: mesh peer link is established + * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled + * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh + * plink are discarded + * @NUM_NL80211_PLINK_STATES: number of peer link states + * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states + */ +enum nl80211_plink_state { + NL80211_PLINK_LISTEN, + NL80211_PLINK_OPN_SNT, + NL80211_PLINK_OPN_RCVD, + NL80211_PLINK_CNF_RCVD, + NL80211_PLINK_ESTAB, + NL80211_PLINK_HOLDING, + NL80211_PLINK_BLOCKED, + + /* keep last */ + NUM_NL80211_PLINK_STATES, + MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1 +}; + +/** + * enum nl80211_plink_action - actions to perform in mesh peers + * + * @NL80211_PLINK_ACTION_NO_ACTION: perform no action + * @NL80211_PLINK_ACTION_OPEN: start mesh peer link establishment + * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer + * @NUM_NL80211_PLINK_ACTIONS: number of possible actions + */ +enum plink_actions { + NL80211_PLINK_ACTION_NO_ACTION, + NL80211_PLINK_ACTION_OPEN, + NL80211_PLINK_ACTION_BLOCK, + + NUM_NL80211_PLINK_ACTIONS, +}; + + +#define NL80211_KCK_LEN 16 +#define NL80211_KEK_LEN 16 +#define NL80211_REPLAY_CTR_LEN 8 + +/** + * enum nl80211_rekey_data - attributes for GTK rekey offload + * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes + * @NL80211_REKEY_DATA_KEK: key encryption key (binary) + * @NL80211_REKEY_DATA_KCK: key confirmation key (binary) + * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary) + * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal) + * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal) + */ +enum nl80211_rekey_data { + __NL80211_REKEY_DATA_INVALID, + NL80211_REKEY_DATA_KEK, + NL80211_REKEY_DATA_KCK, + NL80211_REKEY_DATA_REPLAY_CTR, + + /* keep last */ + NUM_NL80211_REKEY_DATA, + MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1 +}; + +/** + * enum nl80211_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID + * @NL80211_HIDDEN_SSID_NOT_IN_USE: do not hide SSID (i.e., broadcast it in + * Beacon frames) + * @NL80211_HIDDEN_SSID_ZERO_LEN: hide SSID by using zero-length SSID element + * in Beacon frames + * @NL80211_HIDDEN_SSID_ZERO_CONTENTS: hide SSID by using correct length of SSID + * element in Beacon frames but zero out each byte in the SSID + */ +enum nl80211_hidden_ssid { + NL80211_HIDDEN_SSID_NOT_IN_USE, + NL80211_HIDDEN_SSID_ZERO_LEN, + NL80211_HIDDEN_SSID_ZERO_CONTENTS +}; + +/** + * enum nl80211_sta_wme_attr - station WME attributes + * @__NL80211_STA_WME_INVALID: invalid number for nested attribute + * @NL80211_STA_WME_UAPSD_QUEUES: bitmap of uapsd queues. the format + * is the same as the AC bitmap in the QoS info field. + * @NL80211_STA_WME_MAX_SP: max service period. the format is the same + * as the MAX_SP field in the QoS info field (but already shifted down). + * @__NL80211_STA_WME_AFTER_LAST: internal + * @NL80211_STA_WME_MAX: highest station WME attribute + */ +enum nl80211_sta_wme_attr { + __NL80211_STA_WME_INVALID, + NL80211_STA_WME_UAPSD_QUEUES, + NL80211_STA_WME_MAX_SP, + + /* keep last */ + __NL80211_STA_WME_AFTER_LAST, + NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1 +}; + +/** + * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates + * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes + * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher + * priority) + * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets) + * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag) + * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes + * (internal) + * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute + * (internal) + */ +enum nl80211_pmksa_candidate_attr { + __NL80211_PMKSA_CANDIDATE_INVALID, + NL80211_PMKSA_CANDIDATE_INDEX, + NL80211_PMKSA_CANDIDATE_BSSID, + NL80211_PMKSA_CANDIDATE_PREAUTH, + + /* keep last */ + NUM_NL80211_PMKSA_CANDIDATE, + MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 +}; + +/** + * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION + * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request + * @NL80211_TDLS_SETUP: Setup TDLS link + * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established + * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link + * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link + */ +enum nl80211_tdls_operation { + NL80211_TDLS_DISCOVERY_REQ, + NL80211_TDLS_SETUP, + NL80211_TDLS_TEARDOWN, + NL80211_TDLS_ENABLE_LINK, + NL80211_TDLS_DISABLE_LINK, +}; + +/* + * enum nl80211_ap_sme_features - device-integrated AP features + * Reserved for future use, no bits are defined in + * NL80211_ATTR_DEVICE_AP_SME yet. +enum nl80211_ap_sme_features { +}; + */ + +/** + * enum nl80211_feature_flags - device/driver features + * @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back + * TX status to the socket error queue when requested with the + * socket option. + * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates. + * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up + * the connected inactive stations in AP mode. + * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested + * to work properly to suppport receiving regulatory hints from + * cellular base stations. + * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: (no longer available, only + * here to reserve the value for API/ABI compatibility) + * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of + * equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station + * mode + * @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan + * @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported + * @NL80211_FEATURE_AP_SCAN: Support scanning using an AP vif + * @NL80211_FEATURE_VIF_TXPOWER: The driver supports per-vif TX power setting + * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform + * OBSS scans and generate 20/40 BSS coex reports. This flag is used only + * for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied. + * @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window + * setting + * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic + * powersave + * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state + * transitions for AP clients. Without this flag (and if the driver + * doesn't have the AP SME in the device) the driver supports adding + * stations only when they're associated and adds them in associated + * state (to later be transitioned into authorized), with this flag + * they should be added before even sending the authentication reply + * and then transitioned into authenticated, associated and authorized + * states using station flags. + * Note that even for drivers that support this, the default is to add + * stations in authenticated/associated state, so to add unauthenticated + * stations the authenticated/associated bits have to be set in the mask. + * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits + * (HT40, VHT 80/160 MHz) if this flag is set + * @NL80211_FEATURE_USERSPACE_MPM: This driver supports a userspace Mesh + * Peering Management entity which may be implemented by registering for + * beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is + * still generated by the driver. + * @NL80211_FEATURE_ACTIVE_MONITOR: This driver supports an active monitor + * interface. An active monitor interface behaves like a normal monitor + * interface, but gets added to the driver. It ensures that incoming + * unicast packets directed at the configured interface address get ACKed. + * @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic + * channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the + * lifetime of a BSS. + * @NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES: This device adds a DS Parameter + * Set IE to probe requests. + * @NL80211_FEATURE_WFA_TPC_IE_IN_PROBES: This device adds a WFA TPC Report IE + * to probe requests. + * @NL80211_FEATURE_QUIET: This device, in client mode, supports Quiet Period + * requests sent to it by an AP. + * @NL80211_FEATURE_TX_POWER_INSERTION: This device is capable of inserting the + * current tx power value into the TPC Report IE in the spectrum + * management TPC Report action frame, and in the Radio Measurement Link + * Measurement Report action frame. + * @NL80211_FEATURE_ACKTO_ESTIMATION: This driver supports dynamic ACK timeout + * estimation (dynack). %NL80211_ATTR_WIPHY_DYN_ACK flag attribute is used + * to enable dynack. + * @NL80211_FEATURE_STATIC_SMPS: Device supports static spatial + * multiplexing powersave, ie. can turn off all but one chain + * even on HT connections that should be using more chains. + * @NL80211_FEATURE_DYNAMIC_SMPS: Device supports dynamic spatial + * multiplexing powersave, ie. can turn off all but one chain + * and then wake the rest up as required after, for example, + * rts/cts handshake. + * @NL80211_FEATURE_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM + * TSPEC sessions (TID aka TSID 0-7) with the %NL80211_CMD_ADD_TX_TS + * command. Standard IEEE 802.11 TSPEC setup is not yet supported, it + * needs to be able to handle Block-Ack agreements and other things. + * @NL80211_FEATURE_MAC_ON_CREATE: Device supports configuring + * the vif's MAC address upon creation. + * See 'macaddr' field in the vif_params (cfg80211.h). + * @NL80211_FEATURE_TDLS_CHANNEL_SWITCH: Driver supports channel switching when + * operating as a TDLS peer. + * @NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR: This device/driver supports using a + * random MAC address during scan (if the device is unassociated); the + * %NL80211_SCAN_FLAG_RANDOM_ADDR flag may be set for scans and the MAC + * address mask/value will be used. + * @NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR: This device/driver supports + * using a random MAC address for every scan iteration during scheduled + * scan (while not associated), the %NL80211_SCAN_FLAG_RANDOM_ADDR may + * be set for scheduled scan and the MAC address mask/value will be used. + * @NL80211_FEATURE_ND_RANDOM_MAC_ADDR: This device/driver supports using a + * random MAC address for every scan iteration during "net detect", i.e. + * scan in unassociated WoWLAN, the %NL80211_SCAN_FLAG_RANDOM_ADDR may + * be set for scheduled scan and the MAC address mask/value will be used. + */ +enum nl80211_feature_flags { + NL80211_FEATURE_SK_TX_STATUS = 1 << 0, + NL80211_FEATURE_HT_IBSS = 1 << 1, + NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, + NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3, + NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4, + NL80211_FEATURE_SAE = 1 << 5, + NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6, + NL80211_FEATURE_SCAN_FLUSH = 1 << 7, + NL80211_FEATURE_AP_SCAN = 1 << 8, + NL80211_FEATURE_VIF_TXPOWER = 1 << 9, + NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, + NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11, + NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12, + /* bit 13 is reserved */ + NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14, + NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15, + NL80211_FEATURE_USERSPACE_MPM = 1 << 16, + NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17, + NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE = 1 << 18, + NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES = 1 << 19, + NL80211_FEATURE_WFA_TPC_IE_IN_PROBES = 1 << 20, + NL80211_FEATURE_QUIET = 1 << 21, + NL80211_FEATURE_TX_POWER_INSERTION = 1 << 22, + NL80211_FEATURE_ACKTO_ESTIMATION = 1 << 23, + NL80211_FEATURE_STATIC_SMPS = 1 << 24, + NL80211_FEATURE_DYNAMIC_SMPS = 1 << 25, + NL80211_FEATURE_SUPPORTS_WMM_ADMISSION = 1 << 26, + NL80211_FEATURE_MAC_ON_CREATE = 1 << 27, + NL80211_FEATURE_TDLS_CHANNEL_SWITCH = 1 << 28, + NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR = 1 << 29, + NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR = 1 << 30, + NL80211_FEATURE_ND_RANDOM_MAC_ADDR = 1 << 31, +}; + +/** + * enum nl80211_ext_feature_index - bit index of extended features. + * @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates. + * @NL80211_EXT_FEATURE_RRM: This driver supports RRM. When featured, user can + * can request to use RRM (see %NL80211_ATTR_USE_RRM) with + * %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set + * the ASSOC_REQ_USE_RRM flag in the association request even if + * NL80211_FEATURE_QUIET is not advertized. + * @NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER: This device supports MU-MIMO air + * sniffer which means that it can be configured to hear packets from + * certain groups which can be configured by the + * %NL80211_ATTR_MU_MIMO_GROUP_DATA attribute, + * or can be configured to follow a station by configuring the + * %NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR attribute. + * @NL80211_EXT_FEATURE_SCAN_START_TIME: This driver includes the actual + * time the scan started in scan results event. The time is the TSF of + * the BSS that the interface that requested the scan is connected to + * (if available). + * @NL80211_EXT_FEATURE_BSS_PARENT_TSF: Per BSS, this driver reports the + * time the last beacon/probe was received. The time is the TSF of the + * BSS that the interface that requested the scan is connected to + * (if available). + * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of + * channel dwell time. + * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate + * configuration (AP/mesh), supporting a legacy (non HT/VHT) rate. + * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate + * configuration (AP/mesh) with HT rates. + * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate + * configuration (AP/mesh) with VHT rates. + * @NL80211_EXT_FEATURE_FILS_STA: This driver supports Fast Initial Link Setup + * with user space SME (NL80211_CMD_AUTHENTICATE) in station mode. + * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA: This driver supports randomized TA + * in @NL80211_CMD_FRAME while not associated. + * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED: This driver supports + * randomized TA in @NL80211_CMD_FRAME while associated. + * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan + * for reporting BSSs with better RSSI than the current connected BSS + * (%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI). + * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the + * %NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more + * RSSI threshold values to monitor rather than exactly one threshold. + * @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key + * authentication with %NL80211_CMD_CONNECT. + * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK: Device wants to do 4-way + * handshake with PSK in station mode (PSK is passed as part of the connect + * and associate commands), doing it in the host might not be supported. + * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X: Device wants to do doing 4-way + * handshake with 802.1X in station mode (will pass EAP frames to the host + * and accept the set_pmk/del_pmk commands), doing it in the host might not + * be supported. + * @NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME: Driver is capable of overriding + * the max channel attribute in the FILS request params IE with the + * actual dwell time. + * @NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP: Driver accepts broadcast probe + * response + * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE: Driver supports sending + * the first probe request in each channel at rate of at least 5.5Mbps. + * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: Driver supports + * probe request tx deferral and suppression + * @NL80211_EXT_FEATURE_MFP_OPTIONAL: Driver supports the %NL80211_MFP_OPTIONAL + * value in %NL80211_ATTR_USE_MFP. + * @NL80211_EXT_FEATURE_LOW_SPAN_SCAN: Driver supports low span scan. + * @NL80211_EXT_FEATURE_LOW_POWER_SCAN: Driver supports low power scan. + * @NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN: Driver supports high accuracy scan. + * @NL80211_EXT_FEATURE_DFS_OFFLOAD: HW/driver will offload DFS actions. + * Device or driver will do all DFS-related actions by itself, + * informing user-space about CAC progress, radar detection event, + * channel change triggered by radar detection event. + * No need to start CAC from user-space, no need to react to + * "radar detected" event. + * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211: Driver supports sending and + * receiving control port frames over nl80211 instead of the netdevice. + * @NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT: This driver/device supports + * (average) ACK signal strength reporting. + * @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate + * TXQs. + * @NL80211_EXT_FEATURE_SCAN_RANDOM_SN: Driver/device supports randomizing the + * SN in probe request frames if requested by %NL80211_SCAN_FLAG_RANDOM_SN. + * @NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT: Driver/device can omit all data + * except for supported rates from the probe request content if requested + * by the %NL80211_SCAN_FLAG_MIN_PREQ_CONTENT flag. + * @NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER: Driver supports enabling fine + * timing measurement responder role. + * + * @NL80211_EXT_FEATURE_CAN_REPLACE_PTK0: Driver/device confirm that they are + * able to rekey an in-use key correctly. Userspace must not rekey PTK keys + * if this flag is not set. Ignoring this can leak clear text packets and/or + * freeze the connection. + * @NL80211_EXT_FEATURE_EXT_KEY_ID: Driver supports "Extended Key ID for + * Individually Addressed Frames" from IEEE802.11-2016. + * + * @NL80211_EXT_FEATURE_AIRTIME_FAIRNESS: Driver supports getting airtime + * fairness for transmitted packets and has enabled airtime fairness + * scheduling. + * + * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching + * (set/del PMKSA operations) in AP mode. + * + * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Driver supports + * filtering of sched scan results using band specific RSSI thresholds. + * + * @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling tx power + * to a station. + * + * @NUM_NL80211_EXT_FEATURES: number of extended features. + * @MAX_NL80211_EXT_FEATURES: highest extended feature index. + */ +enum nl80211_ext_feature_index { + NL80211_EXT_FEATURE_VHT_IBSS, + NL80211_EXT_FEATURE_RRM, + NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER, + NL80211_EXT_FEATURE_SCAN_START_TIME, + NL80211_EXT_FEATURE_BSS_PARENT_TSF, + NL80211_EXT_FEATURE_SET_SCAN_DWELL, + NL80211_EXT_FEATURE_BEACON_RATE_LEGACY, + NL80211_EXT_FEATURE_BEACON_RATE_HT, + NL80211_EXT_FEATURE_BEACON_RATE_VHT, + NL80211_EXT_FEATURE_FILS_STA, + NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA, + NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED, + NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI, + NL80211_EXT_FEATURE_CQM_RSSI_LIST, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X, + NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME, + NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION, + NL80211_EXT_FEATURE_MFP_OPTIONAL, + NL80211_EXT_FEATURE_LOW_SPAN_SCAN, + NL80211_EXT_FEATURE_LOW_POWER_SCAN, + NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN, + NL80211_EXT_FEATURE_DFS_OFFLOAD, + NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211, + NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT, + /* we renamed this - stay compatible */ + NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT = NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT, + NL80211_EXT_FEATURE_TXQS, + NL80211_EXT_FEATURE_SCAN_RANDOM_SN, + NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT, + NL80211_EXT_FEATURE_CAN_REPLACE_PTK0, + NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER, + NL80211_EXT_FEATURE_AIRTIME_FAIRNESS, + NL80211_EXT_FEATURE_AP_PMKSA_CACHING, + NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD, + NL80211_EXT_FEATURE_EXT_KEY_ID, + NL80211_EXT_FEATURE_STA_TX_PWR, + + /* add new features before the definition below */ + NUM_NL80211_EXT_FEATURES, + MAX_NL80211_EXT_FEATURES = NUM_NL80211_EXT_FEATURES - 1 +}; + +/** + * enum nl80211_probe_resp_offload_support_attr - optional supported + * protocols for probe-response offloading by the driver/FW. + * To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute. + * Each enum value represents a bit in the bitmap of supported + * protocols. Typically a subset of probe-requests belonging to a + * supported protocol will be excluded from offload and uploaded + * to the host. + * + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: Support for WPS ver. 1 + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: Support for WPS ver. 2 + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: Support for P2P + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: Support for 802.11u + */ +enum nl80211_probe_resp_offload_support_attr { + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS = 1<<0, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 = 1<<1, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P = 1<<2, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 1<<3, +}; + +/** + * enum nl80211_connect_failed_reason - connection request failed reasons + * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be + * handled by the AP is reached. + * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL. + */ +enum nl80211_connect_failed_reason { + NL80211_CONN_FAIL_MAX_CLIENTS, + NL80211_CONN_FAIL_BLOCKED_CLIENT, +}; + +/** + * enum nl80211_timeout_reason - timeout reasons + * + * @NL80211_TIMEOUT_UNSPECIFIED: Timeout reason unspecified. + * @NL80211_TIMEOUT_SCAN: Scan (AP discovery) timed out. + * @NL80211_TIMEOUT_AUTH: Authentication timed out. + * @NL80211_TIMEOUT_ASSOC: Association timed out. + */ +enum nl80211_timeout_reason { + NL80211_TIMEOUT_UNSPECIFIED, + NL80211_TIMEOUT_SCAN, + NL80211_TIMEOUT_AUTH, + NL80211_TIMEOUT_ASSOC, +}; + +/** + * enum nl80211_scan_flags - scan request control flags + * + * Scan request control flags are used to control the handling + * of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN + * requests. + * + * NL80211_SCAN_FLAG_LOW_SPAN, NL80211_SCAN_FLAG_LOW_POWER, and + * NL80211_SCAN_FLAG_HIGH_ACCURACY flags are exclusive of each other, i.e., only + * one of them can be used in the request. + * + * @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority + * @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning + * @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured + * as AP and the beaconing has already been configured. This attribute is + * dangerous because will destroy stations performance as a lot of frames + * will be lost while scanning off-channel, therefore it must be used only + * when really needed + * @NL80211_SCAN_FLAG_RANDOM_ADDR: use a random MAC address for this scan (or + * for scheduled scan: a different one for every scan iteration). When the + * flag is set, depending on device capabilities the @NL80211_ATTR_MAC and + * @NL80211_ATTR_MAC_MASK attributes may also be given in which case only + * the masked bits will be preserved from the MAC address and the remainder + * randomised. If the attributes are not given full randomisation (46 bits, + * locally administered 1, multicast 0) is assumed. + * This flag must not be requested when the feature isn't supported, check + * the nl80211 feature flags for the device. + * @NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME: fill the dwell time in the FILS + * request parameters IE in the probe request + * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses + * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at + * rate of at least 5.5M. In case non OCE AP is discovered in the channel, + * only the first probe req in the channel will be sent in high rate. + * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request + * tx deferral (dot11FILSProbeDelay shall be set to 15ms) + * and suppression (if it has received a broadcast Probe Response frame, + * Beacon frame or FILS Discovery frame from an AP that the STA considers + * a suitable candidate for (re-)association - suitable in terms of + * SSID and/or RSSI. + * @NL80211_SCAN_FLAG_LOW_SPAN: Span corresponds to the total time taken to + * accomplish the scan. Thus, this flag intends the driver to perform the + * scan request with lesser span/duration. It is specific to the driver + * implementations on how this is accomplished. Scan accuracy may get + * impacted with this flag. + * @NL80211_SCAN_FLAG_LOW_POWER: This flag intends the scan attempts to consume + * optimal possible power. Drivers can resort to their specific means to + * optimize the power. Scan accuracy may get impacted with this flag. + * @NL80211_SCAN_FLAG_HIGH_ACCURACY: Accuracy here intends to the extent of scan + * results obtained. Thus HIGH_ACCURACY scan flag aims to get maximum + * possible scan results. This flag hints the driver to use the best + * possible scan configuration to improve the accuracy in scanning. + * Latency and power use may get impacted with this flag. + * @NL80211_SCAN_FLAG_RANDOM_SN: randomize the sequence number in probe + * request frames from this scan to avoid correlation/tracking being + * possible. + * @NL80211_SCAN_FLAG_MIN_PREQ_CONTENT: minimize probe request content to + * only have supported rates and no additional capabilities (unless + * added by userspace explicitly.) + */ +enum nl80211_scan_flags { + NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, + NL80211_SCAN_FLAG_FLUSH = 1<<1, + NL80211_SCAN_FLAG_AP = 1<<2, + NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3, + NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME = 1<<4, + NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP = 1<<5, + NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE = 1<<6, + NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION = 1<<7, + NL80211_SCAN_FLAG_LOW_SPAN = 1<<8, + NL80211_SCAN_FLAG_LOW_POWER = 1<<9, + NL80211_SCAN_FLAG_HIGH_ACCURACY = 1<<10, + NL80211_SCAN_FLAG_RANDOM_SN = 1<<11, + NL80211_SCAN_FLAG_MIN_PREQ_CONTENT = 1<<12, +}; + +/** + * enum nl80211_acl_policy - access control policy + * + * Access control policy is applied on a MAC list set by + * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to + * be used with %NL80211_ATTR_ACL_POLICY. + * + * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are + * listed in ACL, i.e. allow all the stations which are not listed + * in ACL to authenticate. + * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed + * in ACL, i.e. deny all the stations which are not listed in ACL. + */ +enum nl80211_acl_policy { + NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED, + NL80211_ACL_POLICY_DENY_UNLESS_LISTED, +}; + +/** + * enum nl80211_smps_mode - SMPS mode + * + * Requested SMPS mode (for AP mode) + * + * @NL80211_SMPS_OFF: SMPS off (use all antennas). + * @NL80211_SMPS_STATIC: static SMPS (use a single antenna) + * @NL80211_SMPS_DYNAMIC: dynamic smps (start with a single antenna and + * turn on other antennas after CTS/RTS). + */ +enum nl80211_smps_mode { + NL80211_SMPS_OFF, + NL80211_SMPS_STATIC, + NL80211_SMPS_DYNAMIC, + + __NL80211_SMPS_AFTER_LAST, + NL80211_SMPS_MAX = __NL80211_SMPS_AFTER_LAST - 1 +}; + +/** + * enum nl80211_radar_event - type of radar event for DFS operation + * + * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace + * about detected radars or success of the channel available check (CAC) + * + * @NL80211_RADAR_DETECTED: A radar pattern has been detected. The channel is + * now unusable. + * @NL80211_RADAR_CAC_FINISHED: Channel Availability Check has been finished, + * the channel is now available. + * @NL80211_RADAR_CAC_ABORTED: Channel Availability Check has been aborted, no + * change to the channel status. + * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is + * over, channel becomes usable. + * @NL80211_RADAR_PRE_CAC_EXPIRED: Channel Availability Check done on this + * non-operating channel is expired and no longer valid. New CAC must + * be done on this channel before starting the operation. This is not + * applicable for ETSI dfs domain where pre-CAC is valid for ever. + * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started, + * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled. + */ +enum nl80211_radar_event { + NL80211_RADAR_DETECTED, + NL80211_RADAR_CAC_FINISHED, + NL80211_RADAR_CAC_ABORTED, + NL80211_RADAR_NOP_FINISHED, + NL80211_RADAR_PRE_CAC_EXPIRED, + NL80211_RADAR_CAC_STARTED, +}; + +/** + * enum nl80211_dfs_state - DFS states for channels + * + * Channel states used by the DFS code. + * + * @NL80211_DFS_USABLE: The channel can be used, but channel availability + * check (CAC) must be performed before using it for AP or IBSS. + * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it + * is therefore marked as not available. + * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available. + */ +enum nl80211_dfs_state { + NL80211_DFS_USABLE, + NL80211_DFS_UNAVAILABLE, + NL80211_DFS_AVAILABLE, +}; + +/** + * enum enum nl80211_protocol_features - nl80211 protocol features + * @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting + * wiphy dumps (if requested by the application with the attribute + * %NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the + * wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or + * %NL80211_ATTR_WDEV. + */ +enum nl80211_protocol_features { + NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 1 << 0, +}; + +/** + * enum nl80211_crit_proto_id - nl80211 critical protocol identifiers + * + * @NL80211_CRIT_PROTO_UNSPEC: protocol unspecified. + * @NL80211_CRIT_PROTO_DHCP: BOOTP or DHCPv6 protocol. + * @NL80211_CRIT_PROTO_EAPOL: EAPOL protocol. + * @NL80211_CRIT_PROTO_APIPA: APIPA protocol. + * @NUM_NL80211_CRIT_PROTO: must be kept last. + */ +enum nl80211_crit_proto_id { + NL80211_CRIT_PROTO_UNSPEC, + NL80211_CRIT_PROTO_DHCP, + NL80211_CRIT_PROTO_EAPOL, + NL80211_CRIT_PROTO_APIPA, + /* add other protocols before this one */ + NUM_NL80211_CRIT_PROTO +}; + +/* maximum duration for critical protocol measures */ +#define NL80211_CRIT_PROTO_MAX_DURATION 5000 /* msec */ + +/** + * enum nl80211_rxmgmt_flags - flags for received management frame. + * + * Used by cfg80211_rx_mgmt() + * + * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver. + * @NL80211_RXMGMT_FLAG_EXTERNAL_AUTH: Host driver intends to offload + * the authentication. Exclusively defined for host drivers that + * advertises the SME functionality but would like the userspace + * to handle certain authentication algorithms (e.g. SAE). + */ +enum nl80211_rxmgmt_flags { + NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0, + NL80211_RXMGMT_FLAG_EXTERNAL_AUTH = 1 << 1, +}; + +/* + * If this flag is unset, the lower 24 bits are an OUI, if set + * a Linux nl80211 vendor ID is used (no such IDs are allocated + * yet, so that's not valid so far) + */ +#define NL80211_VENDOR_ID_IS_LINUX 0x80000000 + +/** + * struct nl80211_vendor_cmd_info - vendor command data + * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the + * value is a 24-bit OUI; if it is set then a separately allocated ID + * may be used, but no such IDs are allocated yet. New IDs should be + * added to this file when needed. + * @subcmd: sub-command ID for the command + */ +struct nl80211_vendor_cmd_info { + __u32 vendor_id; + __u32 subcmd; +}; + +/** + * enum nl80211_tdls_peer_capability - TDLS peer flags. + * + * Used by tdls_mgmt() to determine which conditional elements need + * to be added to TDLS Setup frames. + * + * @NL80211_TDLS_PEER_HT: TDLS peer is HT capable. + * @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable. + * @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable. + */ +enum nl80211_tdls_peer_capability { + NL80211_TDLS_PEER_HT = 1<<0, + NL80211_TDLS_PEER_VHT = 1<<1, + NL80211_TDLS_PEER_WMM = 1<<2, +}; + +/** + * enum nl80211_sched_scan_plan - scanning plan for scheduled scan + * @__NL80211_SCHED_SCAN_PLAN_INVALID: attribute number 0 is reserved + * @NL80211_SCHED_SCAN_PLAN_INTERVAL: interval between scan iterations. In + * seconds (u32). + * @NL80211_SCHED_SCAN_PLAN_ITERATIONS: number of scan iterations in this + * scan plan (u32). The last scan plan must not specify this attribute + * because it will run infinitely. A value of zero is invalid as it will + * make the scan plan meaningless. + * @NL80211_SCHED_SCAN_PLAN_MAX: highest scheduled scan plan attribute number + * currently defined + * @__NL80211_SCHED_SCAN_PLAN_AFTER_LAST: internal use + */ +enum nl80211_sched_scan_plan { + __NL80211_SCHED_SCAN_PLAN_INVALID, + NL80211_SCHED_SCAN_PLAN_INTERVAL, + NL80211_SCHED_SCAN_PLAN_ITERATIONS, + + /* keep last */ + __NL80211_SCHED_SCAN_PLAN_AFTER_LAST, + NL80211_SCHED_SCAN_PLAN_MAX = + __NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1 +}; + +/** + * struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters. + * + * @band: band of BSS that must match for RSSI value adjustment. The value + * of this field is according to &enum nl80211_band. + * @delta: value used to adjust the RSSI value of matching BSS in dB. + */ +struct nl80211_bss_select_rssi_adjust { + __u8 band; + __s8 delta; +} __attribute__((packed)); + +/** + * enum nl80211_bss_select_attr - attributes for bss selection. + * + * @__NL80211_BSS_SELECT_ATTR_INVALID: reserved. + * @NL80211_BSS_SELECT_ATTR_RSSI: Flag indicating only RSSI-based BSS selection + * is requested. + * @NL80211_BSS_SELECT_ATTR_BAND_PREF: attribute indicating BSS + * selection should be done such that the specified band is preferred. + * When there are multiple BSS-es in the preferred band, the driver + * shall use RSSI-based BSS selection as a second step. The value of + * this attribute is according to &enum nl80211_band (u32). + * @NL80211_BSS_SELECT_ATTR_RSSI_ADJUST: When present the RSSI level for + * BSS-es in the specified band is to be adjusted before doing + * RSSI-based BSS selection. The attribute value is a packed structure + * value as specified by &struct nl80211_bss_select_rssi_adjust. + * @NL80211_BSS_SELECT_ATTR_MAX: highest bss select attribute number. + * @__NL80211_BSS_SELECT_ATTR_AFTER_LAST: internal use. + * + * One and only one of these attributes are found within %NL80211_ATTR_BSS_SELECT + * for %NL80211_CMD_CONNECT. It specifies the required BSS selection behaviour + * which the driver shall use. + */ +enum nl80211_bss_select_attr { + __NL80211_BSS_SELECT_ATTR_INVALID, + NL80211_BSS_SELECT_ATTR_RSSI, + NL80211_BSS_SELECT_ATTR_BAND_PREF, + NL80211_BSS_SELECT_ATTR_RSSI_ADJUST, + + /* keep last */ + __NL80211_BSS_SELECT_ATTR_AFTER_LAST, + NL80211_BSS_SELECT_ATTR_MAX = __NL80211_BSS_SELECT_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_nan_function_type - NAN function type + * + * Defines the function type of a NAN function + * + * @NL80211_NAN_FUNC_PUBLISH: function is publish + * @NL80211_NAN_FUNC_SUBSCRIBE: function is subscribe + * @NL80211_NAN_FUNC_FOLLOW_UP: function is follow-up + */ +enum nl80211_nan_function_type { + NL80211_NAN_FUNC_PUBLISH, + NL80211_NAN_FUNC_SUBSCRIBE, + NL80211_NAN_FUNC_FOLLOW_UP, + + /* keep last */ + __NL80211_NAN_FUNC_TYPE_AFTER_LAST, + NL80211_NAN_FUNC_MAX_TYPE = __NL80211_NAN_FUNC_TYPE_AFTER_LAST - 1, +}; + +/** + * enum nl80211_nan_publish_type - NAN publish tx type + * + * Defines how to send publish Service Discovery Frames + * + * @NL80211_NAN_SOLICITED_PUBLISH: publish function is solicited + * @NL80211_NAN_UNSOLICITED_PUBLISH: publish function is unsolicited + */ +enum nl80211_nan_publish_type { + NL80211_NAN_SOLICITED_PUBLISH = 1 << 0, + NL80211_NAN_UNSOLICITED_PUBLISH = 1 << 1, +}; + +/** + * enum nl80211_nan_func_term_reason - NAN functions termination reason + * + * Defines termination reasons of a NAN function + * + * @NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST: requested by user + * @NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED: timeout + * @NL80211_NAN_FUNC_TERM_REASON_ERROR: errored + */ +enum nl80211_nan_func_term_reason { + NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST, + NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED, + NL80211_NAN_FUNC_TERM_REASON_ERROR, +}; + +#define NL80211_NAN_FUNC_SERVICE_ID_LEN 6 +#define NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN 0xff +#define NL80211_NAN_FUNC_SRF_MAX_LEN 0xff + +/** + * enum nl80211_nan_func_attributes - NAN function attributes + * @__NL80211_NAN_FUNC_INVALID: invalid + * @NL80211_NAN_FUNC_TYPE: &enum nl80211_nan_function_type (u8). + * @NL80211_NAN_FUNC_SERVICE_ID: 6 bytes of the service ID hash as + * specified in NAN spec. This is a binary attribute. + * @NL80211_NAN_FUNC_PUBLISH_TYPE: relevant if the function's type is + * publish. Defines the transmission type for the publish Service Discovery + * Frame, see &enum nl80211_nan_publish_type. Its type is u8. + * @NL80211_NAN_FUNC_PUBLISH_BCAST: relevant if the function is a solicited + * publish. Should the solicited publish Service Discovery Frame be sent to + * the NAN Broadcast address. This is a flag. + * @NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE: relevant if the function's type is + * subscribe. Is the subscribe active. This is a flag. + * @NL80211_NAN_FUNC_FOLLOW_UP_ID: relevant if the function's type is follow up. + * The instance ID for the follow up Service Discovery Frame. This is u8. + * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type + * is follow up. This is a u8. + * The requestor instance ID for the follow up Service Discovery Frame. + * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the + * follow up Service Discovery Frame. This is a binary attribute. + * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a + * close range. The range itself (RSSI) is defined by the device. + * This is a flag. + * @NL80211_NAN_FUNC_TTL: strictly positive number of DWs this function should + * stay active. If not present infinite TTL is assumed. This is a u32. + * @NL80211_NAN_FUNC_SERVICE_INFO: array of bytes describing the service + * specific info. This is a binary attribute. + * @NL80211_NAN_FUNC_SRF: Service Receive Filter. This is a nested attribute. + * See &enum nl80211_nan_srf_attributes. + * @NL80211_NAN_FUNC_RX_MATCH_FILTER: Receive Matching filter. This is a nested + * attribute. It is a list of binary values. + * @NL80211_NAN_FUNC_TX_MATCH_FILTER: Transmit Matching filter. This is a + * nested attribute. It is a list of binary values. + * @NL80211_NAN_FUNC_INSTANCE_ID: The instance ID of the function. + * Its type is u8 and it cannot be 0. + * @NL80211_NAN_FUNC_TERM_REASON: NAN function termination reason. + * See &enum nl80211_nan_func_term_reason. + * + * @NUM_NL80211_NAN_FUNC_ATTR: internal + * @NL80211_NAN_FUNC_ATTR_MAX: highest NAN function attribute + */ +enum nl80211_nan_func_attributes { + __NL80211_NAN_FUNC_INVALID, + NL80211_NAN_FUNC_TYPE, + NL80211_NAN_FUNC_SERVICE_ID, + NL80211_NAN_FUNC_PUBLISH_TYPE, + NL80211_NAN_FUNC_PUBLISH_BCAST, + NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE, + NL80211_NAN_FUNC_FOLLOW_UP_ID, + NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID, + NL80211_NAN_FUNC_FOLLOW_UP_DEST, + NL80211_NAN_FUNC_CLOSE_RANGE, + NL80211_NAN_FUNC_TTL, + NL80211_NAN_FUNC_SERVICE_INFO, + NL80211_NAN_FUNC_SRF, + NL80211_NAN_FUNC_RX_MATCH_FILTER, + NL80211_NAN_FUNC_TX_MATCH_FILTER, + NL80211_NAN_FUNC_INSTANCE_ID, + NL80211_NAN_FUNC_TERM_REASON, + + /* keep last */ + NUM_NL80211_NAN_FUNC_ATTR, + NL80211_NAN_FUNC_ATTR_MAX = NUM_NL80211_NAN_FUNC_ATTR - 1 +}; + +/** + * enum nl80211_nan_srf_attributes - NAN Service Response filter attributes + * @__NL80211_NAN_SRF_INVALID: invalid + * @NL80211_NAN_SRF_INCLUDE: present if the include bit of the SRF set. + * This is a flag. + * @NL80211_NAN_SRF_BF: Bloom Filter. Present if and only if + * %NL80211_NAN_SRF_MAC_ADDRS isn't present. This attribute is binary. + * @NL80211_NAN_SRF_BF_IDX: index of the Bloom Filter. Mandatory if + * %NL80211_NAN_SRF_BF is present. This is a u8. + * @NL80211_NAN_SRF_MAC_ADDRS: list of MAC addresses for the SRF. Present if + * and only if %NL80211_NAN_SRF_BF isn't present. This is a nested + * attribute. Each nested attribute is a MAC address. + * @NUM_NL80211_NAN_SRF_ATTR: internal + * @NL80211_NAN_SRF_ATTR_MAX: highest NAN SRF attribute + */ +enum nl80211_nan_srf_attributes { + __NL80211_NAN_SRF_INVALID, + NL80211_NAN_SRF_INCLUDE, + NL80211_NAN_SRF_BF, + NL80211_NAN_SRF_BF_IDX, + NL80211_NAN_SRF_MAC_ADDRS, + + /* keep last */ + NUM_NL80211_NAN_SRF_ATTR, + NL80211_NAN_SRF_ATTR_MAX = NUM_NL80211_NAN_SRF_ATTR - 1, +}; + +/** + * enum nl80211_nan_match_attributes - NAN match attributes + * @__NL80211_NAN_MATCH_INVALID: invalid + * @NL80211_NAN_MATCH_FUNC_LOCAL: the local function that had the + * match. This is a nested attribute. + * See &enum nl80211_nan_func_attributes. + * @NL80211_NAN_MATCH_FUNC_PEER: the peer function + * that caused the match. This is a nested attribute. + * See &enum nl80211_nan_func_attributes. + * + * @NUM_NL80211_NAN_MATCH_ATTR: internal + * @NL80211_NAN_MATCH_ATTR_MAX: highest NAN match attribute + */ +enum nl80211_nan_match_attributes { + __NL80211_NAN_MATCH_INVALID, + NL80211_NAN_MATCH_FUNC_LOCAL, + NL80211_NAN_MATCH_FUNC_PEER, + + /* keep last */ + NUM_NL80211_NAN_MATCH_ATTR, + NL80211_NAN_MATCH_ATTR_MAX = NUM_NL80211_NAN_MATCH_ATTR - 1 +}; + +/** + * nl80211_external_auth_action - Action to perform with external + * authentication request. Used by NL80211_ATTR_EXTERNAL_AUTH_ACTION. + * @NL80211_EXTERNAL_AUTH_START: Start the authentication. + * @NL80211_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication. + */ +enum nl80211_external_auth_action { + NL80211_EXTERNAL_AUTH_START, + NL80211_EXTERNAL_AUTH_ABORT, +}; + +/** + * enum nl80211_ftm_responder_attributes - fine timing measurement + * responder attributes + * @__NL80211_FTM_RESP_ATTR_INVALID: Invalid + * @NL80211_FTM_RESP_ATTR_ENABLED: FTM responder is enabled + * @NL80211_FTM_RESP_ATTR_LCI: The content of Measurement Report Element + * (9.4.2.22 in 802.11-2016) with type 8 - LCI (9.4.2.22.10), + * i.e. starting with the measurement token + * @NL80211_FTM_RESP_ATTR_CIVIC: The content of Measurement Report Element + * (9.4.2.22 in 802.11-2016) with type 11 - Civic (Section 9.4.2.22.13), + * i.e. starting with the measurement token + * @__NL80211_FTM_RESP_ATTR_LAST: Internal + * @NL80211_FTM_RESP_ATTR_MAX: highest FTM responder attribute. + */ +enum nl80211_ftm_responder_attributes { + __NL80211_FTM_RESP_ATTR_INVALID, + + NL80211_FTM_RESP_ATTR_ENABLED, + NL80211_FTM_RESP_ATTR_LCI, + NL80211_FTM_RESP_ATTR_CIVICLOC, + + /* keep last */ + __NL80211_FTM_RESP_ATTR_LAST, + NL80211_FTM_RESP_ATTR_MAX = __NL80211_FTM_RESP_ATTR_LAST - 1, +}; + +/* + * enum nl80211_ftm_responder_stats - FTM responder statistics + * + * These attribute types are used with %NL80211_ATTR_FTM_RESPONDER_STATS + * when getting FTM responder statistics. + * + * @__NL80211_FTM_STATS_INVALID: attribute number 0 is reserved + * @NL80211_FTM_STATS_SUCCESS_NUM: number of FTM sessions in which all frames + * were ssfully answered (u32) + * @NL80211_FTM_STATS_PARTIAL_NUM: number of FTM sessions in which part of the + * frames were successfully answered (u32) + * @NL80211_FTM_STATS_FAILED_NUM: number of failed FTM sessions (u32) + * @NL80211_FTM_STATS_ASAP_NUM: number of ASAP sessions (u32) + * @NL80211_FTM_STATS_NON_ASAP_NUM: number of non-ASAP sessions (u32) + * @NL80211_FTM_STATS_TOTAL_DURATION_MSEC: total sessions durations - gives an + * indication of how much time the responder was busy (u64, msec) + * @NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM: number of unknown FTM triggers - + * triggers from initiators that didn't finish successfully the negotiation + * phase with the responder (u32) + * @NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM: number of FTM reschedule requests + * - initiator asks for a new scheduling although it already has scheduled + * FTM slot (u32) + * @NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM: number of FTM triggers out of + * scheduled window (u32) + * @NL80211_FTM_STATS_PAD: used for padding, ignore + * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal + * @NL80211_FTM_STATS_MAX: highest possible FTM responder stats attribute + */ +enum nl80211_ftm_responder_stats { + __NL80211_FTM_STATS_INVALID, + NL80211_FTM_STATS_SUCCESS_NUM, + NL80211_FTM_STATS_PARTIAL_NUM, + NL80211_FTM_STATS_FAILED_NUM, + NL80211_FTM_STATS_ASAP_NUM, + NL80211_FTM_STATS_NON_ASAP_NUM, + NL80211_FTM_STATS_TOTAL_DURATION_MSEC, + NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM, + NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM, + NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM, + NL80211_FTM_STATS_PAD, + + /* keep last */ + __NL80211_FTM_STATS_AFTER_LAST, + NL80211_FTM_STATS_MAX = __NL80211_FTM_STATS_AFTER_LAST - 1 +}; + +/** + * enum nl80211_preamble - frame preamble types + * @NL80211_PREAMBLE_LEGACY: legacy (HR/DSSS, OFDM, ERP PHY) preamble + * @NL80211_PREAMBLE_HT: HT preamble + * @NL80211_PREAMBLE_VHT: VHT preamble + * @NL80211_PREAMBLE_DMG: DMG preamble + */ +enum nl80211_preamble { + NL80211_PREAMBLE_LEGACY, + NL80211_PREAMBLE_HT, + NL80211_PREAMBLE_VHT, + NL80211_PREAMBLE_DMG, +}; + +/** + * enum nl80211_peer_measurement_type - peer measurement types + * @NL80211_PMSR_TYPE_INVALID: invalid/unused, needed as we use + * these numbers also for attributes + * + * @NL80211_PMSR_TYPE_FTM: flight time measurement + * + * @NUM_NL80211_PMSR_TYPES: internal + * @NL80211_PMSR_TYPE_MAX: highest type number + */ +enum nl80211_peer_measurement_type { + NL80211_PMSR_TYPE_INVALID, + + NL80211_PMSR_TYPE_FTM, + + NUM_NL80211_PMSR_TYPES, + NL80211_PMSR_TYPE_MAX = NUM_NL80211_PMSR_TYPES - 1 +}; + +/** + * enum nl80211_peer_measurement_status - peer measurement status + * @NL80211_PMSR_STATUS_SUCCESS: measurement completed successfully + * @NL80211_PMSR_STATUS_REFUSED: measurement was locally refused + * @NL80211_PMSR_STATUS_TIMEOUT: measurement timed out + * @NL80211_PMSR_STATUS_FAILURE: measurement failed, a type-dependent + * reason may be available in the response data + */ +enum nl80211_peer_measurement_status { + NL80211_PMSR_STATUS_SUCCESS, + NL80211_PMSR_STATUS_REFUSED, + NL80211_PMSR_STATUS_TIMEOUT, + NL80211_PMSR_STATUS_FAILURE, +}; + +/** + * enum nl80211_peer_measurement_req - peer measurement request attributes + * @__NL80211_PMSR_REQ_ATTR_INVALID: invalid + * + * @NL80211_PMSR_REQ_ATTR_DATA: This is a nested attribute with measurement + * type-specific request data inside. The attributes used are from the + * enums named nl80211_peer_measurement__req. + * @NL80211_PMSR_REQ_ATTR_GET_AP_TSF: include AP TSF timestamp, if supported + * (flag attribute) + * + * @NUM_NL80211_PMSR_REQ_ATTRS: internal + * @NL80211_PMSR_REQ_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_req { + __NL80211_PMSR_REQ_ATTR_INVALID, + + NL80211_PMSR_REQ_ATTR_DATA, + NL80211_PMSR_REQ_ATTR_GET_AP_TSF, + + /* keep last */ + NUM_NL80211_PMSR_REQ_ATTRS, + NL80211_PMSR_REQ_ATTR_MAX = NUM_NL80211_PMSR_REQ_ATTRS - 1 +}; + +/** + * enum nl80211_peer_measurement_resp - peer measurement response attributes + * @__NL80211_PMSR_RESP_ATTR_INVALID: invalid + * + * @NL80211_PMSR_RESP_ATTR_DATA: This is a nested attribute with measurement + * type-specific results inside. The attributes used are from the enums + * named nl80211_peer_measurement__resp. + * @NL80211_PMSR_RESP_ATTR_STATUS: u32 value with the measurement status + * (using values from &enum nl80211_peer_measurement_status.) + * @NL80211_PMSR_RESP_ATTR_HOST_TIME: host time (%CLOCK_BOOTTIME) when the + * result was measured; this value is not expected to be accurate to + * more than 20ms. (u64, nanoseconds) + * @NL80211_PMSR_RESP_ATTR_AP_TSF: TSF of the AP that the interface + * doing the measurement is connected to when the result was measured. + * This shall be accurately reported if supported and requested + * (u64, usec) + * @NL80211_PMSR_RESP_ATTR_FINAL: If results are sent to the host partially + * (*e.g. with FTM per-burst data) this flag will be cleared on all but + * the last result; if all results are combined it's set on the single + * result. + * @NL80211_PMSR_RESP_ATTR_PAD: padding for 64-bit attributes, ignore + * + * @NUM_NL80211_PMSR_RESP_ATTRS: internal + * @NL80211_PMSR_RESP_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_resp { + __NL80211_PMSR_RESP_ATTR_INVALID, + + NL80211_PMSR_RESP_ATTR_DATA, + NL80211_PMSR_RESP_ATTR_STATUS, + NL80211_PMSR_RESP_ATTR_HOST_TIME, + NL80211_PMSR_RESP_ATTR_AP_TSF, + NL80211_PMSR_RESP_ATTR_FINAL, + NL80211_PMSR_RESP_ATTR_PAD, + + /* keep last */ + NUM_NL80211_PMSR_RESP_ATTRS, + NL80211_PMSR_RESP_ATTR_MAX = NUM_NL80211_PMSR_RESP_ATTRS - 1 +}; + +/** + * enum nl80211_peer_measurement_peer_attrs - peer attributes for measurement + * @__NL80211_PMSR_PEER_ATTR_INVALID: invalid + * + * @NL80211_PMSR_PEER_ATTR_ADDR: peer's MAC address + * @NL80211_PMSR_PEER_ATTR_CHAN: channel definition, nested, using top-level + * attributes like %NL80211_ATTR_WIPHY_FREQ etc. + * @NL80211_PMSR_PEER_ATTR_REQ: This is a nested attribute indexed by + * measurement type, with attributes from the + * &enum nl80211_peer_measurement_req inside. + * @NL80211_PMSR_PEER_ATTR_RESP: This is a nested attribute indexed by + * measurement type, with attributes from the + * &enum nl80211_peer_measurement_resp inside. + * + * @NUM_NL80211_PMSR_PEER_ATTRS: internal + * @NL80211_PMSR_PEER_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_peer_attrs { + __NL80211_PMSR_PEER_ATTR_INVALID, + + NL80211_PMSR_PEER_ATTR_ADDR, + NL80211_PMSR_PEER_ATTR_CHAN, + NL80211_PMSR_PEER_ATTR_REQ, + NL80211_PMSR_PEER_ATTR_RESP, + + /* keep last */ + NUM_NL80211_PMSR_PEER_ATTRS, + NL80211_PMSR_PEER_ATTR_MAX = NUM_NL80211_PMSR_PEER_ATTRS - 1, +}; + +/** + * enum nl80211_peer_measurement_attrs - peer measurement attributes + * @__NL80211_PMSR_ATTR_INVALID: invalid + * + * @NL80211_PMSR_ATTR_MAX_PEERS: u32 attribute used for capability + * advertisement only, indicates the maximum number of peers + * measurements can be done with in a single request + * @NL80211_PMSR_ATTR_REPORT_AP_TSF: flag attribute in capability + * indicating that the connected AP's TSF can be reported in + * measurement results + * @NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR: flag attribute in capability + * indicating that MAC address randomization is supported. + * @NL80211_PMSR_ATTR_TYPE_CAPA: capabilities reported by the device, + * this contains a nesting indexed by measurement type, and + * type-specific capabilities inside, which are from the enums + * named nl80211_peer_measurement__capa. + * @NL80211_PMSR_ATTR_PEERS: nested attribute, the nesting index is + * meaningless, just a list of peers to measure with, with the + * sub-attributes taken from + * &enum nl80211_peer_measurement_peer_attrs. + * + * @NUM_NL80211_PMSR_ATTR: internal + * @NL80211_PMSR_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_attrs { + __NL80211_PMSR_ATTR_INVALID, + + NL80211_PMSR_ATTR_MAX_PEERS, + NL80211_PMSR_ATTR_REPORT_AP_TSF, + NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR, + NL80211_PMSR_ATTR_TYPE_CAPA, + NL80211_PMSR_ATTR_PEERS, + + /* keep last */ + NUM_NL80211_PMSR_ATTR, + NL80211_PMSR_ATTR_MAX = NUM_NL80211_PMSR_ATTR - 1 +}; + +/** + * enum nl80211_peer_measurement_ftm_capa - FTM capabilities + * @__NL80211_PMSR_FTM_CAPA_ATTR_INVALID: invalid + * + * @NL80211_PMSR_FTM_CAPA_ATTR_ASAP: flag attribute indicating ASAP mode + * is supported + * @NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP: flag attribute indicating non-ASAP + * mode is supported + * @NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI: flag attribute indicating if LCI + * data can be requested during the measurement + * @NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC: flag attribute indicating if civic + * location data can be requested during the measurement + * @NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES: u32 bitmap attribute of bits + * from &enum nl80211_preamble. + * @NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS: bitmap of values from + * &enum nl80211_chan_width indicating the supported channel + * bandwidths for FTM. Note that a higher channel bandwidth may be + * configured to allow for other measurements types with different + * bandwidth requirement in the same measurement. + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT: u32 attribute indicating + * the maximum bursts exponent that can be used (if not present anything + * is valid) + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST: u32 attribute indicating + * the maximum FTMs per burst (if not present anything is valid) + * + * @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_ftm_capa { + __NL80211_PMSR_FTM_CAPA_ATTR_INVALID, + + NL80211_PMSR_FTM_CAPA_ATTR_ASAP, + NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP, + NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI, + NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC, + NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES, + NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST, + + /* keep last */ + NUM_NL80211_PMSR_FTM_CAPA_ATTR, + NL80211_PMSR_FTM_CAPA_ATTR_MAX = NUM_NL80211_PMSR_FTM_CAPA_ATTR - 1 +}; + +/** + * enum nl80211_peer_measurement_ftm_req - FTM request attributes + * @__NL80211_PMSR_FTM_REQ_ATTR_INVALID: invalid + * + * @NL80211_PMSR_FTM_REQ_ATTR_ASAP: ASAP mode requested (flag) + * @NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE: preamble type (see + * &enum nl80211_preamble), optional for DMG (u32) + * @NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP: number of bursts exponent as in + * 802.11-2016 9.4.2.168 "Fine Timing Measurement Parameters element" + * (u8, 0-15, optional with default 15 i.e. "no preference") + * @NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD: interval between bursts in units + * of 100ms (u16, optional with default 0) + * @NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION: burst duration, as in 802.11-2016 + * Table 9-257 "Burst Duration field encoding" (u8, 0-15, optional with + * default 15 i.e. "no preference") + * @NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST: number of successful FTM frames + * requested per burst + * (u8, 0-31, optional with default 0 i.e. "no preference") + * @NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES: number of FTMR frame retries + * (u8, default 3) + * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI: request LCI data (flag) + * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC: request civic location data + * (flag) + * + * @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal + * @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_ftm_req { + __NL80211_PMSR_FTM_REQ_ATTR_INVALID, + + NL80211_PMSR_FTM_REQ_ATTR_ASAP, + NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE, + NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP, + NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD, + NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION, + NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST, + NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES, + NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI, + NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC, + + /* keep last */ + NUM_NL80211_PMSR_FTM_REQ_ATTR, + NL80211_PMSR_FTM_REQ_ATTR_MAX = NUM_NL80211_PMSR_FTM_REQ_ATTR - 1 +}; + +/** + * enum nl80211_peer_measurement_ftm_failure_reasons - FTM failure reasons + * @NL80211_PMSR_FTM_FAILURE_UNSPECIFIED: unspecified failure, not used + * @NL80211_PMSR_FTM_FAILURE_NO_RESPONSE: no response from the FTM responder + * @NL80211_PMSR_FTM_FAILURE_REJECTED: FTM responder rejected measurement + * @NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL: we already know the peer is + * on a different channel, so can't measure (if we didn't know, we'd + * try and get no response) + * @NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE: peer can't actually do FTM + * @NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP: invalid T1/T4 timestamps + * received + * @NL80211_PMSR_FTM_FAILURE_PEER_BUSY: peer reports busy, you may retry + * later (see %NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME) + * @NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS: parameters were changed + * by the peer and are no longer supported + */ +enum nl80211_peer_measurement_ftm_failure_reasons { + NL80211_PMSR_FTM_FAILURE_UNSPECIFIED, + NL80211_PMSR_FTM_FAILURE_NO_RESPONSE, + NL80211_PMSR_FTM_FAILURE_REJECTED, + NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL, + NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE, + NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP, + NL80211_PMSR_FTM_FAILURE_PEER_BUSY, + NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS, +}; + +/** + * enum nl80211_peer_measurement_ftm_resp - FTM response attributes + * @__NL80211_PMSR_FTM_RESP_ATTR_INVALID: invalid + * + * @NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON: FTM-specific failure reason + * (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX: optional, if bursts are reported + * as separate results then it will be the burst index 0...(N-1) and + * the top level will indicate partial results (u32) + * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS: number of FTM Request frames + * transmitted (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES: number of FTM Request frames + * that were acknowleged (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME: retry time received from the + * busy peer (u32, seconds) + * @NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP: actual number of bursts exponent + * used by the responder (similar to request, u8) + * @NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION: actual burst duration used by + * the responder (similar to request, u8) + * @NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST: actual FTMs per burst used + * by the responder (similar to request, u8) + * @NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG: average RSSI across all FTM action + * frames (optional, s32, 1/2 dBm) + * @NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD: RSSI spread across all FTM action + * frames (optional, s32, 1/2 dBm) + * @NL80211_PMSR_FTM_RESP_ATTR_TX_RATE: bitrate we used for the response to the + * FTM action frame (optional, nested, using &enum nl80211_rate_info + * attributes) + * @NL80211_PMSR_FTM_RESP_ATTR_RX_RATE: bitrate the responder used for the FTM + * action frame (optional, nested, using &enum nl80211_rate_info attrs) + * @NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG: average RTT (s64, picoseconds, optional + * but one of RTT/DIST must be present) + * @NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE: RTT variance (u64, ps^2, note that + * standard deviation is the square root of variance, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD: RTT spread (u64, picoseconds, + * optional) + * @NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG: average distance (s64, mm, optional + * but one of RTT/DIST must be present) + * @NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE: distance variance (u64, mm^2, note + * that standard deviation is the square root of variance, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD: distance spread (u64, mm, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_LCI: LCI data from peer (binary, optional); + * this is the contents of the Measurement Report Element (802.11-2016 + * 9.4.2.22.1) starting with the Measurement Token, with Measurement + * Type 8. + * @NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC: civic location data from peer + * (binary, optional); + * this is the contents of the Measurement Report Element (802.11-2016 + * 9.4.2.22.1) starting with the Measurement Token, with Measurement + * Type 11. + * @NL80211_PMSR_FTM_RESP_ATTR_PAD: ignore, for u64/s64 padding only + * + * @NUM_NL80211_PMSR_FTM_RESP_ATTR: internal + * @NL80211_PMSR_FTM_RESP_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_ftm_resp { + __NL80211_PMSR_FTM_RESP_ATTR_INVALID, + + NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON, + NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX, + NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS, + NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES, + NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME, + NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP, + NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION, + NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST, + NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG, + NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD, + NL80211_PMSR_FTM_RESP_ATTR_TX_RATE, + NL80211_PMSR_FTM_RESP_ATTR_RX_RATE, + NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG, + NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE, + NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD, + NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG, + NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE, + NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD, + NL80211_PMSR_FTM_RESP_ATTR_LCI, + NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC, + NL80211_PMSR_FTM_RESP_ATTR_PAD, + + /* keep last */ + NUM_NL80211_PMSR_FTM_RESP_ATTR, + NL80211_PMSR_FTM_RESP_ATTR_MAX = NUM_NL80211_PMSR_FTM_RESP_ATTR - 1 +}; + +#endif /* __LINUX_NL80211_H */ diff --git a/contrib/wpa/src/eap_common/eap_defs.h b/contrib/wpa/src/eap_common/eap_defs.h index 54f26ca38f92..bc3047c79c40 100644 --- a/contrib/wpa/src/eap_common/eap_defs.h +++ b/contrib/wpa/src/eap_common/eap_defs.h @@ -92,6 +92,7 @@ typedef enum { EAP_TYPE_GPSK = 51 /* RFC 5433 */, EAP_TYPE_PWD = 52 /* RFC 5931 */, EAP_TYPE_EKE = 53 /* RFC 6124 */, + EAP_TYPE_TEAP = 55 /* RFC 7170 */, EAP_TYPE_EXPANDED = 254 /* RFC 3748 */ } EapType; diff --git a/contrib/wpa/src/eap_common/eap_pwd_common.c b/contrib/wpa/src/eap_common/eap_pwd_common.c index 884150e6c1eb..2b2b8efdbd01 100644 --- a/contrib/wpa/src/eap_common/eap_pwd_common.c +++ b/contrib/wpa/src/eap_common/eap_pwd_common.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" #include "utils/const_time.h" +#include "common/dragonfly.h" #include "crypto/sha256.h" #include "crypto/crypto.h" #include "eap_defs.h" @@ -85,20 +86,11 @@ static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, } -static int eap_pwd_suitable_group(u16 num) -{ - /* Do not allow ECC groups with prime under 256 bits based on guidance - * for the similar design in SAE. */ - return num == 19 || num == 20 || num == 21 || - num == 28 || num == 29 || num == 30; -} - - EAP_PWD_group * get_eap_pwd_group(u16 num) { EAP_PWD_group *grp; - if (!eap_pwd_suitable_group(num)) { + if (!dragonfly_suitable_group(num, 1)) { wpa_printf(MSG_INFO, "EAP-pwd: unsuitable group %u", num); return NULL; } @@ -119,15 +111,6 @@ EAP_PWD_group * get_eap_pwd_group(u16 num) } -static void buf_shift_right(u8 *buf, size_t len, size_t bits) -{ - size_t i; - for (i = len - 1; i > 0; i--) - buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits); - buf[0] >>= bits; -} - - /* * compute a "random" secret point on an elliptic curve based * on the password and identities. @@ -138,22 +121,24 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, const u8 *id_peer, size_t id_peer_len, const u8 *token) { - struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL; - struct crypto_bignum *qr_or_qnr = NULL; + struct crypto_bignum *qr = NULL, *qnr = NULL; u8 qr_bin[MAX_ECC_PRIME_LEN]; u8 qnr_bin[MAX_ECC_PRIME_LEN]; u8 qr_or_qnr_bin[MAX_ECC_PRIME_LEN]; u8 x_bin[MAX_ECC_PRIME_LEN]; - struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL; + u8 prime_bin[MAX_ECC_PRIME_LEN]; + struct crypto_bignum *tmp2 = NULL; struct crypto_hash *hash; unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr; - int ret = 0, check, res; + int ret = 0, res; u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* * mask */ size_t primebytelen = 0, primebitlen; struct crypto_bignum *x_candidate = NULL; const struct crypto_bignum *prime; - u8 mask, found_ctr = 0, is_odd = 0; + u8 found_ctr = 0, is_odd = 0; + int cmp_prime; + unsigned int in_range; if (grp->pwe) return -1; @@ -161,41 +146,26 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, os_memset(x_bin, 0, sizeof(x_bin)); prime = crypto_ec_get_prime(grp->group); + primebitlen = crypto_ec_prime_len_bits(grp->group); + primebytelen = crypto_ec_prime_len(grp->group); + if (crypto_bignum_to_bin(prime, prime_bin, sizeof(prime_bin), + primebytelen) < 0) + return -1; grp->pwe = crypto_ec_point_init(grp->group); - tmp1 = crypto_bignum_init(); - pm1 = crypto_bignum_init(); - one = crypto_bignum_init_set((const u8 *) "\x01", 1); - if (!grp->pwe || !tmp1 || !pm1 || !one) { + if (!grp->pwe) { wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums"); goto fail; } - primebitlen = crypto_ec_prime_len_bits(grp->group); - primebytelen = crypto_ec_prime_len(grp->group); if ((prfbuf = os_malloc(primebytelen)) == NULL) { wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf " "buffer"); goto fail; } - if (crypto_bignum_sub(prime, one, pm1) < 0) - goto fail; /* get a random quadratic residue and nonresidue */ - while (!qr || !qnr) { - if (crypto_bignum_rand(tmp1, prime) < 0) - goto fail; - res = crypto_bignum_legendre(tmp1, prime); - if (!qr && res == 1) { - qr = tmp1; - tmp1 = crypto_bignum_init(); - } else if (!qnr && res == -1) { - qnr = tmp1; - tmp1 = crypto_bignum_init(); - } - if (!tmp1) - goto fail; - } - if (crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), + if (dragonfly_get_random_qr_qnr(prime, &qr, &qnr) < 0 || + crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), primebytelen) < 0 || crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), primebytelen) < 0) @@ -237,6 +207,13 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, if (primebitlen % 8) buf_shift_right(prfbuf, primebytelen, 8 - primebitlen % 8); + cmp_prime = const_time_memcmp(prfbuf, prime_bin, primebytelen); + /* Create a const_time mask for selection based on prf result + * being smaller than prime. */ + in_range = const_time_fill_msb((unsigned int) cmp_prime); + /* The algorithm description would skip the next steps if + * cmp_prime >= 0, but go through them regardless to minimize + * externally observable differences in behavior. */ crypto_bignum_deinit(x_candidate, 1); x_candidate = crypto_bignum_init_set(prfbuf, primebytelen); @@ -246,9 +223,6 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, goto fail; } - if (crypto_bignum_cmp(x_candidate, prime) >= 0) - continue; - wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: x_candidate", prfbuf, primebytelen); const_time_select_bin(found, x_bin, prfbuf, primebytelen, @@ -264,46 +238,16 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, if (!tmp2) goto fail; - /* - * mask tmp2 so doing legendre won't leak timing info - * - * tmp1 is a random number between 1 and p-1 - */ - if (crypto_bignum_rand(tmp1, pm1) < 0 || - crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0 || - crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0) + res = dragonfly_is_quadratic_residue_blind(grp->group, qr_bin, + qnr_bin, tmp2); + if (res < 0) goto fail; - - /* - * Now tmp2 (y^2) is masked, all values between 1 and p-1 - * are equally probable. Multiplying by r^2 does not change - * whether or not tmp2 is a quadratic residue, just masks it. - * - * Flip a coin, multiply by the random quadratic residue or the - * random quadratic nonresidue and record heads or tails. - */ - mask = const_time_eq_u8(crypto_bignum_is_odd(tmp1), 1); - check = const_time_select_s8(mask, 1, -1); - const_time_select_bin(mask, qr_bin, qnr_bin, primebytelen, - qr_or_qnr_bin); - crypto_bignum_deinit(qr_or_qnr, 1); - qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, primebytelen); - if (!qr_or_qnr || - crypto_bignum_mulmod(tmp2, qr_or_qnr, prime, tmp2) < 0) - goto fail; - - /* - * Now it's safe to do legendre, if check is 1 then it's - * a straightforward test (multiplying by qr does not - * change result), if check is -1 then it's the opposite test - * (multiplying a qr by qnr would make a qnr). - */ - res = crypto_bignum_legendre(tmp2, prime); - if (res == -2) - goto fail; - mask = const_time_eq(res, check); found_ctr = const_time_select_u8(found, found_ctr, ctr); - found |= mask; + /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them + * (with res converted to 0/0xff and masked with prf being below + * prime) handles this in constant time. + */ + found |= (res & in_range) * 0xff; } if (found == 0) { wpa_printf(MSG_INFO, @@ -344,13 +288,9 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, } /* cleanliness and order.... */ crypto_bignum_deinit(x_candidate, 1); - crypto_bignum_deinit(pm1, 0); - crypto_bignum_deinit(tmp1, 1); crypto_bignum_deinit(tmp2, 1); crypto_bignum_deinit(qr, 1); crypto_bignum_deinit(qnr, 1); - crypto_bignum_deinit(qr_or_qnr, 1); - crypto_bignum_deinit(one, 0); bin_clear_free(prfbuf, primebytelen); os_memset(qr_bin, 0, sizeof(qr_bin)); os_memset(qnr_bin, 0, sizeof(qnr_bin)); @@ -504,25 +444,6 @@ int eap_pwd_get_rand_mask(EAP_PWD_group *group, struct crypto_bignum *_rand, struct crypto_bignum *_mask, struct crypto_bignum *scalar) { - const struct crypto_bignum *order; - int count; - - order = crypto_ec_get_order(group->group); - - /* Select two random values rand,mask such that 1 < rand,mask < r and - * rand + mask mod r > 1. */ - for (count = 0; count < 100; count++) { - if (crypto_bignum_rand(_rand, order) == 0 && - !crypto_bignum_is_zero(_rand) && - crypto_bignum_rand(_mask, order) == 0 && - !crypto_bignum_is_zero(_mask) && - crypto_bignum_add(_rand, _mask, scalar) == 0 && - crypto_bignum_mod(scalar, order, scalar) == 0 && - !crypto_bignum_is_zero(scalar) && - !crypto_bignum_is_one(scalar)) - return 0; - } - - wpa_printf(MSG_INFO, "EAP-pwd: unable to get randomness"); - return -1; + return dragonfly_generate_scalar(crypto_ec_get_order(group->group), + _rand, _mask, scalar); } diff --git a/contrib/wpa/src/eap_common/eap_sim_common.c b/contrib/wpa/src/eap_common/eap_sim_common.c index 6290c35f1a6b..1e0f80879daf 100644 --- a/contrib/wpa/src/eap_common/eap_sim_common.c +++ b/contrib/wpa/src/eap_common/eap_sim_common.c @@ -945,10 +945,15 @@ u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, if (decrypted == NULL) return NULL; +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Skip AES-128-CBC decryption for fuzz testing"); +#else /* TEST_FUZZ */ if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) { os_free(decrypted); return NULL; } +#endif /* TEST_FUZZ */ wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA", decrypted, encr_data_len); @@ -1203,3 +1208,19 @@ void eap_sim_report_notification(void *msg_ctx, int notification, int aka) } } } + + +int eap_sim_anonymous_username(const u8 *id, size_t id_len) +{ + static const char *anonymous_id_prefix = "anonymous@"; + size_t anonymous_id_len = os_strlen(anonymous_id_prefix); + + if (id_len > anonymous_id_len && + os_memcmp(id, anonymous_id_prefix, anonymous_id_len) == 0) + return 1; /* 'anonymous@realm' */ + + if (id_len > 1 && id[0] == '@') + return 1; /* '@realm' */ + + return 0; +} diff --git a/contrib/wpa/src/eap_common/eap_sim_common.h b/contrib/wpa/src/eap_common/eap_sim_common.h index daeb0e2da0c4..7142b94c9801 100644 --- a/contrib/wpa/src/eap_common/eap_sim_common.h +++ b/contrib/wpa/src/eap_common/eap_sim_common.h @@ -226,5 +226,6 @@ int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad); void eap_sim_report_notification(void *msg_ctx, int notification, int aka); +int eap_sim_anonymous_username(const u8 *id, size_t id_len); #endif /* EAP_SIM_COMMON_H */ diff --git a/contrib/wpa/src/eap_common/eap_teap_common.c b/contrib/wpa/src/eap_common/eap_teap_common.c new file mode 100644 index 000000000000..fbca1b5e41c0 --- /dev/null +++ b/contrib/wpa/src/eap_common/eap_teap_common.c @@ -0,0 +1,698 @@ +/* + * EAP-TEAP common helper functions (RFC 7170) + * Copyright (c) 2008-2019, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/sha384.h" +#include "crypto/tls.h" +#include "eap_defs.h" +#include "eap_teap_common.h" + + +void eap_teap_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len) +{ + struct teap_tlv_hdr hdr; + + hdr.tlv_type = host_to_be16(type); + hdr.length = host_to_be16(len); + wpabuf_put_data(buf, &hdr, sizeof(hdr)); +} + + +void eap_teap_put_tlv(struct wpabuf *buf, u16 type, const void *data, u16 len) +{ + eap_teap_put_tlv_hdr(buf, type, len); + wpabuf_put_data(buf, data, len); +} + + +void eap_teap_put_tlv_buf(struct wpabuf *buf, u16 type, + const struct wpabuf *data) +{ + eap_teap_put_tlv_hdr(buf, type, wpabuf_len(data)); + wpabuf_put_buf(buf, data); +} + + +struct wpabuf * eap_teap_tlv_eap_payload(struct wpabuf *buf) +{ + struct wpabuf *e; + + if (!buf) + return NULL; + + /* Encapsulate EAP packet in EAP-Payload TLV */ + wpa_printf(MSG_DEBUG, "EAP-TEAP: Add EAP-Payload TLV"); + e = wpabuf_alloc(sizeof(struct teap_tlv_hdr) + wpabuf_len(buf)); + if (!e) { + wpa_printf(MSG_ERROR, + "EAP-TEAP: Failed to allocate memory for TLV encapsulation"); + wpabuf_free(buf); + return NULL; + } + eap_teap_put_tlv_buf(e, TEAP_TLV_MANDATORY | TEAP_TLV_EAP_PAYLOAD, buf); + wpabuf_free(buf); + + /* TODO: followed by optional TLVs associated with the EAP packet */ + + return e; +} + + +static int eap_teap_tls_prf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen) +{ + /* TODO: TLS-PRF for TLSv1.3 */ + return tls_prf_sha256(secret, secret_len, label, seed, seed_len, + out, outlen); +} + + +int eap_teap_derive_eap_msk(const u8 *simck, u8 *msk) +{ + /* + * RFC 7170, Section 5.4: EAP Master Session Key Generation + * MSK = TLS-PRF(S-IMCK[j], "Session Key Generating Function", 64) + */ + + if (eap_teap_tls_prf(simck, EAP_TEAP_SIMCK_LEN, + "Session Key Generating Function", (u8 *) "", 0, + msk, EAP_TEAP_KEY_LEN) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Derived key (MSK)", + msk, EAP_TEAP_KEY_LEN); + return 0; +} + + +int eap_teap_derive_eap_emsk(const u8 *simck, u8 *emsk) +{ + /* + * RFC 7170, Section 5.4: EAP Master Session Key Generation + * EMSK = TLS-PRF(S-IMCK[j], + * "Extended Session Key Generating Function", 64) + */ + + if (eap_teap_tls_prf(simck, EAP_TEAP_SIMCK_LEN, + "Extended Session Key Generating Function", + (u8 *) "", 0, emsk, EAP_EMSK_LEN) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Derived key (EMSK)", + emsk, EAP_EMSK_LEN); + return 0; +} + + +int eap_teap_derive_cmk_basic_pw_auth(const u8 *s_imck_msk, u8 *cmk) +{ + u8 imsk[32], imck[EAP_TEAP_IMCK_LEN]; + int res; + + /* FIX: The Basic-Password-Auth (i.e., no inner EAP) case is + * not fully defined in RFC 7170, so this CMK derivation may + * need to be changed if a fixed definition is eventually + * published. For now, derive CMK[0] based on S-IMCK[0] and + * IMSK of 32 octets of zeros. */ + os_memset(imsk, 0, 32); + res = eap_teap_tls_prf(s_imck_msk, EAP_TEAP_SIMCK_LEN, + "Inner Methods Compound Keys", + imsk, 32, imck, sizeof(imck)); + if (res < 0) + return -1; + os_memcpy(cmk, &imck[EAP_TEAP_SIMCK_LEN], EAP_TEAP_CMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: CMK[no-inner-EAP]", + cmk, EAP_TEAP_CMK_LEN); + forced_memzero(imck, sizeof(imck)); + return 0; +} + + +int eap_teap_derive_imck(const u8 *prev_s_imck_msk, const u8 *prev_s_imck_emsk, + const u8 *msk, size_t msk_len, + const u8 *emsk, size_t emsk_len, + u8 *s_imck_msk, u8 *cmk_msk, + u8 *s_imck_emsk, u8 *cmk_emsk) +{ + u8 imsk[64], imck[EAP_TEAP_IMCK_LEN]; + int res; + + /* + * RFC 7170, Section 5.2: + * IMSK = First 32 octets of TLS-PRF(EMSK, "TEAPbindkey@ietf.org" | + * "\0" | 64) + * (if EMSK is not available, MSK is used instead; if neither is + * available, IMSK is 32 octets of zeros; MSK is truncated to 32 octets + * or padded to 32 octets, if needed) + * (64 is encoded as a 2-octet field in network byte order) + * + * S-IMCK[0] = session_key_seed + * IMCK[j] = TLS-PRF(S-IMCK[j-1], "Inner Methods Compound Keys", + * IMSK[j], 60) + * S-IMCK[j] = first 40 octets of IMCK[j] + * CMK[j] = last 20 octets of IMCK[j] + */ + + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: MSK[j]", msk, msk_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: EMSK[j]", emsk, emsk_len); + + if (emsk && emsk_len > 0) { + u8 context[3]; + + context[0] = 0; + context[1] = 0; + context[2] = 64; + if (eap_teap_tls_prf(emsk, emsk_len, "TEAPbindkey@ietf.org", + context, sizeof(context), imsk, 64) < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: IMSK from EMSK", + imsk, 32); + + res = eap_teap_tls_prf(prev_s_imck_emsk, EAP_TEAP_SIMCK_LEN, + "Inner Methods Compound Keys", + imsk, 32, imck, EAP_TEAP_IMCK_LEN); + forced_memzero(imsk, sizeof(imsk)); + if (res < 0) + return -1; + + os_memcpy(s_imck_emsk, imck, EAP_TEAP_SIMCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: EMSK S-IMCK[j]", + s_imck_emsk, EAP_TEAP_SIMCK_LEN); + os_memcpy(cmk_emsk, &imck[EAP_TEAP_SIMCK_LEN], + EAP_TEAP_CMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: EMSK CMK[j]", + cmk_emsk, EAP_TEAP_CMK_LEN); + forced_memzero(imck, EAP_TEAP_IMCK_LEN); + } + + if (msk && msk_len > 0) { + size_t copy_len = msk_len; + + os_memset(imsk, 0, 32); /* zero pad, if needed */ + if (copy_len > 32) + copy_len = 32; + os_memcpy(imsk, msk, copy_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: IMSK from MSK", imsk, 32); + } else { + os_memset(imsk, 0, 32); + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Zero IMSK", imsk, 32); + } + + res = eap_teap_tls_prf(prev_s_imck_msk, EAP_TEAP_SIMCK_LEN, + "Inner Methods Compound Keys", + imsk, 32, imck, EAP_TEAP_IMCK_LEN); + forced_memzero(imsk, sizeof(imsk)); + if (res < 0) + return -1; + + os_memcpy(s_imck_msk, imck, EAP_TEAP_SIMCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: MSK S-IMCK[j]", + s_imck_msk, EAP_TEAP_SIMCK_LEN); + os_memcpy(cmk_msk, &imck[EAP_TEAP_SIMCK_LEN], EAP_TEAP_CMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: MSK CMK[j]", + cmk_msk, EAP_TEAP_CMK_LEN); + forced_memzero(imck, EAP_TEAP_IMCK_LEN); + + return 0; +} + + +static int tls_cipher_suite_match(const u16 *list, size_t count, u16 cs) +{ + size_t i; + + for (i = 0; i < count; i++) { + if (list[i] == cs) + return 1; + } + + return 0; +} + + +static int tls_cipher_suite_mac_sha1(u16 cs) +{ + static const u16 sha1_cs[] = { + 0x0005, 0x0007, 0x000a, 0x000d, 0x0010, 0x0013, 0x0016, 0x001b, + 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, + 0x0037, 0x0038, 0x0039, 0x003a, 0x0041, 0x0042, 0x0043, 0x0044, + 0x0045, 0x0046, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, + 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, + 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, + 0x009a, 0x009b, + 0xc002, 0xc003, 0xc004, 0xc005, 0xc007, 0xc008, 0xc009, 0xc009, + 0xc00a, 0xc00c, 0xc00d, 0xc00e, 0xc00f, 0xc011, 0xc012, 0xc013, + 0xc014, 0xc016, 0xc017, 0xc018, 0xc019, 0xc01a, 0xc01b, 0xc01c, + 0xc014, 0xc01e, 0xc01f, 0xc020, 0xc021, 0xc022, 0xc033, 0xc034, + 0xc035, 0xc036 + }; + + return tls_cipher_suite_match(sha1_cs, ARRAY_SIZE(sha1_cs), cs); +} + + +static int tls_cipher_suite_mac_sha256(u16 cs) +{ + static const u16 sha256_cs[] = { + 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0067, 0x0068, 0x0069, + 0x006a, 0x006b, 0x006c, 0x006d, 0x009c, 0x009e, 0x00a0, 0x00a2, + 0x00a4, 0x00a6, 0x00a8, 0x00aa, 0x00ac, 0x00ae, 0x00b2, 0x00b6, + 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bd, 0x00be, 0x00be, + 0x00bf, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, + 0x1301, 0x1303, 0x1304, 0x1305, + 0xc023, 0xc025, 0xc027, 0xc029, 0xc02b, 0xc02d, 0xc02f, 0xc031, + 0xc037, 0xc03c, 0xc03e, 0xc040, 0xc040, 0xc042, 0xc044, 0xc046, + 0xc048, 0xc04a, 0xc04c, 0xc04e, 0xc050, 0xc052, 0xc054, 0xc056, + 0xc058, 0xc05a, 0xc05c, 0xc05e, 0xc060, 0xc062, 0xc064, 0xc066, + 0xc068, 0xc06a, 0xc06c, 0xc06e, 0xc070, 0xc072, 0xc074, 0xc076, + 0xc078, 0xc07a, 0xc07c, 0xc07e, 0xc080, 0xc082, 0xc084, 0xc086, + 0xc088, 0xc08a, 0xc08c, 0xc08e, 0xc090, 0xc092, 0xc094, 0xc096, + 0xc098, 0xc09a, 0xc0b0, 0xc0b2, 0xc0b4, + 0xcca8, 0xcca9, 0xccaa, 0xccab, 0xccac, 0xccad, 0xccae, + 0xd001, 0xd003, 0xd005 + }; + + return tls_cipher_suite_match(sha256_cs, ARRAY_SIZE(sha256_cs), cs); +} + + +static int tls_cipher_suite_mac_sha384(u16 cs) +{ + static const u16 sha384_cs[] = { + 0x009d, 0x009f, 0x00a1, 0x00a3, 0x00a5, 0x00a7, 0x00a9, 0x00ab, + 0x00ad, 0x00af, 0x00b3, 0x00b7, 0x1302, + 0xc024, 0xc026, 0xc028, 0xc02a, 0xc02c, 0xc02e, 0xc030, 0xc032, + 0xc038, 0xc03d, 0xc03f, 0xc041, 0xc043, 0xc045, 0xc047, 0xc049, + 0xc04b, 0xc04d, 0xc04f, 0xc051, 0xc053, 0xc055, 0xc057, 0xc059, + 0xc05b, 0xc05d, 0xc05f, 0xc061, 0xc063, 0xc065, 0xc067, 0xc069, + 0xc06b, 0xc06d, 0xc06f, 0xc071, 0xc073, 0xc075, 0xc077, 0xc079, + 0xc07b, 0xc07d, 0xc07f, 0xc081, 0xc083, 0xc085, 0xc087, 0xc089, + 0xc08b, 0xc08d, 0xc08f, 0xc091, 0xc093, 0xc095, 0xc097, 0xc099, + 0xc09b, 0xc0b1, 0xc0b3, 0xc0b5, + 0xd002 + }; + + return tls_cipher_suite_match(sha384_cs, ARRAY_SIZE(sha384_cs), cs); +} + + +static int eap_teap_tls_mac(u16 tls_cs, const u8 *cmk, size_t cmk_len, + const u8 *buffer, size_t buffer_len, + u8 *mac, size_t mac_len) +{ + int res; + u8 tmp[48]; + + os_memset(tmp, 0, sizeof(tmp)); + os_memset(mac, 0, mac_len); + + if (tls_cipher_suite_mac_sha1(tls_cs)) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: MAC algorithm: HMAC-SHA1"); + res = hmac_sha1(cmk, cmk_len, buffer, buffer_len, tmp); + } else if (tls_cipher_suite_mac_sha256(tls_cs)) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: MAC algorithm: HMAC-SHA256"); + res = hmac_sha256(cmk, cmk_len, buffer, buffer_len, tmp); + } else if (tls_cipher_suite_mac_sha384(tls_cs)) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: MAC algorithm: HMAC-SHA384"); + res = hmac_sha384(cmk, cmk_len, buffer, buffer_len, tmp); + } else { + wpa_printf(MSG_INFO, + "EAP-TEAP: Unsupported TLS cipher suite 0x%04x", + tls_cs); + res = -1; + } + if (res < 0) + return res; + + /* FIX: RFC 7170 does not describe how to handle truncation of the + * Compound MAC or if the fields are supposed to be of variable length + * based on the negotiated TLS cipher suite (they are defined as having + * fixed size of 20 octets in the TLV description) */ + if (mac_len > sizeof(tmp)) + mac_len = sizeof(tmp); + os_memcpy(mac, tmp, mac_len); + return 0; +} + + +int eap_teap_compound_mac(u16 tls_cs, const struct teap_tlv_crypto_binding *cb, + const struct wpabuf *server_outer_tlvs, + const struct wpabuf *peer_outer_tlvs, + const u8 *cmk, u8 *compound_mac) +{ + u8 *pos, *buffer; + size_t bind_len, buffer_len; + struct teap_tlv_crypto_binding *tmp_cb; + int res; + + /* RFC 7170, Section 5.3 */ + bind_len = sizeof(struct teap_tlv_hdr) + be_to_host16(cb->length); + buffer_len = bind_len + 1; + if (server_outer_tlvs) + buffer_len += wpabuf_len(server_outer_tlvs); + if (peer_outer_tlvs) + buffer_len += wpabuf_len(peer_outer_tlvs); + buffer = os_malloc(buffer_len); + if (!buffer) + return -1; + + pos = buffer; + /* 1. The entire Crypto-Binding TLV attribute with both the EMSK and MSK + * Compound MAC fields zeroed out. */ + os_memcpy(pos, cb, bind_len); + pos += bind_len; + tmp_cb = (struct teap_tlv_crypto_binding *) buffer; + os_memset(tmp_cb->emsk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN); + os_memset(tmp_cb->msk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN); + + /* 2. The EAP Type sent by the other party in the first TEAP message. */ + /* This is supposed to be the EAP Type sent by the other party in the + * first TEAP message, but since we cannot get here without having + * successfully negotiated use of TEAP, this can only be the fixed EAP + * Type of TEAP. */ + *pos++ = EAP_TYPE_TEAP; + + /* 3. All the Outer TLVs from the first TEAP message sent by EAP server + * to peer. */ + if (server_outer_tlvs) { + os_memcpy(pos, wpabuf_head(server_outer_tlvs), + wpabuf_len(server_outer_tlvs)); + pos += wpabuf_len(server_outer_tlvs); + } + + /* 4. All the Outer TLVs from the first TEAP message sent by the peer to + * the EAP server. */ + if (peer_outer_tlvs) { + os_memcpy(pos, wpabuf_head(peer_outer_tlvs), + wpabuf_len(peer_outer_tlvs)); + pos += wpabuf_len(peer_outer_tlvs); + } + + buffer_len = pos - buffer; + + wpa_hexdump_key(MSG_MSGDUMP, + "EAP-TEAP: CMK for Compound MAC calculation", + cmk, EAP_TEAP_CMK_LEN); + wpa_hexdump(MSG_MSGDUMP, + "EAP-TEAP: BUFFER for Compound MAC calculation", + buffer, buffer_len); + res = eap_teap_tls_mac(tls_cs, cmk, EAP_TEAP_CMK_LEN, + buffer, buffer_len, + compound_mac, EAP_TEAP_COMPOUND_MAC_LEN); + os_free(buffer); + + return res; +} + + +int eap_teap_parse_tlv(struct eap_teap_tlv_parse *tlv, + int tlv_type, u8 *pos, size_t len) +{ + switch (tlv_type) { + case TEAP_TLV_RESULT: + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Result TLV", pos, len); + if (tlv->result) { + wpa_printf(MSG_INFO, + "EAP-TEAP: More than one Result TLV in the message"); + tlv->result = TEAP_STATUS_FAILURE; + return -2; + } + if (len < 2) { + wpa_printf(MSG_INFO, "EAP-TEAP: Too short Result TLV"); + tlv->result = TEAP_STATUS_FAILURE; + break; + } + tlv->result = WPA_GET_BE16(pos); + if (tlv->result != TEAP_STATUS_SUCCESS && + tlv->result != TEAP_STATUS_FAILURE) { + wpa_printf(MSG_INFO, "EAP-TEAP: Unknown Result %d", + tlv->result); + tlv->result = TEAP_STATUS_FAILURE; + } + wpa_printf(MSG_DEBUG, "EAP-TEAP: Result: %s", + tlv->result == TEAP_STATUS_SUCCESS ? + "Success" : "Failure"); + break; + case TEAP_TLV_NAK: + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: NAK TLV", pos, len); + if (len < 6) { + wpa_printf(MSG_INFO, "EAP-TEAP: Too short NAK TLV"); + tlv->result = TEAP_STATUS_FAILURE; + break; + } + tlv->nak = pos; + tlv->nak_len = len; + break; + case TEAP_TLV_REQUEST_ACTION: + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Request-Action TLV", + pos, len); + if (tlv->request_action) { + wpa_printf(MSG_INFO, + "EAP-TEAP: More than one Request-Action TLV in the message"); + tlv->iresult = TEAP_STATUS_FAILURE; + return -2; + } + if (len < 2) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Too short Request-Action TLV"); + tlv->iresult = TEAP_STATUS_FAILURE; + break; + } + tlv->request_action_status = pos[0]; + tlv->request_action = pos[1]; + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Request-Action: Status=%u Action=%u", + tlv->request_action_status, tlv->request_action); + break; + case TEAP_TLV_EAP_PAYLOAD: + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EAP-Payload TLV", + pos, len); + if (tlv->eap_payload_tlv) { + wpa_printf(MSG_INFO, + "EAP-TEAP: More than one EAP-Payload TLV in the message"); + tlv->iresult = TEAP_STATUS_FAILURE; + return -2; + } + tlv->eap_payload_tlv = pos; + tlv->eap_payload_tlv_len = len; + break; + case TEAP_TLV_INTERMEDIATE_RESULT: + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Intermediate-Result TLV", + pos, len); + if (len < 2) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Too short Intermediate-Result TLV"); + tlv->iresult = TEAP_STATUS_FAILURE; + break; + } + if (tlv->iresult) { + wpa_printf(MSG_INFO, + "EAP-TEAP: More than one Intermediate-Result TLV in the message"); + tlv->iresult = TEAP_STATUS_FAILURE; + return -2; + } + tlv->iresult = WPA_GET_BE16(pos); + if (tlv->iresult != TEAP_STATUS_SUCCESS && + tlv->iresult != TEAP_STATUS_FAILURE) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Unknown Intermediate Result %d", + tlv->iresult); + tlv->iresult = TEAP_STATUS_FAILURE; + } + wpa_printf(MSG_DEBUG, "EAP-TEAP: Intermediate Result: %s", + tlv->iresult == TEAP_STATUS_SUCCESS ? + "Success" : "Failure"); + break; + case TEAP_TLV_PAC: + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: PAC TLV", pos, len); + if (tlv->pac) { + wpa_printf(MSG_INFO, + "EAP-TEAP: More than one PAC TLV in the message"); + tlv->iresult = TEAP_STATUS_FAILURE; + return -2; + } + tlv->pac = pos; + tlv->pac_len = len; + break; + case TEAP_TLV_CRYPTO_BINDING: + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Crypto-Binding TLV", + pos, len); + if (tlv->crypto_binding) { + wpa_printf(MSG_INFO, + "EAP-TEAP: More than one Crypto-Binding TLV in the message"); + tlv->iresult = TEAP_STATUS_FAILURE; + return -2; + } + tlv->crypto_binding_len = sizeof(struct teap_tlv_hdr) + len; + if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Too short Crypto-Binding TLV"); + tlv->iresult = TEAP_STATUS_FAILURE; + return -2; + } + tlv->crypto_binding = (struct teap_tlv_crypto_binding *) + (pos - sizeof(struct teap_tlv_hdr)); + break; + case TEAP_TLV_BASIC_PASSWORD_AUTH_REQ: + wpa_hexdump_ascii(MSG_MSGDUMP, + "EAP-TEAP: Basic-Password-Auth-Req TLV", + pos, len); + if (tlv->basic_auth_req) { + wpa_printf(MSG_INFO, + "EAP-TEAP: More than one Basic-Password-Auth-Req TLV in the message"); + tlv->iresult = TEAP_STATUS_FAILURE; + return -2; + } + tlv->basic_auth_req = pos; + tlv->basic_auth_req_len = len; + break; + case TEAP_TLV_BASIC_PASSWORD_AUTH_RESP: + wpa_hexdump_ascii(MSG_MSGDUMP, + "EAP-TEAP: Basic-Password-Auth-Resp TLV", + pos, len); + if (tlv->basic_auth_resp) { + wpa_printf(MSG_INFO, + "EAP-TEAP: More than one Basic-Password-Auth-Resp TLV in the message"); + tlv->iresult = TEAP_STATUS_FAILURE; + return -2; + } + tlv->basic_auth_resp = pos; + tlv->basic_auth_resp_len = len; + break; + default: + /* Unknown TLV */ + return -1; + } + + return 0; +} + + +const char * eap_teap_tlv_type_str(enum teap_tlv_types type) +{ + switch (type) { + case TEAP_TLV_AUTHORITY_ID: + return "Authority-ID"; + case TEAP_TLV_IDENTITY_TYPE: + return "Identity-Type"; + case TEAP_TLV_RESULT: + return "Result"; + case TEAP_TLV_NAK: + return "NAK"; + case TEAP_TLV_ERROR: + return "Error"; + case TEAP_TLV_CHANNEL_BINDING: + return "Channel-Binding"; + case TEAP_TLV_VENDOR_SPECIFIC: + return "Vendor-Specific"; + case TEAP_TLV_REQUEST_ACTION: + return "Request-Action"; + case TEAP_TLV_EAP_PAYLOAD: + return "EAP-Payload"; + case TEAP_TLV_INTERMEDIATE_RESULT: + return "Intermediate-Result"; + case TEAP_TLV_PAC: + return "PAC"; + case TEAP_TLV_CRYPTO_BINDING: + return "Crypto-Binding"; + case TEAP_TLV_BASIC_PASSWORD_AUTH_REQ: + return "Basic-Password-Auth-Req"; + case TEAP_TLV_BASIC_PASSWORD_AUTH_RESP: + return "Basic-Password-Auth-Resp"; + case TEAP_TLV_PKCS7: + return "PKCS#7"; + case TEAP_TLV_PKCS10: + return "PKCS#10"; + case TEAP_TLV_TRUSTED_SERVER_ROOT: + return "Trusted-Server-Root"; + } + + return "?"; +} + + +struct wpabuf * eap_teap_tlv_result(int status, int intermediate) +{ + struct wpabuf *buf; + struct teap_tlv_result *result; + + if (status != TEAP_STATUS_FAILURE && status != TEAP_STATUS_SUCCESS) + return NULL; + + buf = wpabuf_alloc(sizeof(*result)); + if (!buf) + return NULL; + wpa_printf(MSG_DEBUG, "EAP-TEAP: Add %sResult TLV(status=%s)", + intermediate ? "Intermediate-" : "", + status == TEAP_STATUS_SUCCESS ? "Success" : "Failure"); + result = wpabuf_put(buf, sizeof(*result)); + result->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | + (intermediate ? + TEAP_TLV_INTERMEDIATE_RESULT : + TEAP_TLV_RESULT)); + result->length = host_to_be16(2); + result->status = host_to_be16(status); + return buf; +} + + +struct wpabuf * eap_teap_tlv_error(enum teap_error_codes error) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(4 + 4); + if (!buf) + return NULL; + wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Error TLV(Error Code=%d)", + error); + wpabuf_put_be16(buf, TEAP_TLV_MANDATORY | TEAP_TLV_ERROR); + wpabuf_put_be16(buf, 4); + wpabuf_put_be32(buf, error); + return buf; +} + + +int eap_teap_allowed_anon_prov_phase2_method(u8 type) +{ + /* RFC 7170, Section 3.8.3: MUST provide mutual authentication, + * provide key generation, and be resistant to dictionary attack. + * Section 3.8 also mentions requirement for using EMSK Compound MAC. */ + return type == EAP_TYPE_PWD || type == EAP_TYPE_EKE; +} + + +int eap_teap_allowed_anon_prov_cipher_suite(u16 cs) +{ + /* RFC 7170, Section 3.8.3: anonymous ciphersuites MAY be supported as + * long as the TLS pre-master secret is generated form contribution from + * both peers. Accept the recommended TLS_DH_anon_WITH_AES_128_CBC_SHA + * cipher suite and other ciphersuites that use DH in some form, have + * SHA-1 or stronger MAC function, and use reasonable strong cipher. */ + static const u16 ok_cs[] = { + /* DH-anon */ + 0x0034, 0x003a, 0x006c, 0x006d, 0x00a6, 0x00a7, + /* DHE-RSA */ + 0x0033, 0x0039, 0x0067, 0x006b, 0x009e, 0x009f, + /* ECDH-anon */ + 0xc018, 0xc019, + /* ECDH-RSA */ + 0xc003, 0xc00f, 0xc029, 0xc02a, 0xc031, 0xc032, + /* ECDH-ECDSA */ + 0xc004, 0xc005, 0xc025, 0xc026, 0xc02d, 0xc02e, + /* ECDHE-RSA */ + 0xc013, 0xc014, 0xc027, 0xc028, 0xc02f, 0xc030, + /* ECDHE-ECDSA */ + 0xc009, 0xc00a, 0xc023, 0xc024, 0xc02b, 0xc02c, + }; + + return tls_cipher_suite_match(ok_cs, ARRAY_SIZE(ok_cs), cs); +} diff --git a/contrib/wpa/src/eap_common/eap_teap_common.h b/contrib/wpa/src/eap_common/eap_teap_common.h new file mode 100644 index 000000000000..585ec7c2f69a --- /dev/null +++ b/contrib/wpa/src/eap_common/eap_teap_common.h @@ -0,0 +1,218 @@ +/* + * EAP-TEAP definitions (RFC 7170) + * Copyright (c) 2004-2019, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_TEAP_H +#define EAP_TEAP_H + +#define EAP_TEAP_VERSION 1 +#define EAP_TEAP_KEY_LEN 64 +#define EAP_TEAP_IMCK_LEN 60 +#define EAP_TEAP_SIMCK_LEN 40 +#define EAP_TEAP_CMK_LEN 20 +#define EAP_TEAP_COMPOUND_MAC_LEN 20 +#define EAP_TEAP_NONCE_LEN 32 + +#define TEAP_TLS_EXPORTER_LABEL_SKS "EXPORTER: teap session key seed" + +#define TLS_EXT_PAC_OPAQUE 35 + +/* + * RFC 7170: Section 4.2.12.1 - Formats for PAC Attributes + * Note: bit 0x8000 (Mandatory) and bit 0x4000 (Reserved) are also defined + * in the general TLV format (Section 4.2.1). + */ +#define PAC_TYPE_PAC_KEY 1 +#define PAC_TYPE_PAC_OPAQUE 2 +#define PAC_TYPE_CRED_LIFETIME 3 +#define PAC_TYPE_A_ID 4 +#define PAC_TYPE_I_ID 5 +/* 6 - Reserved */ +#define PAC_TYPE_A_ID_INFO 7 +#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8 +#define PAC_TYPE_PAC_INFO 9 +#define PAC_TYPE_PAC_TYPE 10 + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct pac_attr_hdr { + be16 type; + be16 len; +} STRUCT_PACKED; + +struct teap_tlv_hdr { + be16 tlv_type; + be16 length; +} STRUCT_PACKED; + +/* Result TLV and Intermediate-Result TLV */ +struct teap_tlv_result { + be16 tlv_type; + be16 length; + be16 status; + /* for Intermediate-Result TLV, followed by optional TLVs */ +} STRUCT_PACKED; + +struct teap_tlv_nak { + be16 tlv_type; + be16 length; + be32 vendor_id; + be16 nak_type; + /* followed by optional TLVs */ +} STRUCT_PACKED; + +struct teap_tlv_crypto_binding { + be16 tlv_type; /* TLV Type[14b] and M/R flags */ + be16 length; + u8 reserved; + u8 version; + u8 received_version; + u8 subtype; /* Flags[4b] and Sub-Type[4b] */ + u8 nonce[EAP_TEAP_NONCE_LEN]; + u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN]; + u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN]; +} STRUCT_PACKED; + +struct teap_tlv_request_action { + be16 tlv_type; + be16 length; + u8 status; + u8 action; + /* followed by optional TLVs */ +} STRUCT_PACKED; + +enum teap_request_action { + TEAP_REQUEST_ACTION_PROCESS_TLV = 1, + TEAP_REQUEST_ACTION_NEGOTIATE_EAP = 2, +}; + +/* PAC TLV with PAC-Acknowledgement TLV attribute */ +struct teap_tlv_pac_ack { + be16 tlv_type; + be16 length; + be16 pac_type; + be16 pac_len; + be16 result; +} STRUCT_PACKED; + +struct teap_attr_pac_type { + be16 type; /* PAC_TYPE_PAC_TYPE */ + be16 length; /* 2 */ + be16 pac_type; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#define TEAP_CRYPTO_BINDING_SUBTYPE_REQUEST 0 +#define TEAP_CRYPTO_BINDING_SUBTYPE_RESPONSE 1 + +#define TEAP_CRYPTO_BINDING_EMSK_CMAC 1 +#define TEAP_CRYPTO_BINDING_MSK_CMAC 2 +#define TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC 3 + + +#define EAP_TEAP_PAC_KEY_LEN 48 + +/* RFC 7170: 4.2.12.6 PAC-Type TLV */ +#define PAC_TYPE_TUNNEL_PAC 1 + + +/* RFC 7170, 4.2.1: General TLV Format */ +enum teap_tlv_types { + TEAP_TLV_AUTHORITY_ID = 1, + TEAP_TLV_IDENTITY_TYPE = 2, + TEAP_TLV_RESULT = 3, + TEAP_TLV_NAK = 4, + TEAP_TLV_ERROR = 5, + TEAP_TLV_CHANNEL_BINDING = 6, + TEAP_TLV_VENDOR_SPECIFIC = 7, + TEAP_TLV_REQUEST_ACTION = 8, + TEAP_TLV_EAP_PAYLOAD = 9, + TEAP_TLV_INTERMEDIATE_RESULT = 10, + TEAP_TLV_PAC = 11, + TEAP_TLV_CRYPTO_BINDING = 12, + TEAP_TLV_BASIC_PASSWORD_AUTH_REQ = 13, + TEAP_TLV_BASIC_PASSWORD_AUTH_RESP = 14, + TEAP_TLV_PKCS7 = 15, + TEAP_TLV_PKCS10 = 16, + TEAP_TLV_TRUSTED_SERVER_ROOT = 17, +}; + +enum teap_tlv_result_status { + TEAP_STATUS_SUCCESS = 1, + TEAP_STATUS_FAILURE = 2 +}; + +#define TEAP_TLV_MANDATORY 0x8000 +#define TEAP_TLV_TYPE_MASK 0x3fff + +/* RFC 7170, 4.2.6: Error TLV */ +enum teap_error_codes { + TEAP_ERROR_INNER_METHOD = 1001, + TEAP_ERROR_UNSPEC_AUTH_INFRA_PROBLEM = 1002, + TEAP_ERROR_UNSPEC_AUTHENTICATION_FAILURE = 1003, + TEAP_ERROR_UNSPEC_AUTHORIZATION_FAILURE = 1004, + TEAP_ERROR_USER_ACCOUNT_CRED_UNAVAILABLE = 1005, + TEAP_ERROR_USER_ACCOUNT_EXPIRED = 1006, + TEAP_ERROR_USER_ACCOUNT_LOCKED_TRY_AGAIN_LATER = 1007, + TEAP_ERROR_USER_ACCOUNT_LOCKED_ADMIN_REQ = 1008, + TEAP_ERROR_TUNNEL_COMPROMISE_ERROR = 2001, + TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED = 2002, +}; + +struct wpabuf; +struct tls_connection; + +struct eap_teap_tlv_parse { + u8 *eap_payload_tlv; + size_t eap_payload_tlv_len; + struct teap_tlv_crypto_binding *crypto_binding; + size_t crypto_binding_len; + int iresult; + int result; + u8 *nak; + size_t nak_len; + u8 request_action; + u8 request_action_status; + u8 *pac; + size_t pac_len; + u8 *basic_auth_req; + size_t basic_auth_req_len; + u8 *basic_auth_resp; + size_t basic_auth_resp_len; +}; + +void eap_teap_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len); +void eap_teap_put_tlv(struct wpabuf *buf, u16 type, const void *data, u16 len); +void eap_teap_put_tlv_buf(struct wpabuf *buf, u16 type, + const struct wpabuf *data); +struct wpabuf * eap_teap_tlv_eap_payload(struct wpabuf *buf); +int eap_teap_derive_eap_msk(const u8 *simck, u8 *msk); +int eap_teap_derive_eap_emsk(const u8 *simck, u8 *emsk); +int eap_teap_derive_cmk_basic_pw_auth(const u8 *s_imck_msk, u8 *cmk); +int eap_teap_derive_imck(const u8 *prev_s_imck_msk, const u8 *prev_s_imck_emsk, + const u8 *msk, size_t msk_len, + const u8 *emsk, size_t emsk_len, + u8 *s_imck_msk, u8 *cmk_msk, + u8 *s_imck_emsk, u8 *cmk_emsk); +int eap_teap_compound_mac(u16 tls_cs, const struct teap_tlv_crypto_binding *cb, + const struct wpabuf *server_outer_tlvs, + const struct wpabuf *peer_outer_tlvs, + const u8 *cmk, u8 *compound_mac); +int eap_teap_parse_tlv(struct eap_teap_tlv_parse *tlv, + int tlv_type, u8 *pos, size_t len); +const char * eap_teap_tlv_type_str(enum teap_tlv_types type); +struct wpabuf * eap_teap_tlv_result(int status, int intermediate); +struct wpabuf * eap_teap_tlv_error(enum teap_error_codes error); +int eap_teap_allowed_anon_prov_phase2_method(u8 type); +int eap_teap_allowed_anon_prov_cipher_suite(u16 cs); + +#endif /* EAP_TEAP_H */ diff --git a/contrib/wpa/src/eap_peer/eap.c b/contrib/wpa/src/eap_peer/eap.c index 974c475ff2d4..ac15e0e50761 100644 --- a/contrib/wpa/src/eap_peer/eap.c +++ b/contrib/wpa/src/eap_peer/eap.c @@ -2097,12 +2097,8 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, } } - sm->eapol_cb->notify_cert(sm->eapol_ctx, - data->peer_cert.depth, - data->peer_cert.subject, - data->peer_cert.altsubject, - data->peer_cert.num_altsubject, - hash_hex, data->peer_cert.cert); + sm->eapol_cb->notify_cert(sm->eapol_ctx, &data->peer_cert, + hash_hex); break; case TLS_ALERT: if (data->alert.is_local) @@ -2607,7 +2603,7 @@ static int eap_allowed_phase2_type(int vendor, int type) if (vendor != EAP_VENDOR_IETF) return 0; return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS && - type != EAP_TYPE_FAST; + type != EAP_TYPE_FAST && type != EAP_TYPE_TEAP; } diff --git a/contrib/wpa/src/eap_peer/eap.h b/contrib/wpa/src/eap_peer/eap.h index d0837e37a0e0..acd70d05d169 100644 --- a/contrib/wpa/src/eap_peer/eap.h +++ b/contrib/wpa/src/eap_peer/eap.h @@ -16,6 +16,7 @@ struct eap_sm; struct wpa_config_blob; struct wpabuf; +struct tls_cert_data; struct eap_method_type { int vendor; @@ -226,16 +227,11 @@ struct eapol_callbacks { /** * notify_cert - Notification of a peer certificate * @ctx: eapol_ctx from eap_peer_sm_init() call - * @depth: Depth in certificate chain (0 = server) - * @subject: Subject of the peer certificate - * @altsubject: Select fields from AltSubject of the peer certificate - * @num_altsubject: Number of altsubject values + * @cert: Certificate information * @cert_hash: SHA-256 hash of the certificate - * @cert: Peer certificate */ - void (*notify_cert)(void *ctx, int depth, const char *subject, - const char *altsubject[], int num_altsubject, - const char *cert_hash, const struct wpabuf *cert); + void (*notify_cert)(void *ctx, struct tls_cert_data *cert, + const char *cert_hash); /** * notify_status - Notification of the current EAP state diff --git a/contrib/wpa/src/eap_peer/eap_aka.c b/contrib/wpa/src/eap_peer/eap_aka.c index a4441413f0dd..d50bc6186d91 100644 --- a/contrib/wpa/src/eap_peer/eap_aka.c +++ b/contrib/wpa/src/eap_peer/eap_aka.c @@ -31,6 +31,7 @@ struct eap_aka_data { u8 emsk[EAP_EMSK_LEN]; u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN]; u8 auts[EAP_AKA_AUTS_LEN]; + u8 reauth_mac[EAP_SIM_MAC_LEN]; int num_id_req, num_notification; u8 *pseudonym; @@ -622,15 +623,22 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, identity_len = data->reauth_id_len; data->reauth = 1; } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && - data->pseudonym) { + data->pseudonym && + !eap_sim_anonymous_username(data->pseudonym, + data->pseudonym_len)) { identity = data->pseudonym; identity_len = data->pseudonym_len; eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID); } else if (id_req != NO_ID_REQ) { identity = eap_get_config_identity(sm, &identity_len); if (identity) { - eap_aka_clear_identities(sm, data, CLEAR_PSEUDONYM | - CLEAR_REAUTH_ID); + int ids = CLEAR_PSEUDONYM | CLEAR_REAUTH_ID; + + if (data->pseudonym && + eap_sim_anonymous_username(data->pseudonym, + data->pseudonym_len)) + ids &= ~CLEAR_PSEUDONYM; + eap_aka_clear_identities(sm, data, ids); } } if (id_req != NO_ID_REQ) @@ -924,8 +932,13 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, attr->checkcode_len)) { wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " "message"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore AT_CHECKCODE mismatch for fuzz testing"); +#else /* TEST_FUZZ */ return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); +#endif /* TEST_FUZZ */ } #ifdef EAP_AKA_PRIME @@ -1026,7 +1039,9 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, if (data->last_eap_identity) { identity = data->last_eap_identity; identity_len = data->last_eap_identity_len; - } else if (data->pseudonym) { + } else if (data->pseudonym && + !eap_sim_anonymous_username(data->pseudonym, + data->pseudonym_len)) { identity = data->pseudonym; identity_len = data->pseudonym_len; } else { @@ -1055,8 +1070,13 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " "used invalid AT_MAC"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore AT_MAC mismatch for fuzz testing"); +#else /* TEST_FUZZ */ return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); +#endif /* TEST_FUZZ */ } /* Old reauthentication identity must not be used anymore. In @@ -1205,8 +1225,13 @@ static struct wpabuf * eap_aka_process_reauthentication( if (attr->checkcode && eap_aka_verify_checkcode(data, attr->checkcode, attr->checkcode_len)) { +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore AT_CHECKCODE mismatch for fuzz testing"); +#else /* TEST_FUZZ */ wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " "message"); +#endif /* TEST_FUZZ */ return eap_aka_client_error(data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } @@ -1226,6 +1251,14 @@ static struct wpabuf * eap_aka_process_reauthentication( EAP_AKA_UNABLE_TO_PROCESS_PACKET); } + /* At this stage the received MAC has been verified. Use this MAC for + * reauth Session-Id calculation if all other checks pass. + * The peer does not use the local MAC but the received MAC in deriving + * Session-Id. */ + os_memcpy(data->reauth_mac, attr->mac, EAP_SIM_MAC_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-AKA: Server MAC", + data->reauth_mac, EAP_SIM_MAC_LEN); + if (attr->encr_data == NULL || attr->iv == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " "message did not include encrypted data"); @@ -1497,14 +1530,24 @@ static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - *len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN; + if (!data->reauth) + *len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN; + else + *len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN; id = os_malloc(*len); if (id == NULL) return NULL; id[0] = data->eap_method; - os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN); - os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN); + if (!data->reauth) { + os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN); + os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, + EAP_AKA_AUTN_LEN); + } else { + os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN); + os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac, + EAP_SIM_MAC_LEN); + } wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len); return id; diff --git a/contrib/wpa/src/eap_peer/eap_config.h b/contrib/wpa/src/eap_peer/eap_config.h index 3a88f2abd236..148c9066d27c 100644 --- a/contrib/wpa/src/eap_peer/eap_config.h +++ b/contrib/wpa/src/eap_peer/eap_config.h @@ -816,6 +816,8 @@ struct eap_peer_config { EXT_CERT_CHECK_GOOD, EXT_CERT_CHECK_BAD, } pending_ext_cert_check; + + int teap_anon_dh; }; diff --git a/contrib/wpa/src/eap_peer/eap_eke.c b/contrib/wpa/src/eap_peer/eap_eke.c index 0de7d6cbf49b..534af262e88a 100644 --- a/contrib/wpa/src/eap_peer/eap_eke.c +++ b/contrib/wpa/src/eap_peer/eap_eke.c @@ -414,7 +414,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, */ if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); - os_memset(key, 0, sizeof(key)); + forced_memzero(key, sizeof(key)); return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -422,7 +422,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (eap_eke_shared_secret(&data->sess, key, data->dh_priv, dhcomp) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); - os_memset(key, 0, sizeof(key)); + forced_memzero(key, sizeof(key)); return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -431,7 +431,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, data->serverid, data->serverid_len, data->peerid, data->peerid_len) < 0) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); - os_memset(key, 0, sizeof(key)); + forced_memzero(key, sizeof(key)); return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -442,7 +442,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, data->sess.dhcomp_len + data->sess.pnonce_len, EAP_EKE_COMMIT); if (resp == NULL) { - os_memset(key, 0, sizeof(key)); + forced_memzero(key, sizeof(key)); return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } @@ -452,11 +452,11 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) { wpabuf_free(resp); wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P"); - os_memset(key, 0, sizeof(key)); + forced_memzero(key, sizeof(key)); return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } - os_memset(key, 0, sizeof(key)); + forced_memzero(key, sizeof(key)); wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P", rpos, data->sess.dhcomp_len); diff --git a/contrib/wpa/src/eap_peer/eap_leap.c b/contrib/wpa/src/eap_peer/eap_leap.c index 233b9eeb1f83..34758e021149 100644 --- a/contrib/wpa/src/eap_peer/eap_leap.c +++ b/contrib/wpa/src/eap_peer/eap_leap.c @@ -390,8 +390,8 @@ static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN); *len = LEAP_KEY_LEN; - os_memset(pw_hash, 0, sizeof(pw_hash)); - os_memset(pw_hash_hash, 0, sizeof(pw_hash_hash)); + forced_memzero(pw_hash, sizeof(pw_hash)); + forced_memzero(pw_hash_hash, sizeof(pw_hash_hash)); return key; } diff --git a/contrib/wpa/src/eap_peer/eap_methods.h b/contrib/wpa/src/eap_peer/eap_methods.h index b96b211de258..09e08d3cfe3d 100644 --- a/contrib/wpa/src/eap_peer/eap_methods.h +++ b/contrib/wpa/src/eap_peer/eap_methods.h @@ -97,6 +97,7 @@ int eap_peer_psk_register(void); int eap_peer_aka_register(void); int eap_peer_aka_prime_register(void); int eap_peer_fast_register(void); +int eap_peer_teap_register(void); int eap_peer_pax_register(void); int eap_peer_sake_register(void); int eap_peer_gpsk_register(void); diff --git a/contrib/wpa/src/eap_peer/eap_peap.c b/contrib/wpa/src/eap_peer/eap_peap.c index 8dcf7cc2926f..6453afe2fc57 100644 --- a/contrib/wpa/src/eap_peer/eap_peap.c +++ b/contrib/wpa/src/eap_peer/eap_peap.c @@ -295,7 +295,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) res = peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys", isk, sizeof(isk), imck, sizeof(imck)); - os_memset(isk, 0, sizeof(isk)); + forced_memzero(isk, sizeof(isk)); if (res < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", @@ -305,7 +305,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); os_memcpy(data->cmk, imck + 40, 20); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); - os_memset(imck, 0, sizeof(imck)); + forced_memzero(imck, sizeof(imck)); return 0; } @@ -1267,7 +1267,7 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) os_memcpy(key, csk, EAP_TLS_KEY_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", key, EAP_TLS_KEY_LEN); - os_memset(csk, 0, sizeof(csk)); + forced_memzero(csk, sizeof(csk)); } else os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); diff --git a/contrib/wpa/src/eap_peer/eap_pwd.c b/contrib/wpa/src/eap_peer/eap_pwd.c index 76fcad4a50e0..54f102a3b043 100644 --- a/contrib/wpa/src/eap_peer/eap_pwd.c +++ b/contrib/wpa/src/eap_peer/eap_pwd.c @@ -30,6 +30,7 @@ struct eap_pwd_data { u8 *password; size_t password_len; int password_hash; + struct wpa_freq_range_list allowed_groups; u16 group_num; u8 prep; u8 token[4]; @@ -54,6 +55,9 @@ struct eap_pwd_data { }; +static void eap_pwd_deinit(struct eap_sm *sm, void *priv); + + #ifndef CONFIG_NO_STDOUT_DEBUG static const char * eap_pwd_state_txt(int state) { @@ -92,6 +96,7 @@ static void * eap_pwd_init(struct eap_sm *sm) size_t identity_len, password_len; int fragment_size; int pwhash; + const char *phase1; password = eap_get_config_password2(sm, &password_len, &pwhash); if (password == NULL) { @@ -129,6 +134,30 @@ static void * eap_pwd_init(struct eap_sm *sm) data->password_len = password_len; data->password_hash = pwhash; + phase1 = eap_get_config_phase1(sm); + if (phase1) { + const char *pos, *end; + char *copy = NULL; + int res; + + pos = os_strstr(phase1, "eap_pwd_groups="); + if (pos) { + pos += 15; + end = os_strchr(pos, ' '); + if (end) { + copy = os_zalloc(end - pos + 1); + if (!copy) + goto fail; + os_memcpy(copy, pos, end - pos); + pos = copy; + } + res = freq_range_list_parse(&data->allowed_groups, pos); + os_free(copy); + if (res) + goto fail; + } + } + data->out_frag_pos = data->in_frag_pos = 0; data->inbuf = data->outbuf = NULL; fragment_size = eap_get_config_fragment_size(sm); @@ -140,6 +169,9 @@ static void * eap_pwd_init(struct eap_sm *sm) data->state = PWD_ID_Req; return data; +fail: + eap_pwd_deinit(sm, data); + return NULL; } @@ -163,6 +195,7 @@ static void eap_pwd_deinit(struct eap_sm *sm, void *priv) } wpabuf_free(data->inbuf); wpabuf_free(data->outbuf); + os_free(data->allowed_groups.range); bin_clear_free(data, sizeof(*data)); } @@ -203,6 +236,18 @@ static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len) } +static int eap_pwd_allowed_group(struct eap_pwd_data *data, u16 group) +{ + if (!data->allowed_groups.range) { + /* By default, allow the groups using NIST curves P-256, P-384, + * and P-521. */ + return group == 19 || group == 20 || group == 21; + } + + return freq_range_list_includes(&data->allowed_groups, group); +} + + static void eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, struct eap_method_ret *ret, @@ -228,9 +273,11 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, wpa_printf(MSG_DEBUG, "EAP-PWD: Server EAP-pwd-ID proposal: group=%u random=%u prf=%u prep=%u", data->group_num, id->random_function, id->prf, id->prep); - if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || - (id->prf != EAP_PWD_DEFAULT_PRF)) { - ret->ignore = TRUE; + if (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC || + id->prf != EAP_PWD_DEFAULT_PRF || + !eap_pwd_allowed_group(data, data->group_num)) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unsupported or disabled proposal"); eap_pwd_state(data, FAILURE); return; } @@ -362,7 +409,7 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, data->password_len, pwhash); if (res == 0) res = hash_nt_password_hash(pwhash, pwhashhash); - os_memset(pwhash, 0, sizeof(pwhash)); + forced_memzero(pwhash, sizeof(pwhash)); } if (res) { @@ -514,8 +561,8 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, data->id_server, data->id_server_len, data->id_peer, data->id_peer_len, data->token); - os_memset(pwhashhash, 0, sizeof(pwhashhash)); - os_memset(salthashpwd, 0, sizeof(salthashpwd)); + forced_memzero(pwhashhash, sizeof(pwhashhash)); + forced_memzero(salthashpwd, sizeof(salthashpwd)); if (res) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE"); eap_pwd_state(data, FAILURE); diff --git a/contrib/wpa/src/eap_peer/eap_sim.c b/contrib/wpa/src/eap_peer/eap_sim.c index ba5eea9ddfa2..2ea4efd07c6d 100644 --- a/contrib/wpa/src/eap_peer/eap_sim.c +++ b/contrib/wpa/src/eap_peer/eap_sim.c @@ -32,6 +32,7 @@ struct eap_sim_data { u8 msk[EAP_SIM_KEYING_DATA_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 rand[3][GSM_RAND_LEN]; + u8 reauth_mac[EAP_SIM_MAC_LEN]; int num_id_req, num_notification; u8 *pseudonym; @@ -492,15 +493,22 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, identity_len = data->reauth_id_len; data->reauth = 1; } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && - data->pseudonym) { + data->pseudonym && + !eap_sim_anonymous_username(data->pseudonym, + data->pseudonym_len)) { identity = data->pseudonym; identity_len = data->pseudonym_len; eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID); } else if (id_req != NO_ID_REQ) { identity = eap_get_config_identity(sm, &identity_len); if (identity) { - eap_sim_clear_identities(sm, data, CLEAR_PSEUDONYM | - CLEAR_REAUTH_ID); + int ids = CLEAR_PSEUDONYM | CLEAR_REAUTH_ID; + + if (data->pseudonym && + eap_sim_anonymous_username(data->pseudonym, + data->pseudonym_len)) + ids &= ~CLEAR_PSEUDONYM; + eap_sim_clear_identities(sm, data, ids); } } if (id_req != NO_ID_REQ) @@ -768,7 +776,9 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, if (data->last_eap_identity) { identity = data->last_eap_identity; identity_len = data->last_eap_identity_len; - } else if (data->pseudonym) { + } else if (data->pseudonym && + !eap_sim_anonymous_username(data->pseudonym, + data->pseudonym_len)) { identity = data->pseudonym; identity_len = data->pseudonym_len; } else { @@ -794,8 +804,13 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, EAP_SIM_NONCE_MT_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " "used invalid AT_MAC"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore AT_MAC mismatch for fuzz testing"); +#else /* TEST_FUZZ */ return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); +#endif /* TEST_FUZZ */ } /* Old reauthentication identity must not be used anymore. In @@ -954,10 +969,30 @@ static struct wpabuf * eap_sim_process_reauthentication( { wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " "did not have valid AT_MAC"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore AT_MAC mismatch for fuzz testing"); +#else /* TEST_FUZZ */ return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); +#endif /* TEST_FUZZ */ } + /* At this stage the received MAC has been verified. Use this MAC for + * reauth Session-Id calculation if all other checks pass. + * The peer does not use the local MAC but the received MAC in deriving + * Session-Id. */ +#ifdef TEST_FUZZ + if (attr->mac) + os_memcpy(data->reauth_mac, attr->mac, EAP_SIM_MAC_LEN); + else + os_memset(data->reauth_mac, 0x12, EAP_SIM_MAC_LEN); +#else /* TEST_FUZZ */ + os_memcpy(data->reauth_mac, attr->mac, EAP_SIM_MAC_LEN); +#endif /* TEST_FUZZ */ + wpa_hexdump(MSG_DEBUG, "EAP-SIM: Server MAC", + data->reauth_mac, EAP_SIM_MAC_LEN); + if (attr->encr_data == NULL || attr->iv == NULL) { wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " "message did not include encrypted data"); @@ -1216,15 +1251,24 @@ static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN; + if (!data->reauth) + *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN; + else + *len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN; id = os_malloc(*len); if (id == NULL) return NULL; id[0] = EAP_TYPE_SIM; - os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN); - os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt, - EAP_SIM_NONCE_MT_LEN); + if (!data->reauth) { + os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN); + os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, + data->nonce_mt, EAP_SIM_NONCE_MT_LEN); + } else { + os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN); + os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac, + EAP_SIM_MAC_LEN); + } wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len); return id; diff --git a/contrib/wpa/src/eap_peer/eap_teap.c b/contrib/wpa/src/eap_peer/eap_teap.c new file mode 100644 index 000000000000..07ecbd447b1e --- /dev/null +++ b/contrib/wpa/src/eap_peer/eap_teap.c @@ -0,0 +1,2033 @@ +/* + * EAP peer method: EAP-TEAP (RFC 7170) + * Copyright (c) 2004-2019, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/tls.h" +#include "eap_common/eap_teap_common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.h" +#include "eap_teap_pac.h" + +#ifdef EAP_TEAP_DYNAMIC +#include "eap_teap_pac.c" +#endif /* EAP_TEAP_DYNAMIC */ + + +static void eap_teap_deinit(struct eap_sm *sm, void *priv); + + +struct eap_teap_data { + struct eap_ssl_data ssl; + + u8 teap_version; /* Negotiated version */ + u8 received_version; /* Version number received during negotiation */ + u16 tls_cs; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + int inner_method_done; + int result_success_done; + int on_tx_completion; + + struct eap_method_type phase2_type; + struct eap_method_type *phase2_types; + size_t num_phase2_types; + int resuming; /* starting a resumed session */ +#define EAP_TEAP_PROV_UNAUTH 1 +#define EAP_TEAP_PROV_AUTH 2 + int provisioning_allowed; /* Allowed PAC provisioning modes */ + int provisioning; /* doing PAC provisioning (not the normal auth) */ + int anon_provisioning; /* doing anonymous (unauthenticated) + * provisioning */ + int session_ticket_used; + int test_outer_tlvs; + + u8 key_data[EAP_TEAP_KEY_LEN]; + u8 *session_id; + size_t id_len; + u8 emsk[EAP_EMSK_LEN]; + int success; + + struct eap_teap_pac *pac; + struct eap_teap_pac *current_pac; + size_t max_pac_list_len; + int use_pac_binary_format; + + u8 simck_msk[EAP_TEAP_SIMCK_LEN]; + u8 simck_emsk[EAP_TEAP_SIMCK_LEN]; + int simck_idx; + int cmk_emsk_available; + + struct wpabuf *pending_phase2_req; + struct wpabuf *pending_resp; + struct wpabuf *server_outer_tlvs; + struct wpabuf *peer_outer_tlvs; +}; + + +static int eap_teap_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, + const u8 *client_random, + const u8 *server_random, + u8 *master_secret) +{ + struct eap_teap_data *data = ctx; + + wpa_printf(MSG_DEBUG, "EAP-TEAP: SessionTicket callback"); + + if (!master_secret) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: SessionTicket failed - fall back to full TLS handshake"); + data->session_ticket_used = 0; + if (data->provisioning_allowed) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Try to provision a new PAC-Key"); + data->provisioning = 1; + data->current_pac = NULL; + } + return 0; + } + + wpa_hexdump(MSG_DEBUG, "EAP-TEAP: SessionTicket", ticket, len); + + if (!data->current_pac) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: No PAC-Key available for using SessionTicket"); + data->session_ticket_used = 0; + return 0; + } + + /* EAP-TEAP uses PAC-Key as the TLS master_secret */ + os_memcpy(master_secret, data->current_pac->pac_key, + EAP_TEAP_PAC_KEY_LEN); + + data->session_ticket_used = 1; + + return 1; +} + + +static void eap_teap_parse_phase1(struct eap_teap_data *data, + const char *phase1) +{ + const char *pos; + + pos = os_strstr(phase1, "teap_provisioning="); + if (pos) { + data->provisioning_allowed = atoi(pos + 18); + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Automatic PAC provisioning mode: %d", + data->provisioning_allowed); + } + + pos = os_strstr(phase1, "teap_max_pac_list_len="); + if (pos) { + data->max_pac_list_len = atoi(pos + 22); + if (data->max_pac_list_len == 0) + data->max_pac_list_len = 1; + wpa_printf(MSG_DEBUG, "EAP-TEAP: Maximum PAC list length: %lu", + (unsigned long) data->max_pac_list_len); + } + + if (os_strstr(phase1, "teap_pac_format=binary")) { + data->use_pac_binary_format = 1; + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Using binary format for PAC list"); + } + +#ifdef CONFIG_TESTING_OPTIONS + if (os_strstr(phase1, "teap_test_outer_tlvs=1")) + data->test_outer_tlvs = 1; +#endif /* CONFIG_TESTING_OPTIONS */ +} + + +static void * eap_teap_init(struct eap_sm *sm) +{ + struct eap_teap_data *data; + struct eap_peer_config *config = eap_get_config(sm); + + if (!config) + return NULL; + + data = os_zalloc(sizeof(*data)); + if (!data) + return NULL; + data->teap_version = EAP_TEAP_VERSION; + data->max_pac_list_len = 10; + + if (config->phase1) + eap_teap_parse_phase1(data, config->phase1); + + if ((data->provisioning_allowed & EAP_TEAP_PROV_AUTH) && + !config->ca_cert && !config->ca_path) { + /* Prevent PAC provisioning without mutual authentication + * (either by validating server certificate or by suitable + * inner EAP method). */ + wpa_printf(MSG_INFO, + "EAP-TEAP: Disable authenticated provisioning due to no ca_cert/ca_path"); + data->provisioning_allowed &= ~EAP_TEAP_PROV_AUTH; + } + + if (eap_peer_select_phase2_methods(config, "auth=", + &data->phase2_types, + &data->num_phase2_types) < 0) { + eap_teap_deinit(sm, data); + return NULL; + } + + data->phase2_type.vendor = EAP_VENDOR_IETF; + data->phase2_type.method = EAP_TYPE_NONE; + + config->teap_anon_dh = !!(data->provisioning_allowed & + EAP_TEAP_PROV_UNAUTH); + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TEAP)) { + wpa_printf(MSG_INFO, "EAP-TEAP: Failed to initialize SSL"); + eap_teap_deinit(sm, data); + return NULL; + } + + if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn, + eap_teap_session_ticket_cb, + data) < 0) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Failed to set SessionTicket callback"); + eap_teap_deinit(sm, data); + return NULL; + } + + if (!config->pac_file) { + wpa_printf(MSG_INFO, "EAP-TEAP: No PAC file configured"); + eap_teap_deinit(sm, data); + return NULL; + } + + if (data->use_pac_binary_format && + eap_teap_load_pac_bin(sm, &data->pac, config->pac_file) < 0) { + wpa_printf(MSG_INFO, "EAP-TEAP: Failed to load PAC file"); + eap_teap_deinit(sm, data); + return NULL; + } + + if (!data->use_pac_binary_format && + eap_teap_load_pac(sm, &data->pac, config->pac_file) < 0) { + wpa_printf(MSG_INFO, "EAP-TEAP: Failed to load PAC file"); + eap_teap_deinit(sm, data); + return NULL; + } + eap_teap_pac_list_truncate(data->pac, data->max_pac_list_len); + + return data; +} + + +static void eap_teap_clear(struct eap_teap_data *data) +{ + forced_memzero(data->key_data, EAP_TEAP_KEY_LEN); + forced_memzero(data->emsk, EAP_EMSK_LEN); + os_free(data->session_id); + data->session_id = NULL; + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = NULL; + wpabuf_free(data->pending_resp); + data->pending_resp = NULL; + wpabuf_free(data->server_outer_tlvs); + data->server_outer_tlvs = NULL; + wpabuf_free(data->peer_outer_tlvs); + data->peer_outer_tlvs = NULL; + forced_memzero(data->simck_msk, EAP_TEAP_SIMCK_LEN); + forced_memzero(data->simck_emsk, EAP_TEAP_SIMCK_LEN); +} + + +static void eap_teap_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_teap_data *data = priv; + struct eap_teap_pac *pac, *prev; + + if (!data) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->deinit(sm, data->phase2_priv); + eap_teap_clear(data); + os_free(data->phase2_types); + eap_peer_tls_ssl_deinit(sm, &data->ssl); + + pac = data->pac; + prev = NULL; + while (pac) { + prev = pac; + pac = pac->next; + eap_teap_free_pac(prev); + } + + os_free(data); +} + + +static int eap_teap_derive_msk(struct eap_teap_data *data) +{ + /* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j] + * is used in this derivation */ + if (eap_teap_derive_eap_msk(data->simck_msk, data->key_data) < 0 || + eap_teap_derive_eap_emsk(data->simck_msk, data->emsk) < 0) + return -1; + data->success = 1; + return 0; +} + + +static int eap_teap_derive_key_auth(struct eap_sm *sm, + struct eap_teap_data *data) +{ + int res; + + /* RFC 7170, Section 5.1 */ + res = tls_connection_export_key(sm->ssl_ctx, data->ssl.conn, + TEAP_TLS_EXPORTER_LABEL_SKS, NULL, 0, + data->simck_msk, EAP_TEAP_SIMCK_LEN); + if (res) + return res; + wpa_hexdump_key(MSG_DEBUG, + "EAP-TEAP: session_key_seed (S-IMCK[0])", + data->simck_msk, EAP_TEAP_SIMCK_LEN); + os_memcpy(data->simck_emsk, data->simck_msk, EAP_TEAP_SIMCK_LEN); + data->simck_idx = 0; + return 0; +} + + +static int eap_teap_init_phase2_method(struct eap_sm *sm, + struct eap_teap_data *data) +{ + data->inner_method_done = 0; + data->phase2_method = + eap_peer_get_eap_method(data->phase2_type.vendor, + data->phase2_type.method); + if (!data->phase2_method) + return -1; + + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + + return data->phase2_priv == NULL ? -1 : 0; +} + + +static int eap_teap_select_phase2_method(struct eap_teap_data *data, u8 type) +{ + size_t i; + + /* TODO: TNC with anonymous provisioning; need to require both + * completed inner EAP authentication (EAP-pwd or EAP-EKE) and TNC */ + + if (data->anon_provisioning && + !eap_teap_allowed_anon_prov_phase2_method(type)) { + wpa_printf(MSG_INFO, + "EAP-TEAP: EAP type %u not allowed during unauthenticated provisioning", + type); + return -1; + } + +#ifdef EAP_TNC + if (type == EAP_TYPE_TNC) { + data->phase2_type.vendor = EAP_VENDOR_IETF; + data->phase2_type.method = EAP_TYPE_TNC; + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Selected Phase 2 EAP vendor %d method %d for TNC", + data->phase2_type.vendor, + data->phase2_type.method); + return 0; + } +#endif /* EAP_TNC */ + + for (i = 0; i < data->num_phase2_types; i++) { + if (data->phase2_types[i].vendor != EAP_VENDOR_IETF || + data->phase2_types[i].method != type) + continue; + + data->phase2_type.vendor = data->phase2_types[i].vendor; + data->phase2_type.method = data->phase2_types[i].method; + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Selected Phase 2 EAP vendor %d method %d", + data->phase2_type.vendor, + data->phase2_type.method); + break; + } + + if (type != data->phase2_type.method || type == EAP_TYPE_NONE) + return -1; + + return 0; +} + + +static int eap_teap_phase2_request(struct eap_sm *sm, + struct eap_teap_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, + struct wpabuf **resp) +{ + size_t len = be_to_host16(hdr->length); + u8 *pos; + struct eap_method_ret iret; + struct eap_peer_config *config = eap_get_config(sm); + struct wpabuf msg; + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_INFO, + "EAP-TEAP: too short Phase 2 request (len=%lu)", + (unsigned long) len); + return -1; + } + pos = (u8 *) (hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 2 Request: type=%d", *pos); + if (*pos == EAP_TYPE_IDENTITY) { + *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); + return 0; + } + + if (data->phase2_priv && data->phase2_method && + *pos != data->phase2_type.method) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Phase 2 EAP sequence - deinitialize previous method"); + data->phase2_method->deinit(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + data->phase2_type.vendor = EAP_VENDOR_IETF; + data->phase2_type.method = EAP_TYPE_NONE; + } + + if (data->phase2_type.vendor == EAP_VENDOR_IETF && + data->phase2_type.method == EAP_TYPE_NONE && + eap_teap_select_phase2_method(data, *pos) < 0) { + if (eap_peer_tls_phase2_nak(data->phase2_types, + data->num_phase2_types, + hdr, resp)) + return -1; + return 0; + } + + if ((!data->phase2_priv && eap_teap_init_phase2_method(sm, data) < 0) || + !data->phase2_method) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Failed to initialize Phase 2 EAP method %d", + *pos); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + + os_memset(&iret, 0, sizeof(iret)); + wpabuf_set(&msg, hdr, len); + *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, + &msg); + if (iret.methodState == METHOD_DONE) + data->inner_method_done = 1; + if (!(*resp) || + (iret.methodState == METHOD_DONE && + iret.decision == DECISION_FAIL)) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } else if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC)) { + data->phase2_success = 1; + } + + if (!(*resp) && config && + (config->pending_req_identity || config->pending_req_password || + config->pending_req_otp || config->pending_req_new_password || + config->pending_req_sim)) { + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); + } else if (!(*resp)) + return -1; + + return 0; +} + + +static struct wpabuf * eap_teap_tlv_nak(int vendor_id, int tlv_type) +{ + struct wpabuf *buf; + struct teap_tlv_nak *nak; + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Add NAK TLV (Vendor-Id %u NAK-Type %u)", + vendor_id, tlv_type); + buf = wpabuf_alloc(sizeof(*nak)); + if (!buf) + return NULL; + nak = wpabuf_put(buf, sizeof(*nak)); + nak->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | TEAP_TLV_NAK); + nak->length = host_to_be16(6); + nak->vendor_id = host_to_be32(vendor_id); + nak->nak_type = host_to_be16(tlv_type); + return buf; +} + + +static struct wpabuf * eap_teap_tlv_pac_ack(void) +{ + struct wpabuf *buf; + struct teap_tlv_result *res; + struct teap_tlv_pac_ack *ack; + + buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack)); + if (!buf) + return NULL; + + wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV (ack)"); + ack = wpabuf_put(buf, sizeof(*ack)); + ack->tlv_type = host_to_be16(TEAP_TLV_PAC | TEAP_TLV_MANDATORY); + ack->length = host_to_be16(sizeof(*ack) - sizeof(struct teap_tlv_hdr)); + ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT); + ack->pac_len = host_to_be16(2); + ack->result = host_to_be16(TEAP_STATUS_SUCCESS); + + return buf; +} + + +static struct wpabuf * eap_teap_process_eap_payload_tlv( + struct eap_sm *sm, struct eap_teap_data *data, + struct eap_method_ret *ret, + u8 *eap_payload_tlv, size_t eap_payload_tlv_len) +{ + struct eap_hdr *hdr; + struct wpabuf *resp = NULL; + + if (eap_payload_tlv_len < sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: too short EAP Payload TLV (len=%lu)", + (unsigned long) eap_payload_tlv_len); + return NULL; + } + + hdr = (struct eap_hdr *) eap_payload_tlv; + if (be_to_host16(hdr->length) > eap_payload_tlv_len) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: EAP packet overflow in EAP Payload TLV"); + return NULL; + } + + if (hdr->code != EAP_CODE_REQUEST) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Unexpected code=%d in Phase 2 EAP header", + hdr->code); + return NULL; + } + + if (eap_teap_phase2_request(sm, data, ret, hdr, &resp)) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Phase 2 Request processing failed"); + return NULL; + } + + return eap_teap_tlv_eap_payload(resp); +} + + +static struct wpabuf * eap_teap_process_basic_auth_req( + struct eap_sm *sm, struct eap_teap_data *data, + u8 *basic_auth_req, size_t basic_auth_req_len) +{ + const u8 *identity, *password; + size_t identity_len, password_len, plen; + struct wpabuf *resp; + + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: Basic-Password-Auth-Req prompt", + basic_auth_req, basic_auth_req_len); + /* TODO: send over control interface */ + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password(sm, &password_len); + if (!identity || !password || + identity_len > 255 || password_len > 255) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: No username/password suitable for Basic-Password-Auth"); + return eap_teap_tlv_nak(0, TEAP_TLV_BASIC_PASSWORD_AUTH_REQ); + } + + plen = 1 + identity_len + 1 + password_len; + resp = wpabuf_alloc(sizeof(struct teap_tlv_hdr) + plen); + if (!resp) + return NULL; + eap_teap_put_tlv_hdr(resp, TEAP_TLV_BASIC_PASSWORD_AUTH_RESP, plen); + wpabuf_put_u8(resp, identity_len); + wpabuf_put_data(resp, identity, identity_len); + wpabuf_put_u8(resp, password_len); + wpabuf_put_data(resp, password, password_len); + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TEAP: Basic-Password-Auth-Resp", + resp); + + /* Assume this succeeds so that Result TLV(Success) from the server can + * be used to terminate TEAP. */ + data->phase2_success = 1; + + return resp; +} + + +static int +eap_teap_validate_crypto_binding(struct eap_teap_data *data, + const struct teap_tlv_crypto_binding *cb) +{ + u8 flags, subtype; + + subtype = cb->subtype & 0x0f; + flags = cb->subtype >> 4; + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u", + cb->version, cb->received_version, flags, subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce", + cb->nonce, sizeof(cb->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC", + cb->emsk_compound_mac, sizeof(cb->emsk_compound_mac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC", + cb->msk_compound_mac, sizeof(cb->msk_compound_mac)); + + if (cb->version != EAP_TEAP_VERSION || + cb->received_version != data->received_version || + subtype != TEAP_CRYPTO_BINDING_SUBTYPE_REQUEST || + flags < 1 || flags > 3) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Invalid Version/Flags/Sub-Type in Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u", + cb->version, cb->received_version, flags, subtype); + return -1; + } + + if (cb->nonce[EAP_TEAP_NONCE_LEN - 1] & 0x01) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Invalid Crypto-Binding TLV Nonce in request"); + return -1; + } + + return 0; +} + + +static int eap_teap_write_crypto_binding( + struct eap_teap_data *data, + struct teap_tlv_crypto_binding *rbind, + const struct teap_tlv_crypto_binding *cb, + const u8 *cmk_msk, const u8 *cmk_emsk) +{ + u8 subtype, flags; + + rbind->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | + TEAP_TLV_CRYPTO_BINDING); + rbind->length = host_to_be16(sizeof(*rbind) - + sizeof(struct teap_tlv_hdr)); + rbind->version = EAP_TEAP_VERSION; + rbind->received_version = data->received_version; + /* FIX: RFC 7170 is not clear on which Flags value to use when + * Crypto-Binding TLV is used with Basic-Password-Auth */ + flags = cmk_emsk ? TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC : + TEAP_CRYPTO_BINDING_MSK_CMAC; + subtype = TEAP_CRYPTO_BINDING_SUBTYPE_RESPONSE; + rbind->subtype = (flags << 4) | subtype; + os_memcpy(rbind->nonce, cb->nonce, sizeof(cb->nonce)); + inc_byte_array(rbind->nonce, sizeof(rbind->nonce)); + os_memset(rbind->emsk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN); + os_memset(rbind->msk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN); + + if (eap_teap_compound_mac(data->tls_cs, rbind, data->server_outer_tlvs, + data->peer_outer_tlvs, cmk_msk, + rbind->msk_compound_mac) < 0) + return -1; + if (cmk_emsk && + eap_teap_compound_mac(data->tls_cs, rbind, data->server_outer_tlvs, + data->peer_outer_tlvs, cmk_emsk, + rbind->emsk_compound_mac) < 0) + return -1; + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Reply Crypto-Binding TLV: Version %u Received Version %u Flags %u SubType %u", + rbind->version, rbind->received_version, flags, subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce", + rbind->nonce, sizeof(rbind->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC", + rbind->emsk_compound_mac, sizeof(rbind->emsk_compound_mac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC", + rbind->msk_compound_mac, sizeof(rbind->msk_compound_mac)); + + return 0; +} + + +static int eap_teap_get_cmk(struct eap_sm *sm, struct eap_teap_data *data, + u8 *cmk_msk, u8 *cmk_emsk) +{ + u8 *msk = NULL, *emsk = NULL; + size_t msk_len = 0, emsk_len = 0; + int res; + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Determining CMK[%d] for Compound MAC calculation", + data->simck_idx + 1); + + if (!data->phase2_method) + return eap_teap_derive_cmk_basic_pw_auth(data->simck_msk, + cmk_msk); + + if (!data->phase2_method || !data->phase2_priv) { + wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available"); + return -1; + } + + if (data->phase2_method->isKeyAvailable && + !data->phase2_method->isKeyAvailable(sm, data->phase2_priv)) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Phase 2 key material not available"); + return -1; + } + + if (data->phase2_method->isKeyAvailable && + data->phase2_method->getKey) { + msk = data->phase2_method->getKey(sm, data->phase2_priv, + &msk_len); + if (!msk) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Could not fetch Phase 2 MSK"); + return -1; + } + } + + if (data->phase2_method->isKeyAvailable && + data->phase2_method->get_emsk) { + emsk = data->phase2_method->get_emsk(sm, data->phase2_priv, + &emsk_len); + } + + res = eap_teap_derive_imck(data->simck_msk, data->simck_emsk, + msk, msk_len, emsk, emsk_len, + data->simck_msk, cmk_msk, + data->simck_emsk, cmk_emsk); + bin_clear_free(msk, msk_len); + bin_clear_free(emsk, emsk_len); + if (res == 0) { + data->simck_idx++; + if (emsk) + data->cmk_emsk_available = 1; + } + return res; +} + + +static int eap_teap_session_id(struct eap_teap_data *data) +{ + const size_t max_id_len = 100; + int res; + + os_free(data->session_id); + data->session_id = os_malloc(max_id_len); + if (!data->session_id) + return -1; + + data->session_id[0] = EAP_TYPE_TEAP; + res = tls_get_tls_unique(data->ssl.conn, data->session_id + 1, + max_id_len - 1); + if (res < 0) { + os_free(data->session_id); + data->session_id = NULL; + wpa_printf(MSG_ERROR, "EAP-TEAP: Failed to derive Session-Id"); + return -1; + } + + data->id_len = 1 + res; + wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Derived Session-Id", + data->session_id, data->id_len); + return 0; +} + + +static struct wpabuf * eap_teap_process_crypto_binding( + struct eap_sm *sm, struct eap_teap_data *data, + struct eap_method_ret *ret, + const struct teap_tlv_crypto_binding *cb, size_t bind_len) +{ + struct wpabuf *resp; + u8 *pos; + u8 cmk_msk[EAP_TEAP_CMK_LEN]; + u8 cmk_emsk[EAP_TEAP_CMK_LEN]; + const u8 *cmk_emsk_ptr = NULL; + int res; + size_t len; + u8 flags; + + if (eap_teap_validate_crypto_binding(data, cb) < 0 || + eap_teap_get_cmk(sm, data, cmk_msk, cmk_emsk) < 0) + return NULL; + + /* Validate received MSK/EMSK Compound MAC */ + flags = cb->subtype >> 4; + + if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC || + flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) { + u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN]; + + if (eap_teap_compound_mac(data->tls_cs, cb, + data->server_outer_tlvs, + data->peer_outer_tlvs, cmk_msk, + msk_compound_mac) < 0) + return NULL; + res = os_memcmp_const(msk_compound_mac, cb->msk_compound_mac, + EAP_TEAP_COMPOUND_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Received MSK Compound MAC", + cb->msk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, + "EAP-TEAP: Calculated MSK Compound MAC", + msk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN); + if (res != 0) { + wpa_printf(MSG_INFO, + "EAP-TEAP: MSK Compound MAC did not match"); + return NULL; + } + } + + if ((flags == TEAP_CRYPTO_BINDING_EMSK_CMAC || + flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) && + data->cmk_emsk_available) { + u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN]; + + if (eap_teap_compound_mac(data->tls_cs, cb, + data->server_outer_tlvs, + data->peer_outer_tlvs, cmk_emsk, + emsk_compound_mac) < 0) + return NULL; + res = os_memcmp_const(emsk_compound_mac, cb->emsk_compound_mac, + EAP_TEAP_COMPOUND_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Received EMSK Compound MAC", + cb->emsk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, + "EAP-TEAP: Calculated EMSK Compound MAC", + emsk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN); + if (res != 0) { + wpa_printf(MSG_INFO, + "EAP-TEAP: EMSK Compound MAC did not match"); + return NULL; + } + + cmk_emsk_ptr = cmk_emsk; + } + + if (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC && + !data->cmk_emsk_available) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Server included only EMSK Compound MAC, but no locally generated inner EAP EMSK to validate this"); + return NULL; + } + + /* + * Compound MAC was valid, so authentication succeeded. Reply with + * crypto binding to allow server to complete authentication. + */ + + len = sizeof(struct teap_tlv_crypto_binding); + resp = wpabuf_alloc(len); + if (!resp) + return NULL; + + if (data->phase2_success && eap_teap_derive_msk(data) < 0) { + wpa_printf(MSG_INFO, "EAP-TEAP: Failed to generate MSK"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + data->phase2_success = 0; + wpabuf_free(resp); + return NULL; + } + + if (data->phase2_success && eap_teap_session_id(data) < 0) { + wpabuf_free(resp); + return NULL; + } + + pos = wpabuf_put(resp, sizeof(struct teap_tlv_crypto_binding)); + if (eap_teap_write_crypto_binding( + data, (struct teap_tlv_crypto_binding *) pos, + cb, cmk_msk, cmk_emsk_ptr) < 0) { + wpabuf_free(resp); + return NULL; + } + + return resp; +} + + +static void eap_teap_parse_pac_tlv(struct eap_teap_pac *entry, int type, + u8 *pos, size_t len, int *pac_key_found) +{ + switch (type & 0x7fff) { + case PAC_TYPE_PAC_KEY: + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: PAC-Key", pos, len); + if (len != EAP_TEAP_PAC_KEY_LEN) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Invalid PAC-Key length %lu", + (unsigned long) len); + break; + } + *pac_key_found = 1; + os_memcpy(entry->pac_key, pos, len); + break; + case PAC_TYPE_PAC_OPAQUE: + wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Opaque", pos, len); + entry->pac_opaque = pos; + entry->pac_opaque_len = len; + break; + case PAC_TYPE_PAC_INFO: + wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Info", pos, len); + entry->pac_info = pos; + entry->pac_info_len = len; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TEAP: Ignored unknown PAC type %d", + type); + break; + } +} + + +static int eap_teap_process_pac_tlv(struct eap_teap_pac *entry, + u8 *pac, size_t pac_len) +{ + struct pac_attr_hdr *hdr; + u8 *pos; + size_t left, len; + int type, pac_key_found = 0; + + pos = pac; + left = pac_len; + + while (left > sizeof(*hdr)) { + hdr = (struct pac_attr_hdr *) pos; + type = be_to_host16(hdr->type); + len = be_to_host16(hdr->len); + pos += sizeof(*hdr); + left -= sizeof(*hdr); + if (len > left) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: PAC TLV overrun (type=%d len=%lu left=%lu)", + type, (unsigned long) len, + (unsigned long) left); + return -1; + } + + eap_teap_parse_pac_tlv(entry, type, pos, len, &pac_key_found); + + pos += len; + left -= len; + } + + if (!pac_key_found || !entry->pac_opaque || !entry->pac_info) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: PAC TLV does not include all the required fields"); + return -1; + } + + return 0; +} + + +static int eap_teap_parse_pac_info(struct eap_teap_pac *entry, int type, + u8 *pos, size_t len) +{ + u16 pac_type; + u32 lifetime; + struct os_time now; + + switch (type & 0x7fff) { + case PAC_TYPE_CRED_LIFETIME: + if (len != 4) { + wpa_hexdump(MSG_DEBUG, + "EAP-TEAP: PAC-Info - Invalid CRED_LIFETIME length - ignored", + pos, len); + return 0; + } + + /* + * This is not currently saved separately in PAC files since + * the server can automatically initiate PAC update when + * needed. Anyway, the information is available from PAC-Info + * dump if it is needed for something in the future. + */ + lifetime = WPA_GET_BE32(pos); + os_get_time(&now); + wpa_printf(MSG_DEBUG, + "EAP-TEAP: PAC-Info - CRED_LIFETIME %d (%d days)", + lifetime, (lifetime - (u32) now.sec) / 86400); + break; + case PAC_TYPE_A_ID: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - A-ID", + pos, len); + entry->a_id = pos; + entry->a_id_len = len; + break; + case PAC_TYPE_I_ID: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - I-ID", + pos, len); + entry->i_id = pos; + entry->i_id_len = len; + break; + case PAC_TYPE_A_ID_INFO: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - A-ID-Info", + pos, len); + entry->a_id_info = pos; + entry->a_id_info_len = len; + break; + case PAC_TYPE_PAC_TYPE: + /* RFC 7170, Section 4.2.12.6 - PAC-Type TLV */ + if (len != 2) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Invalid PAC-Type length %lu (expected 2)", + (unsigned long) len); + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-TEAP: PAC-Info - PAC-Type", + pos, len); + return -1; + } + pac_type = WPA_GET_BE16(pos); + if (pac_type != PAC_TYPE_TUNNEL_PAC) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Unsupported PAC Type %d", + pac_type); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-TEAP: PAC-Info - PAC-Type %d", + pac_type); + entry->pac_type = pac_type; + break; + default: + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Ignored unknown PAC-Info type %d", type); + break; + } + + return 0; +} + + +static int eap_teap_process_pac_info(struct eap_teap_pac *entry) +{ + struct pac_attr_hdr *hdr; + u8 *pos; + size_t left, len; + int type; + + /* RFC 7170, Section 4.2.12.4 */ + + /* PAC-Type defaults to Tunnel PAC (Type 1) */ + entry->pac_type = PAC_TYPE_TUNNEL_PAC; + + pos = entry->pac_info; + left = entry->pac_info_len; + while (left > sizeof(*hdr)) { + hdr = (struct pac_attr_hdr *) pos; + type = be_to_host16(hdr->type); + len = be_to_host16(hdr->len); + pos += sizeof(*hdr); + left -= sizeof(*hdr); + if (len > left) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: PAC-Info overrun (type=%d len=%lu left=%lu)", + type, (unsigned long) len, + (unsigned long) left); + return -1; + } + + if (eap_teap_parse_pac_info(entry, type, pos, len) < 0) + return -1; + + pos += len; + left -= len; + } + + if (!entry->a_id || !entry->a_id_info) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: PAC-Info does not include all the required fields"); + return -1; + } + + return 0; +} + + +static struct wpabuf * eap_teap_process_pac(struct eap_sm *sm, + struct eap_teap_data *data, + struct eap_method_ret *ret, + u8 *pac, size_t pac_len) +{ + struct eap_peer_config *config = eap_get_config(sm); + struct eap_teap_pac entry; + + os_memset(&entry, 0, sizeof(entry)); + if (eap_teap_process_pac_tlv(&entry, pac, pac_len) || + eap_teap_process_pac_info(&entry)) + return NULL; + + eap_teap_add_pac(&data->pac, &data->current_pac, &entry); + eap_teap_pac_list_truncate(data->pac, data->max_pac_list_len); + if (data->use_pac_binary_format) + eap_teap_save_pac_bin(sm, data->pac, config->pac_file); + else + eap_teap_save_pac(sm, data->pac, config->pac_file); + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Send PAC-Acknowledgement - %s initiated provisioning completed successfully", + data->provisioning ? "peer" : "server"); + return eap_teap_tlv_pac_ack(); +} + + +static int eap_teap_parse_decrypted(struct wpabuf *decrypted, + struct eap_teap_tlv_parse *tlv, + struct wpabuf **resp) +{ + u16 tlv_type; + int mandatory, res; + size_t len; + u8 *pos, *end; + + os_memset(tlv, 0, sizeof(*tlv)); + + /* Parse TLVs from the decrypted Phase 2 data */ + pos = wpabuf_mhead(decrypted); + end = pos + wpabuf_len(decrypted); + while (end - pos >= 4) { + mandatory = pos[0] & 0x80; + tlv_type = WPA_GET_BE16(pos) & 0x3fff; + pos += 2; + len = WPA_GET_BE16(pos); + pos += 2; + if (len > (size_t) (end - pos)) { + wpa_printf(MSG_INFO, "EAP-TEAP: TLV overflow"); + return -1; + } + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Received Phase 2: TLV type %u (%s) length %u%s", + tlv_type, eap_teap_tlv_type_str(tlv_type), + (unsigned int) len, + mandatory ? " (mandatory)" : ""); + + res = eap_teap_parse_tlv(tlv, tlv_type, pos, len); + if (res == -2) + break; + if (res < 0) { + if (mandatory) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: NAK unknown mandatory TLV type %u", + tlv_type); + *resp = eap_teap_tlv_nak(0, tlv_type); + break; + } + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Ignore unknown optional TLV type %u", + tlv_type); + } + + pos += len; + } + + return 0; +} + + +static struct wpabuf * eap_teap_pac_request(void) +{ + struct wpabuf *req; + struct teap_tlv_request_action *act; + struct teap_tlv_hdr *pac; + struct teap_attr_pac_type *type; + + req = wpabuf_alloc(sizeof(*act) + sizeof(*pac) + sizeof(*type)); + if (!req) + return NULL; + + wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Request Action TLV (Process TLV)"); + act = wpabuf_put(req, sizeof(*act)); + act->tlv_type = host_to_be16(TEAP_TLV_REQUEST_ACTION); + act->length = host_to_be16(2); + act->status = TEAP_STATUS_SUCCESS; + act->action = TEAP_REQUEST_ACTION_PROCESS_TLV; + + wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV (PAC-Type = Tunnel)"); + pac = wpabuf_put(req, sizeof(*pac)); + pac->tlv_type = host_to_be16(TEAP_TLV_PAC); + pac->length = host_to_be16(sizeof(*type)); + + type = wpabuf_put(req, sizeof(*type)); + type->type = host_to_be16(PAC_TYPE_PAC_TYPE); + type->length = host_to_be16(2); + type->pac_type = host_to_be16(PAC_TYPE_TUNNEL_PAC); + + return req; +} + + +static int eap_teap_process_decrypted(struct eap_sm *sm, + struct eap_teap_data *data, + struct eap_method_ret *ret, + u8 identifier, + struct wpabuf *decrypted, + struct wpabuf **out_data) +{ + struct wpabuf *resp = NULL, *tmp; + struct eap_teap_tlv_parse tlv; + int failed = 0; + enum teap_error_codes error = 0; + + if (eap_teap_parse_decrypted(decrypted, &tlv, &resp) < 0) { + /* Parsing failed - no response available */ + return 0; + } + + if (resp) { + /* Parsing rejected the message - send out an error response */ + goto send_resp; + } + + if (tlv.result == TEAP_STATUS_FAILURE) { + /* Server indicated failure - respond similarly per + * RFC 7170, 3.6.3. This authentication exchange cannot succeed + * and will be terminated with a cleartext EAP Failure. */ + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Server rejected authentication"); + resp = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + goto send_resp; + } + + if ((tlv.iresult == TEAP_STATUS_SUCCESS || + (!data->result_success_done && + tlv.result == TEAP_STATUS_SUCCESS)) && + !tlv.crypto_binding) { + /* Result TLV or Intermediate-Result TLV indicating success, + * but no Crypto-Binding TLV */ + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Result TLV or Intermediate-Result TLV indicating success, but no Crypto-Binding TLV"); + failed = 1; + error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR; + goto done; + } + + if (tlv.iresult != TEAP_STATUS_SUCCESS && + tlv.iresult != TEAP_STATUS_FAILURE && + data->inner_method_done) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Inner EAP method exchange completed, but no Intermediate-Result TLV included"); + failed = 1; + error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR; + goto done; + } + + if (tlv.basic_auth_req) { + tmp = eap_teap_process_basic_auth_req(sm, data, + tlv.basic_auth_req, + tlv.basic_auth_req_len); + if (!tmp) + failed = 1; + resp = wpabuf_concat(resp, tmp); + } else if (tlv.eap_payload_tlv) { + tmp = eap_teap_process_eap_payload_tlv(sm, data, ret, + tlv.eap_payload_tlv, + tlv.eap_payload_tlv_len); + if (!tmp) + failed = 1; + resp = wpabuf_concat(resp, tmp); + + if (tlv.iresult == TEAP_STATUS_SUCCESS || + tlv.iresult == TEAP_STATUS_FAILURE) { + tmp = eap_teap_tlv_result(failed ? + TEAP_STATUS_FAILURE : + TEAP_STATUS_SUCCESS, 1); + resp = wpabuf_concat(resp, tmp); + if (tlv.iresult == TEAP_STATUS_FAILURE) + failed = 1; + } + } + + if (tlv.crypto_binding) { + if (tlv.iresult != TEAP_STATUS_SUCCESS && + tlv.result != TEAP_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Unexpected Crypto-Binding TLV without Result TLV or Intermediate-Result TLV indicating success"); + failed = 1; + error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED; + goto done; + } + + tmp = eap_teap_process_crypto_binding(sm, data, ret, + tlv.crypto_binding, + tlv.crypto_binding_len); + if (!tmp) { + failed = 1; + error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR; + } else { + resp = wpabuf_concat(resp, tmp); + if (tlv.result == TEAP_STATUS_SUCCESS && !failed) + data->result_success_done = 1; + if (tlv.iresult == TEAP_STATUS_SUCCESS && !failed) + data->inner_method_done = 0; + } + } + + if (data->result_success_done && data->session_ticket_used && + eap_teap_derive_msk(data) == 0) { + /* Assume the server might accept authentication without going + * through inner authentication. */ + wpa_printf(MSG_DEBUG, + "EAP-TEAP: PAC used - server may decide to skip inner authentication"); + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + } + + if (tlv.pac) { + if (tlv.result == TEAP_STATUS_SUCCESS) { + tmp = eap_teap_process_pac(sm, data, ret, + tlv.pac, tlv.pac_len); + resp = wpabuf_concat(resp, tmp); + } else { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: PAC TLV without Result TLV acknowledging success"); + failed = 1; + error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED; + } + } + + if (!data->current_pac && data->provisioning && !failed && !tlv.pac && + tlv.crypto_binding && + (!data->anon_provisioning || + (data->phase2_success && data->phase2_method && + data->phase2_method->vendor == 0 && + eap_teap_allowed_anon_prov_cipher_suite(data->tls_cs) && + eap_teap_allowed_anon_prov_phase2_method( + data->phase2_method->method))) && + (tlv.iresult == TEAP_STATUS_SUCCESS || + tlv.result == TEAP_STATUS_SUCCESS)) { + /* + * Need to request Tunnel PAC when using authenticated + * provisioning. + */ + wpa_printf(MSG_DEBUG, "EAP-TEAP: Request Tunnel PAC"); + tmp = eap_teap_pac_request(); + resp = wpabuf_concat(resp, tmp); + } + +done: + if (failed) { + tmp = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0); + resp = wpabuf_concat(tmp, resp); + + if (error != 0) { + tmp = eap_teap_tlv_error(error); + resp = wpabuf_concat(tmp, resp); + } + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } else if (tlv.result == TEAP_STATUS_SUCCESS) { + tmp = eap_teap_tlv_result(TEAP_STATUS_SUCCESS, 0); + resp = wpabuf_concat(tmp, resp); + } + + if (resp && tlv.result == TEAP_STATUS_SUCCESS && !failed && + tlv.crypto_binding && data->phase2_success) { + /* Successfully completed Phase 2 */ + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Authentication completed successfully"); + ret->methodState = METHOD_MAY_CONT; + data->on_tx_completion = data->provisioning ? + METHOD_MAY_CONT : METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + } + + if (!resp) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: No recognized TLVs - send empty response packet"); + resp = wpabuf_alloc(1); + } + +send_resp: + if (!resp) + return 0; + + wpa_hexdump_buf(MSG_DEBUG, "EAP-TEAP: Encrypting Phase 2 data", resp); + if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TEAP, + data->teap_version, identifier, + resp, out_data)) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Failed to encrypt a Phase 2 frame"); + } + wpabuf_free(resp); + + return 0; +} + + +static int eap_teap_decrypt(struct eap_sm *sm, struct eap_teap_data *data, + struct eap_method_ret *ret, u8 identifier, + const struct wpabuf *in_data, + struct wpabuf **out_data) +{ + struct wpabuf *in_decrypted; + int res; + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Received %lu bytes encrypted data for Phase 2", + (unsigned long) wpabuf_len(in_data)); + + if (data->pending_phase2_req) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Pending Phase 2 request - skip decryption and use old data"); + /* Clear TLS reassembly state. */ + eap_peer_tls_reset_input(&data->ssl); + + in_decrypted = data->pending_phase2_req; + data->pending_phase2_req = NULL; + goto continue_req; + } + + if (wpabuf_len(in_data) == 0) { + /* Received TLS ACK - requesting more fragments */ + res = eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TEAP, + data->teap_version, + identifier, NULL, out_data); + if (res == 0 && !data->ssl.tls_out && + data->on_tx_completion) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Mark authentication completed at full TX of fragments"); + ret->methodState = data->on_tx_completion; + data->on_tx_completion = 0; + ret->decision = DECISION_UNCOND_SUCC; + } + return res; + } + + res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); + if (res) + return res; + +continue_req: + wpa_hexdump_buf(MSG_MSGDUMP, "EAP-TEAP: Decrypted Phase 2 TLV(s)", + in_decrypted); + + if (wpabuf_len(in_decrypted) < 4) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Too short Phase 2 TLV frame (len=%lu)", + (unsigned long) wpabuf_len(in_decrypted)); + wpabuf_free(in_decrypted); + return -1; + } + + res = eap_teap_process_decrypted(sm, data, ret, identifier, + in_decrypted, out_data); + + wpabuf_free(in_decrypted); + + return res; +} + + +static void eap_teap_select_pac(struct eap_teap_data *data, + const u8 *a_id, size_t a_id_len) +{ + if (!a_id) + return; + data->current_pac = eap_teap_get_pac(data->pac, a_id, a_id_len, + PAC_TYPE_TUNNEL_PAC); + if (data->current_pac) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: PAC found for this A-ID (PAC-Type %d)", + data->current_pac->pac_type); + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TEAP: A-ID-Info", + data->current_pac->a_id_info, + data->current_pac->a_id_info_len); + } +} + + +static int eap_teap_use_pac_opaque(struct eap_sm *sm, + struct eap_teap_data *data, + struct eap_teap_pac *pac) +{ + u8 *tlv; + size_t tlv_len, olen; + struct teap_tlv_hdr *ehdr; + + wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC-Opaque TLS extension"); + olen = pac->pac_opaque_len; + tlv_len = sizeof(*ehdr) + olen; + tlv = os_malloc(tlv_len); + if (tlv) { + ehdr = (struct teap_tlv_hdr *) tlv; + ehdr->tlv_type = host_to_be16(PAC_TYPE_PAC_OPAQUE); + ehdr->length = host_to_be16(olen); + os_memcpy(ehdr + 1, pac->pac_opaque, olen); + } + if (!tlv || + tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn, + TLS_EXT_PAC_OPAQUE, + tlv, tlv_len) < 0) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Failed to add PAC-Opaque TLS extension"); + os_free(tlv); + return -1; + } + os_free(tlv); + + return 0; +} + + +static int eap_teap_clear_pac_opaque_ext(struct eap_sm *sm, + struct eap_teap_data *data) +{ + if (tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn, + TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Failed to remove PAC-Opaque TLS extension"); + return -1; + } + return 0; +} + + +static int eap_teap_process_start(struct eap_sm *sm, + struct eap_teap_data *data, u8 flags, + const u8 *pos, size_t left) +{ + const u8 *a_id = NULL; + size_t a_id_len = 0; + + /* TODO: Support (mostly theoretical) case of TEAP/Start request being + * fragmented */ + + /* EAP-TEAP version negotiation (RFC 7170, Section 3.2) */ + data->received_version = flags & EAP_TLS_VERSION_MASK; + wpa_printf(MSG_DEBUG, "EAP-TEAP: Start (server ver=%u, own ver=%u)", + data->received_version, data->teap_version); + if (data->received_version < 1) { + /* Version 1 was the first defined version, so reject 0 */ + wpa_printf(MSG_INFO, + "EAP-TEAP: Server used unknown TEAP version %u", + data->received_version); + return -1; + } + if (data->received_version < data->teap_version) + data->teap_version = data->received_version; + wpa_printf(MSG_DEBUG, "EAP-TEAP: Using TEAP version %d", + data->teap_version); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Start message payload", pos, left); + + /* Parse Authority-ID TLV from Outer TLVs, if present */ + if (flags & EAP_TEAP_FLAGS_OUTER_TLV_LEN) { + const u8 *outer_pos, *outer_end; + u32 outer_tlv_len; + + if (left < 4) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Not enough room for the Outer TLV Length field"); + return -1; + } + + outer_tlv_len = WPA_GET_BE32(pos); + pos += 4; + left -= 4; + + if (outer_tlv_len > left) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Truncated Outer TLVs field (Outer TLV Length: %u; remaining buffer: %u)", + outer_tlv_len, (unsigned int) left); + return -1; + } + + outer_pos = pos + left - outer_tlv_len; + outer_end = outer_pos + outer_tlv_len; + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Start message Outer TLVs", + outer_pos, outer_tlv_len); + wpabuf_free(data->server_outer_tlvs); + data->server_outer_tlvs = wpabuf_alloc_copy(outer_pos, + outer_tlv_len); + if (!data->server_outer_tlvs) + return -1; + left -= outer_tlv_len; + if (left > 0) { + wpa_hexdump(MSG_INFO, + "EAP-TEAP: Unexpected TLS Data in Start message", + pos, left); + return -1; + } + + while (outer_pos < outer_end) { + u16 tlv_type, tlv_len; + + if (outer_end - outer_pos < 4) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Truncated Outer TLV header"); + return -1; + } + tlv_type = WPA_GET_BE16(outer_pos); + outer_pos += 2; + tlv_len = WPA_GET_BE16(outer_pos); + outer_pos += 2; + /* Outer TLVs are required to be optional, so no need to + * check the M flag */ + tlv_type &= TEAP_TLV_TYPE_MASK; + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Outer TLV: Type=%u Length=%u", + tlv_type, tlv_len); + if (outer_end - outer_pos < tlv_len) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Truncated Outer TLV (Type %u)", + tlv_type); + return -1; + } + if (tlv_type == TEAP_TLV_AUTHORITY_ID) { + wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Authority-ID", + outer_pos, tlv_len); + if (a_id) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Multiple Authority-ID TLVs in TEAP/Start"); + return -1; + } + a_id = outer_pos; + a_id_len = tlv_len; + } else { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Ignore unknown Outer TLV (Type %u)", + tlv_type); + } + outer_pos += tlv_len; + } + } else if (left > 0) { + wpa_hexdump(MSG_INFO, + "EAP-TEAP: Unexpected TLS Data in Start message", + pos, left); + return -1; + } + + eap_teap_select_pac(data, a_id, a_id_len); + + if (data->resuming && data->current_pac) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Trying to resume session - do not add PAC-Opaque to TLS ClientHello"); + if (eap_teap_clear_pac_opaque_ext(sm, data) < 0) + return -1; + } else if (data->current_pac) { + /* + * PAC found for the A-ID and we are not resuming an old + * session, so add PAC-Opaque extension to ClientHello. + */ + if (eap_teap_use_pac_opaque(sm, data, data->current_pac) < 0) + return -1; + } else if (data->provisioning_allowed) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: No PAC found - starting provisioning"); + if (eap_teap_clear_pac_opaque_ext(sm, data) < 0) + return -1; + data->provisioning = 1; + } + + return 0; +} + + +#ifdef CONFIG_TESTING_OPTIONS +static struct wpabuf * eap_teap_add_dummy_outer_tlvs(struct eap_teap_data *data, + struct wpabuf *resp) +{ + struct wpabuf *resp2; + u16 len; + const u8 *pos; + u8 flags; + + wpabuf_free(data->peer_outer_tlvs); + data->peer_outer_tlvs = wpabuf_alloc(4 + 4); + if (!data->peer_outer_tlvs) { + wpabuf_free(resp); + return NULL; + } + + /* Outer TLVs (dummy Vendor-Specific TLV for testing) */ + wpabuf_put_be16(data->peer_outer_tlvs, TEAP_TLV_VENDOR_SPECIFIC); + wpabuf_put_be16(data->peer_outer_tlvs, 4); + wpabuf_put_be32(data->peer_outer_tlvs, EAP_VENDOR_HOSTAP); + wpa_hexdump_buf(MSG_DEBUG, "EAP-TEAP: TESTING - Add dummy Outer TLVs", + data->peer_outer_tlvs); + + wpa_hexdump_buf(MSG_DEBUG, + "EAP-TEAP: TEAP/Start response before modification", + resp); + resp2 = wpabuf_alloc(wpabuf_len(resp) + 4 + + wpabuf_len(data->peer_outer_tlvs)); + if (!resp2) { + wpabuf_free(resp); + return NULL; + } + + pos = wpabuf_head(resp); + wpabuf_put_u8(resp2, *pos++); /* Code */ + wpabuf_put_u8(resp2, *pos++); /* Identifier */ + len = WPA_GET_BE16(pos); + pos += 2; + wpabuf_put_be16(resp2, len + 4 + wpabuf_len(data->peer_outer_tlvs)); + wpabuf_put_u8(resp2, *pos++); /* Type */ + /* Flags | Ver (with Outer TLV length included flag set to 1) */ + flags = *pos++; + if (flags & (EAP_TEAP_FLAGS_OUTER_TLV_LEN | + EAP_TLS_FLAGS_LENGTH_INCLUDED)) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Cannot add Outer TLVs for testing"); + wpabuf_free(resp); + wpabuf_free(resp2); + return NULL; + } + flags |= EAP_TEAP_FLAGS_OUTER_TLV_LEN; + wpabuf_put_u8(resp2, flags); + /* Outer TLV Length */ + wpabuf_put_be32(resp2, wpabuf_len(data->peer_outer_tlvs)); + /* TLS Data */ + wpabuf_put_data(resp2, pos, wpabuf_len(resp) - 6); + wpabuf_put_buf(resp2, data->peer_outer_tlvs); /* Outer TLVs */ + + wpabuf_free(resp); + wpa_hexdump_buf(MSG_DEBUG, + "EAP-TEAP: TEAP/Start response after modification", + resp2); + return resp2; +} +#endif /* CONFIG_TESTING_OPTIONS */ + + +static struct wpabuf * eap_teap_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + const struct eap_hdr *req; + size_t left; + int res; + u8 flags, id; + struct wpabuf *resp; + const u8 *pos; + struct eap_teap_data *data = priv; + struct wpabuf msg; + + pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TEAP, ret, + reqData, &left, &flags); + if (!pos) + return NULL; + + req = wpabuf_head(reqData); + id = req->identifier; + + if (flags & EAP_TLS_FLAGS_START) { + if (eap_teap_process_start(sm, data, flags, pos, left) < 0) + return NULL; + + /* Outer TLVs are not used in further packet processing and + * there cannot be TLS Data in this TEAP/Start message, so + * enforce that by ignoring whatever data might remain in the + * buffer. */ + left = 0; + } else if (flags & EAP_TEAP_FLAGS_OUTER_TLV_LEN) { + /* TODO: RFC 7170, Section 4.3.1 indicates that the unexpected + * Outer TLVs MUST be ignored instead of ignoring the full + * message. */ + wpa_printf(MSG_INFO, + "EAP-TEAP: Outer TLVs present in non-Start message -> ignore message"); + return NULL; + } + + wpabuf_set(&msg, pos, left); + + resp = NULL; + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + /* Process tunneled (encrypted) phase 2 data. */ + res = eap_teap_decrypt(sm, data, ret, id, &msg, &resp); + if (res < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + /* + * Ack possible Alert that may have caused failure in + * decryption. + */ + res = 1; + } + } else { + if (sm->waiting_ext_cert_check && data->pending_resp) { + struct eap_peer_config *config = eap_get_config(sm); + + if (config->pending_ext_cert_check == + EXT_CERT_CHECK_GOOD) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: External certificate check succeeded - continue handshake"); + resp = data->pending_resp; + data->pending_resp = NULL; + sm->waiting_ext_cert_check = 0; + return resp; + } + + if (config->pending_ext_cert_check == + EXT_CERT_CHECK_BAD) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: External certificate check failed - force authentication failure"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + sm->waiting_ext_cert_check = 0; + return NULL; + } + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Continuing to wait external server certificate validation"); + return NULL; + } + + /* Continue processing TLS handshake (phase 1). */ + res = eap_peer_tls_process_helper(sm, &data->ssl, + EAP_TYPE_TEAP, + data->teap_version, id, &msg, + &resp); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: TLS processing failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return resp; + } + + if (sm->waiting_ext_cert_check) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Waiting external server certificate validation"); + wpabuf_free(data->pending_resp); + data->pending_resp = resp; + return NULL; + } + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + char cipher[80]; + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: TLS done, proceed to Phase 2"); + data->tls_cs = + tls_connection_get_cipher_suite(data->ssl.conn); + wpa_printf(MSG_DEBUG, + "EAP-TEAP: TLS cipher suite 0x%04x", + data->tls_cs); + + if (data->provisioning && + (!(data->provisioning_allowed & + EAP_TEAP_PROV_AUTH) || + tls_get_cipher(sm->ssl_ctx, data->ssl.conn, + cipher, sizeof(cipher)) < 0 || + os_strstr(cipher, "ADH-") || + os_strstr(cipher, "anon"))) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Using anonymous (unauthenticated) provisioning"); + data->anon_provisioning = 1; + } else { + data->anon_provisioning = 0; + } + data->resuming = 0; + if (eap_teap_derive_key_auth(sm, data) < 0) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Could not derive keys"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + wpabuf_free(resp); + return NULL; + } + } + + if (res == 2) { + /* + * Application data included in the handshake message. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = resp; + resp = NULL; + res = eap_teap_decrypt(sm, data, ret, id, &msg, &resp); + } + } + + if (res == 1) { + wpabuf_free(resp); + return eap_peer_tls_build_ack(id, EAP_TYPE_TEAP, + data->teap_version); + } + +#ifdef CONFIG_TESTING_OPTIONS + if (data->test_outer_tlvs && res == 0 && resp && + (flags & EAP_TLS_FLAGS_START) && wpabuf_len(resp) >= 6) + resp = eap_teap_add_dummy_outer_tlvs(data, resp); +#endif /* CONFIG_TESTING_OPTIONS */ + + return resp; +} + + +#if 0 /* TODO */ +static Boolean eap_teap_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_teap_data *data = priv; + + return tls_connection_established(sm->ssl_ctx, data->ssl.conn); +} + + +static void eap_teap_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_teap_data *data = priv; + + if (data->phase2_priv && data->phase2_method && + data->phase2_method->deinit_for_reauth) + data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); + eap_teap_clear(data); +} + + +static void * eap_teap_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_teap_data *data = priv; + + if (eap_peer_tls_reauth_init(sm, &data->ssl)) { + eap_teap_deinit(sm, data); + return NULL; + } + if (data->phase2_priv && data->phase2_method && + data->phase2_method->init_for_reauth) + data->phase2_method->init_for_reauth(sm, data->phase2_priv); + data->phase2_success = 0; + data->inner_method_done = 0; + data->result_success_done = 0; + data->done_on_tx_completion = 0; + data->resuming = 1; + data->provisioning = 0; + data->anon_provisioning = 0; + data->simck_idx = 0; + return priv; +} +#endif + + +static int eap_teap_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_teap_data *data = priv; + int len, ret; + + len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); + if (data->phase2_method) { + ret = os_snprintf(buf + len, buflen - len, + "EAP-TEAP Phase 2 method=%s\n", + data->phase2_method->name); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + return len; +} + + +static Boolean eap_teap_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_teap_data *data = priv; + + return data->success; +} + + +static u8 * eap_teap_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_teap_data *data = priv; + u8 *key; + + if (!data->success) + return NULL; + + key = os_memdup(data->key_data, EAP_TEAP_KEY_LEN); + if (!key) + return NULL; + + *len = EAP_TEAP_KEY_LEN; + + return key; +} + + +static u8 * eap_teap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_teap_data *data = priv; + u8 *id; + + if (!data->success || !data->session_id) + return NULL; + + id = os_memdup(data->session_id, data->id_len); + if (!id) + return NULL; + + *len = data->id_len; + + return id; +} + + +static u8 * eap_teap_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_teap_data *data = priv; + u8 *key; + + if (!data->success) + return NULL; + + key = os_memdup(data->emsk, EAP_EMSK_LEN); + if (!key) + return NULL; + + *len = EAP_EMSK_LEN; + + return key; +} + + +int eap_peer_teap_register(void) +{ + struct eap_method *eap; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TEAP, "TEAP"); + if (!eap) + return -1; + + eap->init = eap_teap_init; + eap->deinit = eap_teap_deinit; + eap->process = eap_teap_process; + eap->isKeyAvailable = eap_teap_isKeyAvailable; + eap->getKey = eap_teap_getKey; + eap->getSessionId = eap_teap_get_session_id; + eap->get_status = eap_teap_get_status; +#if 0 /* TODO */ + eap->has_reauth_data = eap_teap_has_reauth_data; + eap->deinit_for_reauth = eap_teap_deinit_for_reauth; + eap->init_for_reauth = eap_teap_init_for_reauth; +#endif + eap->get_emsk = eap_teap_get_emsk; + + return eap_peer_method_register(eap); +} diff --git a/contrib/wpa/src/eap_peer/eap_teap_pac.c b/contrib/wpa/src/eap_peer/eap_teap_pac.c new file mode 100644 index 000000000000..34a2743560f0 --- /dev/null +++ b/contrib/wpa/src/eap_peer/eap_teap_pac.c @@ -0,0 +1,931 @@ +/* + * EAP peer method: EAP-TEAP PAC file processing + * Copyright (c) 2004-2019, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_config.h" +#include "eap_i.h" +#include "eap_teap_pac.h" + +/* TODO: encrypt PAC-Key in the PAC file */ + + +/* Text data format */ +static const char *pac_file_hdr = + "wpa_supplicant EAP-TEAP PAC file - version 1"; + +/* + * Binary data format + * 4-octet magic value: 6A E4 92 1C + * 2-octet version (big endian) + * + * + * version=0: + * Sequence of PAC entries: + * 2-octet PAC-Type (big endian) + * 32-octet PAC-Key + * 2-octet PAC-Opaque length (big endian) + * PAC-Opaque data (length bytes) + * 2-octet PAC-Info length (big endian) + * PAC-Info data (length bytes) + */ + +#define EAP_TEAP_PAC_BINARY_MAGIC 0x6ae4921c +#define EAP_TEAP_PAC_BINARY_FORMAT_VERSION 0 + + +/** + * eap_teap_free_pac - Free PAC data + * @pac: Pointer to the PAC entry + * + * Note that the PAC entry must not be in a list since this function does not + * remove the list links. + */ +void eap_teap_free_pac(struct eap_teap_pac *pac) +{ + os_free(pac->pac_opaque); + os_free(pac->pac_info); + os_free(pac->a_id); + os_free(pac->i_id); + os_free(pac->a_id_info); + os_free(pac); +} + + +/** + * eap_teap_get_pac - Get a PAC entry based on A-ID + * @pac_root: Pointer to root of the PAC list + * @a_id: A-ID to search for + * @a_id_len: Length of A-ID + * @pac_type: PAC-Type to search for + * Returns: Pointer to the PAC entry, or %NULL if A-ID not found + */ +struct eap_teap_pac * eap_teap_get_pac(struct eap_teap_pac *pac_root, + const u8 *a_id, size_t a_id_len, + u16 pac_type) +{ + struct eap_teap_pac *pac = pac_root; + + while (pac) { + if (pac->pac_type == pac_type && pac->a_id_len == a_id_len && + os_memcmp(pac->a_id, a_id, a_id_len) == 0) { + return pac; + } + pac = pac->next; + } + return NULL; +} + + +static void eap_teap_remove_pac(struct eap_teap_pac **pac_root, + struct eap_teap_pac **pac_current, + const u8 *a_id, size_t a_id_len, u16 pac_type) +{ + struct eap_teap_pac *pac, *prev; + + pac = *pac_root; + prev = NULL; + + while (pac) { + if (pac->pac_type == pac_type && pac->a_id_len == a_id_len && + os_memcmp(pac->a_id, a_id, a_id_len) == 0) { + if (!prev) + *pac_root = pac->next; + else + prev->next = pac->next; + if (*pac_current == pac) + *pac_current = NULL; + eap_teap_free_pac(pac); + break; + } + prev = pac; + pac = pac->next; + } +} + + +static int eap_teap_copy_buf(u8 **dst, size_t *dst_len, + const u8 *src, size_t src_len) +{ + if (src) { + *dst = os_memdup(src, src_len); + if (!(*dst)) + return -1; + *dst_len = src_len; + } + return 0; +} + + +/** + * eap_teap_add_pac - Add a copy of a PAC entry to a list + * @pac_root: Pointer to PAC list root pointer + * @pac_current: Pointer to the current PAC pointer + * @entry: New entry to clone and add to the list + * Returns: 0 on success, -1 on failure + * + * This function makes a clone of the given PAC entry and adds this copied + * entry to the list (pac_root). If an old entry for the same A-ID is found, + * it will be removed from the PAC list and in this case, pac_current entry + * is set to %NULL if it was the removed entry. + */ +int eap_teap_add_pac(struct eap_teap_pac **pac_root, + struct eap_teap_pac **pac_current, + struct eap_teap_pac *entry) +{ + struct eap_teap_pac *pac; + + if (!entry || !entry->a_id) + return -1; + + /* Remove a possible old entry for the matching A-ID. */ + eap_teap_remove_pac(pac_root, pac_current, + entry->a_id, entry->a_id_len, entry->pac_type); + + /* Allocate a new entry and add it to the list of PACs. */ + pac = os_zalloc(sizeof(*pac)); + if (!pac) + return -1; + + pac->pac_type = entry->pac_type; + os_memcpy(pac->pac_key, entry->pac_key, EAP_TEAP_PAC_KEY_LEN); + if (eap_teap_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len, + entry->pac_opaque, entry->pac_opaque_len) < 0 || + eap_teap_copy_buf(&pac->pac_info, &pac->pac_info_len, + entry->pac_info, entry->pac_info_len) < 0 || + eap_teap_copy_buf(&pac->a_id, &pac->a_id_len, + entry->a_id, entry->a_id_len) < 0 || + eap_teap_copy_buf(&pac->i_id, &pac->i_id_len, + entry->i_id, entry->i_id_len) < 0 || + eap_teap_copy_buf(&pac->a_id_info, &pac->a_id_info_len, + entry->a_id_info, entry->a_id_info_len) < 0) { + eap_teap_free_pac(pac); + return -1; + } + + pac->next = *pac_root; + *pac_root = pac; + + return 0; +} + + +struct eap_teap_read_ctx { + FILE *f; + const char *pos; + const char *end; + int line; + char *buf; + size_t buf_len; +}; + +static int eap_teap_read_line(struct eap_teap_read_ctx *rc, char **value) +{ + char *pos; + + rc->line++; + if (rc->f) { + if (fgets(rc->buf, rc->buf_len, rc->f) == NULL) + return -1; + } else { + const char *l_end; + size_t len; + + if (rc->pos >= rc->end) + return -1; + l_end = rc->pos; + while (l_end < rc->end && *l_end != '\n') + l_end++; + len = l_end - rc->pos; + if (len >= rc->buf_len) + len = rc->buf_len - 1; + os_memcpy(rc->buf, rc->pos, len); + rc->buf[len] = '\0'; + rc->pos = l_end + 1; + } + + rc->buf[rc->buf_len - 1] = '\0'; + pos = rc->buf; + while (*pos != '\0') { + if (*pos == '\n' || *pos == '\r') { + *pos = '\0'; + break; + } + pos++; + } + + pos = os_strchr(rc->buf, '='); + if (pos) + *pos++ = '\0'; + *value = pos; + + return 0; +} + + +static u8 * eap_teap_parse_hex(const char *value, size_t *len) +{ + int hlen; + u8 *buf; + + if (!value) + return NULL; + hlen = os_strlen(value); + if (hlen & 1) + return NULL; + *len = hlen / 2; + buf = os_malloc(*len); + if (!buf) + return NULL; + if (hexstr2bin(value, buf, *len)) { + os_free(buf); + return NULL; + } + return buf; +} + + +static int eap_teap_init_pac_data(struct eap_sm *sm, const char *pac_file, + struct eap_teap_read_ctx *rc) +{ + os_memset(rc, 0, sizeof(*rc)); + + rc->buf_len = 2048; + rc->buf = os_malloc(rc->buf_len); + if (!rc->buf) + return -1; + + if (os_strncmp(pac_file, "blob://", 7) == 0) { + const struct wpa_config_blob *blob; + + blob = eap_get_config_blob(sm, pac_file + 7); + if (!blob) { + wpa_printf(MSG_INFO, + "EAP-TEAP: No PAC blob '%s' - assume no PAC entries have been provisioned", + pac_file + 7); + os_free(rc->buf); + return -1; + } + rc->pos = (char *) blob->data; + rc->end = (char *) blob->data + blob->len; + } else { + rc->f = fopen(pac_file, "rb"); + if (!rc->f) { + wpa_printf(MSG_INFO, + "EAP-TEAP: No PAC file '%s' - assume no PAC entries have been provisioned", + pac_file); + os_free(rc->buf); + return -1; + } + } + + return 0; +} + + +static void eap_teap_deinit_pac_data(struct eap_teap_read_ctx *rc) +{ + os_free(rc->buf); + if (rc->f) + fclose(rc->f); +} + + +static const char * eap_teap_parse_start(struct eap_teap_pac **pac) +{ + if (*pac) + return "START line without END"; + + *pac = os_zalloc(sizeof(struct eap_teap_pac)); + if (!(*pac)) + return "No memory for PAC entry"; + (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC; + return NULL; +} + + +static const char * eap_teap_parse_end(struct eap_teap_pac **pac_root, + struct eap_teap_pac **pac) +{ + if (!(*pac)) + return "END line without START"; + if (*pac_root) { + struct eap_teap_pac *end = *pac_root; + + while (end->next) + end = end->next; + end->next = *pac; + } else + *pac_root = *pac; + + *pac = NULL; + return NULL; +} + + +static const char * eap_teap_parse_pac_type(struct eap_teap_pac *pac, + char *pos) +{ + if (!pos) + return "Cannot parse pac type"; + pac->pac_type = atoi(pos); + if (pac->pac_type != PAC_TYPE_TUNNEL_PAC) + return "Unrecognized PAC-Type"; + + return NULL; +} + + +static const char * eap_teap_parse_pac_key(struct eap_teap_pac *pac, char *pos) +{ + u8 *key; + size_t key_len; + + key = eap_teap_parse_hex(pos, &key_len); + if (!key || key_len != EAP_TEAP_PAC_KEY_LEN) { + os_free(key); + return "Invalid PAC-Key"; + } + + os_memcpy(pac->pac_key, key, EAP_TEAP_PAC_KEY_LEN); + os_free(key); + + return NULL; +} + + +static const char * eap_teap_parse_pac_opaque(struct eap_teap_pac *pac, + char *pos) +{ + os_free(pac->pac_opaque); + pac->pac_opaque = eap_teap_parse_hex(pos, &pac->pac_opaque_len); + if (!pac->pac_opaque) + return "Invalid PAC-Opaque"; + return NULL; +} + + +static const char * eap_teap_parse_a_id(struct eap_teap_pac *pac, char *pos) +{ + os_free(pac->a_id); + pac->a_id = eap_teap_parse_hex(pos, &pac->a_id_len); + if (!pac->a_id) + return "Invalid A-ID"; + return NULL; +} + + +static const char * eap_teap_parse_i_id(struct eap_teap_pac *pac, char *pos) +{ + os_free(pac->i_id); + pac->i_id = eap_teap_parse_hex(pos, &pac->i_id_len); + if (!pac->i_id) + return "Invalid I-ID"; + return NULL; +} + + +static const char * eap_teap_parse_a_id_info(struct eap_teap_pac *pac, + char *pos) +{ + os_free(pac->a_id_info); + pac->a_id_info = eap_teap_parse_hex(pos, &pac->a_id_info_len); + if (!pac->a_id_info) + return "Invalid A-ID-Info"; + return NULL; +} + + +/** + * eap_teap_load_pac - Load PAC entries (text format) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @pac_root: Pointer to root of the PAC list (to be filled) + * @pac_file: Name of the PAC file/blob to load + * Returns: 0 on success, -1 on failure + */ +int eap_teap_load_pac(struct eap_sm *sm, struct eap_teap_pac **pac_root, + const char *pac_file) +{ + struct eap_teap_read_ctx rc; + struct eap_teap_pac *pac = NULL; + int count = 0; + char *pos; + const char *err = NULL; + + if (!pac_file) + return -1; + + if (eap_teap_init_pac_data(sm, pac_file, &rc) < 0) + return 0; + + if (eap_teap_read_line(&rc, &pos) < 0) { + /* empty file - assume it is fine to overwrite */ + eap_teap_deinit_pac_data(&rc); + return 0; + } + if (os_strcmp(pac_file_hdr, rc.buf) != 0) + err = "Unrecognized header line"; + + while (!err && eap_teap_read_line(&rc, &pos) == 0) { + if (os_strcmp(rc.buf, "START") == 0) + err = eap_teap_parse_start(&pac); + else if (os_strcmp(rc.buf, "END") == 0) { + err = eap_teap_parse_end(pac_root, &pac); + count++; + } else if (!pac) + err = "Unexpected line outside START/END block"; + else if (os_strcmp(rc.buf, "PAC-Type") == 0) + err = eap_teap_parse_pac_type(pac, pos); + else if (os_strcmp(rc.buf, "PAC-Key") == 0) + err = eap_teap_parse_pac_key(pac, pos); + else if (os_strcmp(rc.buf, "PAC-Opaque") == 0) + err = eap_teap_parse_pac_opaque(pac, pos); + else if (os_strcmp(rc.buf, "A-ID") == 0) + err = eap_teap_parse_a_id(pac, pos); + else if (os_strcmp(rc.buf, "I-ID") == 0) + err = eap_teap_parse_i_id(pac, pos); + else if (os_strcmp(rc.buf, "A-ID-Info") == 0) + err = eap_teap_parse_a_id_info(pac, pos); + } + + if (pac) { + if (!err) + err = "PAC block not terminated with END"; + eap_teap_free_pac(pac); + } + + eap_teap_deinit_pac_data(&rc); + + if (err) { + wpa_printf(MSG_INFO, "EAP-TEAP: %s in '%s:%d'", + err, pac_file, rc.line); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-TEAP: Read %d PAC entries from '%s'", + count, pac_file); + + return 0; +} + + +static void eap_teap_write(char **buf, char **pos, size_t *buf_len, + const char *field, const u8 *data, + size_t len, int txt) +{ + size_t i, need; + int ret; + char *end; + + if (!data || !buf || !(*buf) || !pos || !(*pos) || *pos < *buf) + return; + + need = os_strlen(field) + len * 2 + 30; + if (txt) + need += os_strlen(field) + len + 20; + + if (*pos - *buf + need > *buf_len) { + char *nbuf = os_realloc(*buf, *buf_len + need); + + if (!nbuf) { + os_free(*buf); + *buf = NULL; + return; + } + *pos = nbuf + (*pos - *buf); + *buf = nbuf; + *buf_len += need; + } + end = *buf + *buf_len; + + ret = os_snprintf(*pos, end - *pos, "%s=", field); + if (os_snprintf_error(end - *pos, ret)) + return; + *pos += ret; + *pos += wpa_snprintf_hex(*pos, end - *pos, data, len); + ret = os_snprintf(*pos, end - *pos, "\n"); + if (os_snprintf_error(end - *pos, ret)) + return; + *pos += ret; + + if (txt) { + ret = os_snprintf(*pos, end - *pos, "%s-txt=", field); + if (os_snprintf_error(end - *pos, ret)) + return; + *pos += ret; + for (i = 0; i < len; i++) { + ret = os_snprintf(*pos, end - *pos, "%c", data[i]); + if (os_snprintf_error(end - *pos, ret)) + return; + *pos += ret; + } + ret = os_snprintf(*pos, end - *pos, "\n"); + if (os_snprintf_error(end - *pos, ret)) + return; + *pos += ret; + } +} + + +static int eap_teap_write_pac(struct eap_sm *sm, const char *pac_file, + char *buf, size_t len) +{ + if (os_strncmp(pac_file, "blob://", 7) == 0) { + struct wpa_config_blob *blob; + + blob = os_zalloc(sizeof(*blob)); + if (!blob) + return -1; + blob->data = (u8 *) buf; + blob->len = len; + buf = NULL; + blob->name = os_strdup(pac_file + 7); + if (!blob->name) { + os_free(blob); + return -1; + } + eap_set_config_blob(sm, blob); + } else { + FILE *f; + + f = fopen(pac_file, "wb"); + if (!f) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Failed to open PAC file '%s' for writing", + pac_file); + return -1; + } + if (fwrite(buf, 1, len, f) != len) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Failed to write all PACs into '%s'", + pac_file); + fclose(f); + return -1; + } + os_free(buf); + fclose(f); + } + + return 0; +} + + +static int eap_teap_add_pac_data(struct eap_teap_pac *pac, char **buf, + char **pos, size_t *buf_len) +{ + int ret; + + ret = os_snprintf(*pos, *buf + *buf_len - *pos, + "START\nPAC-Type=%d\n", pac->pac_type); + if (os_snprintf_error(*buf + *buf_len - *pos, ret)) + return -1; + + *pos += ret; + eap_teap_write(buf, pos, buf_len, "PAC-Key", + pac->pac_key, EAP_TEAP_PAC_KEY_LEN, 0); + eap_teap_write(buf, pos, buf_len, "PAC-Opaque", + pac->pac_opaque, pac->pac_opaque_len, 0); + eap_teap_write(buf, pos, buf_len, "PAC-Info", + pac->pac_info, pac->pac_info_len, 0); + eap_teap_write(buf, pos, buf_len, "A-ID", + pac->a_id, pac->a_id_len, 0); + eap_teap_write(buf, pos, buf_len, "I-ID", + pac->i_id, pac->i_id_len, 1); + eap_teap_write(buf, pos, buf_len, "A-ID-Info", + pac->a_id_info, pac->a_id_info_len, 1); + if (!(*buf)) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: No memory for PAC data"); + return -1; + } + ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n"); + if (os_snprintf_error(*buf + *buf_len - *pos, ret)) + return -1; + *pos += ret; + + return 0; +} + + +/** + * eap_teap_save_pac - Save PAC entries (text format) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @pac_root: Root of the PAC list + * @pac_file: Name of the PAC file/blob + * Returns: 0 on success, -1 on failure + */ +int eap_teap_save_pac(struct eap_sm *sm, struct eap_teap_pac *pac_root, + const char *pac_file) +{ + struct eap_teap_pac *pac; + int ret, count = 0; + char *buf, *pos; + size_t buf_len; + + if (!pac_file) + return -1; + + buf_len = 1024; + pos = buf = os_malloc(buf_len); + if (!buf) + return -1; + + ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr); + if (os_snprintf_error(buf + buf_len - pos, ret)) { + os_free(buf); + return -1; + } + pos += ret; + + pac = pac_root; + while (pac) { + if (eap_teap_add_pac_data(pac, &buf, &pos, &buf_len)) { + os_free(buf); + return -1; + } + count++; + pac = pac->next; + } + + if (eap_teap_write_pac(sm, pac_file, buf, pos - buf)) { + os_free(buf); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-TEAP: Wrote %d PAC entries into '%s'", + count, pac_file); + + return 0; +} + + +/** + * eap_teap_pac_list_truncate - Truncate a PAC list to the given length + * @pac_root: Root of the PAC list + * @max_len: Maximum length of the list (>= 1) + * Returns: Number of PAC entries removed + */ +size_t eap_teap_pac_list_truncate(struct eap_teap_pac *pac_root, + size_t max_len) +{ + struct eap_teap_pac *pac, *prev; + size_t count; + + pac = pac_root; + prev = NULL; + count = 0; + + while (pac) { + count++; + if (count > max_len) + break; + prev = pac; + pac = pac->next; + } + + if (count <= max_len || !prev) + return 0; + + count = 0; + prev->next = NULL; + + while (pac) { + prev = pac; + pac = pac->next; + eap_teap_free_pac(prev); + count++; + } + + return count; +} + + +static void eap_teap_pac_get_a_id(struct eap_teap_pac *pac) +{ + u8 *pos, *end; + u16 type, len; + + pos = pac->pac_info; + end = pos + pac->pac_info_len; + + while (end - pos > 4) { + type = WPA_GET_BE16(pos); + pos += 2; + len = WPA_GET_BE16(pos); + pos += 2; + if (len > (unsigned int) (end - pos)) + break; + + if (type == PAC_TYPE_A_ID) { + os_free(pac->a_id); + pac->a_id = os_memdup(pos, len); + if (!pac->a_id) + break; + pac->a_id_len = len; + } + + if (type == PAC_TYPE_A_ID_INFO) { + os_free(pac->a_id_info); + pac->a_id_info = os_memdup(pos, len); + if (!pac->a_id_info) + break; + pac->a_id_info_len = len; + } + + pos += len; + } +} + + +/** + * eap_teap_load_pac_bin - Load PAC entries (binary format) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @pac_root: Pointer to root of the PAC list (to be filled) + * @pac_file: Name of the PAC file/blob to load + * Returns: 0 on success, -1 on failure + */ +int eap_teap_load_pac_bin(struct eap_sm *sm, struct eap_teap_pac **pac_root, + const char *pac_file) +{ + const struct wpa_config_blob *blob = NULL; + u8 *buf, *end, *pos; + size_t len, count = 0; + struct eap_teap_pac *pac, *prev; + + *pac_root = NULL; + + if (!pac_file) + return -1; + + if (os_strncmp(pac_file, "blob://", 7) == 0) { + blob = eap_get_config_blob(sm, pac_file + 7); + if (!blob) { + wpa_printf(MSG_INFO, + "EAP-TEAP: No PAC blob '%s' - assume no PAC entries have been provisioned", + pac_file + 7); + return 0; + } + buf = blob->data; + len = blob->len; + } else { + buf = (u8 *) os_readfile(pac_file, &len); + if (!buf) { + wpa_printf(MSG_INFO, + "EAP-TEAP: No PAC file '%s' - assume no PAC entries have been provisioned", + pac_file); + return 0; + } + } + + if (len == 0) { + if (!blob) + os_free(buf); + return 0; + } + + if (len < 6 || WPA_GET_BE32(buf) != EAP_TEAP_PAC_BINARY_MAGIC || + WPA_GET_BE16(buf + 4) != EAP_TEAP_PAC_BINARY_FORMAT_VERSION) { + wpa_printf(MSG_INFO, "EAP-TEAP: Invalid PAC file '%s' (bin)", + pac_file); + if (!blob) + os_free(buf); + return -1; + } + + pac = prev = NULL; + pos = buf + 6; + end = buf + len; + while (pos < end) { + u16 val; + + if (end - pos < 2 + EAP_TEAP_PAC_KEY_LEN + 2 + 2) { + pac = NULL; + goto parse_fail; + } + + pac = os_zalloc(sizeof(*pac)); + if (!pac) + goto parse_fail; + + pac->pac_type = WPA_GET_BE16(pos); + pos += 2; + os_memcpy(pac->pac_key, pos, EAP_TEAP_PAC_KEY_LEN); + pos += EAP_TEAP_PAC_KEY_LEN; + val = WPA_GET_BE16(pos); + pos += 2; + if (val > end - pos) + goto parse_fail; + pac->pac_opaque_len = val; + pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len); + if (!pac->pac_opaque) + goto parse_fail; + pos += pac->pac_opaque_len; + if (end - pos < 2) + goto parse_fail; + val = WPA_GET_BE16(pos); + pos += 2; + if (val > end - pos) + goto parse_fail; + pac->pac_info_len = val; + pac->pac_info = os_memdup(pos, pac->pac_info_len); + if (!pac->pac_info) + goto parse_fail; + pos += pac->pac_info_len; + eap_teap_pac_get_a_id(pac); + + count++; + if (prev) + prev->next = pac; + else + *pac_root = pac; + prev = pac; + } + + if (!blob) + os_free(buf); + + wpa_printf(MSG_DEBUG, "EAP-TEAP: Read %lu PAC entries from '%s' (bin)", + (unsigned long) count, pac_file); + + return 0; + +parse_fail: + wpa_printf(MSG_INFO, "EAP-TEAP: Failed to parse PAC file '%s' (bin)", + pac_file); + if (!blob) + os_free(buf); + if (pac) + eap_teap_free_pac(pac); + return -1; +} + + +/** + * eap_teap_save_pac_bin - Save PAC entries (binary format) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @pac_root: Root of the PAC list + * @pac_file: Name of the PAC file/blob + * Returns: 0 on success, -1 on failure + */ +int eap_teap_save_pac_bin(struct eap_sm *sm, struct eap_teap_pac *pac_root, + const char *pac_file) +{ + size_t len, count = 0; + struct eap_teap_pac *pac; + u8 *buf, *pos; + + len = 6; + pac = pac_root; + while (pac) { + if (pac->pac_opaque_len > 65535 || + pac->pac_info_len > 65535) + return -1; + len += 2 + EAP_TEAP_PAC_KEY_LEN + 2 + pac->pac_opaque_len + + 2 + pac->pac_info_len; + pac = pac->next; + } + + buf = os_malloc(len); + if (!buf) + return -1; + + pos = buf; + WPA_PUT_BE32(pos, EAP_TEAP_PAC_BINARY_MAGIC); + pos += 4; + WPA_PUT_BE16(pos, EAP_TEAP_PAC_BINARY_FORMAT_VERSION); + pos += 2; + + pac = pac_root; + while (pac) { + WPA_PUT_BE16(pos, pac->pac_type); + pos += 2; + os_memcpy(pos, pac->pac_key, EAP_TEAP_PAC_KEY_LEN); + pos += EAP_TEAP_PAC_KEY_LEN; + WPA_PUT_BE16(pos, pac->pac_opaque_len); + pos += 2; + os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len); + pos += pac->pac_opaque_len; + WPA_PUT_BE16(pos, pac->pac_info_len); + pos += 2; + os_memcpy(pos, pac->pac_info, pac->pac_info_len); + pos += pac->pac_info_len; + + pac = pac->next; + count++; + } + + if (eap_teap_write_pac(sm, pac_file, (char *) buf, len)) { + os_free(buf); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-TEAP: Wrote %lu PAC entries into '%s' (bin)", + (unsigned long) count, pac_file); + + return 0; +} diff --git a/contrib/wpa/src/eap_peer/eap_teap_pac.h b/contrib/wpa/src/eap_peer/eap_teap_pac.h new file mode 100644 index 000000000000..edf4c5763ad2 --- /dev/null +++ b/contrib/wpa/src/eap_peer/eap_teap_pac.h @@ -0,0 +1,50 @@ +/* + * EAP peer method: EAP-TEAP PAC file processing + * Copyright (c) 2004-2019, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_TEAP_PAC_H +#define EAP_TEAP_PAC_H + +#include "eap_common/eap_teap_common.h" + +struct eap_teap_pac { + struct eap_teap_pac *next; + + u8 pac_key[EAP_TEAP_PAC_KEY_LEN]; + u8 *pac_opaque; + size_t pac_opaque_len; + u8 *pac_info; + size_t pac_info_len; + u8 *a_id; + size_t a_id_len; + u8 *i_id; + size_t i_id_len; + u8 *a_id_info; + size_t a_id_info_len; + u16 pac_type; +}; + + +void eap_teap_free_pac(struct eap_teap_pac *pac); +struct eap_teap_pac * eap_teap_get_pac(struct eap_teap_pac *pac_root, + const u8 *a_id, size_t a_id_len, + u16 pac_type); +int eap_teap_add_pac(struct eap_teap_pac **pac_root, + struct eap_teap_pac **pac_current, + struct eap_teap_pac *entry); +int eap_teap_load_pac(struct eap_sm *sm, struct eap_teap_pac **pac_root, + const char *pac_file); +int eap_teap_save_pac(struct eap_sm *sm, struct eap_teap_pac *pac_root, + const char *pac_file); +size_t eap_teap_pac_list_truncate(struct eap_teap_pac *pac_root, + size_t max_len); +int eap_teap_load_pac_bin(struct eap_sm *sm, struct eap_teap_pac **pac_root, + const char *pac_file); +int eap_teap_save_pac_bin(struct eap_sm *sm, struct eap_teap_pac *pac_root, + const char *pac_file); + +#endif /* EAP_TEAP_PAC_H */ diff --git a/contrib/wpa/src/eap_peer/eap_tls.c b/contrib/wpa/src/eap_peer/eap_tls.c index ffea9d213855..15d60d710094 100644 --- a/contrib/wpa/src/eap_peer/eap_tls.c +++ b/contrib/wpa/src/eap_peer/eap_tls.c @@ -174,6 +174,9 @@ static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, struct eap_method_ret *ret) { const char *label; + const u8 eap_tls13_context[] = { EAP_TYPE_TLS }; + const u8 *context = NULL; + size_t context_len = 0; wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); @@ -184,6 +187,8 @@ static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, if (data->ssl.tls_v13) { label = "EXPORTER_EAP_TLS_Key_Material"; + context = eap_tls13_context; + context_len = 1; /* A possible NewSessionTicket may be received before * EAP-Success, so need to allow it to be received. */ @@ -198,7 +203,7 @@ static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, eap_tls_free_key(data); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label, - NULL, 0, + context, context_len, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (data->key_data) { @@ -291,6 +296,18 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, return NULL; } + if (res == 2) { + /* Application data included in the handshake message (used by + * EAP-TLS 1.3 to indicate conclusion of the exchange). */ + wpa_hexdump_buf(MSG_DEBUG, "EAP-TLS: Received Application Data", + resp); + wpa_hexdump_buf(MSG_DEBUG, "EAP-TLS: Remaining tls_out data", + data->ssl.tls_out); + eap_peer_tls_reset_output(&data->ssl); + /* Send an ACK to allow the server to complete exchange */ + res = 1; + } + if (tls_connection_established(data->ssl_ctx, data->ssl.conn)) eap_tls_success(sm, data, ret); diff --git a/contrib/wpa/src/eap_peer/eap_tls_common.c b/contrib/wpa/src/eap_peer/eap_tls_common.c index cb94c452efce..7e0690c06bf4 100644 --- a/contrib/wpa/src/eap_peer/eap_tls_common.c +++ b/contrib/wpa/src/eap_peer/eap_tls_common.c @@ -159,7 +159,8 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, struct eap_peer_config *config, int phase2) { os_memset(params, 0, sizeof(*params)); - if (sm->workaround && data->eap_type != EAP_TYPE_FAST) { + if (sm->workaround && data->eap_type != EAP_TYPE_FAST && + data->eap_type != EAP_TYPE_TEAP) { /* * Some deployed authentication servers seem to be unable to * handle the TLS Session Ticket extension (they are supposed @@ -171,7 +172,15 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, */ params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; } + if (data->eap_type == EAP_TYPE_TEAP) { + /* RFC 7170 requires TLS v1.2 or newer to be used with TEAP */ + params->flags |= TLS_CONN_DISABLE_TLSv1_0 | + TLS_CONN_DISABLE_TLSv1_1; + if (config->teap_anon_dh) + params->flags |= TLS_CONN_TEAP_ANON_DH; + } if (data->eap_type == EAP_TYPE_FAST || + data->eap_type == EAP_TYPE_TEAP || data->eap_type == EAP_TYPE_TTLS || data->eap_type == EAP_TYPE_PEAP) { /* The current EAP peer implementation is not yet ready for the @@ -404,17 +413,18 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, if (eap_type == EAP_TYPE_TLS && data->tls_v13) { u8 *id, *method_id; + const u8 context[] = { EAP_TYPE_TLS }; /* Session-Id = || Method-Id * Method-Id = TLS-Exporter("EXPORTER_EAP_TLS_Method-Id", - * "", 64) + * Type-Code, 64) */ *len = 1 + 64; id = os_malloc(*len); if (!id) return NULL; method_id = eap_peer_tls_derive_key( - sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64); + sm, data, "EXPORTER_EAP_TLS_Method-Id", context, 1, 64); if (!method_id) { os_free(id); return NULL; diff --git a/contrib/wpa/src/eap_peer/eap_tls_common.h b/contrib/wpa/src/eap_peer/eap_tls_common.h index 5f825294d787..d96eff1c8b82 100644 --- a/contrib/wpa/src/eap_peer/eap_tls_common.h +++ b/contrib/wpa/src/eap_peer/eap_tls_common.h @@ -70,7 +70,8 @@ struct eap_ssl_data { void *ssl_ctx; /** - * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + * eap_type - EAP method used in Phase 1 + * (EAP_TYPE_TLS/PEAP/TTLS/FAST/TEAP) */ u8 eap_type; @@ -85,6 +86,7 @@ struct eap_ssl_data { #define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 #define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 #define EAP_TLS_FLAGS_START 0x20 +#define EAP_TEAP_FLAGS_OUTER_TLV_LEN 0x10 #define EAP_TLS_VERSION_MASK 0x07 /* could be up to 128 bytes, but only the first 64 bytes are used */ diff --git a/contrib/wpa/src/eap_server/eap.h b/contrib/wpa/src/eap_server/eap.h index b130368b64da..a9cf5c97bee7 100644 --- a/contrib/wpa/src/eap_server/eap.h +++ b/contrib/wpa/src/eap_server/eap.h @@ -121,7 +121,10 @@ struct eap_config { int eap_fast_prov; int pac_key_lifetime; int pac_key_refresh_time; + int eap_teap_auth; + int eap_teap_pac_no_inner; int eap_sim_aka_result_ind; + int eap_sim_id; int tnc; struct wps_context *wps; const struct wpabuf *assoc_wps_ie; diff --git a/contrib/wpa/src/eap_server/eap_i.h b/contrib/wpa/src/eap_server/eap_i.h index 1cade10bee55..f9ab32d69d75 100644 --- a/contrib/wpa/src/eap_server/eap_i.h +++ b/contrib/wpa/src/eap_server/eap_i.h @@ -190,7 +190,10 @@ struct eap_sm { } eap_fast_prov; int pac_key_lifetime; int pac_key_refresh_time; + int eap_teap_auth; + int eap_teap_pac_no_inner; int eap_sim_aka_result_ind; + int eap_sim_id; int tnc; u16 pwd_group; struct wps_context *wps; diff --git a/contrib/wpa/src/eap_server/eap_methods.h b/contrib/wpa/src/eap_server/eap_methods.h index 3bf1495f76bf..fdbea7a7ea6e 100644 --- a/contrib/wpa/src/eap_server/eap_methods.h +++ b/contrib/wpa/src/eap_server/eap_methods.h @@ -41,6 +41,7 @@ int eap_server_sake_register(void); int eap_server_gpsk_register(void); int eap_server_vendor_test_register(void); int eap_server_fast_register(void); +int eap_server_teap_register(void); int eap_server_wsc_register(void); int eap_server_ikev2_register(void); int eap_server_tnc_register(void); diff --git a/contrib/wpa/src/eap_server/eap_server.c b/contrib/wpa/src/eap_server/eap_server.c index e8b36e13380d..568eebd7e77e 100644 --- a/contrib/wpa/src/eap_server/eap_server.c +++ b/contrib/wpa/src/eap_server/eap_server.c @@ -1869,7 +1869,10 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->eap_fast_prov = conf->eap_fast_prov; sm->pac_key_lifetime = conf->pac_key_lifetime; sm->pac_key_refresh_time = conf->pac_key_refresh_time; + sm->eap_teap_auth = conf->eap_teap_auth; + sm->eap_teap_pac_no_inner = conf->eap_teap_pac_no_inner; sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; + sm->eap_sim_id = conf->eap_sim_id; sm->tnc = conf->tnc; sm->wps = conf->wps; if (conf->assoc_wps_ie) diff --git a/contrib/wpa/src/eap_server/eap_server_aka.c b/contrib/wpa/src/eap_server/eap_server_aka.c index 1bea706d4990..4dadfe197c6b 100644 --- a/contrib/wpa/src/eap_server/eap_server_aka.c +++ b/contrib/wpa/src/eap_server/eap_server_aka.c @@ -30,6 +30,7 @@ struct eap_aka_data { u8 ck[EAP_AKA_CK_LEN]; u8 ik[EAP_AKA_IK_LEN]; u8 res[EAP_AKA_RES_MAX_LEN]; + u8 reauth_mac[EAP_SIM_MAC_LEN]; size_t res_len; enum { IDENTITY, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE @@ -392,7 +393,10 @@ static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data, const u8 *nonce_s) { os_free(data->next_pseudonym); - if (nonce_s == NULL) { + if (!(sm->eap_sim_id & 0x01)) { + /* Use of pseudonyms disabled in configuration */ + data->next_pseudonym = NULL; + } else if (!nonce_s) { data->next_pseudonym = eap_sim_db_get_next_pseudonym( sm->eap_sim_db_priv, @@ -403,7 +407,10 @@ static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data, data->next_pseudonym = NULL; } os_free(data->next_reauth_id); - if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) { + if (!(sm->eap_sim_id & 0x02)) { + /* Use of fast reauth disabled in configuration */ + data->next_reauth_id = NULL; + } else if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) { data->next_reauth_id = eap_sim_db_get_next_reauth_id( sm->eap_sim_db_priv, @@ -542,6 +549,7 @@ static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm, struct eap_aka_data *data, u8 id) { struct eap_sim_msg *msg; + struct wpabuf *buf; wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication"); @@ -581,7 +589,16 @@ static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0); + buf = eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0); + + /* Remember this MAC before sending it to the peer. This MAC is used for + * Session-Id calculation after receiving response from the peer and + * after all other checks pass. */ + os_memcpy(data->reauth_mac, + wpabuf_head_u8(buf) + wpabuf_len(buf) - EAP_SIM_MAC_LEN, + EAP_SIM_MAC_LEN); + + return buf; } @@ -1304,14 +1321,24 @@ static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - *len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN; + if (!data->reauth) + *len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN; + else + *len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN; id = os_malloc(*len); if (id == NULL) return NULL; id[0] = data->eap_method; - os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN); - os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN); + if (!data->reauth) { + os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN); + os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, + EAP_AKA_AUTN_LEN); + } else { + os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN); + os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac, + EAP_SIM_MAC_LEN); + } wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len); return id; diff --git a/contrib/wpa/src/eap_server/eap_server_pax.c b/contrib/wpa/src/eap_server/eap_server_pax.c index 2e8c1a60c71f..5ed29efd1d3c 100644 --- a/contrib/wpa/src/eap_server/eap_server_pax.c +++ b/contrib/wpa/src/eap_server/eap_server_pax.c @@ -282,8 +282,13 @@ static Boolean eap_pax_check(struct eap_sm *sm, void *priv, if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, wpabuf_mhead(respData), wpabuf_len(respData) - EAP_PAX_ICV_LEN, - NULL, 0, NULL, 0, icvbuf) < 0 || - os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { + NULL, 0, NULL, 0, icvbuf) < 0) { + wpa_printf(MSG_INFO, + "EAP-PAX: Failed to calculate ICV"); + return TRUE; + } + + if (os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", icvbuf, EAP_PAX_ICV_LEN); @@ -413,8 +418,13 @@ static void eap_pax_process_std_2(struct eap_sm *sm, if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, data->rand.r.x, EAP_PAX_RAND_LEN, data->rand.r.y, EAP_PAX_RAND_LEN, - (u8 *) data->cid, data->cid_len, mac) < 0 || - os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) { + (u8 *) data->cid, data->cid_len, mac) < 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate MAC_CK"); + data->state = FAILURE; + return; + } + + if (os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in " "PAX_STD-2"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)", @@ -435,8 +445,12 @@ static void eap_pax_process_std_2(struct eap_sm *sm, if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, wpabuf_head(respData), wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, - NULL, 0, icvbuf) < 0 || - os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { + NULL, 0, icvbuf) < 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate ICV"); + return; + } + + if (os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", icvbuf, EAP_PAX_ICV_LEN); diff --git a/contrib/wpa/src/eap_server/eap_server_peap.c b/contrib/wpa/src/eap_server/eap_server_peap.c index 92c0e5ec9716..5e125acf7f93 100644 --- a/contrib/wpa/src/eap_server/eap_server_peap.c +++ b/contrib/wpa/src/eap_server/eap_server_peap.c @@ -362,7 +362,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) res = peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys", isk, sizeof(isk), imck, sizeof(imck)); - os_memset(isk, 0, sizeof(isk)); + forced_memzero(isk, sizeof(isk)); if (res < 0) { os_free(tk); return -1; @@ -376,7 +376,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); os_memcpy(data->cmk, imck + 40, 20); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); - os_memset(imck, 0, sizeof(imck)); + forced_memzero(imck, sizeof(imck)); return 0; } @@ -1326,7 +1326,7 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) "key"); } - os_memset(csk, 0, sizeof(csk)); + forced_memzero(csk, sizeof(csk)); return eapKeyData; } diff --git a/contrib/wpa/src/eap_server/eap_server_pwd.c b/contrib/wpa/src/eap_server/eap_server_pwd.c index e720a28c85ba..a8087c1d8a63 100644 --- a/contrib/wpa/src/eap_server/eap_server_pwd.c +++ b/contrib/wpa/src/eap_server/eap_server_pwd.c @@ -632,7 +632,7 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, data->id_server, data->id_server_len, data->id_peer, data->id_peer_len, (u8 *) &data->token); - os_memset(pwhashhash, 0, sizeof(pwhashhash)); + forced_memzero(pwhashhash, sizeof(pwhashhash)); if (res) { wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute " "PWE"); diff --git a/contrib/wpa/src/eap_server/eap_server_sim.c b/contrib/wpa/src/eap_server/eap_server_sim.c index 128782735fb3..5243568e71d0 100644 --- a/contrib/wpa/src/eap_server/eap_server_sim.c +++ b/contrib/wpa/src/eap_server/eap_server_sim.c @@ -26,6 +26,7 @@ struct eap_sim_data { u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN]; u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN]; u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; + u8 reauth_mac[EAP_SIM_MAC_LEN]; int num_chal; enum { START, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE @@ -149,7 +150,10 @@ static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data, const u8 *nonce_s) { os_free(data->next_pseudonym); - if (nonce_s == NULL) { + if (!(sm->eap_sim_id & 0x01)) { + /* Use of pseudonyms disabled in configuration */ + data->next_pseudonym = NULL; + } else if (!nonce_s) { data->next_pseudonym = eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, EAP_SIM_DB_SIM); @@ -158,7 +162,10 @@ static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data, data->next_pseudonym = NULL; } os_free(data->next_reauth_id); - if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) { + if (!(sm->eap_sim_id & 0x02)) { + /* Use of fast reauth disabled in configuration */ + data->next_reauth_id = NULL; + } else if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) { data->next_reauth_id = eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, EAP_SIM_DB_SIM); @@ -249,6 +256,7 @@ static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm, struct eap_sim_data *data, u8 id) { struct eap_sim_msg *msg; + struct wpabuf *buf; wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication"); @@ -278,7 +286,16 @@ static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0); + buf = eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0); + + /* Remember this MAC before sending it to the peer. This MAC is used for + * Session-Id calculation after receiving response from the peer and + * after all other checks pass. */ + os_memcpy(data->reauth_mac, + wpabuf_head_u8(buf) + wpabuf_len(buf) - EAP_SIM_MAC_LEN, + EAP_SIM_MAC_LEN); + + return buf; } @@ -829,15 +846,25 @@ static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN; + if (!data->reauth) + *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN; + else + *len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN; id = os_malloc(*len); if (id == NULL) return NULL; id[0] = EAP_TYPE_SIM; - os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN); - os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt, - EAP_SIM_NONCE_MT_LEN); + if (!data->reauth) { + os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN); + os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, + data->nonce_mt, EAP_SIM_NONCE_MT_LEN); + } else { + os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN); + os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac, + EAP_SIM_MAC_LEN); + + } wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len); return id; diff --git a/contrib/wpa/src/eap_server/eap_server_teap.c b/contrib/wpa/src/eap_server/eap_server_teap.c new file mode 100644 index 000000000000..d8e5414d4563 --- /dev/null +++ b/contrib/wpa/src/eap_server/eap_server_teap.c @@ -0,0 +1,1947 @@ +/* + * EAP-TEAP server (RFC 7170) + * Copyright (c) 2004-2019, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/aes_wrap.h" +#include "crypto/tls.h" +#include "crypto/random.h" +#include "eap_common/eap_teap_common.h" +#include "eap_i.h" +#include "eap_tls_common.h" + + +static void eap_teap_reset(struct eap_sm *sm, void *priv); + + +/* Private PAC-Opaque TLV types */ +#define PAC_OPAQUE_TYPE_PAD 0 +#define PAC_OPAQUE_TYPE_KEY 1 +#define PAC_OPAQUE_TYPE_LIFETIME 2 +#define PAC_OPAQUE_TYPE_IDENTITY 3 + +struct eap_teap_data { + struct eap_ssl_data ssl; + enum { + START, PHASE1, PHASE1B, PHASE2_START, PHASE2_ID, + PHASE2_BASIC_AUTH, PHASE2_METHOD, CRYPTO_BINDING, REQUEST_PAC, + FAILURE_SEND_RESULT, SUCCESS, FAILURE + } state; + + u8 teap_version; + u8 peer_version; + u16 tls_cs; + + const struct eap_method *phase2_method; + void *phase2_priv; + + u8 crypto_binding_nonce[32]; + int final_result; + + u8 simck_msk[EAP_TEAP_SIMCK_LEN]; + u8 cmk_msk[EAP_TEAP_CMK_LEN]; + u8 simck_emsk[EAP_TEAP_SIMCK_LEN]; + u8 cmk_emsk[EAP_TEAP_CMK_LEN]; + int simck_idx; + int cmk_emsk_available; + + u8 pac_opaque_encr[16]; + u8 *srv_id; + size_t srv_id_len; + char *srv_id_info; + + int anon_provisioning; + int send_new_pac; /* server triggered re-keying of Tunnel PAC */ + struct wpabuf *pending_phase2_resp; + struct wpabuf *server_outer_tlvs; + struct wpabuf *peer_outer_tlvs; + u8 *identity; /* from PAC-Opaque */ + size_t identity_len; + int eap_seq; + int tnc_started; + + int pac_key_lifetime; + int pac_key_refresh_time; + + enum teap_error_codes error_code; +}; + + +static int eap_teap_process_phase2_start(struct eap_sm *sm, + struct eap_teap_data *data); + + +static const char * eap_teap_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case PHASE1: + return "PHASE1"; + case PHASE1B: + return "PHASE1B"; + case PHASE2_START: + return "PHASE2_START"; + case PHASE2_ID: + return "PHASE2_ID"; + case PHASE2_BASIC_AUTH: + return "PHASE2_BASIC_AUTH"; + case PHASE2_METHOD: + return "PHASE2_METHOD"; + case CRYPTO_BINDING: + return "CRYPTO_BINDING"; + case REQUEST_PAC: + return "REQUEST_PAC"; + case FAILURE_SEND_RESULT: + return "FAILURE_SEND_RESULT"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "Unknown?!"; + } +} + + +static void eap_teap_state(struct eap_teap_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-TEAP: %s -> %s", + eap_teap_state_txt(data->state), + eap_teap_state_txt(state)); + data->state = state; +} + + +static EapType eap_teap_req_failure(struct eap_teap_data *data, + enum teap_error_codes error) +{ + eap_teap_state(data, FAILURE_SEND_RESULT); + return EAP_TYPE_NONE; +} + + +static int eap_teap_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, + const u8 *client_random, + const u8 *server_random, + u8 *master_secret) +{ + struct eap_teap_data *data = ctx; + const u8 *pac_opaque; + size_t pac_opaque_len; + u8 *buf, *pos, *end, *pac_key = NULL; + os_time_t lifetime = 0; + struct os_time now; + u8 *identity = NULL; + size_t identity_len = 0; + + wpa_printf(MSG_DEBUG, "EAP-TEAP: SessionTicket callback"); + wpa_hexdump(MSG_DEBUG, "EAP-TEAP: SessionTicket (PAC-Opaque)", + ticket, len); + + if (len < 4 || WPA_GET_BE16(ticket) != PAC_TYPE_PAC_OPAQUE) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: Ignore invalid SessionTicket"); + return 0; + } + + pac_opaque_len = WPA_GET_BE16(ticket + 2); + pac_opaque = ticket + 4; + if (pac_opaque_len < 8 || pac_opaque_len % 8 || + pac_opaque_len > len - 4) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Ignore invalid PAC-Opaque (len=%lu left=%lu)", + (unsigned long) pac_opaque_len, + (unsigned long) len); + return 0; + } + wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Received PAC-Opaque", + pac_opaque, pac_opaque_len); + + buf = os_malloc(pac_opaque_len - 8); + if (!buf) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Failed to allocate memory for decrypting PAC-Opaque"); + return 0; + } + + if (aes_unwrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr), + (pac_opaque_len - 8) / 8, pac_opaque, buf) < 0) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: Failed to decrypt PAC-Opaque"); + os_free(buf); + /* + * This may have been caused by server changing the PAC-Opaque + * encryption key, so just ignore this PAC-Opaque instead of + * failing the authentication completely. Provisioning can now + * be used to provision a new PAC. + */ + return 0; + } + + end = buf + pac_opaque_len - 8; + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Decrypted PAC-Opaque", + buf, end - buf); + + pos = buf; + while (end - pos > 1) { + u8 id, elen; + + id = *pos++; + elen = *pos++; + if (elen > end - pos) + break; + + switch (id) { + case PAC_OPAQUE_TYPE_PAD: + goto done; + case PAC_OPAQUE_TYPE_KEY: + if (elen != EAP_TEAP_PAC_KEY_LEN) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Invalid PAC-Key length %d", + elen); + os_free(buf); + return -1; + } + pac_key = pos; + wpa_hexdump_key(MSG_DEBUG, + "EAP-TEAP: PAC-Key from decrypted PAC-Opaque", + pac_key, EAP_TEAP_PAC_KEY_LEN); + break; + case PAC_OPAQUE_TYPE_LIFETIME: + if (elen != 4) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Invalid PAC-Key lifetime length %d", + elen); + os_free(buf); + return -1; + } + lifetime = WPA_GET_BE32(pos); + break; + case PAC_OPAQUE_TYPE_IDENTITY: + identity = pos; + identity_len = elen; + break; + } + + pos += elen; + } +done: + + if (!pac_key) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: No PAC-Key included in PAC-Opaque"); + os_free(buf); + return -1; + } + + if (identity) { + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-TEAP: Identity from PAC-Opaque", + identity, identity_len); + os_free(data->identity); + data->identity = os_malloc(identity_len); + if (data->identity) { + os_memcpy(data->identity, identity, identity_len); + data->identity_len = identity_len; + } + } + + if (os_get_time(&now) < 0 || lifetime <= 0 || now.sec > lifetime) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: PAC-Key not valid anymore (lifetime=%ld now=%ld)", + lifetime, now.sec); + data->send_new_pac = 2; + /* + * Allow PAC to be used to allow a PAC update with some level + * of server authentication (i.e., do not fall back to full TLS + * handshake since we cannot be sure that the peer would be + * able to validate server certificate now). However, reject + * the authentication since the PAC was not valid anymore. Peer + * can connect again with the newly provisioned PAC after this. + */ + } else if (lifetime - now.sec < data->pac_key_refresh_time) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: PAC-Key soft timeout; send an update if authentication succeeds"); + data->send_new_pac = 1; + } + + /* EAP-TEAP uses PAC-Key as the TLS master_secret */ + os_memcpy(master_secret, pac_key, EAP_TEAP_PAC_KEY_LEN); + + os_free(buf); + + return 1; +} + + +static int eap_teap_derive_key_auth(struct eap_sm *sm, + struct eap_teap_data *data) +{ + int res; + + /* RFC 7170, Section 5.1 */ + res = tls_connection_export_key(sm->ssl_ctx, data->ssl.conn, + TEAP_TLS_EXPORTER_LABEL_SKS, NULL, 0, + data->simck_msk, EAP_TEAP_SIMCK_LEN); + if (res) + return res; + wpa_hexdump_key(MSG_DEBUG, + "EAP-TEAP: session_key_seed (S-IMCK[0])", + data->simck_msk, EAP_TEAP_SIMCK_LEN); + os_memcpy(data->simck_emsk, data->simck_msk, EAP_TEAP_SIMCK_LEN); + data->simck_idx = 0; + return 0; +} + + +static int eap_teap_update_icmk(struct eap_sm *sm, struct eap_teap_data *data) +{ + u8 *msk = NULL, *emsk = NULL; + size_t msk_len = 0, emsk_len = 0; + int res; + + wpa_printf(MSG_DEBUG, "EAP-TEAP: Deriving ICMK[%d] (S-IMCK and CMK)", + data->simck_idx + 1); + + if (sm->eap_teap_auth == 1) + return eap_teap_derive_cmk_basic_pw_auth(data->simck_msk, + data->cmk_msk); + + if (!data->phase2_method || !data->phase2_priv) { + wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available"); + return -1; + } + + if (data->phase2_method->getKey) { + msk = data->phase2_method->getKey(sm, data->phase2_priv, + &msk_len); + if (!msk) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Could not fetch Phase 2 MSK"); + return -1; + } + } + + if (data->phase2_method->get_emsk) { + emsk = data->phase2_method->get_emsk(sm, data->phase2_priv, + &emsk_len); + } + + res = eap_teap_derive_imck(data->simck_msk, data->simck_emsk, + msk, msk_len, emsk, emsk_len, + data->simck_msk, data->cmk_msk, + data->simck_emsk, data->cmk_emsk); + bin_clear_free(msk, msk_len); + bin_clear_free(emsk, emsk_len); + if (res == 0) { + data->simck_idx++; + if (emsk) + data->cmk_emsk_available = 1; + } + return 0; +} + + +static void * eap_teap_init(struct eap_sm *sm) +{ + struct eap_teap_data *data; + + data = os_zalloc(sizeof(*data)); + if (!data) + return NULL; + data->teap_version = EAP_TEAP_VERSION; + data->state = START; + + if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TEAP)) { + wpa_printf(MSG_INFO, "EAP-TEAP: Failed to initialize SSL."); + eap_teap_reset(sm, data); + return NULL; + } + + /* TODO: Add anon-DH TLS cipher suites (and if one is negotiated, + * enforce inner EAP with mutual authentication to be used) */ + + if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn, + eap_teap_session_ticket_cb, + data) < 0) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Failed to set SessionTicket callback"); + eap_teap_reset(sm, data); + return NULL; + } + + if (!sm->pac_opaque_encr_key) { + wpa_printf(MSG_INFO, + "EAP-TEAP: No PAC-Opaque encryption key configured"); + eap_teap_reset(sm, data); + return NULL; + } + os_memcpy(data->pac_opaque_encr, sm->pac_opaque_encr_key, + sizeof(data->pac_opaque_encr)); + + if (!sm->eap_fast_a_id) { + wpa_printf(MSG_INFO, "EAP-TEAP: No A-ID configured"); + eap_teap_reset(sm, data); + return NULL; + } + data->srv_id = os_malloc(sm->eap_fast_a_id_len); + if (!data->srv_id) { + eap_teap_reset(sm, data); + return NULL; + } + os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len); + data->srv_id_len = sm->eap_fast_a_id_len; + + if (!sm->eap_fast_a_id_info) { + wpa_printf(MSG_INFO, "EAP-TEAP: No A-ID-Info configured"); + eap_teap_reset(sm, data); + return NULL; + } + data->srv_id_info = os_strdup(sm->eap_fast_a_id_info); + if (!data->srv_id_info) { + eap_teap_reset(sm, data); + return NULL; + } + + /* PAC-Key lifetime in seconds (hard limit) */ + data->pac_key_lifetime = sm->pac_key_lifetime; + + /* + * PAC-Key refresh time in seconds (soft limit on remaining hard + * limit). The server will generate a new PAC-Key when this number of + * seconds (or fewer) of the lifetime remains. + */ + data->pac_key_refresh_time = sm->pac_key_refresh_time; + + return data; +} + + +static void eap_teap_reset(struct eap_sm *sm, void *priv) +{ + struct eap_teap_data *data = priv; + + if (!data) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->reset(sm, data->phase2_priv); + eap_server_tls_ssl_deinit(sm, &data->ssl); + os_free(data->srv_id); + os_free(data->srv_id_info); + wpabuf_free(data->pending_phase2_resp); + wpabuf_free(data->server_outer_tlvs); + wpabuf_free(data->peer_outer_tlvs); + os_free(data->identity); + forced_memzero(data->simck_msk, EAP_TEAP_SIMCK_LEN); + forced_memzero(data->simck_emsk, EAP_TEAP_SIMCK_LEN); + forced_memzero(data->cmk_msk, EAP_TEAP_CMK_LEN); + forced_memzero(data->cmk_emsk, EAP_TEAP_CMK_LEN); + forced_memzero(data->pac_opaque_encr, sizeof(data->pac_opaque_encr)); + bin_clear_free(data, sizeof(*data)); +} + + +static struct wpabuf * eap_teap_build_start(struct eap_sm *sm, + struct eap_teap_data *data, u8 id) +{ + struct wpabuf *req; + size_t outer_tlv_len = sizeof(struct teap_tlv_hdr) + data->srv_id_len; + const u8 *start, *end; + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TEAP, + 1 + 4 + outer_tlv_len, EAP_CODE_REQUEST, id); + if (!req) { + wpa_printf(MSG_ERROR, + "EAP-TEAP: Failed to allocate memory for request"); + eap_teap_state(data, FAILURE); + return NULL; + } + + wpabuf_put_u8(req, EAP_TLS_FLAGS_START | EAP_TEAP_FLAGS_OUTER_TLV_LEN | + data->teap_version); + wpabuf_put_be32(req, outer_tlv_len); + + start = wpabuf_put(req, 0); + + /* RFC 7170, Section 4.2.2: Authority-ID TLV */ + eap_teap_put_tlv(req, TEAP_TLV_AUTHORITY_ID, + data->srv_id, data->srv_id_len); + + end = wpabuf_put(req, 0); + wpabuf_free(data->server_outer_tlvs); + data->server_outer_tlvs = wpabuf_alloc_copy(start, end - start); + if (!data->server_outer_tlvs) { + eap_teap_state(data, FAILURE); + return NULL; + } + + eap_teap_state(data, PHASE1); + + return req; +} + + +static int eap_teap_phase1_done(struct eap_sm *sm, struct eap_teap_data *data) +{ + char cipher[64]; + + wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 1 done, starting Phase 2"); + + data->tls_cs = tls_connection_get_cipher_suite(data->ssl.conn); + wpa_printf(MSG_DEBUG, "EAP-TEAP: TLS cipher suite 0x%04x", + data->tls_cs); + + if (tls_get_cipher(sm->ssl_ctx, data->ssl.conn, cipher, sizeof(cipher)) + < 0) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Failed to get cipher information"); + eap_teap_state(data, FAILURE); + return -1; + } + data->anon_provisioning = os_strstr(cipher, "ADH") != NULL; + + if (data->anon_provisioning) + wpa_printf(MSG_DEBUG, "EAP-TEAP: Anonymous provisioning"); + + if (eap_teap_derive_key_auth(sm, data) < 0) { + eap_teap_state(data, FAILURE); + return -1; + } + + eap_teap_state(data, PHASE2_START); + + return 0; +} + + +static struct wpabuf * eap_teap_build_phase2_req(struct eap_sm *sm, + struct eap_teap_data *data, + u8 id) +{ + struct wpabuf *req; + + if (sm->eap_teap_auth == 1) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: Initiate Basic-Password-Auth"); + req = wpabuf_alloc(sizeof(struct teap_tlv_hdr)); + if (!req) + return NULL; + eap_teap_put_tlv_hdr(req, TEAP_TLV_BASIC_PASSWORD_AUTH_REQ, 0); + return req; + } + + wpa_printf(MSG_DEBUG, "EAP-TEAP: Initiate inner EAP method"); + if (!data->phase2_priv) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Phase 2 method not initialized"); + return NULL; + } + + req = data->phase2_method->buildReq(sm, data->phase2_priv, id); + if (!req) + return NULL; + + wpa_hexdump_buf_key(MSG_MSGDUMP, "EAP-TEAP: Phase 2 EAP-Request", req); + return eap_teap_tlv_eap_payload(req); +} + + +static struct wpabuf * eap_teap_build_crypto_binding( + struct eap_sm *sm, struct eap_teap_data *data) +{ + struct wpabuf *buf; + struct teap_tlv_result *result; + struct teap_tlv_crypto_binding *cb; + u8 subtype, flags; + + buf = wpabuf_alloc(2 * sizeof(*result) + sizeof(*cb)); + if (!buf) + return NULL; + + if (data->send_new_pac || data->anon_provisioning || + data->phase2_method) + data->final_result = 0; + else + data->final_result = 1; + + if (!data->final_result || data->eap_seq > 0) { + /* Intermediate-Result */ + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Add Intermediate-Result TLV (status=SUCCESS)"); + result = wpabuf_put(buf, sizeof(*result)); + result->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | + TEAP_TLV_INTERMEDIATE_RESULT); + result->length = host_to_be16(2); + result->status = host_to_be16(TEAP_STATUS_SUCCESS); + } + + if (data->final_result) { + /* Result TLV */ + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Add Result TLV (status=SUCCESS)"); + result = wpabuf_put(buf, sizeof(*result)); + result->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | + TEAP_TLV_RESULT); + result->length = host_to_be16(2); + result->status = host_to_be16(TEAP_STATUS_SUCCESS); + } + + /* Crypto-Binding TLV */ + cb = wpabuf_put(buf, sizeof(*cb)); + cb->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | + TEAP_TLV_CRYPTO_BINDING); + cb->length = host_to_be16(sizeof(*cb) - sizeof(struct teap_tlv_hdr)); + cb->version = EAP_TEAP_VERSION; + cb->received_version = data->peer_version; + /* FIX: RFC 7170 is not clear on which Flags value to use when + * Crypto-Binding TLV is used with Basic-Password-Auth */ + flags = data->cmk_emsk_available ? + TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC : + TEAP_CRYPTO_BINDING_MSK_CMAC; + subtype = TEAP_CRYPTO_BINDING_SUBTYPE_REQUEST; + cb->subtype = (flags << 4) | subtype; + if (random_get_bytes(cb->nonce, sizeof(cb->nonce)) < 0) { + wpabuf_free(buf); + return NULL; + } + + /* + * RFC 7170, Section 4.2.13: + * The nonce in a request MUST have its least significant bit set to 0. + */ + cb->nonce[sizeof(cb->nonce) - 1] &= ~0x01; + + os_memcpy(data->crypto_binding_nonce, cb->nonce, sizeof(cb->nonce)); + + if (eap_teap_compound_mac(data->tls_cs, cb, data->server_outer_tlvs, + data->peer_outer_tlvs, data->cmk_msk, + cb->msk_compound_mac) < 0) { + wpabuf_free(buf); + return NULL; + } + + if (data->cmk_emsk_available && + eap_teap_compound_mac(data->tls_cs, cb, data->server_outer_tlvs, + data->peer_outer_tlvs, data->cmk_emsk, + cb->emsk_compound_mac) < 0) { + wpabuf_free(buf); + return NULL; + } + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Add Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u", + cb->version, cb->received_version, flags, subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce", + cb->nonce, sizeof(cb->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC", + cb->emsk_compound_mac, sizeof(cb->emsk_compound_mac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC", + cb->msk_compound_mac, sizeof(cb->msk_compound_mac)); + + return buf; +} + + +static struct wpabuf * eap_teap_build_pac(struct eap_sm *sm, + struct eap_teap_data *data) +{ + u8 pac_key[EAP_TEAP_PAC_KEY_LEN]; + u8 *pac_buf, *pac_opaque; + struct wpabuf *buf; + u8 *pos; + size_t buf_len, srv_id_info_len, pac_len; + struct teap_tlv_hdr *pac_tlv; + struct pac_attr_hdr *pac_info; + struct teap_tlv_result *result; + struct os_time now; + + wpa_printf(MSG_DEBUG, "EAP-TEAP: Build a new PAC"); + + if (random_get_bytes(pac_key, EAP_TEAP_PAC_KEY_LEN) < 0 || + os_get_time(&now) < 0) + return NULL; + wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Generated PAC-Key", + pac_key, EAP_TEAP_PAC_KEY_LEN); + + pac_len = (2 + EAP_TEAP_PAC_KEY_LEN) + (2 + 4) + + (2 + sm->identity_len) + 8; + pac_buf = os_malloc(pac_len); + if (!pac_buf) + return NULL; + + srv_id_info_len = os_strlen(data->srv_id_info); + + pos = pac_buf; + *pos++ = PAC_OPAQUE_TYPE_KEY; + *pos++ = EAP_TEAP_PAC_KEY_LEN; + os_memcpy(pos, pac_key, EAP_TEAP_PAC_KEY_LEN); + pos += EAP_TEAP_PAC_KEY_LEN; + + wpa_printf(MSG_DEBUG, "EAP-TEAP: PAC-Key lifetime: %u seconds", + data->pac_key_lifetime); + *pos++ = PAC_OPAQUE_TYPE_LIFETIME; + *pos++ = 4; + WPA_PUT_BE32(pos, now.sec + data->pac_key_lifetime); + pos += 4; + + if (sm->identity) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Opaque Identity", + sm->identity, sm->identity_len); + *pos++ = PAC_OPAQUE_TYPE_IDENTITY; + *pos++ = sm->identity_len; + os_memcpy(pos, sm->identity, sm->identity_len); + pos += sm->identity_len; + } + + pac_len = pos - pac_buf; + while (pac_len % 8) { + *pos++ = PAC_OPAQUE_TYPE_PAD; + pac_len++; + } + + pac_opaque = os_malloc(pac_len + 8); + if (!pac_opaque) { + os_free(pac_buf); + return NULL; + } + if (aes_wrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr), + pac_len / 8, pac_buf, pac_opaque) < 0) { + os_free(pac_buf); + os_free(pac_opaque); + return NULL; + } + os_free(pac_buf); + + pac_len += 8; + wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Opaque", pac_opaque, pac_len); + + buf_len = sizeof(*pac_tlv) + + sizeof(struct pac_attr_hdr) + EAP_TEAP_PAC_KEY_LEN + + sizeof(struct pac_attr_hdr) + pac_len + + data->srv_id_len + srv_id_info_len + 100 + sizeof(*result); + buf = wpabuf_alloc(buf_len); + if (!buf) { + os_free(pac_opaque); + return NULL; + } + + /* Result TLV */ + wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Result TLV (status=SUCCESS)"); + result = wpabuf_put(buf, sizeof(*result)); + WPA_PUT_BE16((u8 *) &result->tlv_type, + TEAP_TLV_MANDATORY | TEAP_TLV_RESULT); + WPA_PUT_BE16((u8 *) &result->length, 2); + WPA_PUT_BE16((u8 *) &result->status, TEAP_STATUS_SUCCESS); + + /* PAC TLV */ + wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV"); + pac_tlv = wpabuf_put(buf, sizeof(*pac_tlv)); + pac_tlv->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | TEAP_TLV_PAC); + + /* PAC-Key */ + eap_teap_put_tlv(buf, PAC_TYPE_PAC_KEY, pac_key, EAP_TEAP_PAC_KEY_LEN); + + /* PAC-Opaque */ + eap_teap_put_tlv(buf, PAC_TYPE_PAC_OPAQUE, pac_opaque, pac_len); + os_free(pac_opaque); + + /* PAC-Info */ + pac_info = wpabuf_put(buf, sizeof(*pac_info)); + pac_info->type = host_to_be16(PAC_TYPE_PAC_INFO); + + /* PAC-Lifetime (inside PAC-Info) */ + eap_teap_put_tlv_hdr(buf, PAC_TYPE_CRED_LIFETIME, 4); + wpabuf_put_be32(buf, now.sec + data->pac_key_lifetime); + + /* A-ID (inside PAC-Info) */ + eap_teap_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len); + + /* Note: headers may be misaligned after A-ID */ + + if (sm->identity) { + eap_teap_put_tlv(buf, PAC_TYPE_I_ID, sm->identity, + sm->identity_len); + } + + /* A-ID-Info (inside PAC-Info) */ + eap_teap_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info, + srv_id_info_len); + + /* PAC-Type (inside PAC-Info) */ + eap_teap_put_tlv_hdr(buf, PAC_TYPE_PAC_TYPE, 2); + wpabuf_put_be16(buf, PAC_TYPE_TUNNEL_PAC); + + /* Update PAC-Info and PAC TLV Length fields */ + pos = wpabuf_put(buf, 0); + pac_info->len = host_to_be16(pos - (u8 *) (pac_info + 1)); + pac_tlv->length = host_to_be16(pos - (u8 *) (pac_tlv + 1)); + + return buf; +} + + +static int eap_teap_encrypt_phase2(struct eap_sm *sm, + struct eap_teap_data *data, + struct wpabuf *plain, int piggyback) +{ + struct wpabuf *encr; + + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TEAP: Encrypting Phase 2 TLVs", + plain); + encr = eap_server_tls_encrypt(sm, &data->ssl, plain); + wpabuf_free(plain); + + if (!encr) + return -1; + + if (data->ssl.tls_out && piggyback) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Piggyback Phase 2 data (len=%d) with last Phase 1 Message (len=%d used=%d)", + (int) wpabuf_len(encr), + (int) wpabuf_len(data->ssl.tls_out), + (int) data->ssl.tls_out_pos); + if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) { + wpa_printf(MSG_WARNING, + "EAP-TEAP: Failed to resize output buffer"); + wpabuf_free(encr); + return -1; + } + wpabuf_put_buf(data->ssl.tls_out, encr); + wpabuf_free(encr); + } else { + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = encr; + } + + return 0; +} + + +static struct wpabuf * eap_teap_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_teap_data *data = priv; + struct wpabuf *req = NULL; + int piggyback = 0; + + if (data->ssl.state == FRAG_ACK) { + return eap_server_tls_build_ack(id, EAP_TYPE_TEAP, + data->teap_version); + } + + if (data->ssl.state == WAIT_FRAG_ACK) { + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TEAP, + data->teap_version, id); + } + + switch (data->state) { + case START: + return eap_teap_build_start(sm, data, id); + case PHASE1B: + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + if (eap_teap_phase1_done(sm, data) < 0) + return NULL; + if (data->state == PHASE2_START) { + int res; + + /* + * Try to generate Phase 2 data to piggyback + * with the end of Phase 1 to avoid extra + * roundtrip. + */ + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Try to start Phase 2"); + res = eap_teap_process_phase2_start(sm, data); + if (res == 1) { + req = eap_teap_build_crypto_binding( + sm, data); + piggyback = 1; + break; + } + + if (res) + break; + req = eap_teap_build_phase2_req(sm, data, id); + piggyback = 1; + } + } + break; + case PHASE2_ID: + case PHASE2_BASIC_AUTH: + case PHASE2_METHOD: + req = eap_teap_build_phase2_req(sm, data, id); + break; + case CRYPTO_BINDING: + req = eap_teap_build_crypto_binding(sm, data); + if (data->phase2_method) { + /* + * Include the start of the next EAP method in the + * sequence in the same message with Crypto-Binding to + * save a round-trip. + */ + struct wpabuf *eap; + + eap = eap_teap_build_phase2_req(sm, data, id); + req = wpabuf_concat(req, eap); + eap_teap_state(data, PHASE2_METHOD); + } + break; + case REQUEST_PAC: + req = eap_teap_build_pac(sm, data); + break; + case FAILURE_SEND_RESULT: + req = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0); + if (data->error_code) + req = wpabuf_concat( + req, eap_teap_tlv_error(data->error_code)); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TEAP: %s - unexpected state %d", + __func__, data->state); + return NULL; + } + + if (req && eap_teap_encrypt_phase2(sm, data, req, piggyback) < 0) + return NULL; + + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TEAP, + data->teap_version, id); +} + + +static Boolean eap_teap_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, respData, &len); + if (!pos || len < 1) { + wpa_printf(MSG_INFO, "EAP-TEAP: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static int eap_teap_phase2_init(struct eap_sm *sm, struct eap_teap_data *data, + EapType eap_type) +{ + if (data->phase2_priv && data->phase2_method) { + data->phase2_method->reset(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + } + data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, + eap_type); + if (!data->phase2_method) + return -1; + + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + + return data->phase2_priv ? 0 : -1; +} + + +static void eap_teap_process_phase2_response(struct eap_sm *sm, + struct eap_teap_data *data, + u8 *in_data, size_t in_len) +{ + u8 next_type = EAP_TYPE_NONE; + struct eap_hdr *hdr; + u8 *pos; + size_t left; + struct wpabuf buf; + const struct eap_method *m = data->phase2_method; + void *priv = data->phase2_priv; + + if (!priv) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: %s - Phase 2 not initialized?!", + __func__); + return; + } + + hdr = (struct eap_hdr *) in_data; + pos = (u8 *) (hdr + 1); + + if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { + left = in_len - sizeof(*hdr); + wpa_hexdump(MSG_DEBUG, + "EAP-TEAP: Phase 2 type Nak'ed; allowed types", + pos + 1, left - 1); +#ifdef EAP_SERVER_TNC + if (m && m->vendor == EAP_VENDOR_IETF && + m->method == EAP_TYPE_TNC) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Peer Nak'ed required TNC negotiation"); + next_type = eap_teap_req_failure(data, 0); + eap_teap_phase2_init(sm, data, next_type); + return; + } +#endif /* EAP_SERVER_TNC */ + eap_sm_process_nak(sm, pos + 1, left - 1); + if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && + sm->user->methods[sm->user_eap_method_index].method != + EAP_TYPE_NONE) { + next_type = sm->user->methods[ + sm->user_eap_method_index++].method; + wpa_printf(MSG_DEBUG, "EAP-TEAP: try EAP type %d", + next_type); + } else { + next_type = eap_teap_req_failure(data, 0); + } + eap_teap_phase2_init(sm, data, next_type); + return; + } + + wpabuf_set(&buf, in_data, in_len); + + if (m->check(sm, priv, &buf)) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Phase 2 check() asked to ignore the packet"); + eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD); + return; + } + + m->process(sm, priv, &buf); + + if (!m->isDone(sm, priv)) + return; + + if (!m->isSuccess(sm, priv)) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 2 method failed"); + next_type = eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD); + eap_teap_phase2_init(sm, data, next_type); + return; + } + + switch (data->state) { + case PHASE2_ID: + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-TEAP: Phase 2 Identity not found in the user database", + sm->identity, sm->identity_len); + next_type = eap_teap_req_failure( + data, TEAP_ERROR_INNER_METHOD); + break; + } + + eap_teap_state(data, PHASE2_METHOD); + if (data->anon_provisioning) { + /* TODO: Allow any inner EAP method that provides + * mutual authentication and EMSK derivation (i.e., + * EAP-pwd or EAP-EKE). */ + next_type = EAP_TYPE_PWD; + sm->user_eap_method_index = 0; + } else { + next_type = sm->user->methods[0].method; + sm->user_eap_method_index = 1; + } + wpa_printf(MSG_DEBUG, "EAP-TEAP: Try EAP type %d", next_type); + break; + case PHASE2_METHOD: + case CRYPTO_BINDING: + eap_teap_update_icmk(sm, data); + eap_teap_state(data, CRYPTO_BINDING); + data->eap_seq++; + next_type = EAP_TYPE_NONE; +#ifdef EAP_SERVER_TNC + if (sm->tnc && !data->tnc_started) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: Initialize TNC"); + next_type = EAP_TYPE_TNC; + data->tnc_started = 1; + } +#endif /* EAP_SERVER_TNC */ + break; + case FAILURE: + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TEAP: %s - unexpected state %d", + __func__, data->state); + break; + } + + eap_teap_phase2_init(sm, data, next_type); +} + + +static void eap_teap_process_phase2_eap(struct eap_sm *sm, + struct eap_teap_data *data, + u8 *in_data, size_t in_len) +{ + struct eap_hdr *hdr; + size_t len; + + hdr = (struct eap_hdr *) in_data; + if (in_len < (int) sizeof(*hdr)) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Too short Phase 2 EAP frame (len=%lu)", + (unsigned long) in_len); + eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD); + return; + } + len = be_to_host16(hdr->length); + if (len > in_len) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Length mismatch in Phase 2 EAP frame (len=%lu hdr->length=%lu)", + (unsigned long) in_len, (unsigned long) len); + eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD); + return; + } + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Received Phase 2: code=%d identifier=%d length=%lu", + hdr->code, hdr->identifier, + (unsigned long) len); + switch (hdr->code) { + case EAP_CODE_RESPONSE: + eap_teap_process_phase2_response(sm, data, (u8 *) hdr, len); + break; + default: + wpa_printf(MSG_INFO, + "EAP-TEAP: Unexpected code=%d in Phase 2 EAP header", + hdr->code); + break; + } +} + + +static void eap_teap_process_basic_auth_resp(struct eap_sm *sm, + struct eap_teap_data *data, + u8 *in_data, size_t in_len) +{ + u8 *pos, *end, *username, *password, *new_id; + u8 userlen, passlen; + + pos = in_data; + end = pos + in_len; + + if (end - pos < 1) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: No room for Basic-Password-Auth-Resp Userlen field"); + eap_teap_req_failure(data, 0); + return; + } + userlen = *pos++; + if (end - pos < userlen) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Truncated Basic-Password-Auth-Resp Username field"); + eap_teap_req_failure(data, 0); + return; + } + username = pos; + pos += userlen; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-TEAP: Basic-Password-Auth-Resp Username", + username, userlen); + + if (end - pos < 1) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: No room for Basic-Password-Auth-Resp Passlen field"); + eap_teap_req_failure(data, 0); + return; + } + passlen = *pos++; + if (end - pos < passlen) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Truncated Basic-Password-Auth-Resp Password field"); + eap_teap_req_failure(data, 0); + return; + } + password = pos; + pos += passlen; + wpa_hexdump_ascii_key(MSG_DEBUG, + "EAP-TEAP: Basic-Password-Auth-Resp Password", + password, passlen); + + if (end > pos) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Unexpected %d extra octet(s) at the end of Basic-Password-Auth-Resp TLV", + (int) (end - pos)); + eap_teap_req_failure(data, 0); + return; + } + + if (eap_user_get(sm, username, userlen, 1) != 0) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Username not found in the user database"); + eap_teap_req_failure(data, 0); + return; + } + + if (!sm->user || !sm->user->password || sm->user->password_hash) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: No plaintext user password configured"); + eap_teap_req_failure(data, 0); + return; + } + + if (sm->user->password_len != passlen || + os_memcmp_const(sm->user->password, password, passlen) != 0) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: Invalid password"); + eap_teap_req_failure(data, 0); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-TEAP: Correct password"); + new_id = os_memdup(username, userlen); + if (new_id) { + os_free(sm->identity); + sm->identity = new_id; + sm->identity_len = userlen; + } + eap_teap_state(data, CRYPTO_BINDING); + eap_teap_update_icmk(sm, data); +} + + +static int eap_teap_parse_tlvs(struct wpabuf *data, + struct eap_teap_tlv_parse *tlv) +{ + u16 tlv_type; + int mandatory, res; + size_t len; + u8 *pos, *end; + + os_memset(tlv, 0, sizeof(*tlv)); + + pos = wpabuf_mhead(data); + end = pos + wpabuf_len(data); + while (end - pos > 4) { + mandatory = pos[0] & 0x80; + tlv_type = WPA_GET_BE16(pos) & 0x3fff; + pos += 2; + len = WPA_GET_BE16(pos); + pos += 2; + if (len > (size_t) (end - pos)) { + wpa_printf(MSG_INFO, "EAP-TEAP: TLV overflow"); + return -1; + } + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Received Phase 2: TLV type %u (%s) length %u%s", + tlv_type, eap_teap_tlv_type_str(tlv_type), + (unsigned int) len, + mandatory ? " (mandatory)" : ""); + + res = eap_teap_parse_tlv(tlv, tlv_type, pos, len); + if (res == -2) + break; + if (res < 0) { + if (mandatory) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: NAK unknown mandatory TLV type %u", + tlv_type); + /* TODO: generate NAK TLV */ + break; + } + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Ignore unknown optional TLV type %u", + tlv_type); + } + + pos += len; + } + + return 0; +} + + +static int eap_teap_validate_crypto_binding( + struct eap_teap_data *data, const struct teap_tlv_crypto_binding *cb, + size_t bind_len) +{ + u8 flags, subtype; + + subtype = cb->subtype & 0x0f; + flags = cb->subtype >> 4; + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Reply Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u", + cb->version, cb->received_version, flags, subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce", + cb->nonce, sizeof(cb->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC", + cb->emsk_compound_mac, sizeof(cb->emsk_compound_mac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC", + cb->msk_compound_mac, sizeof(cb->msk_compound_mac)); + + if (cb->version != EAP_TEAP_VERSION || + cb->received_version != data->peer_version) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Unexpected version in Crypto-Binding: Version %u Received Version %u", + cb->version, cb->received_version); + return -1; + } + + if (flags < 1 || flags > 3) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Unexpected Flags in Crypto-Binding: %u", + flags); + return -1; + } + + if (subtype != TEAP_CRYPTO_BINDING_SUBTYPE_RESPONSE) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Unexpected Sub-Type in Crypto-Binding: %u", + subtype); + return -1; + } + + if (os_memcmp_const(data->crypto_binding_nonce, cb->nonce, + EAP_TEAP_NONCE_LEN - 1) != 0 || + (data->crypto_binding_nonce[EAP_TEAP_NONCE_LEN - 1] | 1) != + cb->nonce[EAP_TEAP_NONCE_LEN - 1]) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Invalid Nonce in Crypto-Binding"); + return -1; + } + + if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC || + flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) { + u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN]; + + if (eap_teap_compound_mac(data->tls_cs, cb, + data->server_outer_tlvs, + data->peer_outer_tlvs, data->cmk_msk, + msk_compound_mac) < 0) + return -1; + if (os_memcmp_const(msk_compound_mac, cb->msk_compound_mac, + EAP_TEAP_COMPOUND_MAC_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, + "EAP-TEAP: Calculated MSK Compound MAC", + msk_compound_mac, + EAP_TEAP_COMPOUND_MAC_LEN); + wpa_printf(MSG_INFO, + "EAP-TEAP: MSK Compound MAC did not match"); + return -1; + } + } + + if ((flags == TEAP_CRYPTO_BINDING_EMSK_CMAC || + flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) && + data->cmk_emsk_available) { + u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN]; + + if (eap_teap_compound_mac(data->tls_cs, cb, + data->server_outer_tlvs, + data->peer_outer_tlvs, data->cmk_emsk, + emsk_compound_mac) < 0) + return -1; + if (os_memcmp_const(emsk_compound_mac, cb->emsk_compound_mac, + EAP_TEAP_COMPOUND_MAC_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, + "EAP-TEAP: Calculated EMSK Compound MAC", + emsk_compound_mac, + EAP_TEAP_COMPOUND_MAC_LEN); + wpa_printf(MSG_INFO, + "EAP-TEAP: EMSK Compound MAC did not match"); + return -1; + } + } + + if (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC && + !data->cmk_emsk_available) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Peer included only EMSK Compound MAC, but no locally generated inner EAP EMSK to validate this"); + return -1; + } + + return 0; +} + + +static int eap_teap_pac_type(u8 *pac, size_t len, u16 type) +{ + struct teap_attr_pac_type *tlv; + + if (!pac || len != sizeof(*tlv)) + return 0; + + tlv = (struct teap_attr_pac_type *) pac; + + return be_to_host16(tlv->type) == PAC_TYPE_PAC_TYPE && + be_to_host16(tlv->length) == 2 && + be_to_host16(tlv->pac_type) == type; +} + + +static void eap_teap_process_phase2_tlvs(struct eap_sm *sm, + struct eap_teap_data *data, + struct wpabuf *in_data) +{ + struct eap_teap_tlv_parse tlv; + int check_crypto_binding = data->state == CRYPTO_BINDING; + + if (eap_teap_parse_tlvs(in_data, &tlv) < 0) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Failed to parse received Phase 2 TLVs"); + return; + } + + if (tlv.result == TEAP_STATUS_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: Result TLV indicated failure"); + eap_teap_state(data, FAILURE); + return; + } + + if (tlv.nak) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Peer NAK'ed Vendor-Id %u NAK-Type %u", + WPA_GET_BE32(tlv.nak), WPA_GET_BE16(tlv.nak + 4)); + eap_teap_state(data, FAILURE_SEND_RESULT); + return; + } + + if (data->state == REQUEST_PAC) { + u16 type, len, res; + + if (!tlv.pac || tlv.pac_len < 6) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: No PAC Acknowledgement received"); + eap_teap_state(data, FAILURE); + return; + } + + type = WPA_GET_BE16(tlv.pac); + len = WPA_GET_BE16(tlv.pac + 2); + res = WPA_GET_BE16(tlv.pac + 4); + + if (type != PAC_TYPE_PAC_ACKNOWLEDGEMENT || len != 2 || + res != TEAP_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: PAC TLV did not contain acknowledgement"); + eap_teap_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: PAC-Acknowledgement received - PAC provisioning succeeded"); + eap_teap_state(data, SUCCESS); + return; + } + + if (check_crypto_binding) { + if (!tlv.crypto_binding) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: No Crypto-Binding TLV received"); + eap_teap_state(data, FAILURE); + return; + } + + if (data->final_result && + tlv.result != TEAP_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Crypto-Binding TLV without Success Result"); + eap_teap_state(data, FAILURE); + return; + } + + if (!data->final_result && + tlv.iresult != TEAP_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Crypto-Binding TLV without intermediate Success Result"); + eap_teap_state(data, FAILURE); + return; + } + + if (eap_teap_validate_crypto_binding(data, tlv.crypto_binding, + tlv.crypto_binding_len)) { + eap_teap_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Valid Crypto-Binding TLV received"); + if (data->final_result) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Authentication completed successfully"); + } + + if (data->anon_provisioning && + sm->eap_fast_prov != ANON_PROV && + sm->eap_fast_prov != BOTH_PROV) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Client is trying to use unauthenticated provisioning which is disabled"); + eap_teap_state(data, FAILURE); + return; + } + + if (sm->eap_fast_prov != AUTH_PROV && + sm->eap_fast_prov != BOTH_PROV && + tlv.request_action == TEAP_REQUEST_ACTION_PROCESS_TLV && + eap_teap_pac_type(tlv.pac, tlv.pac_len, + PAC_TYPE_TUNNEL_PAC)) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Client is trying to use authenticated provisioning which is disabled"); + eap_teap_state(data, FAILURE); + return; + } + + if (data->anon_provisioning || + (tlv.request_action == TEAP_REQUEST_ACTION_PROCESS_TLV && + eap_teap_pac_type(tlv.pac, tlv.pac_len, + PAC_TYPE_TUNNEL_PAC))) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Requested a new Tunnel PAC"); + eap_teap_state(data, REQUEST_PAC); + } else if (data->send_new_pac) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Server triggered re-keying of Tunnel PAC"); + eap_teap_state(data, REQUEST_PAC); + } else if (data->final_result) + eap_teap_state(data, SUCCESS); + } + + if (tlv.basic_auth_resp) { + if (sm->eap_teap_auth != 1) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Unexpected Basic-Password-Auth-Resp when trying to use inner EAP"); + eap_teap_state(data, FAILURE); + return; + } + eap_teap_process_basic_auth_resp(sm, data, tlv.basic_auth_resp, + tlv.basic_auth_resp_len); + } + + if (tlv.eap_payload_tlv) { + if (sm->eap_teap_auth == 1) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Unexpected EAP Payload TLV when trying to use Basic-Password-Auth"); + eap_teap_state(data, FAILURE); + return; + } + eap_teap_process_phase2_eap(sm, data, tlv.eap_payload_tlv, + tlv.eap_payload_tlv_len); + } +} + + +static void eap_teap_process_phase2(struct eap_sm *sm, + struct eap_teap_data *data, + struct wpabuf *in_buf) +{ + struct wpabuf *in_decrypted; + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Received %lu bytes encrypted data for Phase 2", + (unsigned long) wpabuf_len(in_buf)); + + if (data->pending_phase2_resp) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Pending Phase 2 response - skip decryption and use old data"); + eap_teap_process_phase2_tlvs(sm, data, + data->pending_phase2_resp); + wpabuf_free(data->pending_phase2_resp); + data->pending_phase2_resp = NULL; + return; + } + + in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_buf); + if (!in_decrypted) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Failed to decrypt Phase 2 data"); + eap_teap_state(data, FAILURE); + return; + } + + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TEAP: Decrypted Phase 2 TLVs", + in_decrypted); + + eap_teap_process_phase2_tlvs(sm, data, in_decrypted); + + if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Phase 2 method is in pending wait state - save decrypted response"); + wpabuf_free(data->pending_phase2_resp); + data->pending_phase2_resp = in_decrypted; + return; + } + + wpabuf_free(in_decrypted); +} + + +static int eap_teap_process_version(struct eap_sm *sm, void *priv, + int peer_version) +{ + struct eap_teap_data *data = priv; + + if (peer_version < 1) { + /* Version 1 was the first defined version, so reject 0 */ + wpa_printf(MSG_INFO, + "EAP-TEAP: Peer used unknown TEAP version %u", + peer_version); + return -1; + } + + if (peer_version < data->teap_version) { + wpa_printf(MSG_DEBUG, "EAP-TEAP: peer ver=%u, own ver=%u; " + "use version %u", + peer_version, data->teap_version, peer_version); + data->teap_version = peer_version; + } + + data->peer_version = peer_version; + + return 0; +} + + +static int eap_teap_process_phase1(struct eap_sm *sm, + struct eap_teap_data *data) +{ + if (eap_server_tls_phase1(sm, &data->ssl) < 0) { + wpa_printf(MSG_INFO, "EAP-TEAP: TLS processing failed"); + eap_teap_state(data, FAILURE); + return -1; + } + + if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || + wpabuf_len(data->ssl.tls_out) > 0) + return 1; + + /* + * Phase 1 was completed with the received message (e.g., when using + * abbreviated handshake), so Phase 2 can be started immediately + * without having to send through an empty message to the peer. + */ + + return eap_teap_phase1_done(sm, data); +} + + +static int eap_teap_process_phase2_start(struct eap_sm *sm, + struct eap_teap_data *data) +{ + u8 next_type; + + if (data->identity) { + /* Used PAC and identity is from PAC-Opaque */ + os_free(sm->identity); + sm->identity = data->identity; + data->identity = NULL; + sm->identity_len = data->identity_len; + data->identity_len = 0; + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-TEAP: Phase 2 Identity not found in the user database", + sm->identity, sm->identity_len); + next_type = EAP_TYPE_NONE; + eap_teap_state(data, PHASE2_METHOD); + } else if (sm->eap_teap_pac_no_inner) { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Used PAC and identity already known - skip inner auth"); + /* FIX: Need to derive CMK here. However, how is that + * supposed to be done? RFC 7170 does not tell that for + * the no-inner-auth case. */ + eap_teap_derive_cmk_basic_pw_auth(data->simck_msk, + data->cmk_msk); + eap_teap_state(data, CRYPTO_BINDING); + return 1; + } else if (sm->eap_teap_auth == 1) { + eap_teap_state(data, PHASE2_BASIC_AUTH); + return 1; + } else { + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Identity already known - skip Phase 2 Identity Request"); + next_type = sm->user->methods[0].method; + sm->user_eap_method_index = 1; + eap_teap_state(data, PHASE2_METHOD); + } + + } else if (sm->eap_teap_auth == 1) { + eap_teap_state(data, PHASE2_BASIC_AUTH); + return 0; + } else { + eap_teap_state(data, PHASE2_ID); + next_type = EAP_TYPE_IDENTITY; + } + + return eap_teap_phase2_init(sm, data, next_type); +} + + +static void eap_teap_process_msg(struct eap_sm *sm, void *priv, + const struct wpabuf *respData) +{ + struct eap_teap_data *data = priv; + + switch (data->state) { + case PHASE1: + case PHASE1B: + if (eap_teap_process_phase1(sm, data)) + break; + + /* fall through */ + case PHASE2_START: + eap_teap_process_phase2_start(sm, data); + break; + case PHASE2_ID: + case PHASE2_BASIC_AUTH: + case PHASE2_METHOD: + case CRYPTO_BINDING: + case REQUEST_PAC: + eap_teap_process_phase2(sm, data, data->ssl.tls_in); + break; + case FAILURE_SEND_RESULT: + /* Protected failure result indication completed. Ignore the + * received message (which is supposed to include Result TLV + * indicating failure) and terminate exchange with cleartext + * EAP-Failure. */ + eap_teap_state(data, FAILURE); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TEAP: Unexpected state %d in %s", + data->state, __func__); + break; + } +} + + +static void eap_teap_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_teap_data *data = priv; + const u8 *pos; + size_t len; + struct wpabuf *resp = respData; + u8 flags; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, respData, &len); + if (!pos || len < 1) + return; + + flags = *pos++; + len--; + + if (flags & EAP_TEAP_FLAGS_OUTER_TLV_LEN) { + /* Extract Outer TLVs from the message before common TLS + * processing */ + u32 message_len = 0, outer_tlv_len; + const u8 *hdr; + + if (data->state != PHASE1) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Unexpected Outer TLVs in a message that is not the first message from the peer"); + return; + } + + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (len < 4) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Too short message to include Message Length field"); + return; + } + + message_len = WPA_GET_BE32(pos); + pos += 4; + len -= 4; + if (message_len < 4) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Message Length field has too msall value to include Outer TLV Length field"); + return; + } + } + + if (len < 4) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Too short message to include Outer TLVs Length field"); + return; + } + + outer_tlv_len = WPA_GET_BE32(pos); + pos += 4; + len -= 4; + + wpa_printf(MSG_DEBUG, + "EAP-TEAP: Message Length %u Outer TLV Length %u", + message_len, outer_tlv_len); + if (len < outer_tlv_len) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Too short message to include Outer TLVs field"); + return; + } + + if (message_len && + (message_len < outer_tlv_len || + message_len < 4 + outer_tlv_len)) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Message Length field has too small value to include Outer TLVs"); + return; + } + + if (wpabuf_len(respData) < 4 + outer_tlv_len || + len < outer_tlv_len) + return; + resp = wpabuf_alloc(wpabuf_len(respData) - 4 - outer_tlv_len); + if (!resp) + return; + hdr = wpabuf_head(respData); + wpabuf_put_u8(resp, *hdr++); /* Code */ + wpabuf_put_u8(resp, *hdr++); /* Identifier */ + wpabuf_put_be16(resp, WPA_GET_BE16(hdr) - 4 - outer_tlv_len); + hdr += 2; + wpabuf_put_u8(resp, *hdr++); /* Type */ + /* Flags | Ver */ + wpabuf_put_u8(resp, flags & ~EAP_TEAP_FLAGS_OUTER_TLV_LEN); + + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) + wpabuf_put_be32(resp, message_len - 4 - outer_tlv_len); + + wpabuf_put_data(resp, pos, len - outer_tlv_len); + pos += len - outer_tlv_len; + wpabuf_free(data->peer_outer_tlvs); + data->peer_outer_tlvs = wpabuf_alloc_copy(pos, outer_tlv_len); + if (!data->peer_outer_tlvs) + return; + wpa_hexdump_buf(MSG_DEBUG, "EAP-TEAP: Outer TLVs", + data->peer_outer_tlvs); + + wpa_hexdump_buf(MSG_DEBUG, + "EAP-TEAP: TLS Data message after Outer TLV removal", + resp); + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, resp, + &len); + if (!pos || len < 1) { + wpa_printf(MSG_INFO, + "EAP-TEAP: Invalid frame after Outer TLV removal"); + return; + } + } + + if (data->state == PHASE1) + eap_teap_state(data, PHASE1B); + + if (eap_server_tls_process(sm, &data->ssl, resp, data, + EAP_TYPE_TEAP, eap_teap_process_version, + eap_teap_process_msg) < 0) + eap_teap_state(data, FAILURE); + + if (resp != respData) + wpabuf_free(resp); +} + + +static Boolean eap_teap_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_teap_data *data = priv; + + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_teap_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_teap_data *data = priv; + u8 *eapKeyData; + + if (data->state != SUCCESS) + return NULL; + + eapKeyData = os_malloc(EAP_TEAP_KEY_LEN); + if (!eapKeyData) + return NULL; + + /* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j] + * is used in this derivation */ + if (eap_teap_derive_eap_msk(data->simck_msk, eapKeyData) < 0) { + os_free(eapKeyData); + return NULL; + } + *len = EAP_TEAP_KEY_LEN; + + return eapKeyData; +} + + +static u8 * eap_teap_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_teap_data *data = priv; + u8 *eapKeyData; + + if (data->state != SUCCESS) + return NULL; + + eapKeyData = os_malloc(EAP_EMSK_LEN); + if (!eapKeyData) + return NULL; + + /* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j] + * is used in this derivation */ + if (eap_teap_derive_eap_emsk(data->simck_msk, eapKeyData) < 0) { + os_free(eapKeyData); + return NULL; + } + *len = EAP_EMSK_LEN; + + return eapKeyData; +} + + +static Boolean eap_teap_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_teap_data *data = priv; + + return data->state == SUCCESS; +} + + +static u8 * eap_teap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_teap_data *data = priv; + const size_t max_id_len = 100; + int res; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + id = os_malloc(max_id_len); + if (!id) + return NULL; + + id[0] = EAP_TYPE_TEAP; + res = tls_get_tls_unique(data->ssl.conn, id + 1, max_id_len - 1); + if (res < 0) { + os_free(id); + wpa_printf(MSG_ERROR, "EAP-TEAP: Failed to derive Session-Id"); + return NULL; + } + + *len = 1 + res; + wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Derived Session-Id", id, *len); + return id; +} + + +int eap_server_teap_register(void) +{ + struct eap_method *eap; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TEAP, "TEAP"); + if (!eap) + return -1; + + eap->init = eap_teap_init; + eap->reset = eap_teap_reset; + eap->buildReq = eap_teap_buildReq; + eap->check = eap_teap_check; + eap->process = eap_teap_process; + eap->isDone = eap_teap_isDone; + eap->getKey = eap_teap_getKey; + eap->get_emsk = eap_teap_get_emsk; + eap->isSuccess = eap_teap_isSuccess; + eap->getSessionId = eap_teap_get_session_id; + + return eap_server_method_register(eap); +} diff --git a/contrib/wpa/src/eap_server/eap_server_tls.c b/contrib/wpa/src/eap_server/eap_server_tls.c index 357e72a825f6..0712d4ccd5aa 100644 --- a/contrib/wpa/src/eap_server/eap_server_tls.c +++ b/contrib/wpa/src/eap_server/eap_server_tls.c @@ -261,8 +261,43 @@ static void eap_tls_process_msg(struct eap_sm *sm, void *priv, "handshake message"); return; } - if (eap_server_tls_phase1(sm, &data->ssl) < 0) + if (eap_server_tls_phase1(sm, &data->ssl) < 0) { eap_tls_state(data, FAILURE); + return; + } + + if (data->ssl.tls_v13 && + tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + struct wpabuf *plain, *encr; + + wpa_printf(MSG_DEBUG, + "EAP-TLS: Send empty application data to indicate end of exchange"); + /* FIX: This should be an empty application data based on + * draft-ietf-emu-eap-tls13-05, but OpenSSL does not allow zero + * length payload (SSL_write() documentation explicitly + * describes this as not allowed), so work around that for now + * by sending out a payload of one octet. Hopefully the draft + * specification will change to allow this so that no crypto + * library changes are needed. */ + plain = wpabuf_alloc(1); + if (!plain) + return; + wpabuf_put_u8(plain, 0); + encr = eap_server_tls_encrypt(sm, &data->ssl, plain); + wpabuf_free(plain); + if (!encr) + return; + if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) { + wpa_printf(MSG_INFO, + "EAP-TLS: Failed to resize output buffer"); + wpabuf_free(encr); + return; + } + wpabuf_put_buf(data->ssl.tls_out, encr); + wpa_hexdump_buf(MSG_DEBUG, + "EAP-TLS: Data appended to the message", encr); + wpabuf_free(encr); + } } @@ -322,16 +357,22 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) struct eap_tls_data *data = priv; u8 *eapKeyData; const char *label; + const u8 eap_tls13_context[] = { EAP_TYPE_TLS }; + const u8 *context = NULL; + size_t context_len = 0; if (data->state != SUCCESS) return NULL; - if (data->ssl.tls_v13) + if (data->ssl.tls_v13) { label = "EXPORTER_EAP_TLS_Key_Material"; - else + context = eap_tls13_context; + context_len = 1; + } else { label = "client EAP encryption"; + } eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label, - NULL, 0, + context, context_len, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; @@ -351,16 +392,22 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) struct eap_tls_data *data = priv; u8 *eapKeyData, *emsk; const char *label; + const u8 eap_tls13_context[] = { EAP_TYPE_TLS }; + const u8 *context = NULL; + size_t context_len = 0; if (data->state != SUCCESS) return NULL; - if (data->ssl.tls_v13) + if (data->ssl.tls_v13) { label = "EXPORTER_EAP_TLS_Key_Material"; - else + context = eap_tls13_context; + context_len = 1; + } else { label = "client EAP encryption"; + } eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label, - NULL, 0, + context, context_len, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { emsk = os_malloc(EAP_EMSK_LEN); diff --git a/contrib/wpa/src/eap_server/eap_server_tls_common.c b/contrib/wpa/src/eap_server/eap_server_tls_common.c index 0eca0ff77409..907101c7e2e3 100644 --- a/contrib/wpa/src/eap_server/eap_server_tls_common.c +++ b/contrib/wpa/src/eap_server/eap_server_tls_common.c @@ -145,20 +145,21 @@ u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, { struct tls_random keys; u8 *out; + const u8 context[] = { EAP_TYPE_TLS }; if (eap_type == EAP_TYPE_TLS && data->tls_v13) { u8 *id, *method_id; /* Session-Id = || Method-Id * Method-Id = TLS-Exporter("EXPORTER_EAP_TLS_Method-Id", - * "", 64) + * Type-Code, 64) */ *len = 1 + 64; id = os_malloc(*len); if (!id) return NULL; method_id = eap_server_tls_derive_key( - sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64); + sm, data, "EXPORTER_EAP_TLS_Method-Id", context, 1, 64); if (!method_id) { os_free(id); return NULL; @@ -373,6 +374,8 @@ static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, unsigned int tls_msg_len = 0; const u8 *end = *pos + *left; + wpa_hexdump(MSG_MSGDUMP, "SSL: Received data", *pos, *left); + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { if (*left < 4) { wpa_printf(MSG_INFO, "SSL: Short frame with TLS " diff --git a/contrib/wpa/src/eap_server/eap_tls_common.h b/contrib/wpa/src/eap_server/eap_tls_common.h index 0b04983adb0e..74b1c728d681 100644 --- a/contrib/wpa/src/eap_server/eap_tls_common.h +++ b/contrib/wpa/src/eap_server/eap_tls_common.h @@ -62,6 +62,7 @@ struct eap_ssl_data { #define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 #define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 #define EAP_TLS_FLAGS_START 0x20 +#define EAP_TEAP_FLAGS_OUTER_TLV_LEN 0x10 #define EAP_TLS_VERSION_MASK 0x07 /* could be up to 128 bytes, but only the first 64 bytes are used */ diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm.c b/contrib/wpa/src/eapol_auth/eapol_auth_sm.c index 36074d3e0474..7206d32d7391 100644 --- a/contrib/wpa/src/eapol_auth/eapol_auth_sm.c +++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm.c @@ -300,7 +300,7 @@ SM_STATE(AUTH_PAE, AUTHENTICATED) if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) sm->authAuthSuccessesWhileAuthenticating++; - + SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae); sm->authPortStatus = Authorized; @@ -835,7 +835,10 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eap_conf.eap_fast_prov = eapol->conf.eap_fast_prov; eap_conf.pac_key_lifetime = eapol->conf.pac_key_lifetime; eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time; + eap_conf.eap_teap_auth = eapol->conf.eap_teap_auth; + eap_conf.eap_teap_pac_no_inner = eapol->conf.eap_teap_pac_no_inner; eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind; + eap_conf.eap_sim_id = eapol->conf.eap_sim_id; eap_conf.tnc = eapol->conf.tnc; eap_conf.wps = eapol->conf.wps; eap_conf.assoc_wps_ie = assoc_wps_ie; @@ -1231,7 +1234,10 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->eap_fast_prov = src->eap_fast_prov; dst->pac_key_lifetime = src->pac_key_lifetime; dst->pac_key_refresh_time = src->pac_key_refresh_time; + dst->eap_teap_auth = src->eap_teap_auth; + dst->eap_teap_pac_no_inner = src->eap_teap_pac_no_inner; dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind; + dst->eap_sim_id = src->eap_sim_id; dst->tnc = src->tnc; dst->wps = src->wps; dst->fragment_size = src->fragment_size; diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm.h b/contrib/wpa/src/eapol_auth/eapol_auth_sm.h index 44f3f31cc601..bcdd50971569 100644 --- a/contrib/wpa/src/eapol_auth/eapol_auth_sm.h +++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm.h @@ -36,7 +36,10 @@ struct eapol_auth_config { int eap_fast_prov; int pac_key_lifetime; int pac_key_refresh_time; + int eap_teap_auth; + int eap_teap_pac_no_inner; int eap_sim_aka_result_ind; + int eap_sim_id; int tnc; struct wps_context *wps; int fragment_size; diff --git a/contrib/wpa/src/eapol_supp/eapol_supp_sm.c b/contrib/wpa/src/eapol_supp/eapol_supp_sm.c index a0f27fd2bdb2..f1ca0a859bde 100644 --- a/contrib/wpa/src/eapol_supp/eapol_supp_sm.c +++ b/contrib/wpa/src/eapol_supp/eapol_supp_sm.c @@ -1998,15 +1998,12 @@ static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field, #define eapol_sm_eap_param_needed NULL #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ -static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject, - const char *altsubject[], - int num_altsubject, const char *cert_hash, - const struct wpabuf *cert) +static void eapol_sm_notify_cert(void *ctx, struct tls_cert_data *cert, + const char *cert_hash) { struct eapol_sm *sm = ctx; if (sm->ctx->cert_cb) - sm->ctx->cert_cb(sm->ctx->ctx, depth, subject, altsubject, - num_altsubject, cert_hash, cert); + sm->ctx->cert_cb(sm->ctx->ctx, cert, cert_hash); } diff --git a/contrib/wpa/src/eapol_supp/eapol_supp_sm.h b/contrib/wpa/src/eapol_supp/eapol_supp_sm.h index 74f40bb1cd63..c9d7522d5f4e 100644 --- a/contrib/wpa/src/eapol_supp/eapol_supp_sm.h +++ b/contrib/wpa/src/eapol_supp/eapol_supp_sm.h @@ -11,6 +11,8 @@ #include "common/defs.h" +struct tls_cert_data; + typedef enum { Unauthorized, Authorized } PortStatus; typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl; @@ -246,16 +248,11 @@ struct eapol_ctx { /** * cert_cb - Notification of a peer certificate * @ctx: Callback context (ctx) - * @depth: Depth in certificate chain (0 = server) - * @subject: Subject of the peer certificate - * @altsubject: Select fields from AltSubject of the peer certificate - * @num_altsubject: Number of altsubject values + * @cert: Certificate information * @cert_hash: SHA-256 hash of the certificate - * @cert: Peer certificate */ - void (*cert_cb)(void *ctx, int depth, const char *subject, - const char *altsubject[], int num_altsubject, - const char *cert_hash, const struct wpabuf *cert); + void (*cert_cb)(void *ctx, struct tls_cert_data *cert, + const char *cert_hash); /** * cert_in_cb - Include server certificates in callback diff --git a/contrib/wpa/src/p2p/p2p.c b/contrib/wpa/src/p2p/p2p.c index 157bf891c4ab..a08ba02686c6 100644 --- a/contrib/wpa/src/p2p/p2p.c +++ b/contrib/wpa/src/p2p/p2p.c @@ -1066,22 +1066,6 @@ static int p2p_run_after_scan(struct p2p_data *p2p) struct p2p_device *dev; enum p2p_after_scan op; - if (p2p->after_scan_tx) { - p2p->after_scan_tx_in_progress = 1; - p2p_dbg(p2p, "Send pending Action frame at p2p_scan completion"); - p2p->cfg->send_action(p2p->cfg->cb_ctx, - p2p->after_scan_tx->freq, - p2p->after_scan_tx->dst, - p2p->after_scan_tx->src, - p2p->after_scan_tx->bssid, - (u8 *) (p2p->after_scan_tx + 1), - p2p->after_scan_tx->len, - p2p->after_scan_tx->wait_time, NULL); - os_free(p2p->after_scan_tx); - p2p->after_scan_tx = NULL; - return 1; - } - op = p2p->start_after_scan; p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; switch (op) { @@ -1646,17 +1630,6 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, if (p2p->state != P2P_IDLE) p2p_stop_find(p2p); - if (p2p->after_scan_tx) { - /* - * We need to drop the pending frame to avoid issues with the - * new GO Negotiation, e.g., when the pending frame was from a - * previous attempt at starting a GO Negotiation. - */ - p2p_dbg(p2p, "Dropped previous pending Action frame TX that was waiting for p2p_scan completion"); - os_free(p2p->after_scan_tx); - p2p->after_scan_tx = NULL; - } - dev->wps_method = wps_method; dev->oob_pw_id = oob_pw_id; dev->status = P2P_SC_SUCCESS; @@ -1667,7 +1640,6 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN); return 0; } - p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; return p2p_connect_send(p2p, dev); } @@ -3050,8 +3022,6 @@ void p2p_flush(struct p2p_data *p2p) p2p_device_free(p2p, dev); } p2p_free_sd_queries(p2p); - os_free(p2p->after_scan_tx); - p2p->after_scan_tx = NULL; p2p->ssid_set = 0; p2ps_prov_free(p2p); p2p_reset_pending_pd(p2p); @@ -3080,13 +3050,6 @@ int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr) dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; - /* Check if after_scan_tx is for this peer. If so free it */ - if (p2p->after_scan_tx && - os_memcmp(addr, p2p->after_scan_tx->dst, ETH_ALEN) == 0) { - os_free(p2p->after_scan_tx); - p2p->after_scan_tx = NULL; - } - return 0; } @@ -3476,23 +3439,6 @@ static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) } -static int p2p_check_after_scan_tx_continuation(struct p2p_data *p2p) -{ - if (p2p->after_scan_tx_in_progress) { - p2p->after_scan_tx_in_progress = 0; - if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING && - p2p_run_after_scan(p2p)) - return 1; - if (p2p->state == P2P_SEARCH) { - p2p_dbg(p2p, "Continue find after after_scan_tx completion"); - p2p_continue_find(p2p); - } - } - - return 0; -} - - static void p2p_prov_disc_resp_cb(struct p2p_data *p2p, int success) { p2p_dbg(p2p, "Provision Discovery Response TX callback: success=%d", @@ -3506,18 +3452,14 @@ static void p2p_prov_disc_resp_cb(struct p2p_data *p2p, int success) p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (!success) - goto continue_search; + return; if (!p2p->cfg->prov_disc_resp_cb || p2p->cfg->prov_disc_resp_cb(p2p->cfg->cb_ctx) < 1) - goto continue_search; + return; p2p_dbg(p2p, "Post-Provision Discovery operations started - do not try to continue other P2P operations"); - return; - -continue_search: - p2p_check_after_scan_tx_continuation(p2p); } @@ -3807,7 +3749,6 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, p2p->send_action_in_progress = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } - p2p_check_after_scan_tx_continuation(p2p); break; case P2P_PENDING_GO_NEG_REQUEST: p2p_go_neg_req_cb(p2p, success); @@ -3835,8 +3776,6 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, break; case P2P_PENDING_INVITATION_RESPONSE: p2p_invitation_resp_cb(p2p, success); - if (p2p->inv_status != P2P_SC_SUCCESS) - p2p_check_after_scan_tx_continuation(p2p); break; case P2P_PENDING_DEV_DISC_REQUEST: p2p_dev_disc_req_cb(p2p, success); @@ -3848,8 +3787,6 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, p2p_go_disc_req_cb(p2p, success); break; } - - p2p->after_scan_tx_in_progress = 0; } @@ -4975,26 +4912,6 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, { int res, scheduled; - if (p2p->p2p_scan_running) { - p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes"); - if (p2p->after_scan_tx) { - p2p_dbg(p2p, "Dropped previous pending Action frame TX"); - os_free(p2p->after_scan_tx); - } - p2p->after_scan_tx = os_malloc(sizeof(*p2p->after_scan_tx) + - len); - if (p2p->after_scan_tx == NULL) - return -1; - p2p->after_scan_tx->freq = freq; - os_memcpy(p2p->after_scan_tx->dst, dst, ETH_ALEN); - os_memcpy(p2p->after_scan_tx->src, src, ETH_ALEN); - os_memcpy(p2p->after_scan_tx->bssid, bssid, ETH_ALEN); - p2p->after_scan_tx->len = len; - p2p->after_scan_tx->wait_time = wait_time; - os_memcpy(p2p->after_scan_tx + 1, buf, len); - return 0; - } - res = p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid, buf, len, wait_time, &scheduled); if (res == 0 && scheduled && p2p->in_listen && freq > 0 && diff --git a/contrib/wpa/src/p2p/p2p_go_neg.c b/contrib/wpa/src/p2p/p2p_go_neg.c index 65ab4b8d3fd6..c94bf41a7081 100644 --- a/contrib/wpa/src/p2p/p2p_go_neg.c +++ b/contrib/wpa/src/p2p/p2p_go_neg.c @@ -676,7 +676,9 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, "Ignore local driver frequency preference %u MHz since it is not acceptable for P2P use (go=%d)", freq_list[i], go); if (size - i - 1 > 0) - os_memmove(&freq_list[i], &freq_list[i + 1], size - i - 1); + os_memmove(&freq_list[i], &freq_list[i + 1], + (size - i - 1) * + sizeof(unsigned int)); size--; continue; } diff --git a/contrib/wpa/src/p2p/p2p_i.h b/contrib/wpa/src/p2p/p2p_i.h index d2c55c9976c2..4195c5f07dd1 100644 --- a/contrib/wpa/src/p2p/p2p_i.h +++ b/contrib/wpa/src/p2p/p2p_i.h @@ -158,16 +158,6 @@ struct p2p_sd_query { struct wpabuf *tlvs; }; -struct p2p_pending_action_tx { - unsigned int freq; - u8 dst[ETH_ALEN]; - u8 src[ETH_ALEN]; - u8 bssid[ETH_ALEN]; - size_t len; - unsigned int wait_time; - /* Followed by len octets of the frame */ -}; - /** * struct p2p_data - P2P module data (internal to P2P module) */ @@ -449,8 +439,6 @@ struct p2p_data { P2P_AFTER_SCAN_CONNECT } start_after_scan; u8 after_scan_peer[ETH_ALEN]; - struct p2p_pending_action_tx *after_scan_tx; - unsigned int after_scan_tx_in_progress:1; unsigned int send_action_in_progress:1; /* Requested device types for find/search */ diff --git a/contrib/wpa/src/pae/ieee802_1x_kay.c b/contrib/wpa/src/pae/ieee802_1x_kay.c index b4455c8f4e08..a330d0cf4559 100644 --- a/contrib/wpa/src/pae/ieee802_1x_kay.c +++ b/contrib/wpa/src/pae/ieee802_1x_kay.c @@ -1085,7 +1085,17 @@ ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant, wpa_printf(MSG_DEBUG, "KaY: My MI - received MN %u, most recently transmitted MN %u", mn, participant->mn); - if (mn == participant->mn) + /* IEEE Std 802.1X-2010 is not exactly clear + * which values of MN should be accepted here. + * It uses "acceptably recent MN" language + * without defining what would be acceptable + * recent. For now, allow the last two used MN + * values (i.e., peer having copied my MI,MN + * from either of the last two MKPDUs that I + * have sent). */ + if (mn == participant->mn || + (participant->mn > 1 && + mn == participant->mn - 1)) return TRUE; } } diff --git a/contrib/wpa/src/radius/radius_server.c b/contrib/wpa/src/radius/radius_server.c index b621ada554ee..70efd11b49d9 100644 --- a/contrib/wpa/src/radius/radius_server.c +++ b/contrib/wpa/src/radius/radius_server.c @@ -238,6 +238,9 @@ struct radius_server_data { */ int pac_key_refresh_time; + int eap_teap_auth; + int eap_teap_pac_no_inner; + /** * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication * @@ -246,6 +249,8 @@ struct radius_server_data { */ int eap_sim_aka_result_ind; + int eap_sim_id; + /** * tnc - Trusted Network Connect (TNC) * @@ -792,7 +797,10 @@ radius_server_get_new_session(struct radius_server_data *data, eap_conf.eap_fast_prov = data->eap_fast_prov; eap_conf.pac_key_lifetime = data->pac_key_lifetime; eap_conf.pac_key_refresh_time = data->pac_key_refresh_time; + eap_conf.eap_teap_auth = data->eap_teap_auth; + eap_conf.eap_teap_pac_no_inner = data->eap_teap_pac_no_inner; eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind; + eap_conf.eap_sim_id = data->eap_sim_id; eap_conf.tnc = data->tnc; eap_conf.wps = data->wps; eap_conf.pwd_group = data->pwd_group; @@ -1136,6 +1144,13 @@ radius_server_encapsulate_eap(struct radius_server_data *data, len)) { RADIUS_DEBUG("Failed to add MPPE key attributes"); } + + if (sess->eap_if->eapSessionId && + !radius_msg_add_attr(msg, RADIUS_ATTR_EAP_KEY_NAME, + sess->eap_if->eapSessionId, + sess->eap_if->eapSessionIdLen)) { + RADIUS_DEBUG("Failed to add EAP-Key-Name attribute"); + } } #ifdef CONFIG_HS20 @@ -2348,6 +2363,8 @@ radius_server_init(struct radius_server_conf *conf) if (data == NULL) return NULL; + data->auth_sock = -1; + data->acct_sock = -1; dl_list_init(&data->erp_keys); os_get_reltime(&data->start_time); data->conf_ctx = conf->conf_ctx; @@ -2375,8 +2392,11 @@ radius_server_init(struct radius_server_conf *conf) data->eap_fast_prov = conf->eap_fast_prov; data->pac_key_lifetime = conf->pac_key_lifetime; data->pac_key_refresh_time = conf->pac_key_refresh_time; + data->eap_teap_auth = conf->eap_teap_auth; + data->eap_teap_pac_no_inner = conf->eap_teap_pac_no_inner; data->get_eap_user = conf->get_eap_user; data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; + data->eap_sim_id = conf->eap_sim_id; data->tnc = conf->tnc; data->wps = conf->wps; data->pwd_group = conf->pwd_group; diff --git a/contrib/wpa/src/radius/radius_server.h b/contrib/wpa/src/radius/radius_server.h index 53728f9d7bf2..54896946eca9 100644 --- a/contrib/wpa/src/radius/radius_server.h +++ b/contrib/wpa/src/radius/radius_server.h @@ -128,6 +128,9 @@ struct radius_server_conf { */ int pac_key_refresh_time; + int eap_teap_auth; + int eap_teap_pac_no_inner; + /** * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication * @@ -136,6 +139,8 @@ struct radius_server_conf { */ int eap_sim_aka_result_ind; + int eap_sim_id; + /** * tnc - Trusted Network Connect (TNC) * diff --git a/contrib/wpa/src/rsn_supp/wpa.c b/contrib/wpa/src/rsn_supp/wpa.c index 9163f61fa2f2..c929e8194a03 100644 --- a/contrib/wpa/src/rsn_supp/wpa.c +++ b/contrib/wpa/src/rsn_supp/wpa.c @@ -305,6 +305,9 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, #endif /* CONFIG_IEEE80211R */ } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) { int res, pmk_len; +#ifdef CONFIG_IEEE80211R + u8 buf[2 * PMK_LEN]; +#endif /* CONFIG_IEEE80211R */ if (wpa_key_mgmt_sha384(sm->key_mgmt)) pmk_len = PMK_LEN_SUITE_B_192; @@ -320,24 +323,42 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); pmk_len = 16; } - } else { -#ifdef CONFIG_IEEE80211R - u8 buf[2 * PMK_LEN]; - if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0) - { - if (wpa_key_mgmt_sha384(sm->key_mgmt)) { - os_memcpy(sm->xxkey, buf, - SHA384_MAC_LEN); - sm->xxkey_len = SHA384_MAC_LEN; - } else { - os_memcpy(sm->xxkey, buf + PMK_LEN, - PMK_LEN); - sm->xxkey_len = PMK_LEN; - } - os_memset(buf, 0, sizeof(buf)); - } -#endif /* CONFIG_IEEE80211R */ } +#ifdef CONFIG_IEEE80211R + if (res == 0 && + eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0) { + if (wpa_key_mgmt_sha384(sm->key_mgmt)) { + os_memcpy(sm->xxkey, buf, SHA384_MAC_LEN); + sm->xxkey_len = SHA384_MAC_LEN; + } else { + os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN); + sm->xxkey_len = PMK_LEN; + } + forced_memzero(buf, sizeof(buf)); + if (sm->proto == WPA_PROTO_RSN && + wpa_key_mgmt_ft(sm->key_mgmt)) { + struct rsn_pmksa_cache_entry *sa = NULL; + const u8 *fils_cache_id = NULL; + +#ifdef CONFIG_FILS + if (sm->fils_cache_id_set) + fils_cache_id = sm->fils_cache_id; +#endif /* CONFIG_FILS */ + wpa_hexdump_key(MSG_DEBUG, + "FT: Cache XXKey/MPMK", + sm->xxkey, sm->xxkey_len); + sa = pmksa_cache_add(sm->pmksa, + sm->xxkey, sm->xxkey_len, + NULL, NULL, 0, + src_addr, sm->own_addr, + sm->network_ctx, + sm->key_mgmt, + fils_cache_id); + if (!sm->cur_pmksa) + sm->cur_pmksa = sa; + } + } +#endif /* CONFIG_IEEE80211R */ if (res == 0) { struct rsn_pmksa_cache_entry *sa = NULL; const u8 *fils_cache_id = NULL; @@ -628,7 +649,7 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, os_memcpy(buf, &ptk->tk[16], 8); os_memcpy(&ptk->tk[16], &ptk->tk[24], 8); os_memcpy(&ptk->tk[24], buf, 8); - os_memset(buf, 0, sizeof(buf)); + forced_memzero(buf, sizeof(buf)); } sm->tptk_set = 1; @@ -902,7 +923,7 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to set GTK to the driver " "(Group only)"); - os_memset(gtk_buf, 0, sizeof(gtk_buf)); + forced_memzero(gtk_buf, sizeof(gtk_buf)); return -1; } } else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr, @@ -912,10 +933,10 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, "WPA: Failed to set GTK to " "the driver (alg=%d keylen=%d keyidx=%d)", gd->alg, gd->gtk_len, gd->keyidx); - os_memset(gtk_buf, 0, sizeof(gtk_buf)); + forced_memzero(gtk_buf, sizeof(gtk_buf)); return -1; } - os_memset(gtk_buf, 0, sizeof(gtk_buf)); + forced_memzero(gtk_buf, sizeof(gtk_buf)); if (wnm_sleep) { sm->gtk_wnm_sleep.gtk_len = gd->gtk_len; @@ -1021,10 +1042,10 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0))) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Failed to install GTK"); - os_memset(&gd, 0, sizeof(gd)); + forced_memzero(&gd, sizeof(gd)); return -1; } - os_memset(&gd, 0, sizeof(gd)); + forced_memzero(&gd, sizeof(gd)); return 0; } @@ -1693,12 +1714,12 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); os_memcpy(gd->gtk, key_data, key_data_len); if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) { - os_memset(ek, 0, sizeof(ek)); + forced_memzero(ek, sizeof(ek)); wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: RC4 failed"); return -1; } - os_memset(ek, 0, sizeof(ek)); + forced_memzero(ek, sizeof(ek)); #endif /* CONFIG_NO_RC4 */ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (maxkeylen % 8) { @@ -1847,7 +1868,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0) || wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0) goto failed; - os_memset(&gd, 0, sizeof(gd)); + forced_memzero(&gd, sizeof(gd)); if (rekey) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying " @@ -1866,7 +1887,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, return; failed: - os_memset(&gd, 0, sizeof(gd)); + forced_memzero(&gd, sizeof(gd)); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } @@ -1980,12 +2001,12 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) { - os_memset(ek, 0, sizeof(ek)); + forced_memzero(ek, sizeof(ek)); wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, "WPA: RC4 failed"); return -1; } - os_memset(ek, 0, sizeof(ek)); + forced_memzero(ek, sizeof(ek)); #endif /* CONFIG_NO_RC4 */ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC || @@ -3425,12 +3446,12 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)", gd.gtk, gd.gtk_len); if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 1)) { - os_memset(&gd, 0, sizeof(gd)); + forced_memzero(&gd, sizeof(gd)); wpa_printf(MSG_DEBUG, "Failed to install the GTK in " "WNM mode"); return -1; } - os_memset(&gd, 0, sizeof(gd)); + forced_memzero(&gd, sizeof(gd)); #ifdef CONFIG_IEEE80211W } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) { const struct wpa_igtk_kde *igtk; @@ -3860,7 +3881,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, dh_ss ? wpabuf_head(dh_ss) : NULL, dh_ss ? wpabuf_len(dh_ss) : 0, sm->pmk, &sm->pmk_len); - os_memset(rmsk, 0, sizeof(rmsk)); + forced_memzero(rmsk, sizeof(rmsk)); /* Don't use DHss in PTK derivation if PMKSA caching is not * used. */ @@ -3935,7 +3956,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, sm->fils_key_auth_ap, &sm->fils_key_auth_len); wpabuf_free(pub); - os_memset(ick, 0, sizeof(ick)); + forced_memzero(ick, sizeof(ick)); return res; fail: wpabuf_free(pub); @@ -4299,6 +4320,26 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len) sm->fils_session, FILS_SESSION_LEN); } + if (!elems.rsn_ie) { + wpa_printf(MSG_DEBUG, + "FILS: No RSNE in (Re)Association Response"); + /* As an interop workaround, allow this for now since IEEE Std + * 802.11ai-2016 did not include all the needed changes to make + * a FILS AP include RSNE in the frame. This workaround might + * eventually be removed and replaced with rejection (goto fail) + * to follow a strict interpretation of the standard. */ + } else if (wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt), + sm->ap_rsn_ie, sm->ap_rsn_ie_len, + elems.rsn_ie - 2, elems.rsn_ie_len + 2)) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "FILS: RSNE mismatch between Beacon/Probe Response and (Re)Association Response"); + wpa_hexdump(MSG_DEBUG, "FILS: RSNE in Beacon/Probe Response", + sm->ap_rsn_ie, sm->ap_rsn_ie_len); + wpa_hexdump(MSG_DEBUG, "FILS: RSNE in (Re)Association Response", + elems.rsn_ie, elems.rsn_ie_len); + goto fail; + } + /* TODO: FILS Public Key */ if (!elems.fils_key_confirm) { @@ -4439,9 +4480,11 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len) wpa_printf(MSG_DEBUG, "FILS: Auth+Assoc completed successfully"); sm->fils_completed = 1; + forced_memzero(&gd, sizeof(gd)); return 0; fail: + forced_memzero(&gd, sizeof(gd)); return -1; } @@ -4653,7 +4696,7 @@ int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid, else if (group == 21) res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info, os_strlen(info), sm->pmk, hash_len); - os_memset(prk, 0, SHA512_MAC_LEN); + forced_memzero(prk, SHA512_MAC_LEN); if (res < 0) { sm->pmk_len = 0; return -1; diff --git a/contrib/wpa/src/rsn_supp/wpa.h b/contrib/wpa/src/rsn_supp/wpa.h index 8903f8e14c69..ae9cd6484baa 100644 --- a/contrib/wpa/src/rsn_supp/wpa.h +++ b/contrib/wpa/src/rsn_supp/wpa.h @@ -26,7 +26,7 @@ struct wpa_sm_ctx { void (*set_state)(void *ctx, enum wpa_states state); enum wpa_states (*get_state)(void *ctx); - void (*deauthenticate)(void * ctx, int reason_code); + void (*deauthenticate)(void * ctx, u16 reason_code); int (*set_key)(void *ctx, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, diff --git a/contrib/wpa/src/rsn_supp/wpa_ft.c b/contrib/wpa/src/rsn_supp/wpa_ft.c index 7dcb1043bfcc..46ffdca67a77 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ft.c +++ b/contrib/wpa/src/rsn_supp/wpa_ft.c @@ -18,6 +18,7 @@ #include "drivers/driver.h" #include "wpa.h" #include "wpa_i.h" +#include "pmksa_cache.h" #ifdef CONFIG_IEEE80211R @@ -27,15 +28,23 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *anonce = key->key_nonce; int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); + const u8 *mpmk; + size_t mpmk_len; - if (sm->xxkey_len == 0) { + if (sm->xxkey_len > 0) { + mpmk = sm->xxkey; + mpmk_len = sm->xxkey_len; + } else if (sm->cur_pmksa) { + mpmk = sm->cur_pmksa->pmk; + mpmk_len = sm->cur_pmksa->pmk_len; + } else { wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " "derivation"); return -1; } sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN; - if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid, + if (wpa_derive_pmk_r0(mpmk, mpmk_len, sm->ssid, sm->ssid_len, sm->mobility_domain, sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0) @@ -819,10 +828,10 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, igtk_elem + 2, 6, igtk, igtk_len) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the " "driver."); - os_memset(igtk, 0, sizeof(igtk)); + forced_memzero(igtk, sizeof(igtk)); return -1; } - os_memset(igtk, 0, sizeof(igtk)); + forced_memzero(igtk, sizeof(igtk)); return 0; } diff --git a/contrib/wpa/src/rsn_supp/wpa_i.h b/contrib/wpa/src/rsn_supp/wpa_i.h index 0c5955c66f88..d86734b0df55 100644 --- a/contrib/wpa/src/rsn_supp/wpa_i.h +++ b/contrib/wpa/src/rsn_supp/wpa_i.h @@ -188,7 +188,7 @@ static inline enum wpa_states wpa_sm_get_state(struct wpa_sm *sm) return sm->ctx->get_state(sm->ctx->ctx); } -static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code) +static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, u16 reason_code) { WPA_ASSERT(sm->ctx->deauthenticate); sm->ctx->deauthenticate(sm->ctx->ctx, reason_code); diff --git a/contrib/wpa/src/tls/asn1.c b/contrib/wpa/src/tls/asn1.c index 822f87c18212..a08c2e1e3ed1 100644 --- a/contrib/wpa/src/tls/asn1.c +++ b/contrib/wpa/src/tls/asn1.c @@ -22,6 +22,36 @@ struct asn1_oid asn1_sha256_oid = { }; +static int asn1_valid_der_boolean(struct asn1_hdr *hdr) +{ + /* Enforce DER requirements for a single way of encoding a BOOLEAN */ + if (hdr->length != 1) { + wpa_printf(MSG_DEBUG, "ASN.1: Unexpected BOOLEAN length (%u)", + hdr->length); + return 0; + } + + if (hdr->payload[0] != 0 && hdr->payload[0] != 0xff) { + wpa_printf(MSG_DEBUG, + "ASN.1: Invalid BOOLEAN value 0x%x (DER requires 0 or 0xff)", + hdr->payload[0]); + return 0; + } + + return 1; +} + + +static int asn1_valid_der(struct asn1_hdr *hdr) +{ + if (hdr->class != ASN1_CLASS_UNIVERSAL) + return 1; + if (hdr->tag == ASN1_TAG_BOOLEAN && !asn1_valid_der_boolean(hdr)) + return 0; + return 1; +} + + int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) { const u8 *pos, *end; @@ -91,7 +121,8 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) } hdr->payload = pos; - return 0; + + return asn1_valid_der(hdr) ? 0 : -1; } diff --git a/contrib/wpa/src/tls/libtommath.c b/contrib/wpa/src/tls/libtommath.c index 4f7a14823d72..715674424324 100644 --- a/contrib/wpa/src/tls/libtommath.c +++ b/contrib/wpa/src/tls/libtommath.c @@ -2441,6 +2441,7 @@ static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) /* clear the carry */ _W = 0; + os_memset(W, 0, sizeof(W)); for (ix = 0; ix < pa; ix++) { int tx, ty; int iy; diff --git a/contrib/wpa/src/tls/x509v3.c b/contrib/wpa/src/tls/x509v3.c index fa4d44229622..1bd5aa009fb5 100644 --- a/contrib/wpa/src/tls/x509v3.c +++ b/contrib/wpa/src/tls/x509v3.c @@ -538,9 +538,43 @@ done: } +static int parse_uint2(const char *pos, size_t len) +{ + char buf[3]; + int ret; + + if (len < 2) + return -1; + buf[0] = pos[0]; + buf[1] = pos[1]; + buf[2] = 0x00; + if (sscanf(buf, "%2d", &ret) != 1) + return -1; + return ret; +} + + +static int parse_uint4(const char *pos, size_t len) +{ + char buf[5]; + int ret; + + if (len < 4) + return -1; + buf[0] = pos[0]; + buf[1] = pos[1]; + buf[2] = pos[2]; + buf[3] = pos[3]; + buf[4] = 0x00; + if (sscanf(buf, "%4d", &ret) != 1) + return -1; + return ret; +} + + int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val) { - const char *pos; + const char *pos, *end; int year, month, day, hour, min, sec; /* @@ -554,6 +588,7 @@ int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val) */ pos = (const char *) buf; + end = pos + len; switch (asn1_tag) { case ASN1_TAG_UTCTIME: @@ -562,7 +597,8 @@ int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val) "UTCTime format", buf, len); return -1; } - if (sscanf(pos, "%02d", &year) != 1) { + year = parse_uint2(pos, end - pos); + if (year < 0) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " "UTCTime year", buf, len); return -1; @@ -579,7 +615,8 @@ int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val) "GeneralizedTime format", buf, len); return -1; } - if (sscanf(pos, "%04d", &year) != 1) { + year = parse_uint4(pos, end - pos); + if (year < 0) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " "GeneralizedTime year", buf, len); return -1; @@ -592,35 +629,40 @@ int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val) return -1; } - if (sscanf(pos, "%02d", &month) != 1) { + month = parse_uint2(pos, end - pos); + if (month < 0) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " "(month)", buf, len); return -1; } pos += 2; - if (sscanf(pos, "%02d", &day) != 1) { + day = parse_uint2(pos, end - pos); + if (day < 0) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " "(day)", buf, len); return -1; } pos += 2; - if (sscanf(pos, "%02d", &hour) != 1) { + hour = parse_uint2(pos, end - pos); + if (hour < 0) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " "(hour)", buf, len); return -1; } pos += 2; - if (sscanf(pos, "%02d", &min) != 1) { + min = parse_uint2(pos, end - pos); + if (min < 0) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " "(min)", buf, len); return -1; } pos += 2; - if (sscanf(pos, "%02d", &sec) != 1) { + sec = parse_uint2(pos, end - pos); + if (sec < 0) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " "(sec)", buf, len); return -1; @@ -773,6 +815,7 @@ static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, struct asn1_hdr hdr; unsigned long value; size_t left; + const u8 *end_seq; /* * BasicConstraints ::= SEQUENCE { @@ -794,6 +837,7 @@ static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, if (hdr.length == 0) return 0; + end_seq = hdr.payload + hdr.length; if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL) { wpa_printf(MSG_DEBUG, "X509: Failed to parse " @@ -802,22 +846,16 @@ static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, } if (hdr.tag == ASN1_TAG_BOOLEAN) { - if (hdr.length != 1) { - wpa_printf(MSG_DEBUG, "X509: Unexpected " - "Boolean length (%u) in BasicConstraints", - hdr.length); - return -1; - } cert->ca = hdr.payload[0]; - if (hdr.length == pos + len - hdr.payload) { + pos = hdr.payload + hdr.length; + if (pos >= end_seq) { + /* No optional pathLenConstraint */ wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d", cert->ca); return 0; } - - if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length, - &hdr) < 0 || + if (asn1_get_next(pos, end_seq - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL) { wpa_printf(MSG_DEBUG, "X509: Failed to parse " "BasicConstraints"); @@ -1263,11 +1301,6 @@ static int x509_parse_extension(struct x509_certificate *cert, } if (hdr.tag == ASN1_TAG_BOOLEAN) { - if (hdr.length != 1) { - wpa_printf(MSG_DEBUG, "X509: Unexpected " - "Boolean length (%u)", hdr.length); - return -1; - } critical_ext = hdr.payload[0]; pos = hdr.payload; if (asn1_get_next(pos, end - pos, &hdr) < 0 || diff --git a/contrib/wpa/src/utils/common.c b/contrib/wpa/src/utils/common.c index b9c8bfdb98e9..27bf435d9691 100644 --- a/contrib/wpa/src/utils/common.c +++ b/contrib/wpa/src/utils/common.c @@ -230,6 +230,16 @@ void inc_byte_array(u8 *counter, size_t len) } +void buf_shift_right(u8 *buf, size_t len, size_t bits) +{ + size_t i; + + for (i = len - 1; i > 0; i--) + buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits); + buf[0] >>= bits; +} + + void wpa_get_ntp_timestamp(u8 *buf) { struct os_time now; @@ -960,7 +970,7 @@ void str_clear_free(char *str) { if (str) { size_t len = os_strlen(str); - os_memset(str, 0, len); + forced_memzero(str, len); os_free(str); } } @@ -969,7 +979,7 @@ void str_clear_free(char *str) void bin_clear_free(void *bin, size_t len) { if (bin) { - os_memset(bin, 0, len); + forced_memzero(bin, len); os_free(bin); } } @@ -1249,3 +1259,22 @@ char * get_param(const char *cmd, const char *param) val[len] = '\0'; return val; } + + +/* Try to prevent most compilers from optimizing out clearing of memory that + * becomes unaccessible after this function is called. This is mostly the case + * for clearing local stack variables at the end of a function. This is not + * exactly perfect, i.e., someone could come up with a compiler that figures out + * the pointer is pointing to memset and then end up optimizing the call out, so + * try go a bit further by storing the first octet (now zero) to make this even + * a bit more difficult to optimize out. Once memset_s() is available, that + * could be used here instead. */ +static void * (* const volatile memset_func)(void *, int, size_t) = memset; +static u8 forced_memzero_val; + +void forced_memzero(void *ptr, size_t len) +{ + memset_func(ptr, 0, len); + if (len) + forced_memzero_val = ((u8 *) ptr)[0]; +} diff --git a/contrib/wpa/src/utils/common.h b/contrib/wpa/src/utils/common.h index 792a30ab9bbe..17411451d2ed 100644 --- a/contrib/wpa/src/utils/common.h +++ b/contrib/wpa/src/utils/common.h @@ -477,6 +477,7 @@ int hwaddr_aton2(const char *txt, u8 *addr); int hex2byte(const char *hex); int hexstr2bin(const char *hex, u8 *buf, size_t len); void inc_byte_array(u8 *counter, size_t len); +void buf_shift_right(u8 *buf, size_t len, size_t bits); void wpa_get_ntp_timestamp(u8 *buf); int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...); int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len, @@ -569,6 +570,8 @@ int str_starts(const char *str, const char *start); u8 rssi_to_rcpi(int rssi); char * get_param(const char *cmd, const char *param); +void forced_memzero(void *ptr, size_t len); + /* * gcc 4.4 ends up generating strict-aliasing warnings about some very common * networking socket uses that do not really result in a real problem and diff --git a/contrib/wpa/src/utils/trace.c b/contrib/wpa/src/utils/trace.c index e0b5b0bb99f5..40843432050e 100644 --- a/contrib/wpa/src/utils/trace.c +++ b/contrib/wpa/src/utils/trace.c @@ -186,7 +186,7 @@ static void wpa_trace_bfd_addr(void *pc) if (abfd == NULL) return; - data.pc = (bfd_hostptr_t) (pc - start_offset); + data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset); data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); @@ -227,7 +227,7 @@ static const char * wpa_trace_bfd_addr2func(void *pc) if (abfd == NULL) return NULL; - data.pc = (bfd_hostptr_t) (pc - start_offset); + data.pc = (bfd_hostptr_t) ((u8 *) pc - start_offset); data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); @@ -299,7 +299,7 @@ size_t wpa_trace_calling_func(const char *buf[], size_t len) for (i = 0; i < btrace_num; i++) { struct bfd_data data; - data.pc = (bfd_hostptr_t) (btrace_res[i] - start_offset); + data.pc = (bfd_hostptr_t) ((u8 *) btrace_res[i] - start_offset); data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); diff --git a/contrib/wpa/src/utils/wpa_debug.c b/contrib/wpa/src/utils/wpa_debug.c index c437000a7f50..c336e5389d01 100644 --- a/contrib/wpa/src/utils/wpa_debug.c +++ b/contrib/wpa/src/utils/wpa_debug.c @@ -144,6 +144,7 @@ int wpa_debug_open_linux_tracing(void) printf("failed to read /proc/mounts\n"); return -1; } + buf[buflen] = '\0'; line = strtok_r(buf, "\n", &tmp1); while (line) { diff --git a/contrib/wpa/src/wps/wps.h b/contrib/wpa/src/wps/wps.h index 14ce863269f6..9963c4687551 100644 --- a/contrib/wpa/src/wps/wps.h +++ b/contrib/wpa/src/wps/wps.h @@ -733,7 +733,7 @@ struct wps_context { * uses this when acting as an Enrollee to notify Registrar of the * current configuration. * - * When using WPA/WPA2-Person, this key can be either the ASCII + * When using WPA/WPA2-Personal, this key can be either the ASCII * passphrase (8..63 characters) or the 32-octet PSK (64 hex * characters). When this is set to the ASCII passphrase, the PSK can * be provided in the psk buffer and used per-Enrollee to control which diff --git a/contrib/wpa/wpa_supplicant/Android.mk b/contrib/wpa/wpa_supplicant/Android.mk index 529693131308..b5d982de3679 100644 --- a/contrib/wpa/wpa_supplicant/Android.mk +++ b/contrib/wpa/wpa_supplicant/Android.mk @@ -243,6 +243,7 @@ L_CFLAGS += -DCONFIG_SAE OBJS += src/common/sae.c NEED_ECC=y NEED_DH_GROUPS=y +NEED_DRAGONFLY=y endif ifdef CONFIG_DPP @@ -643,6 +644,23 @@ CONFIG_IEEE8021X_EAPOL=y NEED_T_PRF=y endif +ifdef CONFIG_EAP_TEAP +# EAP-TEAP +ifeq ($(CONFIG_EAP_TEAP), dyn) +L_CFLAGS += -DEAP_YEAP_DYNAMIC +EAPDYN += src/eap_peer/eap_teap.so +EAPDYN += src/eap_common/eap_teap_common.c +else +L_CFLAGS += -DEAP_TEAP +OBJS += src/eap_peer/eap_teap.c src/eap_peer/eap_teap_pac.c +OBJS += src/eap_common/eap_teap_common.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +NEED_T_PRF=y +NEED_SHA384=y +endif + ifdef CONFIG_EAP_PAX # EAP-PAX ifeq ($(CONFIG_EAP_PAX), dyn) @@ -690,6 +708,7 @@ OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c CONFIG_IEEE8021X_EAPOL=y NEED_SHA256=y NEED_ECC=y +NEED_DRAGONFLY=y endif ifdef CONFIG_EAP_EKE @@ -979,6 +998,10 @@ ifdef CONFIG_SMARTCARD L_CFLAGS += -DCONFIG_SMARTCARD endif +ifdef NEED_DRAGONFLY +OBJS += src/common/dragonfly.c +endif + ifdef MS_FUNCS OBJS += src/crypto/ms_funcs.c NEED_DES=y diff --git a/contrib/wpa/wpa_supplicant/ChangeLog b/contrib/wpa/wpa_supplicant/ChangeLog index 89119e7bd18f..f82e5e0ea5df 100644 --- a/contrib/wpa/wpa_supplicant/ChangeLog +++ b/contrib/wpa/wpa_supplicant/ChangeLog @@ -1,5 +1,34 @@ ChangeLog for wpa_supplicant +2019-08-07 - v2.9 + * SAE changes + - disable use of groups using Brainpool curves + - improved protection against side channel attacks + [https://w1.fi/security/2019-6/] + * EAP-pwd changes + - disable use of groups using Brainpool curves + - allow the set of groups to be configured (eap_pwd_groups) + - improved protection against side channel attacks + [https://w1.fi/security/2019-6/] + * fixed FT-EAP initial mobility domain association using PMKSA caching + (disabled by default for backwards compatibility; can be enabled + with ft_eap_pmksa_caching=1) + * fixed a regression in OpenSSL 1.1+ engine loading + * added validation of RSNE in (Re)Association Response frames + * fixed DPP bootstrapping URI parser of channel list + * extended EAP-SIM/AKA fast re-authentication to allow use with FILS + * extended ca_cert_blob to support PEM format + * improved robustness of P2P Action frame scheduling + * added support for EAP-SIM/AKA using anonymous@realm identity + * fixed Hotspot 2.0 credential selection based on roaming consortium + to ignore credentials without a specific EAP method + * added experimental support for EAP-TEAP peer (RFC 7170) + * added experimental support for EAP-TLS peer with TLS v1.3 + * fixed a regression in WMM parameter configuration for a TDLS peer + * fixed a regression in operation with drivers that offload 802.1X + 4-way handshake + * fixed an ECDH operation corner case with OpenSSL + 2019-04-21 - v2.8 * SAE changes - added support for SAE Password Identifier diff --git a/contrib/wpa/wpa_supplicant/README-DPP b/contrib/wpa/wpa_supplicant/README-DPP index 6496733735d4..dcc15f165c17 100644 --- a/contrib/wpa/wpa_supplicant/README-DPP +++ b/contrib/wpa/wpa_supplicant/README-DPP @@ -100,7 +100,7 @@ On enrollee side: Generate QR code for the device. Store the qr code id returned by the command. -> dpp_bootstrap_gen type=qrcode mac= chan= key= +> dpp_bootstrap_gen type=qrcode mac= chan= key= (returns bootstrapping info id) Get QR Code of device using the bootstrap info id. diff --git a/contrib/wpa/wpa_supplicant/ap.c b/contrib/wpa/wpa_supplicant/ap.c index 4e191694296b..4e3c2814d0f3 100644 --- a/contrib/wpa/wpa_supplicant/ap.c +++ b/contrib/wpa/wpa_supplicant/ap.c @@ -67,7 +67,7 @@ static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s, if (!ssid->p2p_group) { if (!ssid->vht_center_freq1 || - conf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT) + conf->vht_oper_chwidth == CHANWIDTH_USE_HT) goto no_vht; ieee80211_freq_to_chan(ssid->vht_center_freq1, &conf->vht_oper_centr_freq_seg0_idx); @@ -78,14 +78,14 @@ static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s, #ifdef CONFIG_P2P switch (conf->vht_oper_chwidth) { - case VHT_CHANWIDTH_80MHZ: - case VHT_CHANWIDTH_80P80MHZ: + case CHANWIDTH_80MHZ: + case CHANWIDTH_80P80MHZ: center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel); wpa_printf(MSG_DEBUG, "VHT center channel %u for 80 or 80+80 MHz bandwidth", center_chan); break; - case VHT_CHANWIDTH_160MHZ: + case CHANWIDTH_160MHZ: center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel); wpa_printf(MSG_DEBUG, "VHT center channel %u for 160 MHz bandwidth", @@ -97,14 +97,14 @@ static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s, * try oper_cwidth 160 MHz first then VHT 80 MHz, if 160 MHz is * not supported. */ - conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ; + conf->vht_oper_chwidth = CHANWIDTH_160MHZ; center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel); if (center_chan) { wpa_printf(MSG_DEBUG, "VHT center channel %u for auto-selected 160 MHz bandwidth", center_chan); } else { - conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ; + conf->vht_oper_chwidth = CHANWIDTH_80MHZ; center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel); wpa_printf(MSG_DEBUG, @@ -128,7 +128,7 @@ no_vht: conf->channel); conf->vht_oper_centr_freq_seg0_idx = conf->channel + conf->secondary_channel * 2; - conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; + conf->vht_oper_chwidth = CHANWIDTH_USE_HT; } #endif /* CONFIG_IEEE80211N */ @@ -239,6 +239,11 @@ int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, conf->vht_capab |= mode->vht_capab; wpas_conf_ap_vht(wpa_s, ssid, conf, mode); } + + if (mode->he_capab[wpas_mode_to_ieee80211_mode( + ssid->mode)].he_supported && + ssid->he) + conf->ieee80211ax = 1; } } @@ -1387,7 +1392,7 @@ int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos) void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, - int offset, int width, int cf1, int cf2) + int offset, int width, int cf1, int cf2, int finished) { struct hostapd_iface *iface = wpa_s->ap_iface; @@ -1399,7 +1404,7 @@ void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, if (wpa_s->current_ssid) wpa_s->current_ssid->frequency = freq; hostapd_event_ch_switch(iface->bss[0], freq, ht, - offset, width, cf1, cf2); + offset, width, cf1, cf2, finished); } diff --git a/contrib/wpa/wpa_supplicant/ap.h b/contrib/wpa/wpa_supplicant/ap.h index 447b551863a3..6c6e94cdf6a2 100644 --- a/contrib/wpa/wpa_supplicant/ap.h +++ b/contrib/wpa/wpa_supplicant/ap.h @@ -54,7 +54,7 @@ int ap_switch_channel(struct wpa_supplicant *wpa_s, struct csa_settings *settings); int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *txtaddr); void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, - int offset, int width, int cf1, int cf2); + int offset, int width, int cf1, int cf2, int finished); struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s, int ndef); #ifdef CONFIG_AP diff --git a/contrib/wpa/wpa_supplicant/bss.c b/contrib/wpa/wpa_supplicant/bss.c index 9b19f37affa0..441529cb0fd5 100644 --- a/contrib/wpa/wpa_supplicant/bss.c +++ b/contrib/wpa/wpa_supplicant/bss.c @@ -431,6 +431,7 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s, struct os_reltime *fetch_time) { struct wpa_bss *bss; + char extra[50]; bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len); if (bss == NULL) @@ -456,10 +457,15 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s, dl_list_add_tail(&wpa_s->bss, &bss->list); dl_list_add_tail(&wpa_s->bss_id, &bss->list_id); wpa_s->num_bss++; + if (!is_zero_ether_addr(bss->hessid)) + os_snprintf(extra, sizeof(extra), " HESSID " MACSTR, + MAC2STR(bss->hessid)); + else + extra[0] = '\0'; wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR - " SSID '%s' freq %d", + " SSID '%s' freq %d%s", bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len), - bss->freq); + bss->freq, extra); wpas_notify_bss_added(wpa_s, bss->bssid, bss->id); return bss; } diff --git a/contrib/wpa/wpa_supplicant/config.c b/contrib/wpa/wpa_supplicant/config.c index 2058175f885e..7a62f96d6e77 100644 --- a/contrib/wpa/wpa_supplicant/config.c +++ b/contrib/wpa/wpa_supplicant/config.c @@ -2240,8 +2240,8 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(ht, 0, 1) }, { INT_RANGE(vht, 0, 1) }, { INT_RANGE(ht40, -1, 1) }, - { INT_RANGE(max_oper_chwidth, VHT_CHANWIDTH_USE_HT, - VHT_CHANWIDTH_80P80MHZ) }, + { INT_RANGE(max_oper_chwidth, CHANWIDTH_USE_HT, + CHANWIDTH_80P80MHZ) }, { INT(vht_center_freq1) }, { INT(vht_center_freq2) }, #ifdef IEEE8021X_EAPOL @@ -2407,6 +2407,7 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(owe_group, 0, 65535) }, { INT_RANGE(owe_only, 0, 1) }, { INT_RANGE(multi_ap_backhaul_sta, 0, 1) }, + { INT_RANGE(ft_eap_pmksa_caching, 0, 1) }, }; #undef OFFSET @@ -4868,6 +4869,9 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(gas_rand_mac_addr, 0, 2), 0 }, { INT_RANGE(dpp_config_processing, 0, 2), 0 }, { INT_RANGE(coloc_intf_reporting, 0, 1), 0 }, +#ifdef CONFIG_WNM + { INT_RANGE(disable_btm, 0, 1), CFG_CHANGED_DISABLE_BTM }, +#endif /* CONFIG_WNM */ }; #undef FUNC diff --git a/contrib/wpa/wpa_supplicant/config.h b/contrib/wpa/wpa_supplicant/config.h index abbd8c90e2b5..6a297ecfe5bd 100644 --- a/contrib/wpa/wpa_supplicant/config.h +++ b/contrib/wpa/wpa_supplicant/config.h @@ -374,6 +374,7 @@ struct wpa_cred { #define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16) #define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17) #define CFG_CHANGED_WOWLAN_TRIGGERS BIT(18) +#define CFG_CHANGED_DISABLE_BTM BIT(19) /** * struct wpa_config - wpa_supplicant configuration data @@ -1527,6 +1528,15 @@ struct wpa_config { * By default, permanent MAC address is used. */ int p2p_interface_random_mac_addr; + + /** + * disable_btm - Disable BSS transition management in STA + * - Set to 0 to enable BSS transition management + * - Set to 1 to disable BSS transition management + * + * By default BSS transition management is enabled + */ + int disable_btm; }; diff --git a/contrib/wpa/wpa_supplicant/config_file.c b/contrib/wpa/wpa_supplicant/config_file.c index 26f6ee147844..77c326df54de 100644 --- a/contrib/wpa/wpa_supplicant/config_file.c +++ b/contrib/wpa/wpa_supplicant/config_file.c @@ -894,6 +894,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT(owe_group); INT(owe_only); INT(multi_ap_backhaul_sta); + INT(ft_eap_pmksa_caching); #ifdef CONFIG_HT_OVERRIDES INT_DEF(disable_ht, DEFAULT_DISABLE_HT); INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40); @@ -1544,6 +1545,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->p2p_interface_random_mac_addr) fprintf(f, "p2p_interface_random_mac_addr=%d\n", config->p2p_interface_random_mac_addr); + if (config->disable_btm) + fprintf(f, "disable_btm=1\n"); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/contrib/wpa/wpa_supplicant/config_ssid.h b/contrib/wpa/wpa_supplicant/config_ssid.h index 1b2b1f1a36f1..d5c5c00a9dfb 100644 --- a/contrib/wpa/wpa_supplicant/config_ssid.h +++ b/contrib/wpa/wpa_supplicant/config_ssid.h @@ -48,6 +48,15 @@ struct psk_list_entry { u8 p2p; }; +enum wpas_mode { + WPAS_MODE_INFRA = 0, + WPAS_MODE_IBSS = 1, + WPAS_MODE_AP = 2, + WPAS_MODE_P2P_GO = 3, + WPAS_MODE_P2P_GROUP_FORMATION = 4, + WPAS_MODE_MESH = 5, +}; + /** * struct wpa_ssid - Network configuration data * @@ -394,14 +403,7 @@ struct wpa_ssid { * CCMP, but not both), and psk must also be set (either directly or * using ASCII passphrase). */ - enum wpas_mode { - WPAS_MODE_INFRA = 0, - WPAS_MODE_IBSS = 1, - WPAS_MODE_AP = 2, - WPAS_MODE_P2P_GO = 3, - WPAS_MODE_P2P_GROUP_FORMATION = 4, - WPAS_MODE_MESH = 5, - } mode; + enum wpas_mode mode; /** * pbss - Whether to use PBSS. Relevant to DMG networks only. @@ -1005,6 +1007,16 @@ struct wpa_ssid { * 1 = Multi-AP backhaul station */ int multi_ap_backhaul_sta; + + /** + * ft_eap_pmksa_caching - Whether FT-EAP PMKSA caching is allowed + * 0 = do not try to use PMKSA caching with FT-EAP + * 1 = try to use PMKSA caching with FT-EAP + * + * This controls whether to try to use PMKSA caching with FT-EAP for the + * FT initial mobility domain association. + */ + int ft_eap_pmksa_caching; }; #endif /* CONFIG_SSID_H */ diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface.c b/contrib/wpa/wpa_supplicant/ctrl_iface.c index 198ac562d8b6..8efc08d4d906 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface.c @@ -8,10 +8,10 @@ #include "utils/includes.h" #ifdef CONFIG_TESTING_OPTIONS -#include #include #endif /* CONFIG_TESTING_OPTIONS */ +#include #include "utils/common.h" #include "utils/eloop.h" #include "utils/uuid.h" @@ -3129,6 +3129,49 @@ static int wpa_supplicant_ctrl_iface_mesh_peer_add( return wpas_mesh_peer_add(wpa_s, addr, duration); } + +static int wpa_supplicant_ctrl_iface_mesh_link_probe( + struct wpa_supplicant *wpa_s, char *cmd) +{ + struct ether_header *eth; + u8 addr[ETH_ALEN]; + u8 *buf; + char *pos; + size_t payload_len = 0, len; + int ret = -1; + + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = os_strstr(cmd, " payload="); + if (pos) { + pos = pos + 9; + payload_len = os_strlen(pos); + if (payload_len & 1) + return -1; + + payload_len /= 2; + } + + len = ETH_HLEN + payload_len; + buf = os_malloc(len); + if (!buf) + return -1; + + eth = (struct ether_header *) buf; + os_memcpy(eth->ether_dhost, addr, ETH_ALEN); + os_memcpy(eth->ether_shost, wpa_s->own_addr, ETH_ALEN); + eth->ether_type = htons(ETH_P_802_3); + + if (payload_len && hexstr2bin(pos, buf + ETH_HLEN, payload_len) < 0) + goto fail; + + ret = wpa_drv_mesh_link_probe(wpa_s, addr, buf, len); +fail: + os_free(buf); + return -ret; +} + #endif /* CONFIG_MESH */ @@ -5548,17 +5591,17 @@ static int parse_freq(int chwidth, int freq2) if (freq2 < 0) return -1; if (freq2) - return VHT_CHANWIDTH_80P80MHZ; + return CHANWIDTH_80P80MHZ; switch (chwidth) { case 0: case 20: case 40: - return VHT_CHANWIDTH_USE_HT; + return CHANWIDTH_USE_HT; case 80: - return VHT_CHANWIDTH_80MHZ; + return CHANWIDTH_80MHZ; case 160: - return VHT_CHANWIDTH_160MHZ; + return CHANWIDTH_160MHZ; default: wpa_printf(MSG_DEBUG, "Unknown max oper bandwidth: %d", chwidth); @@ -9583,59 +9626,10 @@ static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, return -1; } - if (!enable) { - wpas_mac_addr_rand_scan_clear(wpa_s, type); - if (wpa_s->pno) { - if (type & MAC_ADDR_RAND_PNO) { - wpas_stop_pno(wpa_s); - wpas_start_pno(wpa_s); - } - } else if (wpa_s->sched_scanning && - (type & MAC_ADDR_RAND_SCHED_SCAN)) { - wpas_scan_restart_sched_scan(wpa_s); - } - return 0; - } + if (!enable) + return wpas_disable_mac_addr_randomization(wpa_s, type); - if ((addr && !mask) || (!addr && mask)) { - wpa_printf(MSG_INFO, - "CTRL: MAC_RAND_SCAN invalid addr/mask combination"); - return -1; - } - - if (addr && mask && (!(mask[0] & 0x01) || (addr[0] & 0x01))) { - wpa_printf(MSG_INFO, - "CTRL: MAC_RAND_SCAN cannot allow multicast address"); - return -1; - } - - if (type & MAC_ADDR_RAND_SCAN) { - if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN, - addr, mask)) - return -1; - } - - if (type & MAC_ADDR_RAND_SCHED_SCAN) { - if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN, - addr, mask)) - return -1; - - if (wpa_s->sched_scanning && !wpa_s->pno) - wpas_scan_restart_sched_scan(wpa_s); - } - - if (type & MAC_ADDR_RAND_PNO) { - if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO, - addr, mask)) - return -1; - - if (wpa_s->pno) { - wpas_stop_pno(wpa_s); - wpas_start_pno(wpa_s); - } - } - - return 0; + return wpas_enable_mac_addr_randomization(wpa_s, type, addr, mask); } @@ -10171,6 +10165,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "MESH_PEER_ADD ", 14) == 0) { if (wpa_supplicant_ctrl_iface_mesh_peer_add(wpa_s, buf + 14)) reply_len = -1; + } else if (os_strncmp(buf, "MESH_LINK_PROBE ", 16) == 0) { + if (wpa_supplicant_ctrl_iface_mesh_link_probe(wpa_s, buf + 16)) + reply_len = -1; #endif /* CONFIG_MESH */ #ifdef CONFIG_P2P } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) { @@ -10745,6 +10742,16 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) { if (wpas_dpp_pkex_remove(wpa_s, buf + 16) < 0) reply_len = -1; +#ifdef CONFIG_DPP2 + } else if (os_strncmp(buf, "DPP_CONTROLLER_START ", 21) == 0) { + if (wpas_dpp_controller_start(wpa_s, buf + 20) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "DPP_CONTROLLER_START") == 0) { + if (wpas_dpp_controller_start(wpa_s, NULL) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "DPP_CONTROLLER_STOP") == 0) { + dpp_controller_stop(wpa_s->dpp); +#endif /* CONFIG_DPP2 */ #endif /* CONFIG_DPP */ } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c index 0115e32a1d34..d9009ba85e9c 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c @@ -98,6 +98,7 @@ static DBusMessage * get_all_properties(DBusMessage *message, char *interface, dbus_error_init(&error); if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties, interface, obj_dsc->user_data, &error)) { + wpa_dbus_dict_close_write(&iter, &dict_iter); dbus_message_unref(reply); reply = wpas_dbus_reply_new_from_error( message, &error, DBUS_ERROR_INVALID_ARGS, @@ -741,7 +742,7 @@ static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx) DBusConnection *con = eloop_ctx; struct wpa_dbus_object_desc *obj_desc = timeout_ctx; - wpa_printf(MSG_DEBUG, + wpa_printf(MSG_MSGDUMP, "dbus: %s: Timeout - sending changed properties of object %s", __func__, obj_desc->path); wpa_dbus_flush_object_changed_properties(con, obj_desc->path); @@ -930,6 +931,7 @@ dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, dbus_error_is_set(&error) ? error.name : "none", dbus_error_is_set(&error) ? error.message : "none"); dbus_error_free(&error); + wpa_dbus_dict_close_write(iter, &dict_iter); return FALSE; } diff --git a/contrib/wpa/wpa_supplicant/defconfig b/contrib/wpa/wpa_supplicant/defconfig index 88cd79085f6d..cdfb1974da5c 100644 --- a/contrib/wpa/wpa_supplicant/defconfig +++ b/contrib/wpa/wpa_supplicant/defconfig @@ -111,6 +111,16 @@ CONFIG_EAP_TTLS=y # EAP-FAST CONFIG_EAP_FAST=y +# EAP-TEAP +# Note: The current EAP-TEAP implementation is experimental and should not be +# enabled for production use. The IETF RFC 7170 that defines EAP-TEAP has number +# of conflicting statements and missing details and the implementation has +# vendor specific workarounds for those and as such, may not interoperate with +# any other implementation. This should not be used for anything else than +# experimentation and interoperability testing until those issues has been +# resolved. +#CONFIG_EAP_TEAP=y + # EAP-GTC CONFIG_EAP_GTC=y @@ -120,6 +130,9 @@ CONFIG_EAP_OTP=y # EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used) #CONFIG_EAP_SIM=y +# Enable SIM simulator (Milenage) for EAP-SIM +#CONFIG_SIM_SIMULATOR=y + # EAP-PSK (experimental; this is _not_ needed for WPA-PSK) #CONFIG_EAP_PSK=y diff --git a/contrib/wpa/wpa_supplicant/dpp_supplicant.c b/contrib/wpa/wpa_supplicant/dpp_supplicant.c index e003a8514edb..1f65658eff76 100644 --- a/contrib/wpa/wpa_supplicant/dpp_supplicant.c +++ b/contrib/wpa/wpa_supplicant/dpp_supplicant.c @@ -11,6 +11,7 @@ #include "utils/common.h" #include "utils/eloop.h" +#include "utils/ip_addr.h" #include "common/dpp.h" #include "common/gas.h" #include "common/gas_server.h" @@ -433,8 +434,15 @@ int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd) { const char *pos; struct dpp_bootstrap_info *peer_bi, *own_bi = NULL; + struct dpp_authentication *auth; u8 allowed_roles = DPP_CAPAB_CONFIGURATOR; unsigned int neg_freq = 0; + int tcp = 0; +#ifdef CONFIG_DPP2 + int tcp_port = DPP_TCP_PORT; + struct hostapd_ip_addr ipaddr; + char *addr; +#endif /* CONFIG_DPP2 */ wpa_s->dpp_gas_client = 0; @@ -449,6 +457,25 @@ int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd) return -1; } +#ifdef CONFIG_DPP2 + pos = os_strstr(cmd, " tcp_port="); + if (pos) { + pos += 10; + tcp_port = atoi(pos); + } + + addr = get_param(cmd, " tcp_addr="); + if (addr) { + int res; + + res = hostapd_parse_ip_addr(addr, &ipaddr); + os_free(addr); + if (res) + return -1; + tcp = 1; + } +#endif /* CONFIG_DPP2 */ + pos = os_strstr(cmd, " own="); if (pos) { pos += 5; @@ -491,32 +518,37 @@ int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd) if (pos) neg_freq = atoi(pos + 10); - if (wpa_s->dpp_auth) { + if (!tcp && wpa_s->dpp_auth) { eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL); offchannel_send_action_done(wpa_s); dpp_auth_deinit(wpa_s->dpp_auth); - } - wpa_s->dpp_auth = dpp_auth_init(wpa_s, peer_bi, own_bi, allowed_roles, - neg_freq, - wpa_s->hw.modes, wpa_s->hw.num_modes); - if (!wpa_s->dpp_auth) - goto fail; - wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth); - if (dpp_set_configurator(wpa_s->dpp, wpa_s, wpa_s->dpp_auth, cmd) < 0) { - dpp_auth_deinit(wpa_s->dpp_auth); wpa_s->dpp_auth = NULL; + } + + auth = dpp_auth_init(wpa_s, peer_bi, own_bi, allowed_roles, neg_freq, + wpa_s->hw.modes, wpa_s->hw.num_modes); + if (!auth) + goto fail; + wpas_dpp_set_testing_options(wpa_s, auth); + if (dpp_set_configurator(wpa_s->dpp, wpa_s, auth, cmd) < 0) { + dpp_auth_deinit(auth); goto fail; } - wpa_s->dpp_auth->neg_freq = neg_freq; + auth->neg_freq = neg_freq; if (!is_zero_ether_addr(peer_bi->mac_addr)) - os_memcpy(wpa_s->dpp_auth->peer_mac_addr, peer_bi->mac_addr, - ETH_ALEN); + os_memcpy(auth->peer_mac_addr, peer_bi->mac_addr, ETH_ALEN); +#ifdef CONFIG_DPP2 + if (tcp) + return dpp_tcp_init(wpa_s->dpp, auth, &ipaddr, tcp_port); +#endif /* CONFIG_DPP2 */ + + wpa_s->dpp_auth = auth; return wpas_dpp_auth_init_next(wpa_s); fail: return -1; @@ -1272,6 +1304,15 @@ static void wpas_dpp_rx_conf_result(struct wpa_supplicant *wpa_s, const u8 *src, eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL); } + +static int wpas_dpp_process_conf_obj(void *ctx, + struct dpp_authentication *auth) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpas_dpp_handle_config_obj(wpa_s, auth); +} + #endif /* CONFIG_DPP2 */ @@ -1842,6 +1883,18 @@ wpas_dpp_gas_req_handler(void *ctx, const u8 *sa, const u8 *query, wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); return NULL; } + + if (wpa_s->dpp_auth_ok_on_ack && auth->configurator) { + wpa_printf(MSG_DEBUG, + "DPP: Have not received ACK for Auth Confirm yet - assume it was received based on this GAS request"); + /* wpas_dpp_auth_success() would normally have been called from + * TX status handler, but since there was no such handler call + * yet, simply send out the event message and proceed with + * exchange. */ + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=1"); + wpa_s->dpp_auth_ok_on_ack = 0; + } + wpa_hexdump(MSG_DEBUG, "DPP: Received Configuration Request (GAS Query Request)", query, query_len); @@ -2196,6 +2249,7 @@ void wpas_dpp_stop(struct wpa_supplicant *wpa_s) int wpas_dpp_init(struct wpa_supplicant *wpa_s) { + struct dpp_global_config config; u8 adv_proto_id[7]; adv_proto_id[0] = WLAN_EID_VENDOR_SPECIFIC; @@ -2208,7 +2262,14 @@ int wpas_dpp_init(struct wpa_supplicant *wpa_s) sizeof(adv_proto_id), wpas_dpp_gas_req_handler, wpas_dpp_gas_status_handler, wpa_s) < 0) return -1; - wpa_s->dpp = dpp_global_init(); + + os_memset(&config, 0, sizeof(config)); + config.msg_ctx = wpa_s; + config.cb_ctx = wpa_s; +#ifdef CONFIG_DPP2 + config.process_conf_obj = wpas_dpp_process_conf_obj; +#endif /* CONFIG_DPP2 */ + wpa_s->dpp = dpp_global_init(&config); return wpa_s->dpp ? 0 : -1; } @@ -2244,3 +2305,23 @@ void wpas_dpp_deinit(struct wpa_supplicant *wpa_s) os_free(wpa_s->dpp_configurator_params); wpa_s->dpp_configurator_params = NULL; } + + +#ifdef CONFIG_DPP2 +int wpas_dpp_controller_start(struct wpa_supplicant *wpa_s, const char *cmd) +{ + struct dpp_controller_config config; + const char *pos; + + os_memset(&config, 0, sizeof(config)); + if (cmd) { + pos = os_strstr(cmd, " tcp_port="); + if (pos) { + pos += 10; + config.tcp_port = atoi(pos); + } + } + config.configurator_params = wpa_s->dpp_configurator_params; + return dpp_controller_start(wpa_s->dpp, &config); +} +#endif /* CONFIG_DPP2 */ diff --git a/contrib/wpa/wpa_supplicant/dpp_supplicant.h b/contrib/wpa/wpa_supplicant/dpp_supplicant.h index ecb7a7d684fa..9ba315f55254 100644 --- a/contrib/wpa/wpa_supplicant/dpp_supplicant.h +++ b/contrib/wpa/wpa_supplicant/dpp_supplicant.h @@ -25,5 +25,6 @@ int wpas_dpp_init(struct wpa_supplicant *wpa_s); void wpas_dpp_deinit(struct wpa_supplicant *wpa_s); int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss); +int wpas_dpp_controller_start(struct wpa_supplicant *wpa_s, const char *cmd); #endif /* DPP_SUPPLICANT_H */ diff --git a/contrib/wpa/wpa_supplicant/driver_i.h b/contrib/wpa/wpa_supplicant/driver_i.h index 4a9f472e84ee..cf9972a6b7d5 100644 --- a/contrib/wpa/wpa_supplicant/driver_i.h +++ b/contrib/wpa/wpa_supplicant/driver_i.h @@ -87,6 +87,16 @@ static inline int wpa_drv_leave_mesh(struct wpa_supplicant *wpa_s) return -1; } +static inline int wpa_drv_mesh_link_probe(struct wpa_supplicant *wpa_s, + const u8 *addr, + const u8 *eth, size_t len) +{ + if (wpa_s->driver->probe_mesh_link) + return wpa_s->driver->probe_mesh_link(wpa_s->drv_priv, addr, + eth, len); + return -1; +} + static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { @@ -168,7 +178,7 @@ static inline int wpa_drv_get_seqnum(struct wpa_supplicant *wpa_s, } static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s, - const u8 *addr, int reason_code) + const u8 *addr, u16 reason_code) { if (wpa_s->driver->sta_deauth) { return wpa_s->driver->sta_deauth(wpa_s->drv_priv, @@ -179,7 +189,7 @@ static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s, } static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s, - const u8 *addr, int reason_code) + const u8 *addr, u16 reason_code) { if (wpa_s->driver->deauthenticate) { return wpa_s->driver->deauthenticate(wpa_s->drv_priv, addr, diff --git a/contrib/wpa/wpa_supplicant/eap_register.c b/contrib/wpa/wpa_supplicant/eap_register.c index ece57166c099..3f018c4b3c32 100644 --- a/contrib/wpa/wpa_supplicant/eap_register.c +++ b/contrib/wpa/wpa_supplicant/eap_register.c @@ -102,6 +102,11 @@ int eap_register_methods(void) ret = eap_peer_fast_register(); #endif /* EAP_FAST */ +#ifdef EAP_TEAP + if (ret == 0) + ret = eap_peer_teap_register(); +#endif /* EAP_TEAP */ + #ifdef EAP_PAX if (ret == 0) ret = eap_peer_pax_register(); @@ -237,6 +242,11 @@ int eap_register_methods(void) ret = eap_server_fast_register(); #endif /* EAP_SERVER_FAST */ +#ifdef EAP_SERVER_TEAP + if (ret == 0) + ret = eap_server_teap_register(); +#endif /* EAP_SERVER_TEAP */ + #ifdef EAP_SERVER_WSC if (ret == 0) ret = eap_server_wsc_register(); diff --git a/contrib/wpa/wpa_supplicant/eapol_test.c b/contrib/wpa/wpa_supplicant/eapol_test.c index 3fd4ce61a1c2..524724f19735 100644 --- a/contrib/wpa/wpa_supplicant/eapol_test.c +++ b/contrib/wpa/wpa_supplicant/eapol_test.c @@ -15,6 +15,7 @@ #include "common.h" #include "utils/ext_password.h" #include "common/version.h" +#include "crypto/tls.h" #include "config.h" #include "eapol_supp/eapol_supp_sm.h" #include "eap_peer/eap.h" @@ -497,45 +498,40 @@ static void eapol_test_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field, #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ -static void eapol_test_cert_cb(void *ctx, int depth, const char *subject, - const char *altsubject[], int num_altsubject, - const char *cert_hash, - const struct wpabuf *cert) +static void eapol_test_cert_cb(void *ctx, struct tls_cert_data *cert, + const char *cert_hash) { struct eapol_test_data *e = ctx; + int i; wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT "depth=%d subject='%s'%s%s", - depth, subject, + cert->depth, cert->subject, cert_hash ? " hash=" : "", cert_hash ? cert_hash : ""); - if (cert) { + if (cert->cert) { char *cert_hex; - size_t len = wpabuf_len(cert) * 2 + 1; + size_t len = wpabuf_len(cert->cert) * 2 + 1; cert_hex = os_malloc(len); if (cert_hex) { - wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert), - wpabuf_len(cert)); + wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert->cert), + wpabuf_len(cert->cert)); wpa_msg_ctrl(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT "depth=%d subject='%s' cert=%s", - depth, subject, cert_hex); + cert->depth, cert->subject, cert_hex); os_free(cert_hex); } if (e->server_cert_file) eapol_test_write_cert(e->server_cert_file, - subject, cert); + cert->subject, cert->cert); } - if (altsubject) { - int i; - - for (i = 0; i < num_altsubject; i++) - wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT - "depth=%d %s", depth, altsubject[i]); - } + for (i = 0; i < cert->num_altsubject; i++) + wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT + "depth=%d %s", cert->depth, cert->altsubject[i]); } diff --git a/contrib/wpa/wpa_supplicant/events.c b/contrib/wpa/wpa_supplicant/events.c index f6ec111b77b6..87dad0811e30 100644 --- a/contrib/wpa/wpa_supplicant/events.c +++ b/contrib/wpa/wpa_supplicant/events.c @@ -1222,7 +1222,7 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, continue; } - if (ssid->mode != IEEE80211_MODE_MESH && !bss_is_ess(bss) && + if (ssid->mode != WPAS_MODE_MESH && !bss_is_ess(bss) && !bss_is_pbss(bss)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, @@ -1246,7 +1246,7 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, } #ifdef CONFIG_MESH - if (ssid->mode == IEEE80211_MODE_MESH && ssid->frequency > 0 && + if (ssid->mode == WPAS_MODE_MESH && ssid->frequency > 0 && ssid->frequency != bss->freq) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, @@ -1615,9 +1615,9 @@ wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s) continue; } #endif /* !CONFIG_IBSS_RSN */ - if (ssid->mode == IEEE80211_MODE_IBSS || - ssid->mode == IEEE80211_MODE_AP || - ssid->mode == IEEE80211_MODE_MESH) + if (ssid->mode == WPAS_MODE_IBSS || + ssid->mode == WPAS_MODE_AP || + ssid->mode == WPAS_MODE_MESH) return ssid; } } @@ -2839,7 +2839,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE || (wpa_s->current_ssid && - wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) { + wpa_s->current_ssid->mode == WPAS_MODE_IBSS)) { if (wpa_s->current_ssid && wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE && (wpa_s->drv_flags & @@ -3596,8 +3596,9 @@ static void wpas_event_disassoc(struct wpa_supplicant *wpa_s, ie_len = info->ie_len; reason_code = info->reason_code; locally_generated = info->locally_generated; - wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", reason_code, - locally_generated ? " (locally generated)" : ""); + wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u (%s)%s", reason_code, + reason2str(reason_code), + locally_generated ? " locally_generated=1" : ""); if (addr) wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, MAC2STR(addr)); @@ -3650,9 +3651,9 @@ static void wpas_event_deauth(struct wpa_supplicant *wpa_s, ie_len = info->ie_len; reason_code = info->reason_code; locally_generated = info->locally_generated; - wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", - reason_code, - locally_generated ? " (locally generated)" : ""); + wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u (%s)%s", + reason_code, reason2str(reason_code), + locally_generated ? " locally_generated=1" : ""); if (addr) { wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, MAC2STR(addr)); @@ -4058,9 +4059,18 @@ static void wpas_event_assoc_reject(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { const u8 *bssid = data->assoc_reject.bssid; +#ifdef CONFIG_MBO + struct wpa_bss *reject_bss; +#endif /* CONFIG_MBO */ if (!bssid || is_zero_ether_addr(bssid)) bssid = wpa_s->pending_bssid; +#ifdef CONFIG_MBO + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) + reject_bss = wpa_s->current_bss; + else + reject_bss = wpa_bss_get_bssid(wpa_s, bssid); +#endif /* CONFIG_MBO */ if (data->assoc_reject.bssid) wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT @@ -4111,8 +4121,7 @@ static void wpas_event_assoc_reject(struct wpa_supplicant *wpa_s, #ifdef CONFIG_MBO if (data->assoc_reject.status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS && - wpa_s->current_bss && data->assoc_reject.bssid && - data->assoc_reject.resp_ies) { + reject_bss && data->assoc_reject.resp_ies) { const u8 *rssi_rej; rssi_rej = mbo_get_attr_from_ies( @@ -4123,13 +4132,12 @@ static void wpas_event_assoc_reject(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "OCE: RSSI-based association rejection from " MACSTR " (Delta RSSI: %u, Retry Delay: %u)", - MAC2STR(data->assoc_reject.bssid), + MAC2STR(reject_bss->bssid), rssi_rej[2], rssi_rej[3]); wpa_bss_tmp_disallow(wpa_s, - data->assoc_reject.bssid, + reject_bss->bssid, rssi_rej[3], - rssi_rej[2] + - wpa_s->current_bss->level); + rssi_rej[2] + reject_bss->level); } } #endif /* CONFIG_MBO */ @@ -4461,18 +4469,24 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->rx_from_unknown.wds); break; #endif /* CONFIG_AP */ + + case EVENT_CH_SWITCH_STARTED: case EVENT_CH_SWITCH: if (!data || !wpa_s->current_ssid) break; - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CHANNEL_SWITCH - "freq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d", + wpa_msg(wpa_s, MSG_INFO, + "%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d", + event == EVENT_CH_SWITCH ? WPA_EVENT_CHANNEL_SWITCH : + WPA_EVENT_CHANNEL_SWITCH_STARTED, data->ch_switch.freq, data->ch_switch.ht_enabled, data->ch_switch.ch_offset, channel_width_to_string(data->ch_switch.ch_width), data->ch_switch.cf1, data->ch_switch.cf2); + if (event == EVENT_CH_SWITCH_STARTED) + break; wpa_s->assoc_freq = data->ch_switch.freq; wpa_s->current_ssid->frequency = data->ch_switch.freq; @@ -4488,7 +4502,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->ch_switch.ch_offset, data->ch_switch.ch_width, data->ch_switch.cf1, - data->ch_switch.cf2); + data->ch_switch.cf2, + 1); } #endif /* CONFIG_AP */ @@ -4701,6 +4716,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled"); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_supplicant_update_mac_addr(wpa_s); + wpa_supplicant_set_default_scan_ies(wpa_s); if (wpa_s->p2p_mgmt) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); diff --git a/contrib/wpa/wpa_supplicant/ibss_rsn.c b/contrib/wpa/wpa_supplicant/ibss_rsn.c index e96ea65a7887..6934c4725da3 100644 --- a/contrib/wpa/wpa_supplicant/ibss_rsn.c +++ b/contrib/wpa/wpa_supplicant/ibss_rsn.c @@ -193,7 +193,7 @@ static void supp_cancel_auth_timeout(void *ctx) } -static void supp_deauthenticate(void * ctx, int reason_code) +static void supp_deauthenticate(void * ctx, u16 reason_code) { wpa_printf(MSG_DEBUG, "SUPP: %s (TODO)", __func__); } diff --git a/contrib/wpa/wpa_supplicant/interworking.c b/contrib/wpa/wpa_supplicant/interworking.c index f94cd1614c60..dd35571d914b 100644 --- a/contrib/wpa/wpa_supplicant/interworking.c +++ b/contrib/wpa/wpa_supplicant/interworking.c @@ -1388,6 +1388,9 @@ static struct wpa_cred * interworking_credentials_available_roaming_consortium( cred->num_roaming_consortiums == 0) continue; + if (!cred->eap_method) + continue; + if ((cred->roaming_consortium_len == 0 || !roaming_consortium_match(ie, anqp, cred->roaming_consortium, @@ -2669,7 +2672,8 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) found++; bss->flags |= WPA_BSS_ANQP_FETCH_TRIED; wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for " - MACSTR, MAC2STR(bss->bssid)); + MACSTR " (HESSID " MACSTR ")", + MAC2STR(bss->bssid), MAC2STR(bss->hessid)); interworking_anqp_send_req(wpa_s, bss); break; } diff --git a/contrib/wpa/wpa_supplicant/mesh.c b/contrib/wpa/wpa_supplicant/mesh.c index 92600211ac2d..7354c1b79161 100644 --- a/contrib/wpa/wpa_supplicant/mesh.c +++ b/contrib/wpa/wpa_supplicant/mesh.c @@ -208,6 +208,7 @@ static int wpas_mesh_complete(struct wpa_supplicant *wpa_s) wpa_printf(MSG_ERROR, "mesh: RSN initialization failed - deinit mesh"); wpa_supplicant_mesh_deinit(wpa_s); + wpa_drv_leave_mesh(wpa_s); return -1; } @@ -333,14 +334,14 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, if (ssid->max_oper_chwidth != DEFAULT_MAX_OPER_CHWIDTH) conf->vht_oper_chwidth = ssid->max_oper_chwidth; switch (conf->vht_oper_chwidth) { - case VHT_CHANWIDTH_80MHZ: - case VHT_CHANWIDTH_80P80MHZ: + case CHANWIDTH_80MHZ: + case CHANWIDTH_80P80MHZ: ieee80211_freq_to_chan( frequency, &conf->vht_oper_centr_freq_seg0_idx); conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2; break; - case VHT_CHANWIDTH_160MHZ: + case CHANWIDTH_160MHZ: ieee80211_freq_to_chan( frequency, &conf->vht_oper_centr_freq_seg0_idx); @@ -455,6 +456,7 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, ibss_mesh_setup_freq(wpa_s, ssid, ¶ms->freq); wpa_s->mesh_ht_enabled = !!params->freq.ht_enabled; wpa_s->mesh_vht_enabled = !!params->freq.vht_enabled; + wpa_s->mesh_he_enabled = !!params->freq.he_enabled; if (params->freq.ht_enabled && params->freq.sec_channel_offset) ssid->ht40 = params->freq.sec_channel_offset; @@ -464,21 +466,23 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, switch (params->freq.bandwidth) { case 80: if (params->freq.center_freq2) { - ssid->max_oper_chwidth = VHT_CHANWIDTH_80P80MHZ; + ssid->max_oper_chwidth = CHANWIDTH_80P80MHZ; ssid->vht_center_freq2 = params->freq.center_freq2; } else { - ssid->max_oper_chwidth = VHT_CHANWIDTH_80MHZ; + ssid->max_oper_chwidth = CHANWIDTH_80MHZ; } break; case 160: - ssid->max_oper_chwidth = VHT_CHANWIDTH_160MHZ; + ssid->max_oper_chwidth = CHANWIDTH_160MHZ; break; default: - ssid->max_oper_chwidth = VHT_CHANWIDTH_USE_HT; + ssid->max_oper_chwidth = CHANWIDTH_USE_HT; break; } } + if (wpa_s->mesh_he_enabled) + ssid->he = 1; if (ssid->beacon_int > 0) params->beacon_int = ssid->beacon_int; else if (wpa_s->conf->beacon_int > 0) diff --git a/contrib/wpa/wpa_supplicant/mesh_mpm.c b/contrib/wpa/wpa_supplicant/mesh_mpm.c index 9d6ab8da1ebe..4a163b6eb6d8 100644 --- a/contrib/wpa/wpa_supplicant/mesh_mpm.c +++ b/contrib/wpa/wpa_supplicant/mesh_mpm.c @@ -245,6 +245,16 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, 2 + 5; /* VHT Operation */ } #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + if (type != PLINK_CLOSE && wpa_s->mesh_he_enabled) { + buf_len += 3 + + HE_MAX_MAC_CAPAB_SIZE + + HE_MAX_PHY_CAPAB_SIZE + + HE_MAX_MCS_CAPAB_SIZE + + HE_MAX_PPET_CAPAB_SIZE; + buf_len += 3 + sizeof(struct ieee80211_he_operation); + } +#endif /* CONFIG_IEEE80211AX */ if (type != PLINK_CLOSE) buf_len += conf->rsn_ie_len; /* RSN IE */ #ifdef CONFIG_OCV @@ -362,6 +372,21 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, wpabuf_put_data(buf, vht_capa_oper, pos - vht_capa_oper); } #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + if (type != PLINK_CLOSE && wpa_s->mesh_he_enabled) { + u8 he_capa_oper[3 + + HE_MAX_MAC_CAPAB_SIZE + + HE_MAX_PHY_CAPAB_SIZE + + HE_MAX_MCS_CAPAB_SIZE + + HE_MAX_PPET_CAPAB_SIZE + + 3 + sizeof(struct ieee80211_he_operation)]; + + pos = hostapd_eid_he_capab(bss, he_capa_oper, + IEEE80211_MODE_MESH); + pos = hostapd_eid_he_operation(bss, pos); + wpabuf_put_data(buf, he_capa_oper, pos - he_capa_oper); + } +#endif /* CONFIG_IEEE80211AX */ #ifdef CONFIG_OCV if (type != PLINK_CLOSE && conf->ocv) { @@ -725,6 +750,11 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, set_sta_vht_opmode(data, sta, elems->vht_opmode_notif); #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + copy_sta_he_capab(data, sta, IEEE80211_MODE_MESH, + elems->he_capabilities, elems->he_capabilities_len); +#endif /* CONFIG_IEEE80211AX */ + if (hostapd_get_aid(data, sta) < 0) { wpa_msg(wpa_s, MSG_ERROR, "No AIDs available"); ap_free_sta(data, sta); @@ -742,6 +772,8 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, params.listen_interval = 100; params.ht_capabilities = sta->ht_capabilities; params.vht_capabilities = sta->vht_capabilities; + params.he_capab = sta->he_capab; + params.he_capab_len = sta->he_capab_len; 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 bedb74b34440..e41d7c41c61c 100644 --- a/contrib/wpa/wpa_supplicant/notify.c +++ b/contrib/wpa/wpa_supplicant/notify.c @@ -18,6 +18,7 @@ #include "dbus/dbus_new.h" #include "rsn_supp/wpa.h" #include "fst/fst.h" +#include "crypto/tls.h" #include "driver_i.h" #include "scan.h" #include "p2p_supplicant.h" @@ -786,42 +787,41 @@ void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s, } -void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth, - const char *subject, const char *altsubject[], - int num_altsubject, const char *cert_hash, - const struct wpabuf *cert) +void wpas_notify_certification(struct wpa_supplicant *wpa_s, + struct tls_cert_data *cert, + const char *cert_hash) { - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT - "depth=%d subject='%s'%s%s", - depth, subject, cert_hash ? " hash=" : "", - cert_hash ? cert_hash : ""); + int i; - if (cert) { + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT + "depth=%d subject='%s'%s%s%s", + cert->depth, cert->subject, cert_hash ? " hash=" : "", + cert_hash ? cert_hash : "", + cert->tod ? " tod=1" : ""); + + if (cert->cert) { char *cert_hex; - size_t len = wpabuf_len(cert) * 2 + 1; + size_t len = wpabuf_len(cert->cert) * 2 + 1; cert_hex = os_malloc(len); if (cert_hex) { - wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert), - wpabuf_len(cert)); + wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert->cert), + wpabuf_len(cert->cert)); wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT "depth=%d subject='%s' cert=%s", - depth, subject, cert_hex); + cert->depth, cert->subject, cert_hex); os_free(cert_hex); } } - if (altsubject) { - int i; - - for (i = 0; i < num_altsubject; i++) - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT - "depth=%d %s", depth, altsubject[i]); - } + for (i = 0; i < cert->num_altsubject; i++) + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT + "depth=%d %s", cert->depth, cert->altsubject[i]); /* notify the new DBus API */ - wpas_dbus_signal_certification(wpa_s, depth, subject, altsubject, - num_altsubject, cert_hash, cert); + wpas_dbus_signal_certification(wpa_s, cert->depth, cert->subject, + cert->altsubject, cert->num_altsubject, + cert_hash, cert->cert); } @@ -901,7 +901,7 @@ void wpas_notify_mesh_group_started(struct wpa_supplicant *wpa_s, void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s, const u8 *meshid, u8 meshid_len, - int reason_code) + u16 reason_code) { if (wpa_s->p2p_mgmt) return; @@ -922,7 +922,7 @@ void wpas_notify_mesh_peer_connected(struct wpa_supplicant *wpa_s, void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s, - const u8 *peer_addr, int reason_code) + const u8 *peer_addr, u16 reason_code) { if (wpa_s->p2p_mgmt) return; diff --git a/contrib/wpa/wpa_supplicant/notify.h b/contrib/wpa/wpa_supplicant/notify.h index 65f513ea9770..e843aa124b39 100644 --- a/contrib/wpa/wpa_supplicant/notify.h +++ b/contrib/wpa/wpa_supplicant/notify.h @@ -14,6 +14,7 @@ struct wps_credential; struct wps_event_m2d; struct wps_event_fail; +struct tls_cert_data; int wpas_notify_supplicant_initialized(struct wpa_global *global); void wpas_notify_supplicant_deinitialized(struct wpa_global *global); @@ -130,10 +131,9 @@ void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s, void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail); -void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth, - const char *subject, const char *altsubject[], - int num_altsubject, const char *cert_hash, - const struct wpabuf *cert); +void wpas_notify_certification(struct wpa_supplicant *wpa_s, + struct tls_cert_data *cert, + const char *cert_hash); void wpas_notify_preq(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, u32 ssi_signal); @@ -151,10 +151,10 @@ void wpas_notify_mesh_group_started(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s, const u8 *meshid, u8 meshid_len, - int reason_code); + u16 reason_code); void wpas_notify_mesh_peer_connected(struct wpa_supplicant *wpa_s, const u8 *peer_addr); void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s, - const u8 *peer_addr, int reason_code); + const u8 *peer_addr, u16 reason_code); #endif /* NOTIFY_H */ diff --git a/contrib/wpa/wpa_supplicant/op_classes.c b/contrib/wpa/wpa_supplicant/op_classes.c index 947917bb05f4..6a85af4eaf37 100644 --- a/contrib/wpa/wpa_supplicant/op_classes.c +++ b/contrib/wpa/wpa_supplicant/op_classes.c @@ -348,7 +348,7 @@ size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, * TODO: Use the secondary channel and VHT channel width that will be * used after association. */ - if (ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT, + if (ieee80211_freq_to_channel_ext(freq, 0, CHANWIDTH_USE_HT, ¤t, &chan) == NUM_HOSTAPD_MODES) return 0; diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.c b/contrib/wpa/wpa_supplicant/p2p_supplicant.c index e7c1f5d5aca4..55b3b08efe50 100644 --- a/contrib/wpa/wpa_supplicant/p2p_supplicant.c +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.c @@ -1556,7 +1556,7 @@ static int wpas_send_action_work(struct wpa_supplicant *wpa_s, { struct send_action_work *awork; - if (wpa_s->p2p_send_action_work) { + if (radio_work_pending(wpa_s, "p2p-send-action")) { wpa_printf(MSG_DEBUG, "P2P: Cannot schedule new p2p-send-action work since one is already pending"); return -1; } @@ -1573,7 +1573,7 @@ static int wpas_send_action_work(struct wpa_supplicant *wpa_s, awork->wait_time = wait_time; os_memcpy(awork->buf, buf, len); - if (radio_add_work(wpa_s, freq, "p2p-send-action", 0, + if (radio_add_work(wpa_s, freq, "p2p-send-action", 1, wpas_send_action_cb, awork) < 0) { os_free(awork); return -1; @@ -2268,6 +2268,8 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) res->ht40 = 1; if (wpa_s->p2p_go_vht) res->vht = 1; + if (wpa_s->p2p_go_he) + res->he = 1; res->max_oper_chwidth = wpa_s->p2p_go_max_oper_chwidth; res->vht_center_freq2 = wpa_s->p2p_go_vht_center_freq2; @@ -5476,7 +5478,7 @@ exit_free: * @ht40: Start GO with 40 MHz channel width * @vht: Start GO with VHT support * @vht_chwidth: Channel width supported by GO operating with VHT support - * (VHT_CHANWIDTH_*). + * (CHANWIDTH_*). * @group_ssid: Specific Group SSID for join or %NULL if not set * @group_ssid_len: Length of @group_ssid in octets * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified @@ -9193,11 +9195,11 @@ static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s) csa_settings.freq_params.center_freq2 = freq2; switch (conf->vht_oper_chwidth) { - case VHT_CHANWIDTH_80MHZ: - case VHT_CHANWIDTH_80P80MHZ: + case CHANWIDTH_80MHZ: + case CHANWIDTH_80P80MHZ: csa_settings.freq_params.bandwidth = 80; break; - case VHT_CHANWIDTH_160MHZ: + case CHANWIDTH_160MHZ: csa_settings.freq_params.bandwidth = 160; break; } diff --git a/contrib/wpa/wpa_supplicant/preauth_test.c b/contrib/wpa/wpa_supplicant/preauth_test.c index f2fff550aa81..3f2da34e5bf0 100644 --- a/contrib/wpa/wpa_supplicant/preauth_test.c +++ b/contrib/wpa/wpa_supplicant/preauth_test.c @@ -35,7 +35,7 @@ struct preauth_test_data { }; -static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code) +static void _wpa_supplicant_deauthenticate(void *wpa_s, u16 reason_code) { wpa_supplicant_deauthenticate(wpa_s, reason_code); } diff --git a/contrib/wpa/wpa_supplicant/rrm.c b/contrib/wpa/wpa_supplicant/rrm.c index cb3c6c995dd9..8468b2f86bd0 100644 --- a/contrib/wpa/wpa_supplicant/rrm.c +++ b/contrib/wpa/wpa_supplicant/rrm.c @@ -717,20 +717,20 @@ static int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, seg0 = vht_oper->vht_op_info_chan_center_freq_seg0_idx; seg1 = vht_oper->vht_op_info_chan_center_freq_seg1_idx; if (seg1 && abs(seg1 - seg0) == 8) - vht = VHT_CHANWIDTH_160MHZ; + vht = CHANWIDTH_160MHZ; else if (seg1) - vht = VHT_CHANWIDTH_80P80MHZ; + vht = CHANWIDTH_80P80MHZ; else - vht = VHT_CHANWIDTH_80MHZ; + vht = CHANWIDTH_80MHZ; break; case 2: - vht = VHT_CHANWIDTH_160MHZ; + vht = CHANWIDTH_160MHZ; break; case 3: - vht = VHT_CHANWIDTH_80P80MHZ; + vht = CHANWIDTH_80P80MHZ; break; default: - vht = VHT_CHANWIDTH_USE_HT; + vht = CHANWIDTH_USE_HT; break; } } diff --git a/contrib/wpa/wpa_supplicant/sme.c b/contrib/wpa/wpa_supplicant/sme.c index 17a984d1a17d..dd5020179f3b 100644 --- a/contrib/wpa/wpa_supplicant/sme.c +++ b/contrib/wpa/wpa_supplicant/sme.c @@ -932,21 +932,23 @@ static int sme_external_auth_build_buf(struct wpabuf *buf, } -static void sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s, - const u8 *bssid, - struct wpa_ssid *ssid) +static int sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s, + const u8 *bssid, + struct wpa_ssid *ssid) { struct wpabuf *resp, *buf; resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0); - if (!resp) - return; + if (!resp) { + wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit"); + return -1; + } wpa_s->sme.sae.state = SAE_COMMITTED; buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp)); if (!buf) { wpabuf_free(resp); - return; + return -1; } wpa_s->sme.seq_num++; @@ -955,6 +957,8 @@ static void sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s, wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0); wpabuf_free(resp); wpabuf_free(buf); + + return 0; } @@ -965,15 +969,15 @@ static void sme_send_external_auth_status(struct wpa_supplicant *wpa_s, os_memset(¶ms, 0, sizeof(params)); params.status = status; - params.ssid = wpa_s->sme.ext_auth.ssid; - params.ssid_len = wpa_s->sme.ext_auth.ssid_len; - params.bssid = wpa_s->sme.ext_auth.bssid; + params.ssid = wpa_s->sme.ext_auth_ssid; + params.ssid_len = wpa_s->sme.ext_auth_ssid_len; + params.bssid = wpa_s->sme.ext_auth_bssid; wpa_drv_send_external_auth_status(wpa_s, ¶ms); } -static void sme_handle_external_auth_start(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) +static int sme_handle_external_auth_start(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) { struct wpa_ssid *ssid; size_t ssid_str_len = data->external_auth.ssid_len; @@ -987,13 +991,12 @@ static void sme_handle_external_auth_start(struct wpa_supplicant *wpa_s, (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE))) break; } - if (ssid) - sme_external_auth_send_sae_commit(wpa_s, - data->external_auth.bssid, - ssid); - else - sme_send_external_auth_status(wpa_s, - WLAN_STATUS_UNSPECIFIED_FAILURE); + if (!ssid || + sme_external_auth_send_sae_commit(wpa_s, data->external_auth.bssid, + ssid) < 0) + return -1; + + return 0; } @@ -1032,13 +1035,20 @@ void sme_external_auth_trigger(struct wpa_supplicant *wpa_s, return; if (data->external_auth.action == EXT_AUTH_START) { - os_memcpy(&wpa_s->sme.ext_auth, data, - sizeof(struct external_auth)); + if (!data->external_auth.bssid || !data->external_auth.ssid) + return; + os_memcpy(wpa_s->sme.ext_auth_bssid, data->external_auth.bssid, + ETH_ALEN); + os_memcpy(wpa_s->sme.ext_auth_ssid, data->external_auth.ssid, + data->external_auth.ssid_len); + wpa_s->sme.ext_auth_ssid_len = data->external_auth.ssid_len; wpa_s->sme.seq_num = 0; wpa_s->sme.sae.state = SAE_NOTHING; wpa_s->sme.sae.send_confirm = 0; wpa_s->sme.sae_group_index = 0; - sme_handle_external_auth_start(wpa_s, data); + if (sme_handle_external_auth_start(wpa_s, data) < 0) + sme_send_external_auth_status(wpa_s, + WLAN_STATUS_UNSPECIFIED_FAILURE); } else if (data->external_auth.action == EXT_AUTH_ABORT) { /* Report failure to driver for the wrong trigger */ sme_send_external_auth_status(wpa_s, @@ -1091,7 +1101,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, wpa_s->current_ssid, 2); else sme_external_auth_send_sae_commit( - wpa_s, wpa_s->sme.ext_auth.bssid, + wpa_s, wpa_s->sme.ext_auth_bssid, wpa_s->current_ssid); return 0; } @@ -1110,7 +1120,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, wpa_s->current_ssid, 1); else sme_external_auth_send_sae_commit( - wpa_s, wpa_s->sme.ext_auth.bssid, + wpa_s, wpa_s->sme.ext_auth_bssid, wpa_s->current_ssid); return 0; } @@ -2236,7 +2246,7 @@ void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable) */ if (!((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OBSS_SCAN)) || - ssid == NULL || ssid->mode != IEEE80211_MODE_INFRA) + ssid == NULL || ssid->mode != WPAS_MODE_INFRA) return; if (!wpa_s->hw.modes) diff --git a/contrib/wpa/wpa_supplicant/wnm_sta.c b/contrib/wpa/wpa_supplicant/wnm_sta.c index 22a21361ad8a..c14764963b78 100644 --- a/contrib/wpa/wpa_supplicant/wnm_sta.c +++ b/contrib/wpa/wpa_supplicant/wnm_sta.c @@ -451,6 +451,11 @@ static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, rep->preference_present = 1; break; case WNM_NEIGHBOR_BSS_TERMINATION_DURATION: + if (elen < 10) { + wpa_printf(MSG_DEBUG, + "WNM: Too short BSS termination duration"); + break; + } rep->bss_term_tsf = WPA_GET_LE64(pos); rep->bss_term_dur = WPA_GET_LE16(pos + 8); rep->bss_term_present = 1; @@ -920,9 +925,9 @@ static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, if (ie && ie[1] >= 1) { vht_oper = (struct ieee80211_vht_operation *) (ie + 2); - if (vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80MHZ || - vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_160MHZ || - vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80P80MHZ) + if (vht_oper->vht_op_info_chwidth == CHANWIDTH_80MHZ || + vht_oper->vht_op_info_chwidth == CHANWIDTH_160MHZ || + vht_oper->vht_op_info_chwidth == CHANWIDTH_80P80MHZ) vht = vht_oper->vht_op_info_chwidth; } @@ -1366,6 +1371,9 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, const u8 *vendor; #endif /* CONFIG_MBO */ + if (wpa_s->conf->disable_btm) + return; + if (end - pos < 5) return; diff --git a/contrib/wpa/wpa_supplicant/wpa_cli.c b/contrib/wpa/wpa_supplicant/wpa_cli.c index 695fcbe04995..43ac427203ec 100644 --- a/contrib/wpa/wpa_supplicant/wpa_cli.c +++ b/contrib/wpa/wpa_supplicant/wpa_cli.c @@ -2064,6 +2064,13 @@ static int wpa_cli_cmd_mesh_peer_add(struct wpa_ctrl *ctrl, int argc, return wpa_cli_cmd(ctrl, "MESH_PEER_ADD", 1, argc, argv); } + +static int wpa_cli_cmd_mesh_link_probe(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MESH_LINK_PROBE", 1, argc, argv); +} + #endif /* CONFIG_MESH */ @@ -3384,6 +3391,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "mesh_peer_add", wpa_cli_cmd_mesh_peer_add, NULL, cli_cmd_flag_none, " [duration=] = Add a mesh peer" }, + { "mesh_link_probe", wpa_cli_cmd_mesh_link_probe, NULL, + cli_cmd_flag_none, + " [payload=] = Probe a mesh link for a given peer by injecting a frame." }, #endif /* CONFIG_MESH */ #ifdef CONFIG_P2P { "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find, @@ -3967,6 +3977,8 @@ static void wpa_cli_action_process(const char *msg) wpa_cli_connected = 0; wpa_cli_exec(action_file, ifname, "DISCONNECTED"); } + } else if (str_starts(pos, WPA_EVENT_CHANNEL_SWITCH_STARTED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); } else if (str_starts(pos, AP_EVENT_ENABLED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); } else if (str_starts(pos, AP_EVENT_DISABLED)) { @@ -4009,6 +4021,22 @@ static void wpa_cli_action_process(const char *msg) wpa_cli_exec(action_file, ifname, pos); } else if (str_starts(pos, HS20_T_C_ACCEPTANCE)) { wpa_cli_exec(action_file, ifname, pos); + } else if (str_starts(pos, DPP_EVENT_CONF_RECEIVED)) { + wpa_cli_exec(action_file, ifname, pos); + } else if (str_starts(pos, DPP_EVENT_CONFOBJ_AKM)) { + wpa_cli_exec(action_file, ifname, pos); + } else if (str_starts(pos, DPP_EVENT_CONFOBJ_SSID)) { + wpa_cli_exec(action_file, ifname, pos); + } else if (str_starts(pos, DPP_EVENT_CONNECTOR)) { + wpa_cli_exec(action_file, ifname, pos); + } else if (str_starts(pos, DPP_EVENT_CONFOBJ_PASS)) { + wpa_cli_exec(action_file, ifname, pos); + } else if (str_starts(pos, DPP_EVENT_CONFOBJ_PSK)) { + wpa_cli_exec(action_file, ifname, pos); + } else if (str_starts(pos, DPP_EVENT_C_SIGN_KEY)) { + wpa_cli_exec(action_file, ifname, pos); + } else if (str_starts(pos, DPP_EVENT_NET_ACCESS_KEY)) { + wpa_cli_exec(action_file, ifname, pos); } else if (str_starts(pos, WPA_EVENT_TERMINATING)) { printf("wpa_supplicant is terminating - stop monitoring\n"); wpa_cli_quit = 1; diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.c b/contrib/wpa/wpa_supplicant/wpa_supplicant.c index 85769870c142..31b1bc8e5c80 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.c @@ -36,6 +36,7 @@ #include "rsn_supp/preauth.h" #include "rsn_supp/pmksa_cache.h" #include "common/wpa_ctrl.h" +#include "common/ieee802_11_common.h" #include "common/ieee802_11_defs.h" #include "common/hw_features_common.h" #include "common/gas_server.h" @@ -1416,9 +1417,10 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X-SHA384"); - if (pmksa_cache_get_current(wpa_s->wpa)) { - /* PMKSA caching with FT is not fully functional, so - * disable the case for now. */ + if (!ssid->ft_eap_pmksa_caching && + pmksa_cache_get_current(wpa_s->wpa)) { + /* PMKSA caching with FT may have interoperability + * issues, so disable that case by default for now. */ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Disable PMKSA caching for FT/802.1X connection"); pmksa_cache_clear_current(wpa_s->wpa); @@ -1457,9 +1459,10 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X"); - if (pmksa_cache_get_current(wpa_s->wpa)) { - /* PMKSA caching with FT is not fully functional, so - * disable the case for now. */ + if (!ssid->ft_eap_pmksa_caching && + pmksa_cache_get_current(wpa_s->wpa)) { + /* PMKSA caching with FT may have interoperability + * issues, so disable that case by default for now. */ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Disable PMKSA caching for FT/802.1X connection"); pmksa_cache_clear_current(wpa_s->wpa); @@ -1708,7 +1711,8 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) case 2: /* Bits 16-23 */ #ifdef CONFIG_WNM *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ - *pos |= 0x08; /* Bit 19 - BSS Transition */ + if (!wpa_s->conf->disable_btm) + *pos |= 0x08; /* Bit 19 - BSS Transition */ #endif /* CONFIG_WNM */ break; case 3: /* Bits 24-31 */ @@ -2062,7 +2066,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, #endif /* CONFIG_TDLS */ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && - ssid->mode == IEEE80211_MODE_INFRA) { + ssid->mode == WPAS_MODE_INFRA) { sme_authenticate(wpa_s, bss, ssid); return; } @@ -2136,6 +2140,7 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, struct hostapd_freq_params *freq) { + int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode); enum hostapd_hw_mode hw_mode; struct hostapd_hw_modes *mode = NULL; int ht40plus[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, @@ -2199,6 +2204,9 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, if (!mode) return; + /* HE can work without HT + VHT */ + freq->he_enabled = mode->he_capab[ieee80211_mode].he_supported; + #ifdef CONFIG_HT_OVERRIDES if (ssid->disable_ht) { freq->ht_enabled = 0; @@ -2352,11 +2360,11 @@ skip_ht40: return; } - chwidth = VHT_CHANWIDTH_80MHZ; + chwidth = CHANWIDTH_80MHZ; seg0 = vht80[j] + 6; seg1 = 0; - if (ssid->max_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) { + if (ssid->max_oper_chwidth == CHANWIDTH_80P80MHZ) { /* setup center_freq2, bandwidth */ for (k = 0; k < ARRAY_SIZE(vht80); k++) { /* Only accept 80 MHz segments separated by a gap */ @@ -2375,27 +2383,27 @@ skip_ht40: continue; /* Found a suitable second segment for 80+80 */ - chwidth = VHT_CHANWIDTH_80P80MHZ; + chwidth = CHANWIDTH_80P80MHZ; vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; seg1 = vht80[k] + 6; } - if (chwidth == VHT_CHANWIDTH_80P80MHZ) + if (chwidth == CHANWIDTH_80P80MHZ) break; } - } else if (ssid->max_oper_chwidth == VHT_CHANWIDTH_160MHZ) { + } else if (ssid->max_oper_chwidth == CHANWIDTH_160MHZ) { if (freq->freq == 5180) { - chwidth = VHT_CHANWIDTH_160MHZ; + chwidth = CHANWIDTH_160MHZ; vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; seg0 = 50; } else if (freq->freq == 5520) { - chwidth = VHT_CHANWIDTH_160MHZ; + chwidth = CHANWIDTH_160MHZ; vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; seg0 = 114; } - } else if (ssid->max_oper_chwidth == VHT_CHANWIDTH_USE_HT) { - chwidth = VHT_CHANWIDTH_USE_HT; + } else if (ssid->max_oper_chwidth == CHANWIDTH_USE_HT) { + chwidth = CHANWIDTH_USE_HT; seg0 = vht80[j] + 2; #ifdef CONFIG_HT_OVERRIDES if (ssid->disable_ht40) @@ -2405,9 +2413,10 @@ skip_ht40: if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq, freq->channel, freq->ht_enabled, - vht_freq.vht_enabled, + vht_freq.vht_enabled, freq->he_enabled, freq->sec_channel_offset, - chwidth, seg0, seg1, vht_caps) != 0) + chwidth, seg0, seg1, vht_caps, + &mode->he_capab[ieee80211_mode]) != 0) return; *freq = vht_freq; @@ -3220,7 +3229,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B || params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)) - params.req_key_mgmt_offload = 1; + params.req_handshake_offload = 1; if (wpa_s->conf->key_mgmt_offload) { if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || @@ -3421,16 +3430,17 @@ static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s, * current AP. */ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, - int reason_code) + u16 reason_code) { u8 *addr = NULL; union wpa_event_data event; int zero_addr = 0; wpa_dbg(wpa_s, MSG_DEBUG, "Request to deauthenticate - bssid=" MACSTR - " pending_bssid=" MACSTR " reason=%d state=%s", + " pending_bssid=" MACSTR " reason=%d (%s) state=%s", MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid), - reason_code, wpa_supplicant_state_txt(wpa_s->wpa_state)); + reason_code, reason2str(reason_code), + wpa_supplicant_state_txt(wpa_s->wpa_state)); if (!is_zero_ether_addr(wpa_s->pending_bssid) && (wpa_s->wpa_state == WPA_AUTHENTICATING || @@ -3472,7 +3482,7 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, if (addr) { wpa_drv_deauthenticate(wpa_s, addr, reason_code); os_memset(&event, 0, sizeof(event)); - event.deauth_info.reason_code = (u16) reason_code; + event.deauth_info.reason_code = reason_code; event.deauth_info.locally_generated = 1; wpa_supplicant_event(wpa_s, EVENT_DEAUTH, &event); if (zero_addr) @@ -4227,7 +4237,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || wpa_s->wpa_state != WPA_COMPLETED) && (wpa_s->current_ssid == NULL || - wpa_s->current_ssid->mode != IEEE80211_MODE_IBSS)) { + wpa_s->current_ssid->mode != WPAS_MODE_IBSS)) { /* Timeout for completing IEEE 802.1X and WPA authentication */ int timeout = 10; @@ -6619,6 +6629,9 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) wpa_s->conf->wowlan_triggers); } + if (wpa_s->conf->changed_parameters & CFG_CHANGED_DISABLE_BTM) + wpa_supplicant_set_default_scan_ies(wpa_s); + #ifdef CONFIG_WPS wpas_wps_update_config(wpa_s); #endif /* CONFIG_WPS */ @@ -7466,3 +7479,66 @@ int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, return 1; } + + +int wpas_enable_mac_addr_randomization(struct wpa_supplicant *wpa_s, + unsigned int type, const u8 *addr, + const u8 *mask) +{ + if ((addr && !mask) || (!addr && mask)) { + wpa_printf(MSG_INFO, + "MAC_ADDR_RAND_SCAN invalid addr/mask combination"); + return -1; + } + + if (addr && mask && (!(mask[0] & 0x01) || (addr[0] & 0x01))) { + wpa_printf(MSG_INFO, + "MAC_ADDR_RAND_SCAN cannot allow multicast address"); + return -1; + } + + if (type & MAC_ADDR_RAND_SCAN) { + if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN, + addr, mask)) + return -1; + } + + if (type & MAC_ADDR_RAND_SCHED_SCAN) { + if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN, + addr, mask)) + return -1; + + if (wpa_s->sched_scanning && !wpa_s->pno) + wpas_scan_restart_sched_scan(wpa_s); + } + + if (type & MAC_ADDR_RAND_PNO) { + if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO, + addr, mask)) + return -1; + + if (wpa_s->pno) { + wpas_stop_pno(wpa_s); + wpas_start_pno(wpa_s); + } + } + + return 0; +} + + +int wpas_disable_mac_addr_randomization(struct wpa_supplicant *wpa_s, + unsigned int type) +{ + wpas_mac_addr_rand_scan_clear(wpa_s, type); + if (wpa_s->pno) { + if (type & MAC_ADDR_RAND_PNO) { + wpas_stop_pno(wpa_s); + wpas_start_pno(wpa_s); + } + } else if (wpa_s->sched_scanning && (type & MAC_ADDR_RAND_SCHED_SCAN)) { + wpas_scan_restart_sched_scan(wpa_s); + } + + return 0; +} diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf index f5c2cfcdb63c..f12b7b6c8ee9 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf @@ -1046,6 +1046,14 @@ fast_reauth=1 # 0 = disabled (default unless changed with the global okc parameter) # 1 = enabled # +# ft_eap_pmksa_caching: +# Whether FT-EAP PMKSA caching is allowed +# 0 = do not try to use PMKSA caching with FT-EAP (default) +# 1 = try to use PMKSA caching with FT-EAP +# This controls whether to try to use PMKSA caching with FT-EAP for the +# FT initial mobility domain association. +#ft_eap_pmksa_caching=0 +# # wep_key0..3: Static WEP key (ASCII in double quotation, e.g. "abcde" or # hex without quotation, e.g., 0102030405) # wep_tx_keyidx: Default WEP key index (TX) (0..3) @@ -1496,6 +1504,12 @@ fast_reauth=1 # Transitioning between states). #fst_llt=100 +# BSS Transition Management +# disable_btm - Disable BSS transition management in STA +# Set to 0 to enable BSS transition management (default behavior) +# Set to 1 to disable BSS transition management +#disable_btm=0 + # Example blocks: # Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h index 16e4db62aeef..8a4bdf8cbc33 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h @@ -802,7 +802,9 @@ struct wpa_supplicant { int sae_group_index; unsigned int sae_pmksa_caching:1; u16 seq_num; - struct external_auth ext_auth; + u8 ext_auth_bssid[ETH_ALEN]; + u8 ext_auth_ssid[SSID_MAX_LEN]; + size_t ext_auth_ssid_len; #endif /* CONFIG_SAE */ } sme; #endif /* CONFIG_SME */ @@ -821,6 +823,7 @@ struct wpa_supplicant { unsigned int mesh_if_created:1; unsigned int mesh_ht_enabled:1; unsigned int mesh_vht_enabled:1; + unsigned int mesh_he_enabled:1; struct wpa_driver_mesh_join_params *mesh_params; #ifdef CONFIG_PMKSA_CACHE_EXTERNAL /* struct external_pmksa_cache::list */ @@ -1291,7 +1294,7 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s); const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s); void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s); void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, - int reason_code); + u16 reason_code); struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s); int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id); @@ -1415,6 +1418,12 @@ size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int freq, u8 *pos, size_t len); +int wpas_enable_mac_addr_randomization(struct wpa_supplicant *wpa_s, + unsigned int type, const u8 *addr, + const u8 *mask); +int wpas_disable_mac_addr_randomization(struct wpa_supplicant *wpa_s, + unsigned int type); + /** * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response * @wpa_s: Pointer to wpa_supplicant data @@ -1462,6 +1471,25 @@ static inline int network_is_persistent_group(struct wpa_ssid *ssid) return ssid->disabled == 2 && ssid->p2p_persistent_group; } + +static inline int wpas_mode_to_ieee80211_mode(enum wpas_mode mode) +{ + switch (mode) { + default: + case WPAS_MODE_INFRA: + return IEEE80211_MODE_INFRA; + case WPAS_MODE_IBSS: + return IEEE80211_MODE_IBSS; + case WPAS_MODE_AP: + case WPAS_MODE_P2P_GO: + case WPAS_MODE_P2P_GROUP_FORMATION: + return IEEE80211_MODE_AP; + case WPAS_MODE_MESH: + return IEEE80211_MODE_MESH; + } +} + + 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); diff --git a/contrib/wpa/wpa_supplicant/wpas_glue.c b/contrib/wpa/wpa_supplicant/wpas_glue.c index 449e04acded8..62af7f6b1013 100644 --- a/contrib/wpa/wpa_supplicant/wpas_glue.c +++ b/contrib/wpa/wpa_supplicant/wpas_glue.c @@ -464,7 +464,7 @@ static enum wpa_states _wpa_supplicant_get_state(void *wpa_s) } -static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code) +static void _wpa_supplicant_deauthenticate(void *wpa_s, u16 reason_code) { wpa_supplicant_deauthenticate(wpa_s, reason_code); /* Schedule a scan to make sure we continue looking for networks */ @@ -1017,15 +1017,12 @@ static void wpa_supplicant_port_cb(void *ctx, int authorized) } -static void wpa_supplicant_cert_cb(void *ctx, int depth, const char *subject, - const char *altsubject[], int num_altsubject, - const char *cert_hash, - const struct wpabuf *cert) +static void wpa_supplicant_cert_cb(void *ctx, struct tls_cert_data *cert, + const char *cert_hash) { struct wpa_supplicant *wpa_s = ctx; - wpas_notify_certification(wpa_s, depth, subject, altsubject, - num_altsubject, cert_hash, cert); + wpas_notify_certification(wpa_s, cert, cert_hash); }