diff options
69 files changed, 2405 insertions, 130 deletions
diff --git a/hostapd/Android.mk b/hostapd/Android.mk index 695e99c6..f235e70d 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -831,6 +831,7 @@ OBJS += src/ap/wmm.c OBJS += src/ap/ap_list.c OBJS += src/ap/ieee802_11.c OBJS += src/ap/hw_features.c +OBJS += src/ap/dfs.c L_CFLAGS += -DNEED_AP_MLME endif ifdef CONFIG_IEEE80211N diff --git a/hostapd/Makefile b/hostapd/Makefile index fda4e4e3..c56d3687 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -782,6 +782,7 @@ OBJS += ../src/ap/wmm.o OBJS += ../src/ap/ap_list.o OBJS += ../src/ap/ieee802_11.o OBJS += ../src/ap/hw_features.o +OBJS += ../src/ap/dfs.o CFLAGS += -DNEED_AP_MLME endif ifdef CONFIG_IEEE80211N diff --git a/hostapd/config_file.c b/hostapd/config_file.c index dc68fc8b..fdd7504d 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -1561,6 +1561,47 @@ fail: return -1; } + +static int parse_qos_map_set(struct hostapd_bss_config *bss, + char *buf, int line) +{ + u8 qos_map_set[16 + 2 * 21], count = 0; + char *pos = buf; + int val; + + for (;;) { + if (count == sizeof(qos_map_set)) { + wpa_printf(MSG_ERROR, "Line %d: Too many qos_map_set " + "parameters '%s'", line, buf); + return -1; + } + + val = atoi(pos); + if (val > 255 || val < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set " + "'%s'", line, buf); + return -1; + } + + qos_map_set[count++] = val; + pos = os_strchr(pos, ','); + if (!pos) + break; + pos++; + } + + if (count < 16 || count & 1) { + wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set '%s'", + line, buf); + return -1; + } + + os_memcpy(bss->qos_map_set, qos_map_set, count); + bss->qos_map_set_len = count; + + return 0; +} + #endif /* CONFIG_INTERWORKING */ @@ -2886,6 +2927,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->gas_frag_limit = atoi(pos); } else if (os_strcmp(buf, "gas_comeback_delay") == 0) { bss->gas_comeback_delay = atoi(pos); + } else if (os_strcmp(buf, "qos_map_set") == 0) { + if (parse_qos_map_set(bss, pos, line) < 0) + errors++; #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_RADIUS_TEST } else if (os_strcmp(buf, "dump_msk_file") == 0) { @@ -2958,6 +3002,25 @@ static int hostapd_config_fill(struct hostapd_config *conf, PARSE_TEST_PROBABILITY(ignore_assoc_probability) PARSE_TEST_PROBABILITY(ignore_reassoc_probability) PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability) + } else if (os_strcmp(buf, "bss_load_test") == 0) { + WPA_PUT_LE16(bss->bss_load_test, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "bss_load_test", line); + return 1; + } + pos++; + bss->bss_load_test[2] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "bss_load_test", line); + return 1; + } + pos++; + WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos)); + bss->bss_load_test_set = 1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strcmp(buf, "vendor_elements") == 0) { struct wpabuf *elems; diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index be941c44..0d899925 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -559,6 +559,106 @@ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd, #endif /* CONFIG_WPS */ +#ifdef CONFIG_INTERWORKING + +static int hostapd_ctrl_iface_set_qos_map_set(struct hostapd_data *hapd, + const char *cmd) +{ + u8 qos_map_set[16 + 2 * 21], count = 0; + const char *pos = cmd; + int val, ret; + + for (;;) { + if (count == sizeof(qos_map_set)) { + wpa_printf(MSG_ERROR, "Too many qos_map_set parameters"); + return -1; + } + + val = atoi(pos); + if (val < 0 || val > 255) { + wpa_printf(MSG_INFO, "Invalid QoS Map Set"); + return -1; + } + + qos_map_set[count++] = val; + pos = os_strchr(pos, ','); + if (!pos) + break; + pos++; + } + + if (count < 16 || count & 1) { + wpa_printf(MSG_INFO, "Invalid QoS Map Set"); + return -1; + } + + ret = hostapd_drv_set_qos_map(hapd, qos_map_set, count); + if (ret) { + wpa_printf(MSG_INFO, "Failed to set QoS Map Set"); + return -1; + } + + os_memcpy(hapd->conf->qos_map_set, qos_map_set, count); + hapd->conf->qos_map_set_len = count; + + return 0; +} + + +static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + struct wpabuf *buf; + u8 *qos_map_set = hapd->conf->qos_map_set; + u8 qos_map_set_len = hapd->conf->qos_map_set_len; + int ret; + + if (!qos_map_set_len) { + wpa_printf(MSG_INFO, "QoS Map Set is not set"); + return -1; + } + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Station " MACSTR " not found " + "for QoS Map Configuration message", + MAC2STR(addr)); + return -1; + } + + if (!sta->qos_map_enabled) { + wpa_printf(MSG_DEBUG, "Station " MACSTR " did not indicate " + "support for QoS Map", MAC2STR(addr)); + return -1; + } + + buf = wpabuf_alloc(2 + 2 + qos_map_set_len); + if (buf == NULL) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_QOS); + wpabuf_put_u8(buf, QOS_QOS_MAP_CONFIG); + + /* QoS Map Set Element */ + wpabuf_put_u8(buf, WLAN_EID_QOS_MAP_SET); + wpabuf_put_u8(buf, qos_map_set_len); + wpabuf_put_data(buf, qos_map_set, qos_map_set_len); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + + return ret; +} + +#endif /* CONFIG_INTERWORKING */ + + #ifdef CONFIG_WNM static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd, @@ -643,6 +743,7 @@ static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd, mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; mgmt->u.action.u.bss_tm_req.dialog_token = 1; mgmt->u.action.u.bss_tm_req.req_mode = + WNM_BSS_TM_REQ_DISASSOC_IMMINENT | WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT; mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(disassoc_timer); @@ -1093,6 +1194,14 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, reply_len = -1; #endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS */ +#ifdef CONFIG_INTERWORKING + } else if (os_strncmp(buf, "SET_QOS_MAP_SET ", 16) == 0) { + if (hostapd_ctrl_iface_set_qos_map_set(hapd, buf + 16)) + reply_len = -1; + } else if (os_strncmp(buf, "SEND_QOS_MAP_CONF ", 18) == 0) { + if (hostapd_ctrl_iface_send_qos_map_conf(hapd, buf + 18)) + reply_len = -1; +#endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_WNM } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) { if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18)) diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c index 2e61ac39..c041887e 100644 --- a/hostapd/hlr_auc_gw.c +++ b/hostapd/hlr_auc_gw.c @@ -18,6 +18,9 @@ * SIM-REQ-AUTH <IMSI> <max_chal> * SIM-RESP-AUTH <IMSI> Kc1:SRES1:RAND1 Kc2:SRES2:RAND2 [Kc3:SRES3:RAND3] * SIM-RESP-AUTH <IMSI> FAILURE + * GSM-AUTH-REQ <IMSI> RAND1:RAND2[:RAND3] + * GSM-AUTH-RESP <IMSI> Kc1:SRES1:Kc2:SRES2[:Kc3:SRES3] + * GSM-AUTH-RESP <IMSI> FAILURE * * EAP-AKA / UMTS query/response: * AKA-REQ-AUTH <IMSI> @@ -692,6 +695,56 @@ static int sim_req_auth(char *imsi, char *resp, size_t resp_len) } +static int gsm_auth_req(char *imsi, char *resp, size_t resp_len) +{ + int count, ret; + char *pos, *rpos, *rend; + struct milenage_parameters *m; + + resp[0] = '\0'; + + pos = os_strchr(imsi, ' '); + if (!pos) + return -1; + *pos++ = '\0'; + + rend = resp + resp_len; + rpos = resp; + ret = os_snprintf(rpos, rend - rpos, "GSM-AUTH-RESP %s", imsi); + if (ret < 0 || ret >= rend - rpos) + return -1; + rpos += ret; + + m = get_milenage(imsi); + if (m) { + u8 _rand[16], sres[4], kc[8]; + for (count = 0; count < EAP_SIM_MAX_CHAL; count++) { + if (hexstr2bin(pos, _rand, 16) != 0) + return -1; + gsm_milenage(m->opc, m->ki, _rand, sres, kc); + *rpos++ = count == 0 ? ' ' : ':'; + rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8); + *rpos++ = ':'; + rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4); + pos += 16 * 2; + if (*pos != ':') + break; + pos++; + } + *rpos = '\0'; + return 0; + } + + printf("No GSM triplets found for %s\n", imsi); + ret = os_snprintf(rpos, rend - rpos, " FAILURE"); + if (ret < 0 || ret >= rend - rpos) + return -1; + rpos += ret; + + return 0; +} + + static void inc_sqn(u8 *sqn) { u64 val, seq, ind; @@ -847,6 +900,9 @@ static int process_cmd(char *cmd, char *resp, size_t resp_len) if (os_strncmp(cmd, "SIM-REQ-AUTH ", 13) == 0) return sim_req_auth(cmd + 13, resp, resp_len); + if (os_strncmp(cmd, "GSM-AUTH-REQ ", 13) == 0) + return gsm_auth_req(cmd + 13, resp, resp_len); + if (os_strncmp(cmd, "AKA-REQ-AUTH ", 13) == 0) return aka_req_auth(cmd + 13, resp, resp_len); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index de1bf75f..eeb00551 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -416,6 +416,12 @@ wmm_ac_vo_acm=0 # associated stations in the BSS. By default, this bridging is allowed. #ap_isolate=1 +# Fixed BSS Load value for testing purposes +# This field can be used to configure hostapd to add a fixed BSS Load element +# into Beacon and Probe Response frames for testing purposes. The format is +# <station count>:<channel utilization>:<available admission capacity> +#bss_load_test=12:80:20000 + ##### IEEE 802.11n related configuration ###################################### # ieee80211n: Whether IEEE 802.11n (HT) is enabled @@ -1524,6 +1530,23 @@ own_ip_addr=127.0.0.1 # username/password #nai_realm=0,example.org,13[5:6],21[2:4][5:7] +# QoS Map Set configuration +# +# Comma delimited QoS Map Set in decimal values +# (see IEEE Std 802.11-2012, 8.4.2.97) +# +# format: +# [<DSCP Exceptions[DSCP,UP]>,]<UP 0 range[low,high]>,...<UP 7 range[low,high]> +# +# There can be up to 21 optional DSCP Exceptions which are pairs of DSCP Value +# (0..63 or 255) and User Priority (0..7). This is followed by eight DSCP Range +# descriptions with DSCP Low Value and DSCP High Value pairs (0..63 or 255) for +# each UP starting from 0. If both low and high value are set to 255, the +# corresponding UP is not used. +# +# default: not set +#qos_map_set=53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,255,255 + ##### Hotspot 2.0 ############################################################# # Enable Hotspot 2.0 support diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index 7187abcf..709b86c0 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -688,6 +688,45 @@ static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char buf[200]; + int res; + + if (argc != 1) { + printf("Invalid 'set_qos_map_set' command - " + "one argument (comma delimited QoS map set) " + "is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]); + if (res < 0 || res >= (int) sizeof(buf)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char buf[50]; + int res; + + if (argc != 1) { + printf("Invalid 'send_qos_map_conf' command - " + "one argument (STA addr) is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]); + if (res < 0 || res >= (int) sizeof(buf)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) { hostapd_cli_quit = 1; @@ -843,6 +882,8 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = { { "quit", hostapd_cli_cmd_quit }, { "set", hostapd_cli_cmd_set }, { "get", hostapd_cli_cmd_get }, + { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set }, + { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf }, { NULL, NULL } }; diff --git a/hostapd/main.c b/hostapd/main.c index 90e59665..6a673479 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -282,6 +282,15 @@ static int hostapd_driver_init(struct hostapd_iface *iface) iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs; } +#ifdef CONFIG_INTERWORKING + if (hapd->driver->set_qos_map && conf->qos_map_set_len && + hapd->driver->set_qos_map(hapd->drv_priv, conf->qos_map_set, + conf->qos_map_set_len)) { + wpa_printf(MSG_ERROR, "Failed to initialize QoS Map."); + return -1; + } +#endif /* CONFIG_INTERWORKING */ + return 0; } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index c5531faa..4f6a7390 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -442,6 +442,9 @@ struct hostapd_bss_config { u16 gas_comeback_delay; int gas_frag_limit; + u8 qos_map_set[16 + 2 * 21]; + unsigned int qos_map_set_len; + #ifdef CONFIG_HS20 int hs20; int disable_dgaf; @@ -464,6 +467,11 @@ struct hostapd_bss_config { unsigned int sae_anti_clogging_threshold; int *sae_groups; + +#ifdef CONFIG_TESTING_OPTIONS + u8 bss_load_test[5]; + u8 bss_load_test_set; +#endif /* CONFIG_TESTING_OPTIONS */ }; diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index 3072562e..9023eaba 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -463,44 +463,44 @@ int hostapd_flush(struct hostapd_data *hapd) } -int hostapd_set_freq(struct hostapd_data *hapd, int 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) +static int hostapd_set_freq_params(struct hostapd_freq_params *data, int 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) { - struct hostapd_freq_params data; int tmp; - 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.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; + 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->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; /* * This validation code is probably misplaced, maybe it should be * in src/ap/hw_features.c and check the hardware support as well. */ - if (data.vht_enabled) switch (vht_oper_chwidth) { + if (data->vht_enabled) switch (vht_oper_chwidth) { case VHT_CHANWIDTH_USE_HT: if (center_segment1) return -1; - if (5000 + center_segment0 * 5 != data.center_freq1) + if (5000 + center_segment0 * 5 != data->center_freq1) return -1; break; case VHT_CHANWIDTH_80P80MHZ: if (center_segment1 == center_segment0 + 4 || center_segment1 == center_segment0 - 4) return -1; - data.center_freq2 = 5000 + center_segment1 * 5; + data->center_freq2 = 5000 + center_segment1 * 5; /* fall through */ case VHT_CHANWIDTH_80MHZ: - data.bandwidth = 80; + data->bandwidth = 80; if (vht_oper_chwidth == 1 && center_segment1) return -1; if (vht_oper_chwidth == 3 && !center_segment1) @@ -510,13 +510,13 @@ int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, /* primary 40 part must match the HT configuration */ tmp = (30 + freq - 5000 - center_segment0 * 5)/20; tmp /= 2; - if (data.center_freq1 != 5000 + + if (data->center_freq1 != 5000 + center_segment0 * 5 - 20 + 40 * tmp) return -1; - data.center_freq1 = 5000 + center_segment0 * 5; + data->center_freq1 = 5000 + center_segment0 * 5; break; case VHT_CHANWIDTH_160MHZ: - data.bandwidth = 160; + data->bandwidth = 160; if (center_segment1) return -1; if (!sec_channel_offset) @@ -524,12 +524,30 @@ int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, /* primary 40 part must match the HT configuration */ tmp = (70 + freq - 5000 - center_segment0 * 5)/20; tmp /= 2; - if (data.center_freq1 != 5000 + + if (data->center_freq1 != 5000 + center_segment0 * 5 - 60 + 40 * tmp) return -1; - data.center_freq1 = 5000 + center_segment0 * 5; + data->center_freq1 = 5000 + center_segment0 * 5; break; } + + return 0; +} + + +int hostapd_set_freq(struct hostapd_data *hapd, int 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) +{ + struct hostapd_freq_params data; + + if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled, + vht_enabled, sec_channel_offset, + vht_oper_chwidth, + center_segment0, center_segment1)) + return -1; + if (hapd->driver == NULL) return 0; if (hapd->driver->set_freq == NULL) @@ -699,3 +717,38 @@ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, hapd->own_addr, hapd->own_addr, data, len, 0); } + +int hostapd_start_dfs_cac(struct hostapd_data *hapd, int 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) +{ + struct hostapd_freq_params data; + + if (!hapd->driver || !hapd->driver->start_dfs_cac) + return 0; + + if (!hapd->iface->conf->ieee80211h) { + wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality " + "is not enabled"); + return -1; + } + + if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled, + vht_enabled, sec_channel_offset, + vht_oper_chwidth, center_segment0, + center_segment1)) + return -1; + + return hapd->driver->start_dfs_cac(hapd->drv_priv, &data); +} + + +int hostapd_drv_set_qos_map(struct hostapd_data *hapd, + const u8 *qos_map_set, u8 qos_map_set_len) +{ + if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL) + return 0; + return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set, + qos_map_set_len); +} diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 23277b6b..8a2b4dc7 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -101,6 +101,10 @@ int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, int reassoc, u16 status, const u8 *ie, size_t len); int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, u8 *tspec_ie, size_t tspec_ielen); +int hostapd_start_dfs_cac(struct hostapd_data *hapd, int 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); #include "drivers/driver.h" @@ -109,6 +113,9 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper, const u8 *peer, u8 *buf, u16 *buf_len); +int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, + u8 qos_map_set_len); + static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd, int enabled) { diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 2f4ba23a..360001fc 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -38,6 +38,22 @@ #ifdef NEED_AP_MLME +static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len) +{ +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->bss_load_test_set) { + if (2 + 5 > len) + return eid; + *eid++ = WLAN_EID_BSS_LOAD; + *eid++ = 5; + os_memcpy(eid, hapd->conf->bss_load_test, 5); + eid += 5; + } +#endif /* CONFIG_TESTING_OPTIONS */ + return eid; +} + + static u8 ieee802_11_erp_info(struct hostapd_data *hapd) { u8 erp = 0; @@ -251,6 +267,8 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, /* RSN, MDIE, WPA */ pos = hostapd_eid_wpa(hapd, pos, epos - pos); + pos = hostapd_eid_bss_load(hapd, pos, epos - pos); + #ifdef CONFIG_IEEE80211N pos = hostapd_eid_ht_capabilities(hapd, pos); pos = hostapd_eid_ht_operation(hapd, pos); @@ -662,6 +680,9 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); + tailpos = hostapd_eid_bss_load(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - tailpos); + #ifdef CONFIG_IEEE80211N tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); tailpos = hostapd_eid_ht_operation(hapd, tailpos); diff --git a/src/ap/dfs.c b/src/ap/dfs.c new file mode 100644 index 00000000..37bbd203 --- /dev/null +++ b/src/ap/dfs.c @@ -0,0 +1,601 @@ +/* + * DFS - Dynamic Frequency Selection + * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * 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 "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "ap_drv_ops.h" +#include "drivers/driver.h" +#include "dfs.h" + + +static int dfs_get_used_n_chans(struct hostapd_data *hapd) +{ + int n_chans = 1; + + if (hapd->iconf->ieee80211n && hapd->iconf->secondary_channel) + n_chans = 2; + + if (hapd->iconf->ieee80211ac) { + switch (hapd->iconf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + break; + case VHT_CHANWIDTH_80MHZ: + n_chans = 4; + break; + case VHT_CHANWIDTH_160MHZ: + n_chans = 8; + break; + default: + break; + } + } + + return n_chans; +} + + +static int dfs_channel_available(struct hostapd_channel_data *chan) +{ + if (chan->flag & HOSTAPD_CHAN_DISABLED) + return 0; + if ((chan->flag & HOSTAPD_CHAN_RADAR) && + ((chan->flag & HOSTAPD_CHAN_DFS_MASK) == + HOSTAPD_CHAN_DFS_UNAVAILABLE)) + return 0; + return 1; +} + + +static int dfs_is_ht40_allowed(struct hostapd_channel_data *chan) +{ + int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, + 184, 192 }; + unsigned int i; + + for (i = 0; i < sizeof(allowed) / sizeof(allowed[0]); i++) { + if (chan->chan == allowed[i]) + return 1; + } + + return 0; +} + + +static int dfs_find_channel(struct hostapd_data *hapd, + struct hostapd_channel_data **ret_chan, + int idx) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan, *next_chan; + int i, j, channel_idx = 0, n_chans; + + mode = hapd->iface->current_mode; + n_chans = dfs_get_used_n_chans(hapd); + + wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans); + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + + /* Skip not available channels */ + if (!dfs_channel_available(chan)) + continue; + + /* Skip HT40/VHT uncompatible channels */ + if (hapd->iconf->ieee80211n && + hapd->iconf->secondary_channel) { + if (!dfs_is_ht40_allowed(chan)) + continue; + + for (j = 1; j < n_chans; j++) { + next_chan = &mode->channels[i + j]; + if (!dfs_channel_available(next_chan)) + break; + } + if (j != n_chans) + continue; + + /* Set HT40+ */ + hapd->iconf->secondary_channel = 1; + } + + if (ret_chan && idx == channel_idx) { + wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan); + *ret_chan = chan; + return idx; + } + wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan); + channel_idx++; + } + return channel_idx; +} + + +static void dfs_adjust_vht_center_freq(struct hostapd_data *hapd, + struct hostapd_channel_data *chan) +{ + if (!hapd->iconf->ieee80211ac) + return; + + if (!chan) + return; + + switch (hapd->iconf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan + 2; + break; + case VHT_CHANWIDTH_80MHZ: + hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan + 6; + break; + case VHT_CHANWIDTH_160MHZ: + hapd->iconf->vht_oper_centr_freq_seg0_idx = + chan->chan + 14; + default: + wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now"); + break; + } + + wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d", + hapd->iconf->vht_oper_centr_freq_seg0_idx); +} + + +/* Return start channel idx we will use for mode->channels[idx] */ +static int dfs_get_start_chan_idx(struct hostapd_data *hapd) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan; + int channel_no = hapd->iconf->channel; + int res = -1, i; + + /* HT40- */ + if (hapd->iconf->ieee80211n && hapd->iconf->secondary_channel == -1) + channel_no -= 4; + + /* VHT */ + if (hapd->iconf->ieee80211ac) { + switch (hapd->iconf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + break; + case VHT_CHANWIDTH_80MHZ: + channel_no = + hapd->iconf->vht_oper_centr_freq_seg0_idx - 6; + break; + case VHT_CHANWIDTH_160MHZ: + channel_no = + hapd->iconf->vht_oper_centr_freq_seg0_idx - 14; + break; + default: + wpa_printf(MSG_INFO, + "DFS only VHT20/40/80/160 is supported now"); + channel_no = -1; + break; + } + } + + /* Get idx */ + mode = hapd->iface->current_mode; + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + if (chan->chan == channel_no) { + res = i; + break; + } + } + + if (res == -1) + wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1"); + + return res; +} + + +/* At least one channel have radar flag */ +static int dfs_check_chans_radar(struct hostapd_data *hapd, int start_chan_idx, + int n_chans) +{ + struct hostapd_channel_data *channel; + struct hostapd_hw_modes *mode; + int i, res = 0; + + mode = hapd->iface->current_mode; + + for (i = 0; i < n_chans; i++) { + channel = &mode->channels[start_chan_idx + i]; + if (channel->flag & HOSTAPD_CHAN_RADAR) + res++; + } + + return res; +} + + +/* All channels available */ +static int dfs_check_chans_available(struct hostapd_data *hapd, + int start_chan_idx, int n_chans) +{ + struct hostapd_channel_data *channel; + struct hostapd_hw_modes *mode; + int i; + + mode = hapd->iface->current_mode; + + for(i = 0; i < n_chans; i++) { + channel = &mode->channels[start_chan_idx + i]; + if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) != + HOSTAPD_CHAN_DFS_AVAILABLE) + break; + } + + return i == n_chans; +} + + +/* At least one channel unavailable */ +static int dfs_check_chans_unavailable(struct hostapd_data *hapd, + int start_chan_idx, + int n_chans) +{ + struct hostapd_channel_data *channel; + struct hostapd_hw_modes *mode; + int i, res = 0; + + mode = hapd->iface->current_mode; + + for(i = 0; i < n_chans; i++) { + channel = &mode->channels[start_chan_idx + i]; + if (channel->flag & HOSTAPD_CHAN_DISABLED) + res++; + if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) == + HOSTAPD_CHAN_DFS_UNAVAILABLE) + res++; + } + + return res; +} + + +static struct hostapd_channel_data * dfs_get_valid_channel( + struct hostapd_data *hapd) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan = NULL; + int channel_idx, new_channel_idx; + u32 _rand; + + wpa_printf(MSG_DEBUG, "DFS: Selecting random channel"); + + if (hapd->iface->current_mode == NULL) + return NULL; + + mode = hapd->iface->current_mode; + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return NULL; + + /* get random available channel */ + channel_idx = dfs_find_channel(hapd, NULL, 0); + if (channel_idx > 0) { + os_get_random((u8 *) &_rand, sizeof(_rand)); + new_channel_idx = _rand % channel_idx; + dfs_find_channel(hapd, &chan, new_channel_idx); + } + + /* VHT */ + dfs_adjust_vht_center_freq(hapd, chan); + + return chan; +} + + +static int set_dfs_state_freq(struct hostapd_data *hapd, int freq, u32 state) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan = NULL; + int i; + + mode = hapd->iface->current_mode; + if (mode == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq); + for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { + chan = &hapd->iface->current_mode->channels[i]; + if (chan->freq == freq) { + if (chan->flag & HOSTAPD_CHAN_RADAR) { + chan->flag &= ~HOSTAPD_CHAN_DFS_MASK; + chan->flag |= state; + return 1; /* Channel found */ + } + } + } + wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq); + return 0; +} + + +static int set_dfs_state(struct hostapd_data *hapd, int freq, int ht_enabled, + int chan_offset, int chan_width, int cf1, + int cf2, u32 state) +{ + int n_chans = 1, i; + struct hostapd_hw_modes *mode; + int frequency = freq; + int ret = 0; + + mode = hapd->iface->current_mode; + if (mode == NULL) + return 0; + + if (mode->mode != HOSTAPD_MODE_IEEE80211A) { + wpa_printf(MSG_WARNING, "current_mode != IEEE80211A"); + return 0; + } + + /* Seems cf1 and chan_width is enough here */ + switch (chan_width) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + n_chans = 1; + frequency = cf1; + break; + case CHAN_WIDTH_40: + n_chans = 2; + frequency = cf1 - 10; + break; + case CHAN_WIDTH_80: + n_chans = 4; + frequency = cf1 - 30; + break; + case CHAN_WIDTH_160: + n_chans = 8; + frequency = cf1 - 70; + break; + default: + wpa_printf(MSG_INFO, "DFS chan_width %d not supported", + chan_width); + break; + } + + wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency, + n_chans); + for (i = 0; i < n_chans; i++) { + ret += set_dfs_state_freq(hapd, frequency, state); + frequency = frequency + 20; + } + + return ret; +} + + +static int dfs_are_channels_overlapped(struct hostapd_data *hapd, int freq, + int chan_width, int cf1, int cf2) +{ + int start_chan_idx; + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan; + int n_chans, i, j, frequency = freq, radar_n_chans = 1; + u8 radar_chan; + int res = 0; + + if (hapd->iface->freq == freq) + res++; + + /* Our configuration */ + mode = hapd->iface->current_mode; + start_chan_idx = dfs_get_start_chan_idx(hapd); + n_chans = dfs_get_used_n_chans(hapd); + + /* Reported via radar event */ + switch (chan_width) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + radar_n_chans = 1; + frequency = cf1; + break; + case CHAN_WIDTH_40: + radar_n_chans = 2; + frequency = cf1 - 10; + break; + case CHAN_WIDTH_80: + radar_n_chans = 4; + frequency = cf1 - 30; + break; + case CHAN_WIDTH_160: + radar_n_chans = 8; + frequency = cf1 - 70; + break; + default: + wpa_printf(MSG_INFO, "DFS chan_width %d not supported", + chan_width); + break; + } + + ieee80211_freq_to_chan(frequency, &radar_chan); + + for (i = 0; i < n_chans; i++) { + chan = &mode->channels[start_chan_idx + i]; + for (j = 0; j < radar_n_chans; j++) { + wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d", + chan->chan, radar_chan + j * 4); + if (chan->chan == radar_chan + j * 4) + res++; + } + } + + wpa_printf(MSG_DEBUG, "overlapped: %d", res); + + return res; +} + + +/* + * Main DFS handler + * 1 - continue channel/ap setup + * 0 - channel/ap setup will be continued after CAC + * -1 - hit critical error + */ +int hostapd_handle_dfs(struct hostapd_data *hapd) +{ + struct hostapd_channel_data *channel; + int res, n_chans, start_chan_idx; + + do { + /* Get start (first) channel for current configuration */ + start_chan_idx = dfs_get_start_chan_idx(hapd); + if (start_chan_idx == -1) + return -1; + + /* Get number of used channels, depend on width */ + n_chans = dfs_get_used_n_chans(hapd); + + /* Check if any of configured channels require DFS */ + res = dfs_check_chans_radar(hapd, start_chan_idx, n_chans); + wpa_printf(MSG_DEBUG, + "DFS %d channels required radar detection", + res); + if (!res) + return 1; + + /* Check if all channels are DFS available */ + res = dfs_check_chans_available(hapd, start_chan_idx, n_chans); + wpa_printf(MSG_DEBUG, + "DFS all channels available, (SKIP CAC): %s", + res ? "yes" : "no"); + if (res) + return 1; + + /* Check if any of configured channels is unavailable */ + res = dfs_check_chans_unavailable(hapd, start_chan_idx, + n_chans); + wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s", + res, res ? "yes": "no"); + if (res) { + channel = dfs_get_valid_channel(hapd); + if (!channel) { + wpa_printf(MSG_ERROR, "could not get valid channel"); + return -1; + } + hapd->iconf->channel = channel->chan; + hapd->iface->freq = channel->freq; + } + } while (res); + + /* Finally start CAC */ + wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", hapd->iface->freq); + if (hostapd_start_dfs_cac(hapd, hapd->iconf->hw_mode, + hapd->iface->freq, + hapd->iconf->channel, + hapd->iconf->ieee80211n, + hapd->iconf->ieee80211ac, + 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)) { + wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed"); + return -1; + } + + return 0; +} + + +int hostapd_dfs_complete_cac(struct hostapd_data *hapd, int success, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +{ + struct hostapd_channel_data *channel; + int err = 1; + + if (success) { + /* Complete iface/ap configuration */ + set_dfs_state(hapd, freq, ht_enabled, chan_offset, + chan_width, cf1, cf2, + HOSTAPD_CHAN_DFS_AVAILABLE); + hostapd_setup_interface_complete(hapd->iface, 0); + } else { + /* Switch to new channel */ + set_dfs_state(hapd, freq, ht_enabled, chan_offset, + chan_width, cf1, cf2, + HOSTAPD_CHAN_DFS_UNAVAILABLE); + channel = dfs_get_valid_channel(hapd); + if (channel) { + hapd->iconf->channel = channel->chan; + hapd->iface->freq = channel->freq; + err = 0; + } else + wpa_printf(MSG_ERROR, "No valid channel available"); + + hostapd_setup_interface_complete(hapd->iface, err); + } + + return 0; +} + + +static int hostapd_dfs_start_channel_switch(struct hostapd_data *hapd) +{ + struct hostapd_channel_data *channel; + int err = 1; + + wpa_printf(MSG_DEBUG, "%s called", __func__); + channel = dfs_get_valid_channel(hapd); + if (channel) { + hapd->iconf->channel = channel->chan; + hapd->iface->freq = channel->freq; + err = 0; + } + + hapd->driver->stop_ap(hapd->drv_priv); + + hostapd_setup_interface_complete(hapd->iface, err); + return 0; +} + + +int hostapd_dfs_radar_detected(struct hostapd_data *hapd, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +{ + int res; + + if (!hapd->iconf->ieee80211h) + return 0; + + /* mark radar frequency as invalid */ + res = set_dfs_state(hapd, freq, ht_enabled, chan_offset, + chan_width, cf1, cf2, + HOSTAPD_CHAN_DFS_UNAVAILABLE); + + /* Skip if reported radar event not overlapped our channels */ + res = dfs_are_channels_overlapped(hapd, freq, chan_width, cf1, cf2); + if (!res) + return 0; + + /* we are working on non-DFS channel - skip event */ + if (res == 0) + return 0; + + /* radar detected while operating, switch the channel. */ + res = hostapd_dfs_start_channel_switch(hapd); + + return res; +} + + +int hostapd_dfs_nop_finished(struct hostapd_data *hapd, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +{ + /* TODO add correct implementation here */ + set_dfs_state(hapd, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, + HOSTAPD_CHAN_DFS_USABLE); + return 0; +} diff --git a/src/ap/dfs.h b/src/ap/dfs.h new file mode 100644 index 00000000..c9f0578e --- /dev/null +++ b/src/ap/dfs.h @@ -0,0 +1,25 @@ +/* + * DFS - Dynamic Frequency Selection + * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#ifndef DFS_H +#define DFS_H + +int hostapd_handle_dfs(struct hostapd_data *hapd); + +int hostapd_dfs_complete_cac(struct hostapd_data *hapd, int success, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2); +int hostapd_dfs_radar_detected(struct hostapd_data *hapd, int freq, + int ht_enabled, + int chan_offset, int chan_width, + int cf1, int cf2); +int hostapd_dfs_nop_finished(struct hostapd_data *hapd, int freq, + int ht_enabled, + int chan_offset, int chan_width, int cf1, int cf2); + +#endif /* DFS_H */ diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index d6bc98d0..b30da145 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -29,6 +29,7 @@ #include "ap_drv_ops.h" #include "ap_config.h" #include "hw_features.h" +#include "dfs.h" int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, @@ -114,6 +115,13 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + if (elems.ext_capab && elems.ext_capab_len > 4) { + if (elems.ext_capab[4] & 0x01) + sta->qos_map_enabled = 1; + } +#endif /* CONFIG_INTERWORKING */ + #ifdef CONFIG_HS20 wpabuf_free(sta->hs20_ie); if (elems.hs20 && elems.hs20_len > 4) { @@ -785,6 +793,50 @@ static void hostapd_event_get_survey(struct hostapd_data *hapd, } +#ifdef NEED_AP_MLME + +static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq); + hostapd_dfs_radar_detected(hapd, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + +static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq); + hostapd_dfs_complete_cac(hapd, 1, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + +static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq); + hostapd_dfs_complete_cac(hapd, 0, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + +static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq); + hostapd_dfs_nop_finished(hapd, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + +#endif /* NEED_AP_MLME */ + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -929,6 +981,34 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, case EVENT_SURVEY: hostapd_event_get_survey(hapd, &data->survey_results); break; +#ifdef NEED_AP_MLME + case EVENT_DFS_RADAR_DETECTED: + if (!data) + break; + hostapd_event_dfs_radar_detected(hapd, &data->dfs_event); + break; + case EVENT_DFS_CAC_FINISHED: + if (!data) + break; + hostapd_event_dfs_cac_finished(hapd, &data->dfs_event); + break; + case EVENT_DFS_CAC_ABORTED: + if (!data) + break; + hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event); + break; + case EVENT_DFS_NOP_FINISHED: + if (!data) + break; + hostapd_event_dfs_nop_finished(hapd, &data->dfs_event); + break; + case EVENT_CHANNEL_LIST_CHANGED: + /* channel list changed (regulatory?), update channel list */ + /* TODO: check this. hostapd_get_hw_features() initializes + * too much stuff. */ + /* hostapd_get_hw_features(hapd->iface); */ + break; +#endif /* NEED_AP_MLME */ default: wpa_printf(MSG_DEBUG, "Unknown event %d", event); break; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 575ef2ad..3bca385a 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -32,6 +32,7 @@ #include "ap_config.h" #include "p2p_hostapd.h" #include "gas_serv.h" +#include "dfs.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); @@ -931,6 +932,9 @@ static int setup_interface(struct hostapd_iface *iface) "be completed in a callback"); return 0; } + + if (iface->conf->ieee80211h) + wpa_printf(MSG_DEBUG, "DFS support is enabled"); } return hostapd_setup_interface_complete(iface, 0); } @@ -950,12 +954,23 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) wpa_printf(MSG_DEBUG, "Completing interface initialization"); if (hapd->iconf->channel) { +#ifdef NEED_AP_MLME + int res; +#endif /* NEED_AP_MLME */ + iface->freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel); wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d " "Frequency: %d MHz", hostapd_hw_mode_txt(hapd->iconf->hw_mode), hapd->iconf->channel, iface->freq); +#ifdef NEED_AP_MLME + /* Check DFS */ + res = hostapd_handle_dfs(hapd); + if (res <= 0) + return res; +#endif /* NEED_AP_MLME */ + if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, hapd->iconf->channel, hapd->iconf->ieee80211n, diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 8a239f49..9e5becc3 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -45,6 +45,36 @@ void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, } +#ifndef CONFIG_NO_STDOUT_DEBUG +static char * dfs_info(struct hostapd_channel_data *chan) +{ + static char info[256]; + char *state; + + switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) { + case HOSTAPD_CHAN_DFS_UNKNOWN: + state = "unknown"; + break; + case HOSTAPD_CHAN_DFS_USABLE: + state = "usable"; + break; + case HOSTAPD_CHAN_DFS_UNAVAILABLE: + state = "unavailable"; + break; + case HOSTAPD_CHAN_DFS_AVAILABLE: + state = "available"; + break; + default: + return ""; + } + os_snprintf(info, sizeof(info), " (DFS state = %s)", state); + info[sizeof(info) - 1] = '\0'; + + return info; +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + int hostapd_get_hw_features(struct hostapd_iface *iface) { struct hostapd_data *hapd = iface->bss[0]; @@ -71,30 +101,40 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) for (i = 0; i < num_modes; i++) { struct hostapd_hw_modes *feature = &modes[i]; + int dfs_enabled = hapd->iconf->ieee80211h && + (iface->drv_flags & WPA_DRIVER_FLAGS_RADAR); + /* set flag for channels we can use in current regulatory * domain */ for (j = 0; j < feature->num_channels; j++) { + int dfs = 0; + /* * Disable all channels that are marked not to allow - * IBSS operation or active scanning. In addition, - * disable all channels that require radar detection, - * since that (in addition to full DFS) is not yet - * supported. + * IBSS operation or active scanning. + * Use radar channels only if the driver supports DFS. */ - if (feature->channels[j].flag & - (HOSTAPD_CHAN_NO_IBSS | - HOSTAPD_CHAN_PASSIVE_SCAN | - HOSTAPD_CHAN_RADAR)) + if ((feature->channels[j].flag & + HOSTAPD_CHAN_RADAR) && dfs_enabled) { + dfs = 1; + } else if (feature->channels[j].flag & + (HOSTAPD_CHAN_NO_IBSS | + HOSTAPD_CHAN_PASSIVE_SCAN | + HOSTAPD_CHAN_RADAR)) { feature->channels[j].flag |= HOSTAPD_CHAN_DISABLED; + } + if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED) continue; + wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d " - "chan=%d freq=%d MHz max_tx_power=%d dBm", + "chan=%d freq=%d MHz max_tx_power=%d dBm%s", feature->mode, feature->channels[j].chan, feature->channels[j].freq, - feature->channels[j].max_tx_power); + feature->channels[j].max_tx_power, + dfs ? dfs_info(&feature->channels[j]) : ""); } } diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 781f8269..c7db7f44 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -859,6 +859,21 @@ static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, } +static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ext_capab_ie, size_t ext_capab_ie_len) +{ +#ifdef CONFIG_INTERWORKING + /* check for QoS Map support */ + if (ext_capab_ie_len >= 5) { + if (ext_capab_ie[4] & 0x01) + sta->qos_map_enabled = 1; + } +#endif /* CONFIG_INTERWORKING */ + + return WLAN_STATUS_SUCCESS; +} + + static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ies, size_t ies_len, int reassoc) { @@ -881,6 +896,9 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len); if (resp != WLAN_STATUS_SUCCESS) return resp; + resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; resp = copy_supp_rates(hapd, sta, &elems); if (resp != WLAN_STATUS_SUCCESS) return resp; @@ -1169,6 +1187,8 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, p = hostapd_eid_ext_capab(hapd, p); p = hostapd_eid_bss_max_idle_period(hapd, p); + if (sta->qos_map_enabled) + p = hostapd_eid_qos_map_set(hapd, p); if (sta->flags & WLAN_STA_WMM) p = hostapd_eid_wmm(hapd, p); diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 2aab56de..61f13167 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -41,6 +41,7 @@ static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd, u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, int probe); u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index c36bbe39..41722189 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -189,6 +189,8 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) *pos |= 0x80; /* Bit 31 - Interworking */ break; case 4: /* Bits 32-39 */ + if (hapd->conf->qos_map_set_len) + *pos |= 0x01; /* Bit 32 - QoS Map */ if (hapd->conf->tdls & TDLS_PROHIBIT) *pos |= 0x40; /* Bit 38 - TDLS Prohibited */ if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) { @@ -250,6 +252,23 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) } +u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + u8 len = hapd->conf->qos_map_set_len; + + if (!len) + return eid; + + *pos++ = WLAN_EID_QOS_MAP_SET; + *pos++ = len; + os_memcpy(pos, hapd->conf->qos_map_set, len); + pos += len; + + return pos; +} + + u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index e5b50693..197e46bc 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -57,6 +57,7 @@ struct sta_info { unsigned int no_ht_set:1; unsigned int ht_20mhz_set:1; unsigned int no_p2p_set:1; + unsigned int qos_map_enabled:1; u16 auth_alg; u8 previous_ap[6]; diff --git a/src/common/defs.h b/src/common/defs.h index 281dd8a5..0c90c249 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -312,6 +312,7 @@ enum wpa_ctrl_req_type { WPA_CTRL_REQ_EAP_PIN, WPA_CTRL_REQ_EAP_OTP, WPA_CTRL_REQ_EAP_PASSPHRASE, + WPA_CTRL_REQ_SIM, NUM_WPA_CTRL_REQS }; diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index aab8ac61..304dfc69 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -276,6 +276,12 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->interworking = pos; elems->interworking_len = elen; break; + case WLAN_EID_QOS_MAP_SET: + if (elen < 16) + break; + elems->qos_map_set = pos; + elems->qos_map_set_len = elen; + break; case WLAN_EID_EXT_CAPAB: elems->ext_capab = pos; elems->ext_capab_len = elen; diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 68c6b96f..c4618b2d 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -40,6 +40,7 @@ struct ieee802_11_elems { const u8 *wfd; const u8 *link_id; const u8 *interworking; + const u8 *qos_map_set; const u8 *hs20; const u8 *ext_capab; const u8 *bss_max_idle_period; @@ -73,6 +74,7 @@ struct ieee802_11_elems { u8 p2p_len; u8 wfd_len; u8 interworking_len; + u8 qos_map_set_len; u8 hs20_len; u8 ext_capab_len; u8 ssid_list_len; diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 137c3090..ca38701b 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -203,6 +203,7 @@ #define WLAN_EID_TIM 5 #define WLAN_EID_IBSS_PARAMS 6 #define WLAN_EID_COUNTRY 7 +#define WLAN_EID_BSS_LOAD 11 #define WLAN_EID_CHALLENGE 16 /* EIDs defined by IEEE 802.11h - START */ #define WLAN_EID_PWR_CONSTRAINT 32 @@ -242,6 +243,7 @@ #define WLAN_EID_LINK_ID 101 #define WLAN_EID_INTERWORKING 107 #define WLAN_EID_ADV_PROTO 108 +#define WLAN_EID_QOS_MAP_SET 110 #define WLAN_EID_ROAMING_CONSORTIUM 111 #define WLAN_EID_EXT_CAPAB 127 #define WLAN_EID_CCKM 156 @@ -1079,6 +1081,15 @@ enum bss_trans_mgmt_status_code { #define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70 #define WNM_NEIGHBOR_MULTIPLE_BSSID 71 +/* QoS action */ +enum qos_action { + QOS_ADDTS_REQ = 0, + QOS_ADDTS_RESP = 1, + QOS_DELTS = 2, + QOS_SCHEDULE = 3, + QOS_QOS_MAP_CONFIG = 4, +}; + /* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */ #define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0) #define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1) diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 2fdaa026..feba13ff 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -40,7 +40,8 @@ enum tls_fail_reason { TLS_FAIL_SUBJECT_MISMATCH = 5, TLS_FAIL_ALTSUBJECT_MISMATCH = 6, TLS_FAIL_BAD_CERTIFICATE = 7, - TLS_FAIL_SERVER_CHAIN_PROBE = 8 + TLS_FAIL_SERVER_CHAIN_PROBE = 8, + TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9 }; union tls_event_data { @@ -96,6 +97,8 @@ struct tls_config { * %NULL to allow all subjects * @altsubject_match: String to match in the alternative subject of the peer * certificate or %NULL to allow all alternative subjects + * @suffix_match: String to suffix match in the dNSName or CN of the peer + * certificate or %NULL to allow all domain names * @client_cert: File or reference name for client X.509 certificate in PEM or * DER format * @client_cert_blob: client_cert as inlined data or %NULL if not used @@ -137,6 +140,7 @@ struct tls_connection_params { const char *ca_path; const char *subject_match; const char *altsubject_match; + const char *suffix_match; const char *client_cert; const u8 *client_cert_blob; size_t client_cert_blob_len; diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 18e0e789..48c48760 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -93,7 +93,7 @@ struct tls_connection { ENGINE *engine; /* functional reference to the engine */ EVP_PKEY *private_key; /* the private key if using engine */ #endif /* OPENSSL_NO_ENGINE */ - char *subject_match, *altsubject_match; + char *subject_match, *altsubject_match, *suffix_match; int read_alerts, write_alerts, failed; tls_session_ticket_cb session_ticket_cb; @@ -1049,6 +1049,7 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) tls_engine_deinit(conn); os_free(conn->subject_match); os_free(conn->altsubject_match); + os_free(conn->suffix_match); os_free(conn->session_ticket); os_free(conn); } @@ -1139,6 +1140,97 @@ static int tls_match_altsubject(X509 *cert, const char *match) } +static int domain_suffix_match(const u8 *val, size_t len, const char *match) +{ + size_t i, match_len; + + /* Check for embedded nuls that could mess up suffix matching */ + for (i = 0; i < len; i++) { + if (val[i] == '\0') { + wpa_printf(MSG_DEBUG, "TLS: Embedded null in a string - reject"); + return 0; + } + } + + match_len = os_strlen(match); + if (match_len > len) + return 0; + + if (os_strncasecmp((const char *) val + len - match_len, match, + match_len) != 0) + return 0; /* no match */ + + if (match_len == len) + return 1; /* exact match */ + + if (val[len - match_len - 1] == '.') + return 1; /* full label match completes suffix match */ + + wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match"); + return 0; +} + + +static int tls_match_suffix(X509 *cert, const char *match) +{ + GENERAL_NAME *gen; + void *ext; + int i; + int dns_name = 0; + X509_NAME *name; + + wpa_printf(MSG_DEBUG, "TLS: Match domain against suffix %s", match); + + ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + + for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { + gen = sk_GENERAL_NAME_value(ext, i); + if (gen->type != GEN_DNS) + continue; + dns_name++; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName", + gen->d.dNSName->data, + gen->d.dNSName->length); + if (domain_suffix_match(gen->d.dNSName->data, + gen->d.dNSName->length, match) == 1) { + wpa_printf(MSG_DEBUG, "TLS: Suffix match in dNSName found"); + return 1; + } + } + + if (dns_name) { + wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched"); + return 0; + } + + name = X509_get_subject_name(cert); + i = -1; + for (;;) { + X509_NAME_ENTRY *e; + ASN1_STRING *cn; + + i = X509_NAME_get_index_by_NID(name, NID_commonName, i); + if (i == -1) + break; + e = X509_NAME_get_entry(name, i); + if (e == NULL) + continue; + cn = X509_NAME_ENTRY_get_data(e); + if (cn == NULL) + continue; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", + cn->data, cn->length); + if (domain_suffix_match(cn->data, cn->length, match) == 1) { + wpa_printf(MSG_DEBUG, "TLS: Suffix match in commonName found"); + return 1; + } + } + + wpa_printf(MSG_DEBUG, "TLS: No CommonName suffix match found"); + return 0; +} + + static enum tls_fail_reason openssl_tls_fail_reason(int err) { switch (err) { @@ -1267,7 +1359,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) SSL *ssl; struct tls_connection *conn; struct tls_context *context; - char *match, *altmatch; + char *match, *altmatch, *suffix_match; const char *err_str; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); @@ -1289,6 +1381,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) context = conn->context; match = conn->subject_match; altmatch = conn->altsubject_match; + suffix_match = conn->suffix_match; if (!preverify_ok && !conn->ca_cert_verify) preverify_ok = 1; @@ -1357,6 +1450,14 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) openssl_tls_fail_event(conn, err_cert, err, depth, buf, "AltSubject mismatch", TLS_FAIL_ALTSUBJECT_MISMATCH); + } else if (depth == 0 && suffix_match && + !tls_match_suffix(err_cert, suffix_match)) { + wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found", + suffix_match); + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Domain suffix mismatch", + TLS_FAIL_DOMAIN_SUFFIX_MISMATCH); } else openssl_tls_cert_event(conn, err_cert, depth, buf); @@ -1619,7 +1720,8 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) static int tls_connection_set_subject_match(struct tls_connection *conn, const char *subject_match, - const char *altsubject_match) + const char *altsubject_match, + const char *suffix_match) { os_free(conn->subject_match); conn->subject_match = NULL; @@ -1637,6 +1739,14 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, return -1; } + os_free(conn->suffix_match); + conn->suffix_match = NULL; + if (suffix_match) { + conn->suffix_match = os_strdup(suffix_match); + if (conn->suffix_match == NULL) + return -1; + } + return 0; } @@ -2909,7 +3019,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required"); return 0; } - wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue"); + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue"); return 1; } @@ -2974,7 +3084,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } if (tls_connection_set_subject_match(conn, params->subject_match, - params->altsubject_match)) + params->altsubject_match, + params->suffix_match)) return -1; if (params->engine && params->ca_cert_id) { diff --git a/src/drivers/driver.h b/src/drivers/driver.h index edd05c4f..fd75cd2d 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2572,6 +2572,15 @@ struct wpa_driver_ops { u8 *buf, u16 *buf_len); /** + * set_qos_map - Set QoS Map + * @priv: Private driver interface data + * @qos_map_set: QoS Map + * @qos_map_set_len: Length of QoS Map + */ + int (*set_qos_map)(void *priv, const u8 *qos_map_set, + u8 qos_map_set_len); + + /** * signal_poll - Get current connection information * @priv: Private driver interface data * @signal_info: Connection info structure @@ -2746,10 +2755,10 @@ struct wpa_driver_ops { /** * start_dfs_cac - Listen for radar interference on the channel * @priv: Private driver interface data - * @freq: Frequency (in MHz) of the channel + * @freq: Channel parameters * Returns: 0 on success, -1 on failure */ - int (*start_dfs_cac)(void *priv, int freq); + int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq); /** * stop_ap - Removes beacon from AP @@ -3970,6 +3979,11 @@ union wpa_event_data { */ struct dfs_event { int freq; + int ht_enabled; + int chan_offset; + enum chan_width chan_width; + int cf1; + int cf2; } dfs_event; /** diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index b7e92dd0..2921afb7 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -872,8 +872,59 @@ static void atheros_raw_recv_hs20(void *ctx, const u8 *src_addr, const u8 *buf, event.rx_mgmt.frame_len = len; wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); } + #endif /* CONFIG_HS20 */ + +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 { + memset(&req, 0, sizeof(struct ieee80211req_athdbg)); + req.cmd = IEEE80211_DBGREQ_SETQOSMAPCONF; + os_memset(&iwr, 0, sizeof(iwr)); + os_strncpy(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) { + perror("ioctl[IEEE80211_IOCTL_DBGREQ]"); + wpa_printf(MSG_DEBUG, "%s: %s: Failed to set QoS Map", + __func__, drv->iface); + return -1; + } +#endif /* CONFIG_ATHEROS_QOS_MAP */ + + return 0; +} + #if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R) static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) @@ -2153,4 +2204,5 @@ const struct wpa_driver_ops wpa_driver_atheros_ops = { #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/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index c0981500..508022f4 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -2513,6 +2513,8 @@ static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv, } +static enum chan_width convert2width(int width); + static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { @@ -2523,11 +2525,40 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv, return; os_memset(&data, 0, sizeof(data)); - data.dfs_event.freq = nla_get_u16(tb[NL80211_ATTR_WIPHY_FREQ]); - event_type = nla_get_u8(tb[NL80211_ATTR_RADAR_EVENT]); + data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]); + + /* Check HT params */ + if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + data.dfs_event.ht_enabled = 1; + data.dfs_event.chan_offset = 0; + + switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) { + case NL80211_CHAN_NO_HT: + data.dfs_event.ht_enabled = 0; + break; + case NL80211_CHAN_HT20: + break; + case NL80211_CHAN_HT40PLUS: + data.dfs_event.chan_offset = 1; + break; + case NL80211_CHAN_HT40MINUS: + data.dfs_event.chan_offset = -1; + break; + } + } - wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz", - data.dfs_event.freq); + /* Get VHT params */ + data.dfs_event.chan_width = + convert2width(nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH])); + data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); + if (tb[NL80211_ATTR_CENTER_FREQ2]) + data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); + + wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz", + data.dfs_event.freq, data.dfs_event.ht_enabled, + data.dfs_event.chan_offset, data.dfs_event.chan_width, + data.dfs_event.cf1, data.dfs_event.cf2); switch (event_type) { case NL80211_RADAR_DETECTED: @@ -3798,6 +3829,11 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP " "handle %p", bss->nl_mgmt); +#ifdef CONFIG_INTERWORKING + /* QoS Map Configure */ + if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0) + return -1; +#endif /* CONFIG_INTERWORKING */ #if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) /* GAS Initial Request */ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0) @@ -6137,6 +6173,7 @@ static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, { struct wpa_driver_nl80211_data *drv = bss->drv; u64 cookie; + int res; if (freq == 0) { wpa_printf(MSG_DEBUG, "nl80211: send_frame - Use bss->freq=%u", @@ -6152,8 +6189,26 @@ static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, } wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd"); - return nl80211_send_frame_cmd(bss, freq, wait_time, data, len, - &cookie, no_cck, noack, offchanok); + res = nl80211_send_frame_cmd(bss, freq, wait_time, data, len, + &cookie, no_cck, noack, offchanok); + if (res == 0 && !noack) { + const struct ieee80211_mgmt *mgmt; + u16 fc; + + mgmt = (const struct ieee80211_mgmt *) data; + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { + wpa_printf(MSG_MSGDUMP, + "nl80211: Update send_action_cookie from 0x%llx to 0x%llx", + (long long unsigned int) + drv->send_action_cookie, + (long long unsigned int) cookie); + drv->send_action_cookie = cookie; + } + } + + return res; } @@ -9233,10 +9288,8 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d", __func__, type, ifname, ifindex); - if (ifindex <= 0) - return -1; - - nl80211_remove_iface(drv, ifindex); + if (ifindex > 0) + nl80211_remove_iface(drv, ifindex); #ifdef HOSTAPD if (type != WPA_IF_AP_BSS) @@ -10385,14 +10438,18 @@ static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps, } -static int nl80211_start_radar_detection(void *priv, int freq) +static int nl80211_start_radar_detection(void *priv, + struct hostapd_freq_params *freq) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; - wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC)"); + wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC) %d MHz (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)", + freq->freq, freq->ht_enabled, freq->vht_enabled, + freq->bandwidth, freq->center_freq1, freq->center_freq2); + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) { wpa_printf(MSG_DEBUG, "nl80211: Driver does not support radar " "detection"); @@ -10405,10 +10462,53 @@ static int nl80211_start_radar_detection(void *priv, int freq) nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_RADAR_DETECT); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq); - /* only HT20 is supported at this point */ - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20); + if (freq->vht_enabled) { + switch (freq->bandwidth) { + case 20: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_20); + break; + case 40: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_40); + break; + case 80: + if (freq->center_freq2) + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_80P80); + else + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_80); + break; + case 160: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_160); + break; + default: + return -1; + } + NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1); + if (freq->center_freq2) + NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, + freq->center_freq2); + } else if (freq->ht_enabled) { + switch (freq->sec_channel_offset) { + case -1: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT40MINUS); + break; + case 1: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT40PLUS); + break; + default: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT20); + break; + } + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret == 0) diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index 554e7e94..3439c2df 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -1638,7 +1638,8 @@ static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, const char *msg, size_t msglen) { struct eap_peer_config *config; - char *txt = NULL, *tmp; + const char *txt = NULL; + char *tmp; if (sm == NULL) return; @@ -1681,6 +1682,9 @@ static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, case WPA_CTRL_REQ_EAP_PASSPHRASE: config->pending_req_passphrase++; break; + case WPA_CTRL_REQ_SIM: + txt = msg; + break; default: return; } @@ -1792,6 +1796,17 @@ void eap_sm_request_passphrase(struct eap_sm *sm) /** + * eap_sm_request_sim - Request external SIM processing + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @req: EAP method specific request + */ +void eap_sm_request_sim(struct eap_sm *sm, const char *req) +{ + eap_sm_request(sm, WPA_CTRL_REQ_SIM, req, os_strlen(req)); +} + + +/** * eap_sm_notify_ctrl_attached - Notification of attached monitor * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @@ -2304,6 +2319,17 @@ void eap_set_force_disabled(struct eap_sm *sm, int disabled) } +/** + * eap_set_external_sim - Set external_sim flag + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @external_sim: Whether external SIM/USIM processing is used + */ +void eap_set_external_sim(struct eap_sm *sm, int external_sim) +{ + sm->external_sim = external_sim; +} + + /** * eap_notify_pending - Notify that EAP method is ready to re-process a request * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h index f87f9b3b..711f41ff 100644 --- a/src/eap_peer/eap.h +++ b/src/eap_peer/eap.h @@ -296,6 +296,7 @@ void eap_sm_request_new_password(struct eap_sm *sm); void eap_sm_request_pin(struct eap_sm *sm); void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len); void eap_sm_request_passphrase(struct eap_sm *sm); +void eap_sm_request_sim(struct eap_sm *sm, const char *req); void eap_sm_notify_ctrl_attached(struct eap_sm *sm); u32 eap_get_phase2_type(const char *name, int *vendor); struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, @@ -303,6 +304,7 @@ struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, void eap_set_fast_reauth(struct eap_sm *sm, int enabled); void eap_set_workaround(struct eap_sm *sm, unsigned int workaround); void eap_set_force_disabled(struct eap_sm *sm, int disabled); +void eap_set_external_sim(struct eap_sm *sm, int external_sim); int eap_key_available(struct eap_sm *sm); void eap_notify_success(struct eap_sm *sm); void eap_notify_lower_layer_success(struct eap_sm *sm); diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index dc424d78..d3cbaca6 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -142,6 +142,89 @@ static void eap_aka_deinit(struct eap_sm *sm, void *priv) } +static int eap_aka_ext_sim_req(struct eap_sm *sm, struct eap_aka_data *data) +{ + char req[200], *pos, *end; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Use external USIM processing"); + pos = req; + end = pos + sizeof(req); + pos += os_snprintf(pos, end - pos, "UMTS-AUTH"); + pos += os_snprintf(pos, end - pos, ":"); + pos += wpa_snprintf_hex(pos, end - pos, data->rand, EAP_AKA_RAND_LEN); + pos += os_snprintf(pos, end - pos, ":"); + pos += wpa_snprintf_hex(pos, end - pos, data->autn, EAP_AKA_AUTN_LEN); + + eap_sm_request_sim(sm, req); + return 1; +} + + +static int eap_aka_ext_sim_result(struct eap_sm *sm, struct eap_aka_data *data, + struct eap_peer_config *conf) +{ + char *resp, *pos; + + wpa_printf(MSG_DEBUG, + "EAP-AKA: Use result from external USIM processing"); + + resp = conf->external_sim_resp; + conf->external_sim_resp = NULL; + + if (os_strncmp(resp, "UMTS-AUTS:", 10) == 0) { + pos = resp + 10; + if (hexstr2bin(pos, data->auts, EAP_AKA_AUTS_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: AUTS", data->auts, + EAP_AKA_AUTS_LEN); + os_free(resp); + return -2; + } + + if (os_strncmp(resp, "UMTS-AUTH:", 10) != 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized external USIM processing response"); + os_free(resp); + return -1; + } + + pos = resp + 10; + wpa_hexdump(MSG_DEBUG, "EAP-AKA: RAND", data->rand, EAP_AKA_RAND_LEN); + + if (hexstr2bin(pos, data->ik, EAP_AKA_IK_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", data->ik, EAP_AKA_IK_LEN); + pos += EAP_AKA_IK_LEN * 2; + if (*pos != ':') + goto invalid; + pos++; + + if (hexstr2bin(pos, data->ck, EAP_AKA_CK_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", data->ck, EAP_AKA_CK_LEN); + pos += EAP_AKA_CK_LEN * 2; + if (*pos != ':') + goto invalid; + pos++; + + data->res_len = os_strlen(pos) / 2; + if (data->res_len > EAP_AKA_RES_MAX_LEN) { + data->res_len = 0; + goto invalid; + } + if (hexstr2bin(pos, data->res, data->res_len) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: RES", data->res, data->res_len); + + os_free(resp); + return 0; + +invalid: + wpa_printf(MSG_DEBUG, "EAP-AKA: Invalid external USIM processing UMTS-AUTH response"); + os_free(resp); + return -1; +} + + static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) { struct eap_peer_config *conf; @@ -151,6 +234,14 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) conf = eap_get_config(sm); if (conf == NULL) return -1; + + if (sm->external_sim) { + if (conf->external_sim_resp) + return eap_aka_ext_sim_result(sm, data, conf); + else + return eap_aka_ext_sim_req(sm, data); + } + if (conf->pcsc) { return scard_umts_auth(sm->scard_ctx, data->rand, data->autn, data->res, &data->res_len, @@ -861,6 +952,9 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " "failed (AUTN seq# -> AUTS)"); return eap_aka_synchronization_failure(data, id); + } else if (res > 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing"); + return NULL; } else if (res) { wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed"); return eap_aka_client_error(data, id, diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index 42f525b9..98ec1f76 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -208,6 +208,24 @@ struct eap_peer_config { u8 *altsubject_match; /** + * domain_suffix_match - Constraint for server domain name + * + * If set, this FQDN is used as a suffix match requirement for the + * server certificate in SubjectAltName dNSName element(s). If a + * matching dNSName is found, this constraint is met. If no dNSName + * values are present, this constraint is matched against SubjetName CN + * using same suffix match comparison. Suffix match here means that the + * host/domain name is compared one label at a time starting from the + * top-level domain and all the labels in domain_suffix_match shall be + * included in the certificate. The certificate may include additional + * sub-level labels in addition to the required labels. + * + * For example, domain_suffix_match=example.com would match + * test.example.com but would not match test-example.com. + */ + char *domain_suffix_match; + + /** * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2) * * This file can have one or more trusted CA certificates. If ca_cert2 @@ -303,6 +321,14 @@ struct eap_peer_config { u8 *altsubject_match2; /** + * domain_suffix_match2 - Constraint for server domain name + * + * This field is like domain_suffix_match, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + char *domain_suffix_match2; + + /** * eap_methods - Allowed EAP methods * * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of @@ -643,6 +669,15 @@ struct eap_peer_config { * 2 = require valid OCSP stapling response */ int ocsp; + + /** + * external_sim_resp - Response from external SIM processing + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request external + * SIM/USIM processing. + */ + char *external_sim_resp; }; diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index 62c867ca..9307f3f6 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -348,6 +348,8 @@ struct eap_sm { struct ext_password_data *ext_pw; struct wpabuf *ext_pw_buf; + + int external_sim; }; const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len); diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c index 82ea18de..d8560543 100644 --- a/src/eap_peer/eap_sim.c +++ b/src/eap_peer/eap_sim.c @@ -145,6 +145,80 @@ static void eap_sim_deinit(struct eap_sm *sm, void *priv) } +static int eap_sim_ext_sim_req(struct eap_sm *sm, struct eap_sim_data *data) +{ + char req[200], *pos, *end; + size_t i; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Use external SIM processing"); + pos = req; + end = pos + sizeof(req); + pos += os_snprintf(pos, end - pos, "GSM-AUTH"); + for (i = 0; i < data->num_chal; i++) { + pos += os_snprintf(pos, end - pos, ":"); + pos += wpa_snprintf_hex(pos, end - pos, data->rand[i], + GSM_RAND_LEN); + } + + eap_sm_request_sim(sm, req); + return 1; +} + + +static int eap_sim_ext_sim_result(struct eap_sm *sm, struct eap_sim_data *data, + struct eap_peer_config *conf) +{ + char *resp, *pos; + size_t i; + + wpa_printf(MSG_DEBUG, + "EAP-SIM: Use result from external SIM processing"); + + resp = conf->external_sim_resp; + conf->external_sim_resp = NULL; + + if (os_strncmp(resp, "GSM-AUTH:", 9) != 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized external SIM processing response"); + os_free(resp); + return -1; + } + + pos = resp + 9; + for (i = 0; i < data->num_chal; i++) { + wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND", + data->rand[i], GSM_RAND_LEN); + + if (hexstr2bin(pos, data->kc[i], EAP_SIM_KC_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc", + data->kc[i], EAP_SIM_KC_LEN); + pos += EAP_SIM_KC_LEN * 2; + if (*pos != ':') + goto invalid; + pos++; + + if (hexstr2bin(pos, data->sres[i], EAP_SIM_SRES_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES", + data->sres[i], EAP_SIM_SRES_LEN); + pos += EAP_SIM_SRES_LEN * 2; + if (i + 1 < data->num_chal) { + if (*pos != ':') + goto invalid; + pos++; + } + } + + os_free(resp); + return 0; + +invalid: + wpa_printf(MSG_DEBUG, "EAP-SIM: Invalid external SIM processing GSM-AUTH response"); + os_free(resp); + return -1; +} + + static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) { struct eap_peer_config *conf; @@ -154,6 +228,14 @@ static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) conf = eap_get_config(sm); if (conf == NULL) return -1; + + if (sm->external_sim) { + if (conf->external_sim_resp) + return eap_sim_ext_sim_result(sm, data, conf); + else + return eap_sim_ext_sim_req(sm, data); + } + if (conf->pcsc) { if (scard_gsm_auth(sm->scard_ctx, data->rand[0], data->sres[0], data->kc[0]) || @@ -605,6 +687,7 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, const u8 *identity; size_t identity_len; struct eap_sim_attrs eattr; + int res; wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge"); data->reauth = 0; @@ -648,8 +731,13 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN); data->num_chal = attr->num_chal; - - if (eap_sim_gsm_auth(sm, data)) { + + res = eap_sim_gsm_auth(sm, data); + if (res > 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Wait for external SIM processing"); + return NULL; + } + if (res) { wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index be8c3018..008af37b 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -78,6 +78,7 @@ static void eap_tls_params_from_conf1(struct tls_connection_params *params, params->dh_file = (char *) config->dh_file; params->subject_match = (char *) config->subject_match; params->altsubject_match = (char *) config->altsubject_match; + params->suffix_match = config->domain_suffix_match; params->engine = config->engine; params->engine_id = config->engine_id; params->pin = config->pin; @@ -99,6 +100,7 @@ static void eap_tls_params_from_conf2(struct tls_connection_params *params, params->dh_file = (char *) config->dh_file2; params->subject_match = (char *) config->subject_match2; params->altsubject_match = (char *) config->altsubject_match2; + params->suffix_match = config->domain_suffix_match2; params->engine = config->engine2; params->engine_id = config->engine2_id; params->pin = config->pin2; diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index c4475e57..03ec2cb4 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -1479,6 +1479,7 @@ void eapol_sm_notify_config(struct eapol_sm *sm, eap_set_fast_reauth(sm->eap, conf->fast_reauth); eap_set_workaround(sm->eap, conf->workaround); eap_set_force_disabled(sm->eap, conf->eap_disabled); + eap_set_external_sim(sm->eap, conf->external_sim); } } diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index c4b87da1..6faf816d 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -53,6 +53,11 @@ struct eapol_config { * eap_disabled - Whether EAP is disabled */ int eap_disabled; + + /** + * external_sim - Use external processing for SIM/USIM operations + */ + int external_sim; }; struct eapol_sm; diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 738436c4..64ca006d 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -1672,6 +1672,7 @@ static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, rx_freq); break; case P2P_INVITATION_RESP: + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_process_invitation_resp(p2p, sa, data + 1, len - 1); break; case P2P_PROV_DISC_REQ: @@ -2774,7 +2775,8 @@ static void p2p_retry_pd(struct p2p_data *p2p) MAC2STR(dev->info.p2p_device_addr), dev->req_config_methods); p2p_send_prov_disc_req(p2p, dev, - dev->flags & P2P_DEV_PD_FOR_JOIN, 0); + dev->flags & P2P_DEV_PD_FOR_JOIN, + p2p->pd_force_freq); return; } } diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 81e521ec..f323ef72 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -450,6 +450,14 @@ struct p2p_data { */ int pd_retries; + /** + * pd_force_freq - Forced frequency for PD retries or 0 to auto-select + * + * This is is used during PD retries for join-a-group case to use the + * correct operating frequency determined from a BSS entry for the GO. + */ + int pd_force_freq; + u8 go_timeout; u8 client_timeout; diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index 687b9431..4e4593e0 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -407,7 +407,7 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, return; } - if (!msg.channel_list) { + if (!msg.channel_list && *msg.status == P2P_SC_SUCCESS) { p2p_dbg(p2p, "Mandatory Channel List attribute missing in Invitation Response from " MACSTR, MAC2STR(sa)); #ifdef CONFIG_P2P_STRICT @@ -416,6 +416,9 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, #endif /* CONFIG_P2P_STRICT */ /* Try to survive without peer channel list */ channels = &p2p->channels; + } else if (!msg.channel_list) { + /* Non-success cases are not required to include Channel List */ + channels = &p2p->channels; } else if (p2p_peer_channels_check(p2p, &p2p->channels, dev, msg.channel_list, msg.channel_list_len) < 0) { diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index 54aa4282..409405fb 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -437,6 +437,7 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, } p2p->user_initiated_pd = user_initiated_pd; + p2p->pd_force_freq = force_freq; if (p2p->user_initiated_pd) p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES; @@ -472,4 +473,5 @@ void p2p_reset_pending_pd(struct p2p_data *p2p) p2p->user_initiated_pd = 0; os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); p2p->pd_retries = 0; + p2p->pd_force_freq = 0; } diff --git a/src/wps/http_server.c b/src/wps/http_server.c index 6ca32140..06c8bee2 100644 --- a/src/wps/http_server.c +++ b/src/wps/http_server.c @@ -232,6 +232,7 @@ struct http_server * http_server_init(struct in_addr *addr, int port, { struct sockaddr_in sin; struct http_server *srv; + int on = 1; srv = os_zalloc(sizeof(*srv)); if (srv == NULL) @@ -242,6 +243,9 @@ struct http_server * http_server_init(struct in_addr *addr, int port, srv->fd = socket(AF_INET, SOCK_STREAM, 0); if (srv->fd < 0) goto fail; + + setsockopt(srv->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0) goto fail; if (port < 0) diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index 8515b5a8..50759769 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -794,6 +794,7 @@ OBJS += src/ap/wmm.c OBJS += src/ap/ap_list.c OBJS += src/ap/ieee802_11.c OBJS += src/ap/hw_features.c +OBJS += src/ap/dfs.c L_CFLAGS += -DNEED_AP_MLME endif ifdef CONFIG_WPS diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 56986196..7c442413 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -772,6 +772,7 @@ OBJS += ../src/ap/wmm.o OBJS += ../src/ap/ap_list.o OBJS += ../src/ap/ieee802_11.o OBJS += ../src/ap/hw_features.o +OBJS += ../src/ap/dfs.o CFLAGS += -DNEED_AP_MLME endif ifdef CONFIG_WPS diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20 index 5669c55c..61e4a4dc 100644 --- a/wpa_supplicant/README-HS20 +++ b/wpa_supplicant/README-HS20 @@ -166,9 +166,25 @@ Credentials can be pre-configured for automatic network selection: # milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN> # format # -# domain: Home service provider FQDN +# domain_suffix_match: Constraint for server domain name +# If set, this FQDN is used as a suffix match requirement for the AAA +# server certificate in SubjectAltName dNSName element(s). If a +# matching dNSName is found, this constraint is met. If no dNSName +# values are present, this constraint is matched against SubjetName CN +# using same suffix match comparison. Suffix match here means that the +# host/domain name is compared one label at a time starting from the +# top-level domain and all the labels in @domain_suffix_match shall be +# included in the certificate. The certificate may include additional +# sub-level labels in addition to the required labels. +# +# For example, domain_suffix_match=example.com would match +# test.example.com but would not match test-example.com. +# +# domain: Home service provider FQDN(s) # This is used to compare against the Domain Name List to figure out -# whether the AP is operated by the Home SP. +# whether the AP is operated by the Home SP. Multiple domain entries can +# be used to configure alternative FQDNs that will be considered home +# networks. # # roaming_consortium: Roaming Consortium OI # If roaming_consortium_len is non-zero, this field contains the @@ -203,6 +219,7 @@ Credentials can be pre-configured for automatic network selection: # password="password" # ca_cert="/etc/wpa_supplicant/ca.pem" # domain="example.com" +# domain_suffix_match="example.com" #} # #cred={ @@ -267,6 +284,17 @@ OK Note: the return value of add_cred is used as the first argument to the following set_cred commands. +Add a SIM credential using a external SIM/USIM processing: + +> set external_sim 1 +OK +> add_cred +1 +> set_cred 1 imsi "23456-0000000000" +OK +> set_cred 1 eap SIM +OK + Add a WPA2-Enterprise network: diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 2b173650..ea7ac5af 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1579,6 +1579,7 @@ static const struct parse_data ssid_fields[] = { { STRe(dh_file) }, { STRe(subject_match) }, { STRe(altsubject_match) }, + { STRe(domain_suffix_match) }, { STRe(ca_cert2) }, { STRe(ca_path2) }, { STRe(client_cert2) }, @@ -1587,6 +1588,7 @@ static const struct parse_data ssid_fields[] = { { STRe(dh_file2) }, { STRe(subject_match2) }, { STRe(altsubject_match2) }, + { STRe(domain_suffix_match2) }, { STRe(phase1) }, { STRe(phase2) }, { STRe(pcsc) }, @@ -1786,6 +1788,7 @@ static void eap_peer_config_free(struct eap_peer_config *eap) os_free(eap->dh_file); os_free(eap->subject_match); os_free(eap->altsubject_match); + os_free(eap->domain_suffix_match); os_free(eap->ca_cert2); os_free(eap->ca_path2); os_free(eap->client_cert2); @@ -1794,6 +1797,7 @@ static void eap_peer_config_free(struct eap_peer_config *eap) os_free(eap->dh_file2); os_free(eap->subject_match2); os_free(eap->altsubject_match2); + os_free(eap->domain_suffix_match2); os_free(eap->phase1); os_free(eap->phase2); os_free(eap->pcsc); @@ -1811,6 +1815,7 @@ static void eap_peer_config_free(struct eap_peer_config *eap) os_free(eap->pending_req_otp); os_free(eap->pac_file); os_free(eap->new_password); + os_free(eap->external_sim_resp); } #endif /* IEEE8021X_EAPOL */ @@ -1851,6 +1856,8 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) void wpa_config_free_cred(struct wpa_cred *cred) { + size_t i; + os_free(cred->realm); os_free(cred->username); os_free(cred->password); @@ -1860,7 +1867,10 @@ void wpa_config_free_cred(struct wpa_cred *cred) os_free(cred->private_key_passwd); os_free(cred->imsi); os_free(cred->milenage); + for (i = 0; i < cred->num_domain; i++) + os_free(cred->domain[i]); os_free(cred->domain); + os_free(cred->domain_suffix_match); os_free(cred->eap_method); os_free(cred->phase1); os_free(cred->phase2); @@ -2432,9 +2442,23 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, return 0; } + if (os_strcmp(var, "domain_suffix_match") == 0) { + os_free(cred->domain_suffix_match); + cred->domain_suffix_match = val; + return 0; + } + if (os_strcmp(var, "domain") == 0) { - os_free(cred->domain); - cred->domain = val; + char **new_domain; + new_domain = os_realloc_array(cred->domain, + cred->num_domain + 1, + sizeof(char *)); + if (new_domain == NULL) { + os_free(val); + return -1; + } + new_domain[cred->num_domain++] = val; + cred->domain = new_domain; return 0; } @@ -2464,6 +2488,21 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, return 0; } + if (os_strcmp(var, "required_roaming_consortium") == 0) { + if (len < 3 || len > sizeof(cred->required_roaming_consortium)) + { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "required_roaming_consortium length %d " + "(3..15 expected)", line, (int) len); + os_free(val); + return -1; + } + os_memcpy(cred->required_roaming_consortium, val, len); + cred->required_roaming_consortium_len = len; + os_free(val); + return 0; + } + if (os_strcmp(var, "excluded_ssid") == 0) { struct excluded_ssid *e; @@ -3156,6 +3195,7 @@ static const struct global_parse_data global_fields[] = { { STR(pkcs11_module_path), 0 }, { STR(pcsc_reader), 0 }, { STR(pcsc_pin), 0 }, + { INT(external_sim), 0 }, { STR(driver_param), 0 }, { INT(dot11RSNAConfigPMKLifetime), 0 }, { INT(dot11RSNAConfigPMKReauthThreshold), 0 }, diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 1748cf3b..2e558fdb 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -150,12 +150,37 @@ struct wpa_cred { char *milenage; /** - * domain - Home service provider FQDN + * domain_suffix_match - Constraint for server domain name + * + * If set, this FQDN is used as a suffix match requirement for the AAA + * server certificate in SubjectAltName dNSName element(s). If a + * matching dNSName is found, this constraint is met. If no dNSName + * values are present, this constraint is matched against SubjetName CN + * using same suffix match comparison. Suffix match here means that the + * host/domain name is compared one label at a time starting from the + * top-level domain and all the labels in @domain_suffix_match shall be + * included in the certificate. The certificate may include additional + * sub-level labels in addition to the required labels. + * + * For example, domain_suffix_match=example.com would match + * test.example.com but would not match test-example.com. + */ + char *domain_suffix_match; + + /** + * domain - Home service provider FQDN(s) * * This is used to compare against the Domain Name List to figure out - * whether the AP is operated by the Home SP. + * whether the AP is operated by the Home SP. Multiple domain entries + * can be used to configure alternative FQDNs that will be considered + * home networks. + */ + char **domain; + + /** + * num_domain - Number of FQDNs in the domain array */ - char *domain; + size_t num_domain; /** * roaming_consortium - Roaming Consortium OI @@ -175,6 +200,9 @@ struct wpa_cred { */ size_t roaming_consortium_len; + u8 required_roaming_consortium[15]; + size_t required_roaming_consortium_len; + /** * eap_method - EAP method to use * @@ -425,6 +453,11 @@ struct wpa_config { char *pcsc_pin; /** + * external_sim - Use external processing for SIM/USIM operations + */ + int external_sim; + + /** * driver_param - Driver interface parameters * * This text string is passed to the selected driver interface with the diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 0d2bd8cf..b8fff70c 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -667,6 +667,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(dh_file); STR(subject_match); STR(altsubject_match); + STR(domain_suffix_match); STR(ca_cert2); STR(ca_path2); STR(client_cert2); @@ -675,6 +676,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(dh_file2); STR(subject_match2); STR(altsubject_match2); + STR(domain_suffix_match2); STR(phase1); STR(phase2); STR(pcsc); @@ -726,6 +728,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred) { + size_t i; + if (cred->priority) fprintf(f, "\tpriority=%d\n", cred->priority); if (cred->pcsc) @@ -751,10 +755,12 @@ static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred) fprintf(f, "\timsi=\"%s\"\n", cred->imsi); if (cred->milenage) fprintf(f, "\tmilenage=\"%s\"\n", cred->milenage); - if (cred->domain) - fprintf(f, "\tdomain=\"%s\"\n", cred->domain); + for (i = 0; i < cred->num_domain; i++) + fprintf(f, "\tdomain=\"%s\"\n", cred->domain[i]); + if (cred->domain_suffix_match) + fprintf(f, "\tdomain_suffix_match=\"%s\"", + cred->domain_suffix_match); if (cred->roaming_consortium_len) { - size_t i; fprintf(f, "\troaming_consortium="); for (i = 0; i < cred->roaming_consortium_len; i++) fprintf(f, "%02x", cred->roaming_consortium[i]); @@ -771,7 +777,7 @@ static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred) if (cred->phase2) fprintf(f, "\tphase2=\"%s\"\n", cred->phase2); if (cred->excluded_ssid) { - size_t i, j; + size_t j; for (i = 0; i < cred->num_excluded_ssid; i++) { struct excluded_ssid *e = &cred->excluded_ssid[i]; fprintf(f, "\texcluded_ssid="); @@ -1049,6 +1055,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->sched_scan_interval) fprintf(f, "sched_scan_interval=%u\n", config->sched_scan_interval); + + if (config->external_sim) + fprintf(f, "external_sim=%d\n", config->external_sim); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c index 1b5e2379..00a10045 100644 --- a/wpa_supplicant/config_winreg.c +++ b/wpa_supplicant/config_winreg.c @@ -623,6 +623,9 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk) wpa_config_write_reg_dword(hk, TEXT("okc"), config->okc, 0); wpa_config_write_reg_dword(hk, TEXT("pmf"), config->pmf, 0); + wpa_config_write_reg_dword(hk, TEXT("external_sim"), + config->external_sim, 0); + return 0; } diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 4aa67f77..15f5268f 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -1565,16 +1565,21 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, char *type; for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + size_t i; + if (wpa_s->current_ssid->parent_cred != cred) continue; if (!cred->domain) continue; - ret = os_snprintf(pos, end - pos, "home_sp=%s\n", - cred->domain); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; + for (i = 0; i < cred->num_domain; i++) { + ret = os_snprintf(pos, end - pos, + "home_sp=%s\n", + cred->domain[i]); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } if (wpa_s->current_bss == NULL || wpa_s->current_bss->anqp == NULL) @@ -2467,7 +2472,7 @@ static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s, ret = os_snprintf(pos, end - pos, "%d\t%s\t%s\t%s\t%s\n", cred->id, cred->realm ? cred->realm : "", cred->username ? cred->username : "", - cred->domain ? cred->domain : "", + cred->domain ? cred->domain[0] : "", cred->imsi ? cred->imsi : ""); if (ret < 0 || ret >= end - pos) return pos - buf; @@ -2552,9 +2557,16 @@ static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s, while (cred) { prev = cred; cred = cred->next; - if (prev->domain && - os_strcmp(prev->domain, cmd + 8) == 0) - wpas_ctrl_remove_cred(wpa_s, prev); + if (prev->domain) { + size_t i; + for (i = 0; i < prev->num_domain; i++) { + if (os_strcmp(prev->domain[i], cmd + 8) + != 0) + continue; + wpas_ctrl_remove_cred(wpa_s, prev); + break; + } + } } return 0; } @@ -5194,6 +5206,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) #ifdef CONFIG_WPS wpas_wps_cancel(wpa_s); #endif /* CONFIG_WPS */ + wpa_s->after_wps = 0; #ifdef CONFIG_TDLS_TESTING extern unsigned int tdls_testing; @@ -5229,12 +5242,18 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) } +static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + eapol_sm_notify_ctrl_response(wpa_s->eapol); +} + + char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, char *buf, size_t *resp_len) { char *reply; const int reply_size = 4096; - int ctrl_rsp = 0; int reply_len; if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 || @@ -5573,8 +5592,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpa_supplicant_ctrl_iface_ctrl_rsp( wpa_s, buf + os_strlen(WPA_CTRL_RSP))) reply_len = -1; - else - ctrl_rsp = 1; + else { + /* + * Notify response from timeout to allow the control + * interface response to be sent first. + */ + eloop_register_timeout(0, 0, wpas_ctrl_eapol_response, + wpa_s, NULL); + } } else if (os_strcmp(buf, "RECONFIGURE") == 0) { if (wpa_supplicant_reload_configuration(wpa_s)) reply_len = -1; @@ -5615,6 +5640,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, (wpa_s->wpa_state == WPA_COMPLETED))) { wpa_s->normal_scans = 0; wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_s->after_wps = 0; wpa_supplicant_req_scan(wpa_s, 0, 0); } else if (wpa_s->sched_scanning) { wpa_printf(MSG_DEBUG, "Stop ongoing " @@ -5777,9 +5803,6 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = 5; } - if (ctrl_rsp) - eapol_sm_notify_ctrl_response(wpa_s->eapol); - *resp_len = reply_len; return reply; } diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c index ddd2c825..f9521f6d 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/wpa_supplicant/dbus/dbus_new.c @@ -1362,7 +1362,7 @@ void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s, DBusMessageIter iter, dict_iter; struct wpas_dbus_priv *iface; - wpa_printf(MSG_INFO, "%s\n", __func__); + wpa_printf(MSG_DEBUG, "%s", __func__); iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c index 478d02f6..bb6b7b98 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/wpa_supplicant/dbus/dbus_new_handlers.c @@ -1236,6 +1236,23 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, } +static int wpas_dbus_get_scan_allow_roam(DBusMessage *message, + DBusMessageIter *var, + dbus_bool_t *allow, + DBusMessage **reply) +{ + if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN) { + wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " + "Type must be a boolean"); + *reply = wpas_dbus_error_invalid_args( + message, "Wrong Type value type. Boolean required"); + return -1; + } + dbus_message_iter_get_basic(var, allow); + return 0; +} + + /** * wpas_dbus_handler_scan - Request a wireless scan on an interface * @message: Pointer to incoming dbus message @@ -1254,6 +1271,7 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, char *key = NULL, *type = NULL; struct wpa_driver_scan_params params; size_t i; + dbus_bool_t allow_roam = 1; os_memset(¶ms, 0, sizeof(params)); @@ -1284,6 +1302,12 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, if (wpas_dbus_get_scan_channels(message, &variant_iter, ¶ms, &reply) < 0) goto out; + } else if (os_strcmp(key, "AllowRoam") == 0) { + if (wpas_dbus_get_scan_allow_roam(message, + &variant_iter, + &allow_roam, + &reply) < 0) + goto out; } else { wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " "Unknown argument %s", key); @@ -1332,6 +1356,9 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, goto out; } + if (!allow_roam) + wpa_s->scan_res_handler = scan_only_handler; + out: for (i = 0; i < WPAS_MAX_SCAN_SSIDS; i++) os_free((u8 *) params.ssids[i].ssid); diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 6a96331e..628b81b4 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -718,4 +718,13 @@ static inline int wpa_drv_status(struct wpa_supplicant *wpa_s, return wpa_s->driver->status(wpa_s->drv_priv, buf, buflen); } +static inline int wpa_drv_set_qos_map(struct wpa_supplicant *wpa_s, + const u8 *qos_map_set, u8 qos_map_set_len) +{ + if (!wpa_s->driver->set_qos_map) + return -1; + return wpa_s->driver->set_qos_map(wpa_s->drv_priv, qos_map_set, + qos_map_set_len); +} + #endif /* DRIVER_I_H */ diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c index dad27654..9024f23c 100644 --- a/wpa_supplicant/eapol_test.c +++ b/wpa_supplicant/eapol_test.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - test code - * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,6 +27,7 @@ #include "common/wpa_ctrl.h" #include "ctrl_iface.h" #include "pcsc_funcs.h" +#include "wpas_glue.h" extern int wpa_debug_level; @@ -395,6 +396,54 @@ static void eapol_test_write_cert(FILE *f, const char *subject, } +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static void eapol_test_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field, + const char *default_txt) +{ + struct eapol_test_data *e = ctx; + struct wpa_supplicant *wpa_s = e->wpa_s; + struct wpa_ssid *ssid = wpa_s->current_ssid; + const char *field_name, *txt = NULL; + char *buf; + size_t buflen; + int len; + + if (ssid == NULL) + return; + + field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt, + &txt); + if (field_name == NULL) { + wpa_printf(MSG_WARNING, "Unhandled EAP param %d needed", + field); + return; + } + + buflen = 100 + os_strlen(txt) + ssid->ssid_len; + buf = os_malloc(buflen); + if (buf == NULL) + return; + len = os_snprintf(buf, buflen, + WPA_CTRL_REQ "%s-%d:%s needed for SSID ", + field_name, ssid->id, txt); + if (len < 0 || (size_t) len >= buflen) { + os_free(buf); + return; + } + if (ssid->ssid && buflen > len + ssid->ssid_len) { + os_memcpy(buf + len, ssid->ssid, ssid->ssid_len); + len += ssid->ssid_len; + buf[len] = '\0'; + } + buf[buflen - 1] = '\0'; + wpa_msg(wpa_s, MSG_INFO, "%s", buf); + os_free(buf); +} +#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +#define eapol_test_eap_param_needed NULL +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + static void eapol_test_cert_cb(void *ctx, int depth, const char *subject, const char *cert_hash, const struct wpabuf *cert) @@ -484,6 +533,7 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path; ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path; ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path; + ctx->eap_param_needed = eapol_test_eap_param_needed; ctx->cert_cb = eapol_test_cert_cb; ctx->cert_in_cb = 1; ctx->set_anon_id = eapol_test_set_anon_id; @@ -501,6 +551,7 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, eapol_conf.required_keys = 0; eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; eapol_conf.workaround = ssid->eap_workaround; + eapol_conf.external_sim = wpa_s->conf->external_sim; eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); @@ -1094,6 +1145,7 @@ static void usage(void) int main(int argc, char *argv[]) { + struct wpa_global global; struct wpa_supplicant wpa_s; int c, ret = 1, wait_for_monitor = 0, save_config = 0; char *as_addr = "127.0.0.1"; @@ -1230,8 +1282,12 @@ int main(int argc, char *argv[]) return -1; } + os_memset(&global, 0, sizeof(global)); os_memset(&wpa_s, 0, sizeof(wpa_s)); + wpa_s.global = &global; eapol_test.wpa_s = &wpa_s; + dl_list_init(&wpa_s.bss); + dl_list_init(&wpa_s.bss_id); wpa_s.conf = wpa_config_read(conf, NULL); if (wpa_s.conf == NULL) { printf("Failed to parse configuration file '%s'.\n", conf); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 77005951..fdf25b50 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -278,7 +278,8 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, #ifdef PCSC_FUNCS int aka = 0, sim = 0; - if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL) + if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL || + wpa_s->conf->external_sim) return 0; if (ssid->eap.eap_methods == NULL) { @@ -1506,6 +1507,43 @@ void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s) } +#ifdef CONFIG_INTERWORKING + +static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map, + size_t len) +{ + int res; + + wpa_hexdump(MSG_DEBUG, "Interworking: QoS Map Set", qos_map, len); + res = wpa_drv_set_qos_map(wpa_s, qos_map, len); + if (res) { + wpa_printf(MSG_DEBUG, "Interworking: Failed to configure QoS Map Set to the driver"); + } + + return res; +} + + +static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t ies_len) +{ + struct ieee802_11_elems elems; + + if (ies == NULL) + return; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) + return; + + if (elems.qos_map_set) { + wpas_qos_map_set(wpa_s, elems.qos_map_set, + elems.qos_map_set_len); + } +} + +#endif /* CONFIG_INTERWORKING */ + + static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { @@ -1530,6 +1568,10 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #endif /* CONFIG_WNM */ +#ifdef CONFIG_INTERWORKING + interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); +#endif /* CONFIG_INTERWORKING */ } if (data->assoc_info.beacon_ies) wpa_hexdump(MSG_DEBUG, "beacon_ies", @@ -2958,6 +3000,19 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; } #endif /* CONFIG_TDLS */ +#ifdef CONFIG_INTERWORKING + if (data->rx_action.category == WLAN_ACTION_QOS && + data->rx_action.len >= 1 && + data->rx_action.data[0] == QOS_QOS_MAP_CONFIG) { + wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: Received QoS Map Configure frame from " + MACSTR, MAC2STR(data->rx_action.sa)); + if (os_memcmp(data->rx_action.sa, wpa_s->bssid, ETH_ALEN) + == 0) + wpas_qos_map_set(wpa_s, data->rx_action.data + 1, + data->rx_action.len - 1); + break; + } +#endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_P2P wpas_p2p_rx_action(wpa_s, data->rx_action.da, data->rx_action.sa, diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c index 06a97d35..f9885e39 100644 --- a/wpa_supplicant/gas_query.c +++ b/wpa_supplicant/gas_query.c @@ -1,7 +1,7 @@ /* * Generic advertisement service (GAS) query * Copyright (c) 2009, Atheros Communications - * Copyright (c) 2011, Qualcomm Atheros + * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -21,6 +21,8 @@ /** GAS query timeout in seconds */ #define GAS_QUERY_TIMEOUT_PERIOD 2 +/** Retry period for GAS query requests in milliseconds */ +#define GAS_SERVICE_RETRY_PERIOD_MS 500 /** @@ -35,6 +37,7 @@ struct gas_query_pending { unsigned int offchannel_tx_started:1; int freq; u16 status_code; + struct wpabuf *req; struct wpabuf *adv_proto; struct wpabuf *resp; void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, @@ -50,11 +53,13 @@ struct gas_query_pending { struct gas_query { struct wpa_supplicant *wpa_s; struct dl_list pending; /* struct gas_query_pending */ + struct gas_query_pending *current; }; static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx); static void gas_query_timeout(void *eloop_data, void *user_ctx); +static void gas_service_timeout(void *eloop_data, void *user_ctx); /** @@ -81,13 +86,17 @@ static void gas_query_done(struct gas_query *gas, struct gas_query_pending *query, enum gas_query_result result) { + if (gas->current == query) + gas->current = NULL; if (query->offchannel_tx_started) offchannel_send_action_done(gas->wpa_s); eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); eloop_cancel_timeout(gas_query_timeout, gas, query); + eloop_cancel_timeout(gas_service_timeout, gas, query); dl_list_del(&query->list); query->cb(query->ctx, query->addr, query->dialog_token, result, query->adv_proto, query->resp, query->status_code); + wpabuf_free(query->req); wpabuf_free(query->adv_proto); wpabuf_free(query->resp); os_free(query); @@ -138,6 +147,44 @@ static int gas_query_append(struct gas_query_pending *query, const u8 *data, } +static void gas_query_tx_status(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result result) +{ + struct gas_query_pending *query; + struct gas_query *gas = wpa_s->gas; + + if (gas->current == NULL) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst=" + MACSTR " result=%d - no query in progress", + freq, MAC2STR(dst), result); + return; + } + + query = gas->current; + + wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR + " result=%d query=%p dialog_token=%u", + freq, MAC2STR(dst), result, query, query->dialog_token); + if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination"); + return; + } + + if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) { + eloop_cancel_timeout(gas_query_timeout, gas, query); + eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, + gas_query_timeout, gas, query); + } + if (result == OFFCHANNEL_SEND_ACTION_FAILED) { + eloop_cancel_timeout(gas_query_timeout, gas, query); + eloop_register_timeout(0, 0, gas_query_timeout, gas, query); + } +} + + static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, struct wpabuf *req) { @@ -148,7 +195,7 @@ static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, res = offchannel_send_action(gas->wpa_s, query->freq, query->addr, gas->wpa_s->own_addr, query->addr, wpabuf_head(req), wpabuf_len(req), 1000, - NULL, 0); + gas_query_tx_status, 0); if (res == 0) query->offchannel_tx_started = 1; return res; @@ -426,12 +473,50 @@ static void gas_query_timeout(void *eloop_data, void *user_ctx) struct gas_query *gas = eloop_data; struct gas_query_pending *query = user_ctx; - wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR, - MAC2STR(query->addr)); + wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR + " dialog token %u", + MAC2STR(query->addr), query->dialog_token); gas_query_done(gas, query, GAS_QUERY_TIMEOUT); } +static void gas_service_timeout(void *eloop_data, void *user_ctx) +{ + struct gas_query *gas = eloop_data; + struct wpa_supplicant *wpa_s = gas->wpa_s; + struct gas_query_pending *query = user_ctx; + int conn; + + conn = wpas_wpa_is_in_progress(wpa_s, 1); + if (conn || wpa_s->scanning || gas->current) { + wpa_printf(MSG_DEBUG, "GAS: Delaying GAS query Tx while another operation is in progress:%s%s%s", + conn ? " connection" : "", + wpa_s->scanning ? " scanning" : "", + gas->current ? " gas_query" : ""); + eloop_register_timeout( + GAS_SERVICE_RETRY_PERIOD_MS / 1000, + (GAS_SERVICE_RETRY_PERIOD_MS % 1000) * 1000, + gas_service_timeout, gas, query); + return; + } + + if (gas_query_tx(gas, query, query->req) < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " + MACSTR, MAC2STR(query->addr)); + dl_list_del(&query->list); + wpabuf_free(query->req); + os_free(query); + return; + } + gas->current = query; + + wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u", + query->dialog_token); + eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, + gas_query_timeout, gas, query); +} + + static int gas_query_dialog_token_available(struct gas_query *gas, const u8 *dst, u8 dialog_token) { @@ -451,7 +536,8 @@ static int gas_query_dialog_token_available(struct gas_query *gas, * @gas: GAS query data from gas_query_init() * @dst: Destination MAC address for the query * @freq: Frequency (in MHz) for the channel on which to send the query - * @req: GAS query payload + * @req: GAS query payload (to be freed by gas_query module in case of success + * return) * @cb: Callback function for reporting GAS query result and response * @ctx: Context pointer to use with the @cb call * Returns: dialog token (>= 0) on success or -1 on failure @@ -490,22 +576,15 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, query->freq = freq; query->cb = cb; query->ctx = ctx; + query->req = req; dl_list_add(&gas->pending, &query->list); *(wpabuf_mhead_u8(req) + 2) = dialog_token; wpa_printf(MSG_DEBUG, "GAS: Starting request for " MACSTR " dialog_token %u", MAC2STR(dst), dialog_token); - if (gas_query_tx(gas, query, req) < 0) { - wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " - MACSTR, MAC2STR(query->addr)); - dl_list_del(&query->list); - os_free(query); - return -1; - } - eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout, - gas, query); + eloop_register_timeout(0, 0, gas_service_timeout, gas, query); return dialog_token; } @@ -526,3 +605,9 @@ void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token) gas_query_done(gas, query, GAS_QUERY_CANCELLED); } + + +int gas_query_in_progress(struct gas_query *gas) +{ + return gas->current != NULL; +} diff --git a/wpa_supplicant/gas_query.h b/wpa_supplicant/gas_query.h index 5c3d161a..6b6c77ce 100644 --- a/wpa_supplicant/gas_query.h +++ b/wpa_supplicant/gas_query.h @@ -18,6 +18,7 @@ struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s); void gas_query_deinit(struct gas_query *gas); int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, const u8 *bssid, const u8 *data, size_t len, int freq); +int gas_query_in_progress(struct gas_query *gas); /** * enum gas_query_result - GAS query result diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c index 4048cf76..5f30313b 100644 --- a/wpa_supplicant/hs20_supplicant.c +++ b/wpa_supplicant/hs20_supplicant.c @@ -125,12 +125,12 @@ int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s); if (res < 0) { wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); + wpabuf_free(buf); ret = -1; } else wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " "%u", res); - wpabuf_free(buf); return ret; } diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index 36f75a16..e2949179 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -112,6 +112,8 @@ static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s) for (cred = wpa_s->conf->cred; cred; cred = cred->next) { if (cred->roaming_consortium_len) return 1; + if (cred->required_roaming_consortium_len) + return 1; } return 0; } @@ -242,6 +244,7 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, interworking_anqp_resp_cb, wpa_s); if (res < 0) { wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); + wpabuf_free(buf); ret = -1; eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s, NULL); @@ -249,7 +252,6 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " "%u", res); - wpabuf_free(buf); return ret; } @@ -944,6 +946,27 @@ static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp, } +static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss) +{ + const u8 *ie; + + if (cred->required_roaming_consortium_len == 0) + return 0; + + ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); + + if (ie == NULL && + (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL)) + return 1; + + return !roaming_consortium_match(ie, + bss->anqp ? + bss->anqp->roaming_consortium : NULL, + cred->required_roaming_consortium, + cred->required_roaming_consortium_len); +} + + static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss) { size_t i; @@ -991,6 +1014,8 @@ static struct wpa_cred * interworking_credentials_available_roaming_consortium( if (cred_excluded_ssid(cred, bss)) continue; + if (cred_no_required_oi_match(cred, bss)) + continue; if (selected == NULL || selected->priority < cred->priority) @@ -1100,6 +1125,11 @@ static int interworking_set_eap_params(struct wpa_ssid *ssid, wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0) return -1; + if (cred->domain_suffix_match && cred->domain_suffix_match[0] && + wpa_config_set_quoted(ssid, "domain_suffix_match", + cred->domain_suffix_match) < 0) + return -1; + return 0; } @@ -1377,7 +1407,8 @@ static struct wpa_cred * interworking_credentials_available_3gpp( #endif /* CONFIG_EAP_PROXY */ if (cred->imsi == NULL || !cred->imsi[0] || - cred->milenage == NULL || !cred->milenage[0]) + (!wpa_s->conf->external_sim && + (cred->milenage == NULL || !cred->milenage[0]))) continue; sep = os_strchr(cred->imsi, '-'); @@ -1404,6 +1435,8 @@ static struct wpa_cred * interworking_credentials_available_3gpp( if (ret) { if (cred_excluded_ssid(cred, bss)) continue; + if (cred_no_required_oi_match(cred, bss)) + continue; if (selected == NULL || selected->priority < cred->priority) selected = cred; @@ -1446,6 +1479,8 @@ static struct wpa_cred * interworking_credentials_available_realm( if (nai_realm_find_eap(cred, &realm[i])) { if (cred_excluded_ssid(cred, bss)) continue; + if (cred_no_required_oi_match(cred, bss)) + continue; if (selected == NULL || selected->priority < cred->priority) selected = cred; @@ -1514,6 +1549,7 @@ int interworking_home_sp_cred(struct wpa_supplicant *wpa_s, struct wpa_cred *cred, struct wpabuf *domain_names) { + size_t i; #ifdef INTERWORKING_3GPP char nai[100], *realm; @@ -1528,6 +1564,12 @@ int interworking_home_sp_cred(struct wpa_supplicant *wpa_s, mnc_len = wpa_s->mnc_len; } #endif /* CONFIG_PCSC */ +#ifdef CONFIG_EAP_PROXY + else if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) { + imsi = wpa_s->imsi; + mnc_len = wpa_s->mnc_len; + } +#endif /* CONFIG_EAP_PROXY */ if (domain_names && imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) { realm = os_strchr(nai, '@'); @@ -1544,10 +1586,12 @@ int interworking_home_sp_cred(struct wpa_supplicant *wpa_s, if (domain_names == NULL || cred->domain == NULL) return 0; - wpa_printf(MSG_DEBUG, "Interworking: Search for match with " - "home SP FQDN %s", cred->domain); - if (domain_name_list_contains(domain_names, cred->domain)) - return 1; + for (i = 0; i < cred->num_domain; i++) { + wpa_printf(MSG_DEBUG, "Interworking: Search for match with " + "home SP FQDN %s", cred->domain[i]); + if (domain_name_list_contains(domain_names, cred->domain[i])) + return 1; + } return 0; } @@ -1833,12 +1877,12 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s); if (res < 0) { wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); + wpabuf_free(buf); ret = -1; } else wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " "%u", res); - wpabuf_free(buf); return ret; } @@ -2133,11 +2177,11 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s); if (res < 0) { wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request"); + wpabuf_free(buf); ret = -1; } else wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token " "%u", res); - wpabuf_free(buf); return ret; } diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 86bcf870..48cd4290 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -3794,7 +3794,8 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, if (bss) { freq = bss->freq; wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency " - "from BSS table: %d MHz", freq); + "from BSS table: %d MHz (SSID %s)", freq, + wpa_ssid_txt(bss->ssid, bss->ssid_len)); } if (freq > 0) { u16 method; @@ -4009,6 +4010,9 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s) res.freq = bss->freq; res.ssid_len = bss->ssid_len; os_memcpy(res.ssid, bss->ssid, bss->ssid_len); + wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency " + "from BSS table: %d MHz (SSID %s)", bss->freq, + wpa_ssid_txt(bss->ssid, bss->ssid_len)); } if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { @@ -5261,7 +5265,7 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, u8 *bssid = NULL; struct wpa_ssid *ssid; int persistent; - int force_freq = 0, pref_freq = 0; + int freq = 0, force_freq = 0, pref_freq = 0; int res; wpa_s->p2p_persistent_go_freq = 0; @@ -5293,6 +5297,7 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, bssid = wpa_s->own_addr; if (go_dev_addr == NULL) go_dev_addr = wpa_s->global->p2p_dev_addr; + freq = ssid->frequency; } else { role = P2P_INVITE_ROLE_CLIENT; if (wpa_s->wpa_state < WPA_ASSOCIATED) { @@ -5304,6 +5309,8 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, if (go_dev_addr == NULL && !is_zero_ether_addr(wpa_s->go_dev_addr)) go_dev_addr = wpa_s->go_dev_addr; + freq = wpa_s->current_bss ? wpa_s->current_bss->freq : + (int) wpa_s->assoc_freq; } wpa_s->parent->pending_invite_ssid_id = -1; @@ -5315,7 +5322,7 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; - res = wpas_p2p_setup_freqs(wpa_s, 0, &force_freq, &pref_freq); + res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq); if (res) return res; wpas_p2p_set_own_freq_preference(wpa_s, force_freq); diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 7eec4683..dc98361d 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -21,6 +21,7 @@ #include "hs20_supplicant.h" #include "notify.h" #include "bss.h" +#include "gas_query.h" #include "scan.h" @@ -595,7 +596,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) } #ifdef CONFIG_P2P - if (wpas_p2p_in_progress(wpa_s) || wpas_wpa_is_in_progress(wpa_s)) { + if (wpas_p2p_in_progress(wpa_s) || wpas_wpa_is_in_progress(wpa_s, 0)) { if (wpa_s->sta_scan_pending && wpas_p2p_in_progress(wpa_s) == 2 && wpa_s->global->p2p_cb_on_scan_complete) { @@ -611,6 +612,14 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) } #endif /* CONFIG_P2P */ +#ifdef CONFIG_GAS + if (gas_query_in_progress(wpa_s->gas)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Delay scan while GAS query is in progress"); + wpa_supplicant_req_scan(wpa_s, 1, 0); + return; + } +#endif /* CONFIG_GAS */ + if (wpa_s->conf->ap_scan == 2) max_ssids = 1; else { @@ -887,8 +896,10 @@ void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec) new_int.usec = remaining.usec; } - eloop_register_timeout(new_int.sec, new_int.usec, wpa_supplicant_scan, - wpa_s, NULL); + if (cancelled) { + eloop_register_timeout(new_int.sec, new_int.usec, + wpa_supplicant_scan, wpa_s, NULL); + } wpa_s->scan_interval = sec; } diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 67275265..8c900c2d 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -625,7 +625,7 @@ static char ** wpa_cli_complete_set(const char *str, int pos) "wps_nfc_dev_pw", "ext_password_backend", "p2p_go_max_inactivity", "auto_interworking", "okc", "pmf", "sae_groups", "dtim_period", "beacon_int", "ap_vendor_elements", - "ignore_old_scan_res", "freq_list" + "ignore_old_scan_res", "freq_list", "external_sim" }; int i, num_fields = sizeof(fields) / sizeof(fields[0]); @@ -1270,6 +1270,38 @@ static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_sim(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid SIM command: needs two arguments " + "(network id and SIM operation response)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "SIM-%s:%s", + argv[0], argv[1]); + if (ret < 0 || ret >= end - pos) { + printf("Too long SIM command.\n"); + return -1; + } + pos += ret; + for (i = 2; i < argc; i++) { + ret = os_snprintf(pos, end - pos, " %s", argv[i]); + if (ret < 0 || ret >= end - pos) { + printf("Too long SIM command.\n"); + return -1; + } + pos += ret; + } + return wpa_ctrl_command(ctrl, cmd); +} + + static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -2488,6 +2520,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { cli_cmd_flag_sensitive, "<network id> <passphrase> = configure private key passphrase\n" " for an SSID" }, + { "sim", wpa_cli_cmd_sim, NULL, + cli_cmd_flag_sensitive, + "<network id> <pin> = report SIM operation result" }, { "bssid", wpa_cli_cmd_bssid, NULL, cli_cmd_flag_none, "<network id> <BSSID> = set preferred BSSID for an SSID" }, diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index f9243167..fc3471d1 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -303,6 +303,7 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s) !wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) && wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA && wpa_s->key_mgmt != WPA_KEY_MGMT_WPS; + eapol_conf.external_sim = wpa_s->conf->external_sim; eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); #endif /* IEEE8021X_EAPOL */ } @@ -1225,6 +1226,9 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) #endif /* CONFIG_INTERWORKING */ break; case 4: /* Bits 32-39 */ +#ifdef CONFIG_INTERWORKING + *pos |= 0x01; /* Bit 32 - QoS Map */ +#endif /* CONFIG_INTERWORKING */ break; case 5: /* Bits 40-47 */ break; @@ -3768,6 +3772,10 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; break; + case WPA_CTRL_REQ_SIM: + os_free(eap->external_sim_resp); + eap->external_sim_resp = os_strdup(value); + break; default: wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field); return -1; @@ -3958,37 +3966,53 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s) } +static int wpas_conn_in_progress(struct wpa_supplicant *wpa_s) +{ + return wpa_s->wpa_state >= WPA_AUTHENTICATING && + wpa_s->wpa_state != WPA_COMPLETED; +} + + /** * wpas_wpa_is_in_progress - Check whether a connection is in progress * @wpa_s: Pointer to wpa_supplicant data + * @include_current: Whether to consider specified interface * * This function is to check if the wpa state is in beginning of the connection * during 4-way handshake or group key handshake with WPA on any shared * interface. */ -int wpas_wpa_is_in_progress(struct wpa_supplicant *wpa_s) +int wpas_wpa_is_in_progress(struct wpa_supplicant *wpa_s, int include_current) { const char *rn, *rn2; struct wpa_supplicant *ifs; - if (!wpa_s->driver->get_radio_name) + if (!wpa_s->driver->get_radio_name) { + if (include_current && wpas_conn_in_progress(wpa_s)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Connection is in progress on interface %s - defer", + wpa_s->ifname); + return 1; + } + return 0; + } rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv); if (rn == NULL || rn[0] == '\0') return 0; for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { - if (ifs == wpa_s || !ifs->driver->get_radio_name) + if (!include_current && ifs == wpa_s) + continue; + if (!ifs->driver->get_radio_name) continue; rn2 = ifs->driver->get_radio_name(ifs->drv_priv); if (!rn2 || os_strcmp(rn, rn2) != 0) continue; - if (ifs->wpa_state >= WPA_AUTHENTICATING && - ifs->wpa_state != WPA_COMPLETED) { + if (wpas_conn_in_progress(ifs)) { wpa_dbg(wpa_s, MSG_DEBUG, "Connection is in progress " - "on interface %s - defer scan", ifs->ifname); + "on interface %s - defer", ifs->ifname); return 1; } } diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index d73d3715..6414f447 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -399,9 +399,11 @@ fast_reauth=1 # milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN> # format # -# domain: Home service provider FQDN +# domain: Home service provider FQDN(s) # This is used to compare against the Domain Name List to figure out -# whether the AP is operated by the Home SP. +# whether the AP is operated by the Home SP. Multiple domain entries can +# be used to configure alternative FQDNs that will be considered home +# networks. # # roaming_consortium: Roaming Consortium OI # If roaming_consortium_len is non-zero, this field contains the diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index be493cfe..d44f0a28 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -823,7 +823,7 @@ int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid, size_t ssid_len); void wpas_request_connection(struct wpa_supplicant *wpa_s); int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf); -int wpas_wpa_is_in_progress(struct wpa_supplicant *wpa_s); +int wpas_wpa_is_in_progress(struct wpa_supplicant *wpa_s, int include_current); /** * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index 61a42bd0..ab6cdca7 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -611,6 +611,8 @@ enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field) return WPA_CTRL_REQ_EAP_OTP; else if (os_strcmp(field, "PASSPHRASE") == 0) return WPA_CTRL_REQ_EAP_PASSPHRASE; + else if (os_strcmp(field, "SIM") == 0) + return WPA_CTRL_REQ_SIM; return WPA_CTRL_REQ_UNKNOWN; } @@ -647,6 +649,9 @@ const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field, *txt = "Private key passphrase"; ret = "PASSPHRASE"; break; + case WPA_CTRL_REQ_SIM: + ret = "SIM"; + break; default: break; } diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index a2b941cf..0d754640 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -882,6 +882,8 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s) int id; struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current; + wpa_s->after_wps = 0; + prev_current = wpa_s->current_ssid; /* Enable the networks disabled during wpas_wps_reassoc */ @@ -1157,6 +1159,8 @@ int wpas_wps_cancel(struct wpa_supplicant *wpa_s) wpas_wps_clear_ap_info(wpa_s); } + wpa_s->after_wps = 0; + return 0; } @@ -2465,6 +2469,9 @@ void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wps_ap_info *ap; + + wpa_s->after_wps = 0; + if (!wpa_s->wps_ap_iter) return; ap = wpas_wps_get_ap_info(wpa_s, bssid); |
