Cumulative patch from commit 5e32f8256f5fcce8b70a95e070375ac549ac935a
5e32f82 tests: Verify HS 2.0R2 deauthentication request
b61e70c HS 2.0R2: Add WFA server-only EAP-TLS server method
8d2a992 HS 2.0R2: RADIUS server support to request Subscr Remediation
ae6d15c HS 2.0R2 AP: Add OSU Providers list ANQP element
f7bd7a0 HS 2.0R2 AP: Add Icon Request and Icon binary File ANQP elements
97596f8 HS 2.0R2 AP: Add support for Session Info URL RADIUS AVP
8e1146d HS 2.0R2 AP: Add support for deauthentication request
a14896e HS 2.0R2 AP: Add OSEN implementation
6ca0853 HS 2.0R2 AP: Use Subscr Remediation request from RADIUS server
7bc9c25 HS 2.0R2 AP: Add STA's Hotspot 2.0 Release Number into Access-Request
76579ec HS 2.0R2 AP: Add AP Hotspot 2.0 Release Number as WFA RADIUS VSA
0dd100f HS 2.0R2 AP: Add definition and helper function for WFA RADIUS VSA
3fb17a9 HS 2.0R2 AP: Add WNM-Notification Request for Subscription Remediation
d5d2478 HS 2.0R2 AP: Update HS 2.0 Indication element to Release 2
a6739e1 HS 2.0R2: Try to scan multiple times for OSU providers
cf6d08a Interworking: Add OCSP parameter to the cred block
6402f2f Interworking: Add more debug info on roaming partner preferences
7479489 Interworking: Add sp_priority cred parameter
751ac99 Interworking: Use a helper function to compare cred priority
aff419f Interworking: Remove separate credential priority tracking
533536d HS 2.0R2: Disable full ESS for as a workaround for per-BSS issues
8a77f1b HS 2.0R2: Slow down connection attempts on EAP failures
76a55a8 HS 2.0R2: Add more debug to network selection
8b4b9fb HS 2.0R2: Fix bandwidth policy BSS selection
28f2a7c HS 2.0R2: Allow excluded network to be selected based on user override
33fb8c5 HS 2.0R2: Add support for Policy/RequiredProtoPortTuple
a45b2dc HS 2.0R2: Add support for Policy/MaximumBSSLoadValue
4cad9df HS 2.0R2: Add support for Policy/MinBackhaulThreshold
aa26ba6 HS 2.0R2: Add tracking of provisioning SP
8e5fdfa HS 2.0R2: Add WFA server-only EAP-TLS peer method
df0f01d HS 2.0R2: Add OSEN client implementation
a5d7563 HS 2.0R2: Add common OSEN definitions
230e373 HS 2.0R2: Add GAS operation duration statistics into debug
b572df8 HS 2.0R2: Add routine for fetching OSU provider information
1d2215f HS 2.0R2: Add OSU Providers list ANQP element
184e110 HS 2.0R2: Add Icon Request and Icon binary File ANQP elements
7ef6947 HS 2.0R2: Add STA support for Deauthentication Request notification
95a3ea9 HS 2.0R2: Add WNM-Notification Request for Subscription Remediation
f9cd147 HS 2.0R2: Update Indication element to Release 2
bc00053 Interworking: Allow roaming partner configuration
ae6f927 nl80211: Add driver capability for GTK_NOT_USED
2c49d04 Do not clear global pmf setting on FLUSH
eef7235 Only try fast reconnect if network is not disabled
3d910ef Interworking: Prefer last added network during network selection
2a33687 P2P: Remove unnecessary ifdef CONFIG_NO_CONFIG_WRITE
050d8b5 Fix documentation for wpa_supplicant_global_ctrl_iface_process()
8c9cb81 DFS: Fix coding style (missing whitespace)
4f1e01b DFS: Add VHT160 available channels
b8058a6 hostapd: DFS allow mixed channels
4db216f wpa_supplicant: Add support for IPv6 with UDP ctrl_iface
e2364d1 hostapd: Deauthenticate clients forbidden by maclist changes
1748f1d hostapd: Make it possible to remove addresses from maclists
064eb05 Add os_remove_in_array()
c1151e4 Force OFDM/HT/VHT to be disabled on channel 14
bfb79dd nl80211: Show regulatory rule flags in debug output
3d7ad2f hostapd: Configure spectrum management capability
e0392f8 hostapd: Add Power Constraint element
891330f Fix spelling s/algorith/algorithm/
f0e30c8 Do not start another connect work while one is pending
3290398 WPS: Fix UNSUBSCRIBE error returns if NT or CALLBACK header is used
f34df28 WPS: Fix UNSUBSCRIBE to return 412 if no SID match found
80f256a WPS: Remove unnecessary filename NULL check
Change-Id: I7dc25a8bb0074f4970ade8d42dfa60da166baf96
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 2880b2d..c745cb2 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -280,6 +280,7 @@
OBJS += hs20_supplicant.c
L_CFLAGS += -DCONFIG_HS20
CONFIG_INTERWORKING=y
+NEED_AES_OMAC1=y
endif
ifdef CONFIG_INTERWORKING
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 7b556e8..2b8cb93 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -286,6 +286,7 @@
OBJS += hs20_supplicant.o
CFLAGS += -DCONFIG_HS20
CONFIG_INTERWORKING=y
+NEED_AES_OMAC1=y
endif
ifdef CONFIG_INTERWORKING
@@ -1267,6 +1268,11 @@
ifeq ($(CONFIG_CTRL_IFACE), udp)
CFLAGS += -DCONFIG_CTRL_IFACE_UDP
endif
+ifeq ($(CONFIG_CTRL_IFACE), udp6)
+CONFIG_CTRL_IFACE=udp
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6
+endif
ifeq ($(CONFIG_CTRL_IFACE), named_pipe)
CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE
endif
@@ -1275,6 +1281,12 @@
CFLAGS += -DCONFIG_CTRL_IFACE_UDP
CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
endif
+ifeq ($(CONFIG_CTRL_IFACE), udp6-remote)
+CONFIG_CTRL_IFACE=udp
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6
+endif
OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o
endif
diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20
index ad29ef7..b6f0673 100644
--- a/wpa_supplicant/README-HS20
+++ b/wpa_supplicant/README-HS20
@@ -213,6 +213,63 @@
# matching with the network. Multiple entries can be used to specify more
# than one SSID.
#
+# roaming_partner: Roaming partner information
+# This optional field can be used to configure preferences between roaming
+# partners. The field is a string in following format:
+# <FQDN>,<0/1 exact match>,<priority>,<* or country code>
+# (non-exact match means any subdomain matches the entry; priority is in
+# 0..255 range with 0 being the highest priority)
+#
+# update_identifier: PPS MO ID
+# (Hotspot 2.0 PerProviderSubscription/UpdateIdentifier)
+#
+# provisioning_sp: FQDN of the SP that provisioned the credential
+# This optional field can be used to keep track of the SP that provisioned
+# the credential to find the PPS MO (./Wi-Fi/<provisioning_sp>).
+#
+# sp_priority: Credential priority within a provisioning SP
+# This is the priority of the credential among all credentials
+# provisionined by the same SP (i.e., for entries that have identical
+# provisioning_sp value). The range of this priority is 0-255 with 0
+# being the highest and 255 the lower priority.
+#
+# Minimum backhaul threshold (PPS/<X+>/Policy/MinBackhauldThreshold/*)
+# These fields can be used to specify minimum download/upload backhaul
+# bandwidth that is preferred for the credential. This constraint is
+# ignored if the AP does not advertise WAN Metrics information or if the
+# limit would prevent any connection. Values are in kilobits per second.
+# min_dl_bandwidth_home
+# min_ul_bandwidth_home
+# min_dl_bandwidth_roaming
+# min_ul_bandwidth_roaming
+#
+# max_bss_load: Maximum BSS Load Channel Utilization (1..255)
+# (PPS/<X+>/Policy/MaximumBSSLoadValue)
+# This value is used as the maximum channel utilization for network
+# selection purposes for home networks. If the AP does not advertise
+# BSS Load or if the limit would prevent any connection, this constraint
+# will be ignored.
+#
+# req_conn_capab: Required connection capability
+# (PPS/<X+>/Policy/RequiredProtoPortTuple)
+# This value is used to configure set of required protocol/port pairs that
+# a roaming network shall support (include explicitly in Connection
+# Capability ANQP element). This constraint is ignored if the AP does not
+# advertise Connection Capability or if this constraint would prevent any
+# network connection. This policy is not used in home networks.
+# Format: <protocol>[:<comma-separated list of ports]
+# Multiple entries can be used to list multiple requirements.
+# For example, number of common TCP protocols:
+# req_conn_capab=6,22,80,443
+# For example, IPSec/IKE:
+# req_conn_capab=17:500
+# req_conn_capab=50
+#
+# ocsp: Whether to use/require OCSP to check server certificate
+# 0 = do not use OCSP stapling (TLS certificate status extension)
+# 1 = try to use OCSP stapling, but not require response
+# 2 = require valid OCSP stapling response
+#
# for example:
#
#cred={
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 9ea6903..482fc64 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -98,6 +98,7 @@
ANQP_DUP(hs20_wan_metrics);
ANQP_DUP(hs20_connection_capability);
ANQP_DUP(hs20_operating_class);
+ ANQP_DUP(hs20_osu_providers_list);
#endif /* CONFIG_HS20 */
#undef ANQP_DUP
@@ -166,6 +167,7 @@
wpabuf_free(anqp->hs20_wan_metrics);
wpabuf_free(anqp->hs20_connection_capability);
wpabuf_free(anqp->hs20_operating_class);
+ wpabuf_free(anqp->hs20_osu_providers_list);
#endif /* CONFIG_HS20 */
os_free(anqp);
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 4deeb5f..30df7ca 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -39,6 +39,7 @@
struct wpabuf *hs20_wan_metrics;
struct wpabuf *hs20_connection_capability;
struct wpabuf *hs20_operating_class;
+ struct wpabuf *hs20_osu_providers_list;
#endif /* CONFIG_HS20 */
};
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 2dd7054..da9580e 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -405,6 +405,8 @@
else if (os_strcmp(start, "RSN") == 0 ||
os_strcmp(start, "WPA2") == 0)
val |= WPA_PROTO_RSN;
+ else if (os_strcmp(start, "OSEN") == 0)
+ val |= WPA_PROTO_OSEN;
else {
wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'",
line, start);
@@ -516,6 +518,10 @@
else if (os_strcmp(start, "FT-SAE") == 0)
val |= WPA_KEY_MGMT_FT_SAE;
#endif /* CONFIG_SAE */
+#ifdef CONFIG_HS20
+ else if (os_strcmp(start, "OSEN") == 0)
+ val |= WPA_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
else {
wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
line, start);
@@ -1923,6 +1929,12 @@
os_free(cred->phase1);
os_free(cred->phase2);
os_free(cred->excluded_ssid);
+ os_free(cred->roaming_partner);
+ os_free(cred->provisioning_sp);
+ for (i = 0; i < cred->num_req_conn_capab; i++)
+ os_free(cred->req_conn_capab_port[i]);
+ os_free(cred->req_conn_capab_port);
+ os_free(cred->req_conn_capab_proto);
os_free(cred);
}
@@ -1998,6 +2010,7 @@
os_free(config->ext_password_backend);
os_free(config->sae_groups);
wpabuf_free(config->ap_vendor_elements);
+ os_free(config->osu_dir);
os_free(config);
}
@@ -2393,6 +2406,69 @@
}
+static int wpa_config_set_cred_req_conn_capab(struct wpa_cred *cred,
+ const char *value)
+{
+ u8 *proto;
+ int **port;
+ int *ports, *nports;
+ const char *pos;
+ unsigned int num_ports;
+
+ proto = os_realloc_array(cred->req_conn_capab_proto,
+ cred->num_req_conn_capab + 1, sizeof(u8));
+ if (proto == NULL)
+ return -1;
+ cred->req_conn_capab_proto = proto;
+
+ port = os_realloc_array(cred->req_conn_capab_port,
+ cred->num_req_conn_capab + 1, sizeof(int *));
+ if (port == NULL)
+ return -1;
+ cred->req_conn_capab_port = port;
+
+ proto[cred->num_req_conn_capab] = atoi(value);
+
+ pos = os_strchr(value, ':');
+ if (pos == NULL) {
+ port[cred->num_req_conn_capab] = NULL;
+ cred->num_req_conn_capab++;
+ return 0;
+ }
+ pos++;
+
+ ports = NULL;
+ num_ports = 0;
+
+ while (*pos) {
+ nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
+ if (nports == NULL) {
+ os_free(ports);
+ return -1;
+ }
+ ports = nports;
+ ports[num_ports++] = atoi(pos);
+
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ break;
+ pos++;
+ }
+
+ nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
+ if (nports == NULL) {
+ os_free(ports);
+ return -1;
+ }
+ ports = nports;
+ ports[num_ports] = -1;
+
+ port[cred->num_req_conn_capab] = ports;
+ cred->num_req_conn_capab++;
+ return 0;
+}
+
+
int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
const char *value, int line)
{
@@ -2409,6 +2485,14 @@
return 0;
}
+ if (os_strcmp(var, "sp_priority") == 0) {
+ int prio = atoi(value);
+ if (prio < 0 || prio > 255)
+ return -1;
+ cred->sp_priority = prio;
+ return 0;
+ }
+
if (os_strcmp(var, "pcsc") == 0) {
cred->pcsc = atoi(value);
return 0;
@@ -2439,6 +2523,44 @@
return 0;
}
+ if (os_strcmp(var, "update_identifier") == 0) {
+ cred->update_identifier = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "min_dl_bandwidth_home") == 0) {
+ cred->min_dl_bandwidth_home = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "min_ul_bandwidth_home") == 0) {
+ cred->min_ul_bandwidth_home = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0) {
+ cred->min_dl_bandwidth_roaming = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0) {
+ cred->min_ul_bandwidth_roaming = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "max_bss_load") == 0) {
+ cred->max_bss_load = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "req_conn_capab") == 0)
+ return wpa_config_set_cred_req_conn_capab(cred, value);
+
+ if (os_strcmp(var, "ocsp") == 0) {
+ cred->ocsp = atoi(value);
+ return 0;
+ }
+
val = wpa_config_parse_string(value, &len);
if (val == NULL) {
wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
@@ -2590,6 +2712,69 @@
return 0;
}
+ if (os_strcmp(var, "roaming_partner") == 0) {
+ struct roaming_partner *p;
+ char *pos;
+
+ p = os_realloc_array(cred->roaming_partner,
+ cred->num_roaming_partner + 1,
+ sizeof(struct roaming_partner));
+ if (p == NULL) {
+ os_free(val);
+ return -1;
+ }
+ cred->roaming_partner = p;
+
+ p = &cred->roaming_partner[cred->num_roaming_partner];
+
+ pos = os_strchr(val, ',');
+ if (pos == NULL) {
+ os_free(val);
+ return -1;
+ }
+ *pos++ = '\0';
+ if (pos - val - 1 >= (int) sizeof(p->fqdn)) {
+ os_free(val);
+ return -1;
+ }
+ os_memcpy(p->fqdn, val, pos - val);
+
+ p->exact_match = atoi(pos);
+
+ pos = os_strchr(pos, ',');
+ if (pos == NULL) {
+ os_free(val);
+ return -1;
+ }
+ *pos++ = '\0';
+
+ p->priority = atoi(pos);
+
+ pos = os_strchr(pos, ',');
+ if (pos == NULL) {
+ os_free(val);
+ return -1;
+ }
+ *pos++ = '\0';
+
+ if (os_strlen(pos) >= sizeof(p->country)) {
+ os_free(val);
+ return -1;
+ }
+ os_memcpy(p->country, pos, os_strlen(pos) + 1);
+
+ cred->num_roaming_partner++;
+ os_free(val);
+
+ return 0;
+ }
+
+ if (os_strcmp(var, "provisioning_sp") == 0) {
+ os_free(cred->provisioning_sp);
+ cred->provisioning_sp = val;
+ return 0;
+ }
+
if (line) {
wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.",
line, var);
@@ -3401,6 +3586,7 @@
{ INT(scan_cur_freq), 0 },
{ INT(sched_scan_interval), 0 },
{ INT(tdls_external_control), 0},
+ { STR(osu_dir), 0 },
};
#undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index e7bdaa5..de43970 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -236,6 +236,58 @@
size_t ssid_len;
} *excluded_ssid;
size_t num_excluded_ssid;
+
+ struct roaming_partner {
+ char fqdn[128];
+ int exact_match;
+ u8 priority;
+ char country[3];
+ } *roaming_partner;
+ size_t num_roaming_partner;
+
+ int update_identifier;
+
+ /**
+ * provisioning_sp - FQDN of the SP that provisioned the credential
+ */
+ char *provisioning_sp;
+
+ /**
+ * sp_priority - Credential priority within a provisioning SP
+ *
+ * This is the priority of the credential among all credentials
+ * provisionined by the same SP (i.e., for entries that have identical
+ * provisioning_sp value). The range of this priority is 0-255 with 0
+ * being the highest and 255 the lower priority.
+ */
+ int sp_priority;
+
+ unsigned int min_dl_bandwidth_home;
+ unsigned int min_ul_bandwidth_home;
+ unsigned int min_dl_bandwidth_roaming;
+ unsigned int min_ul_bandwidth_roaming;
+
+ /**
+ * max_bss_load - Maximum BSS Load Channel Utilization (1..255)
+ * This value is used as the maximum channel utilization for network
+ * selection purposes for home networks. If the AP does not advertise
+ * BSS Load or if the limit would prevent any connection, this
+ * constraint will be ignored.
+ */
+ unsigned int max_bss_load;
+
+ unsigned int num_req_conn_capab;
+ u8 *req_conn_capab_proto;
+ int **req_conn_capab_port;
+
+ /**
+ * ocsp - Whether to use/require OCSP to check server certificate
+ *
+ * 0 = do not use OCSP stapling (TLS certificate status extension)
+ * 1 = try to use OCSP stapling, but not require response
+ * 2 = require valid OCSP stapling response
+ */
+ int ocsp;
};
@@ -953,6 +1005,15 @@
u8 ip_addr_mask[4];
u8 ip_addr_start[4];
u8 ip_addr_end[4];
+
+ /**
+ * osu_dir - OSU provider information directory
+ *
+ * If set, allow FETCH_OSU control interface command to be used to fetch
+ * OSU provider information into all APs and store the results in this
+ * directory.
+ */
+ char *osu_dir;
};
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 6312a77..850a6cf 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -796,6 +796,41 @@
fprintf(f, "\n");
}
}
+ if (cred->roaming_partner) {
+ for (i = 0; i < cred->num_roaming_partner; i++) {
+ struct roaming_partner *p = &cred->roaming_partner[i];
+ fprintf(f, "\troaming_partner=\"%s,%d,%u,%s\"\n",
+ p->fqdn, p->exact_match, p->priority,
+ p->country);
+ }
+ }
+ if (cred->update_identifier)
+ fprintf(f, "\tupdate_identifier=%d\n", cred->update_identifier);
+
+ if (cred->provisioning_sp)
+ fprintf(f, "\tprovisioning_sp=%s\n", cred->provisioning_sp);
+ if (cred->sp_priority)
+ fprintf(f, "\tsp_priority=%d\n", cred->sp_priority);
+
+ if (cred->min_dl_bandwidth_home)
+ fprintf(f, "\tmin_dl_bandwidth_home=%u\n",
+ cred->min_dl_bandwidth_home);
+ if (cred->min_ul_bandwidth_home)
+ fprintf(f, "\tmin_ul_bandwidth_home=%u\n",
+ cred->min_ul_bandwidth_home);
+ if (cred->min_dl_bandwidth_roaming)
+ fprintf(f, "\tmin_dl_bandwidth_roaming=%u\n",
+ cred->min_dl_bandwidth_roaming);
+ if (cred->min_ul_bandwidth_roaming)
+ fprintf(f, "\tmin_ul_bandwidth_roaming=%u\n",
+ cred->min_ul_bandwidth_roaming);
+
+ if (cred->max_bss_load)
+ fprintf(f, "\tmax_bss_load=%u\n",
+ cred->max_bss_load);
+
+ if (cred->ocsp)
+ fprintf(f, "\tocsp=%d\n", cred->ocsp);
}
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index e95b55b..9f5d4f4 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -1554,6 +1554,9 @@
{
char *pos, *end, tmp[30];
int res, verbose, wps, ret;
+#ifdef CONFIG_HS20
+ const u8 *hs20;
+#endif /* CONFIG_HS20 */
if (os_strcmp(params, "-DRIVER") == 0)
return wpa_drv_status(wpa_s, buf, buflen);
@@ -1692,10 +1695,16 @@
#ifdef CONFIG_HS20
if (wpa_s->current_bss &&
- wpa_bss_get_vendor_ie(wpa_s->current_bss, HS20_IE_VENDOR_TYPE) &&
+ (hs20 = wpa_bss_get_vendor_ie(wpa_s->current_bss,
+ HS20_IE_VENDOR_TYPE)) &&
wpa_s->wpa_proto == WPA_PROTO_RSN &&
wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
- ret = os_snprintf(pos, end - pos, "hs20=1\n");
+ int release = 1;
+ if (hs20[1] >= 5) {
+ u8 rel_num = (hs20[6] & 0xf0) >> 4;
+ release = rel_num + 1;
+ }
+ ret = os_snprintf(pos, end - pos, "hs20=%d\n", release);
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
@@ -1711,15 +1720,38 @@
if (wpa_s->current_ssid->parent_cred != cred)
continue;
- for (i = 0; cred->domain && i < cred->num_domain; i++) {
+ if (cred->provisioning_sp) {
ret = os_snprintf(pos, end - pos,
- "home_sp=%s\n",
- cred->domain[i]);
+ "provisioning_sp=%s\n",
+ cred->provisioning_sp);
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
+ if (!cred->domain)
+ goto no_domain;
+
+ i = 0;
+ if (wpa_s->current_bss && wpa_s->current_bss->anqp) {
+ struct wpabuf *names =
+ wpa_s->current_bss->anqp->domain_name;
+ for (i = 0; names && i < cred->num_domain; i++)
+ {
+ if (domain_name_list_contains(
+ names, cred->domain[i], 1))
+ break;
+ }
+ if (i == cred->num_domain)
+ i = 0; /* show first entry by default */
+ }
+ ret = os_snprintf(pos, end - pos, "home_sp=%s\n",
+ cred->domain[i]);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ no_domain:
if (wpa_s->current_bss == NULL ||
wpa_s->current_bss->anqp == NULL)
res = -1;
@@ -2686,7 +2718,8 @@
int id;
struct wpa_cred *cred, *prev;
- /* cmd: "<cred id>", "all", or "sp_fqdn=<FQDN>" */
+ /* cmd: "<cred id>", "all", "sp_fqdn=<FQDN>", or
+ * "provisioning_sp=<FQDN> */
if (os_strcmp(cmd, "all") == 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED all");
cred = wpa_s->conf->cred;
@@ -2719,6 +2752,20 @@
return 0;
}
+ if (os_strncmp(cmd, "provisioning_sp=", 16) == 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED provisioning SP FQDN '%s'",
+ cmd + 16);
+ cred = wpa_s->conf->cred;
+ while (cred) {
+ prev = cred;
+ cred = cred->next;
+ if (prev->provisioning_sp &&
+ os_strcmp(prev->provisioning_sp, cmd + 16) == 0)
+ wpas_ctrl_remove_cred(wpa_s, prev);
+ }
+ return 0;
+ }
+
id = atoi(cmd);
wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED id=%d", id);
@@ -3518,6 +3565,10 @@
anqp->hs20_wan_metrics);
pos = anqp_add_hex(pos, end, "hs20_connection_capability",
anqp->hs20_connection_capability);
+ pos = anqp_add_hex(pos, end, "hs20_operating_class",
+ anqp->hs20_operating_class);
+ pos = anqp_add_hex(pos, end, "hs20_osu_providers_list",
+ anqp->hs20_osu_providers_list);
#endif /* CONFIG_HS20 */
}
#endif /* CONFIG_INTERWORKING */
@@ -5169,6 +5220,26 @@
return ret;
}
+
+static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 dst_addr[ETH_ALEN];
+ int used;
+ char *icon;
+
+ used = hwaddr_aton2(cmd, dst_addr);
+ if (used < 0)
+ return -1;
+
+ while (cmd[used] == ' ')
+ used++;
+ icon = &cmd[used];
+
+ wpa_s->fetch_osu_icon_in_progress = 0;
+ return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST),
+ (u8 *) icon, os_strlen(icon));
+}
+
#endif /* CONFIG_HS20 */
@@ -5454,7 +5525,6 @@
wpa_config_flush_blobs(wpa_s->conf);
wpa_s->conf->auto_interworking = 0;
wpa_s->conf->okc = 0;
- wpa_s->conf->pmf = 0;
wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200);
wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70);
@@ -5462,6 +5532,12 @@
eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
radio_remove_works(wpa_s, NULL, 1);
+
+ wpa_s->next_ssid = NULL;
+
+#ifdef CONFIG_INTERWORKING
+ hs20_cancel_fetch_osu(wpa_s);
+#endif /* CONFIG_INTERWORKING */
}
@@ -6093,6 +6169,14 @@
} else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) {
if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0)
reply_len = -1;
+ } else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) {
+ if (hs20_icon_request(wpa_s, buf + 18) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "FETCH_OSU") == 0) {
+ if (hs20_fetch_osu(wpa_s) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) {
+ hs20_cancel_fetch_osu(wpa_s);
#endif /* CONFIG_HS20 */
} else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
{
diff --git a/wpa_supplicant/ctrl_iface.h b/wpa_supplicant/ctrl_iface.h
index b0dec53..d54cc07 100644
--- a/wpa_supplicant/ctrl_iface.h
+++ b/wpa_supplicant/ctrl_iface.h
@@ -32,7 +32,7 @@
char *buf, size_t *resp_len);
/**
- * wpa_supplicant_ctrl_iface_process - Process global ctrl_iface command
+ * wpa_supplicant_global_ctrl_iface_process - Process global ctrl_iface command
* @global: Pointer to global data from wpa_supplicant_init()
* @buf: Received command buffer (nul terminated string)
* @resp_len: Variable to be set to the response length
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 8c09ba1..9d0674d 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -30,7 +30,11 @@
*/
struct wpa_ctrl_dst {
struct wpa_ctrl_dst *next;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ struct sockaddr_in6 addr;
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in addr;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t addrlen;
int debug_level;
int errors;
@@ -51,38 +55,68 @@
static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv,
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ struct sockaddr_in6 *from,
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in *from,
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t fromlen)
{
struct wpa_ctrl_dst *dst;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_UDP_IPV6 */
dst = os_zalloc(sizeof(*dst));
if (dst == NULL)
return -1;
- os_memcpy(&dst->addr, from, sizeof(struct sockaddr_in));
+ os_memcpy(&dst->addr, from, sizeof(*from));
dst->addrlen = fromlen;
dst->debug_level = MSG_INFO;
dst->next = priv->ctrl_dst;
priv->ctrl_dst = dst;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
+ inet_ntop(AF_INET6, &from->sin6_addr, addr, sizeof(*from)),
+ ntohs(from->sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
inet_ntoa(from->sin_addr), ntohs(from->sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
return 0;
}
static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv,
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ struct sockaddr_in6 *from,
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in *from,
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t fromlen)
{
struct wpa_ctrl_dst *dst, *prev = NULL;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
dst = priv->ctrl_dst;
while (dst) {
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ if (from->sin6_port == dst->addr.sin6_port &&
+ !os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr,
+ sizeof(from->sin6_addr))) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s:%d",
+ inet_ntop(AF_INET6, &from->sin6_addr, addr,
+ sizeof(*from)),
+ ntohs(from->sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
from->sin_port == dst->addr.sin_port) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached "
"%s:%d", inet_ntoa(from->sin_addr),
ntohs(from->sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (prev == NULL)
priv->ctrl_dst = dst->next;
else
@@ -98,21 +132,38 @@
static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ struct sockaddr_in6 *from,
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in *from,
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t fromlen,
char *level)
{
struct wpa_ctrl_dst *dst;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
dst = priv->ctrl_dst;
while (dst) {
+#if CONFIG_CTRL_IFACE_UDP_IPV6
+ if (from->sin6_port == dst->addr.sin6_port &&
+ !os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr,
+ sizeof(from->sin6_addr))) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level %s:%d",
+ inet_ntop(AF_INET6, &from->sin6_addr, addr,
+ sizeof(*from)),
+ ntohs(from->sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
from->sin_port == dst->addr.sin_port) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor "
"level %s:%d", inet_ntoa(from->sin_addr),
ntohs(from->sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
dst->debug_level = atoi(level);
return 0;
}
@@ -150,7 +201,14 @@
struct ctrl_iface_priv *priv = sock_ctx;
char buf[256], *pos;
int res;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ struct sockaddr_in6 from;
+#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
+ char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in from;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
socklen_t fromlen = sizeof(from);
char *reply = NULL;
size_t reply_len = 0;
@@ -165,6 +223,13 @@
}
#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ inet_ntop(AF_INET6, &from.sin6_addr, addr, sizeof(from));
+ if (os_strcmp(addr, "::1")) {
+ wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s",
+ addr);
+ }
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
/*
* The OS networking stack is expected to drop this kind of
@@ -176,6 +241,7 @@
"source %s", inet_ntoa(from.sin_addr));
return;
}
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
buf[res] = '\0';
@@ -269,8 +335,14 @@
wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
{
struct ctrl_iface_priv *priv;
- struct sockaddr_in addr;
int port = WPA_CTRL_IFACE_PORT;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ struct sockaddr_in6 addr;
+ int domain = PF_INET6;
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+ struct sockaddr_in addr;
+ int domain = PF_INET;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
priv = os_zalloc(sizeof(*priv));
if (priv == NULL)
@@ -282,21 +354,34 @@
if (wpa_s->conf->ctrl_interface == NULL)
return priv;
- priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+ priv->sock = socket(domain, SOCK_DGRAM, 0);
if (priv->sock < 0) {
perror("socket(PF_INET)");
goto fail;
}
os_memset(&addr, 0, sizeof(addr));
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ addr.sin6_family = AF_INET6;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+ addr.sin6_addr = in6addr_any;
+#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+ inet_pton(AF_INET6, "::1", &addr.sin6_addr);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
addr.sin_family = AF_INET;
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
addr.sin_addr.s_addr = INADDR_ANY;
#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
addr.sin_addr.s_addr = htonl((127 << 24) | 1);
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
try_again:
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ addr.sin6_port = htons(port);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
addr.sin_port = htons(port);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
port--;
if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT)
@@ -362,6 +447,9 @@
int idx;
char *sbuf;
int llen;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
dst = priv->ctrl_dst;
if (priv->sock < 0 || dst == NULL)
@@ -381,9 +469,16 @@
while (dst) {
next = dst->next;
if (level >= dst->debug_level) {
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
+ inet_ntop(AF_INET6, &dst->addr.sin6_addr,
+ addr, sizeof(dst->addr)),
+ ntohs(dst->addr.sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
inet_ntoa(dst->addr.sin_addr),
ntohs(dst->addr.sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (sendto(priv->sock, sbuf, llen + len, 0,
(struct sockaddr *) &dst->addr,
sizeof(dst->addr)) < 0) {
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 6684782..91eea35 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -192,8 +192,10 @@
# Select control interface backend for external programs, e.g, wpa_cli:
# unix = UNIX domain sockets (default for Linux/*BSD)
# udp = UDP sockets using localhost (127.0.0.1)
+# udp6 = UDP IPv6 sockets using localhost (::1)
# named_pipe = Windows Named Pipe (default for Windows)
# udp-remote = UDP sockets with remote access (only for tests systems/purpose)
+# udp6-remote = UDP IPv6 sockets with remote access (only for tests purpose)
# y = use default (backwards compatibility)
# If this option is commented out, control interface is not included in the
# build.
diff --git a/wpa_supplicant/eap_register.c b/wpa_supplicant/eap_register.c
index 6cd2fc5..ece5716 100644
--- a/wpa_supplicant/eap_register.c
+++ b/wpa_supplicant/eap_register.c
@@ -40,6 +40,13 @@
ret = eap_peer_unauth_tls_register();
#endif /* EAP_UNAUTH_TLS */
+#ifdef EAP_TLS
+#ifdef CONFIG_HS20
+ if (ret == 0)
+ ret = eap_peer_wfa_unauth_tls_register();
+#endif /* CONFIG_HS20 */
+#endif /* EAP_TLS */
+
#ifdef EAP_MSCHAPv2
if (ret == 0)
ret = eap_peer_mschapv2_register();
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index c5e65ef..c0f55ee 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -398,6 +398,9 @@
if (wpa_key_mgmt_wpa(ssid->key_mgmt))
privacy = 1;
+ if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)
+ privacy = 1;
+
if (bss->caps & IEEE80211_CAP_PRIVACY)
return privacy;
return !privacy;
@@ -539,6 +542,12 @@
return 0;
}
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) &&
+ wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " allow in OSEN");
+ return 1;
+ }
+
if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) {
wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2");
return 1;
@@ -728,13 +737,15 @@
static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
int i, struct wpa_bss *bss,
- struct wpa_ssid *group)
+ struct wpa_ssid *group,
+ int only_first_ssid)
{
u8 wpa_ie_len, rsn_ie_len;
int wpa;
struct wpa_blacklist *e;
const u8 *ie;
struct wpa_ssid *ssid;
+ int osen;
ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
wpa_ie_len = ie ? ie[1] : 0;
@@ -742,14 +753,18 @@
ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
rsn_ie_len = ie ? ie[1] : 0;
+ ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
+ osen = ie != NULL;
+
wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
- "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s%s",
+ "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s%s%s",
i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len),
wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "",
(wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ?
- " p2p" : "");
+ " p2p" : "",
+ osen ? " osen=1" : "");
e = wpa_blacklist_get(wpa_s, bss->bssid);
if (e) {
@@ -789,7 +804,7 @@
wpa = wpa_ie_len > 0 || rsn_ie_len > 0;
- for (ssid = group; ssid; ssid = ssid->pnext) {
+ for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) {
int check_ssid = wpa ? 1 : (ssid->ssid_len != 0);
int res;
@@ -847,7 +862,7 @@
if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
continue;
- if (!wpa &&
+ if (!osen && !wpa &&
!(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
!(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
!(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) {
@@ -862,6 +877,12 @@
continue;
}
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-OSEN network "
+ "not allowed");
+ continue;
+ }
+
if (!wpa_supplicant_match_privacy(bss, ssid)) {
wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy "
"mismatch");
@@ -938,16 +959,22 @@
static struct wpa_bss *
wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
struct wpa_ssid *group,
- struct wpa_ssid **selected_ssid)
+ struct wpa_ssid **selected_ssid,
+ int only_first_ssid)
{
unsigned int i;
- wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
- group->priority);
+ if (only_first_ssid)
+ wpa_dbg(wpa_s, MSG_DEBUG, "Try to find BSS matching pre-selected network id=%d",
+ group->id);
+ else
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
+ group->priority);
for (i = 0; i < wpa_s->last_scan_res_used; i++) {
struct wpa_bss *bss = wpa_s->last_scan_res[i];
- *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group);
+ *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group,
+ only_first_ssid);
if (!*selected_ssid)
continue;
wpa_dbg(wpa_s, MSG_DEBUG, " selected BSS " MACSTR
@@ -972,10 +999,27 @@
return NULL; /* no scan results from last update */
while (selected == NULL) {
+ if (wpa_s->next_ssid) {
+ struct wpa_ssid *ssid;
+
+ /* check that next_ssid is still valid */
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+ if (ssid == wpa_s->next_ssid)
+ break;
+ wpa_s->next_ssid = NULL;
+
+ if (ssid) {
+ selected = wpa_supplicant_select_bss(
+ wpa_s, ssid, selected_ssid, 1);
+ if (selected)
+ break;
+ }
+ }
+
for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
selected = wpa_supplicant_select_bss(
wpa_s, wpa_s->conf->pssid[prio],
- selected_ssid);
+ selected_ssid, 0);
if (selected)
break;
}
@@ -2162,7 +2206,12 @@
wpa_s->current_ssid = last_ssid;
}
- if (fast_reconnect) {
+ if (fast_reconnect &&
+ !wpas_network_disabled(wpa_s, fast_reconnect_ssid) &&
+ !disallowed_bssid(wpa_s, fast_reconnect->bssid) &&
+ !disallowed_ssid(wpa_s, fast_reconnect->ssid,
+ fast_reconnect->ssid_len) &&
+ !wpas_temp_disabled(wpa_s, fast_reconnect_ssid)) {
#ifndef CONFIG_NO_SCAN_PROCESSING
wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS");
if (wpa_supplicant_connect(wpa_s, fast_reconnect,
@@ -2171,6 +2220,14 @@
wpa_supplicant_req_scan(wpa_s, 0, 100000);
}
#endif /* CONFIG_NO_SCAN_PROCESSING */
+ } else if (fast_reconnect) {
+ /*
+ * Could not reconnect to the same BSS due to network being
+ * disabled. Use a new scan to match the alternative behavior
+ * above, i.e., to continue automatic reconnection attempt in a
+ * way that enforces disabled network rules.
+ */
+ wpa_supplicant_req_scan(wpa_s, 0, 100000);
}
}
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index b255847..a63ee6c 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -42,6 +42,7 @@
struct wpabuf *req;
struct wpabuf *adv_proto;
struct wpabuf *resp;
+ struct os_reltime last_oper;
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
enum gas_query_result result,
const struct wpabuf *adv_proto,
@@ -64,6 +65,16 @@
static void gas_query_timeout(void *eloop_data, void *user_ctx);
+static int ms_from_time(struct os_reltime *last)
+{
+ struct os_reltime now, res;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, last, &res);
+ return res.sec * 1000 + res.usec / 1000;
+}
+
+
/**
* gas_query_init - Initialize GAS query component
* @wpa_s: Pointer to wpa_supplicant data
@@ -199,6 +210,7 @@
{
struct gas_query_pending *query;
struct gas_query *gas = wpa_s->gas;
+ int dur;
if (gas->current == NULL) {
wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst="
@@ -209,13 +221,15 @@
query = gas->current;
+ dur = ms_from_time(&query->last_oper);
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);
+ " result=%d query=%p dialog_token=%u dur=%d ms",
+ freq, MAC2STR(dst), result, query, query->dialog_token, dur);
if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
return;
}
+ os_get_reltime(&query->last_oper);
if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) {
eloop_cancel_timeout(gas_query_timeout, gas, query);
@@ -251,6 +265,7 @@
u8 *categ = wpabuf_mhead_u8(req);
*categ = WLAN_ACTION_PROTECTED_DUAL;
}
+ os_get_reltime(&query->last_oper);
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,
@@ -452,6 +467,9 @@
return -1;
}
+ wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
+ ms_from_time(&query->last_oper), MAC2STR(sa));
+
if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
MACSTR " dialog token %u when waiting for comeback "
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index 5f30313..b873c7c 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2009, Atheros Communications, Inc.
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -14,22 +14,65 @@
#include "common/ieee802_11_defs.h"
#include "common/gas.h"
#include "common/wpa_ctrl.h"
+#include "rsn_supp/wpa.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "config.h"
+#include "scan.h"
#include "bss.h"
+#include "blacklist.h"
#include "gas_query.h"
#include "interworking.h"
#include "hs20_supplicant.h"
-void wpas_hs20_add_indication(struct wpabuf *buf)
+#define OSU_MAX_ITEMS 10
+
+struct osu_lang_string {
+ char lang[4];
+ char text[253];
+};
+
+struct osu_icon {
+ u16 width;
+ u16 height;
+ char lang[4];
+ char icon_type[256];
+ char filename[256];
+ unsigned int id;
+ unsigned int failed:1;
+};
+
+struct osu_provider {
+ u8 bssid[ETH_ALEN];
+ u8 osu_ssid[32];
+ u8 osu_ssid_len;
+ char server_uri[256];
+ u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */
+ char osu_nai[256];
+ struct osu_lang_string friendly_name[OSU_MAX_ITEMS];
+ size_t friendly_name_count;
+ struct osu_lang_string serv_desc[OSU_MAX_ITEMS];
+ size_t serv_desc_count;
+ struct osu_icon icon[OSU_MAX_ITEMS];
+ size_t icon_count;
+};
+
+
+void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id)
{
+ u8 conf;
+
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
- wpabuf_put_u8(buf, 5);
+ wpabuf_put_u8(buf, pps_mo_id >= 0 ? 7 : 5);
wpabuf_put_be24(buf, OUI_WFA);
wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE);
- wpabuf_put_u8(buf, 0x00); /* Hotspot Configuration */
+ conf = HS20_VERSION;
+ if (pps_mo_id >= 0)
+ conf |= HS20_PPS_MO_ID_PRESENT;
+ wpabuf_put_u8(buf, conf);
+ if (pps_mo_id >= 0)
+ wpabuf_put_le16(buf, pps_mo_id);
}
@@ -62,6 +105,22 @@
}
+int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+ struct wpa_cred *cred;
+
+ if (ssid == NULL || ssid->parent_cred == NULL)
+ return 0;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (ssid->parent_cred == cred)
+ return cred->update_identifier;
+ }
+
+ return 0;
+}
+
+
struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
size_t payload_len)
{
@@ -80,6 +139,11 @@
wpabuf_put_u8(buf, 0); /* Reserved */
if (payload)
wpabuf_put_data(buf, payload, payload_len);
+ } else if (stypes == BIT(HS20_STYPE_ICON_REQUEST)) {
+ wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ if (payload)
+ wpabuf_put_data(buf, payload, payload_len);
} else {
u8 i;
wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST);
@@ -135,6 +199,116 @@
}
+static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *pos,
+ size_t slen)
+{
+ char fname[256];
+ int png;
+ FILE *f;
+ u16 data_len;
+
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR " Icon Binary File",
+ MAC2STR(sa));
+
+ if (slen < 4) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+ "value from " MACSTR, MAC2STR(sa));
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: Download Status Code %u", *pos);
+ if (*pos != 0)
+ return -1;
+ pos++;
+ slen--;
+
+ if ((size_t) 1 + pos[0] > slen) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+ "value from " MACSTR, MAC2STR(sa));
+ return -1;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "Icon Type", pos + 1, pos[0]);
+ png = os_strncasecmp((char *) pos + 1, "image/png", 9) == 0;
+ slen -= 1 + pos[0];
+ pos += 1 + pos[0];
+
+ if (slen < 2) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+ "value from " MACSTR, MAC2STR(sa));
+ return -1;
+ }
+ data_len = WPA_GET_LE16(pos);
+ pos += 2;
+ slen -= 2;
+
+ if (data_len > slen) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+ "value from " MACSTR, MAC2STR(sa));
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "Icon Binary Data: %u bytes", data_len);
+ if (wpa_s->conf->osu_dir == NULL)
+ return -1;
+
+ wpa_s->osu_icon_id++;
+ if (wpa_s->osu_icon_id == 0)
+ wpa_s->osu_icon_id++;
+ snprintf(fname, sizeof(fname), "%s/osu-icon-%u.%s",
+ wpa_s->conf->osu_dir, wpa_s->osu_icon_id,
+ png ? "png" : "icon");
+ f = fopen(fname, "wb");
+ if (f == NULL)
+ return -1;
+ if (fwrite(pos, slen, 1, f) != 1) {
+ fclose(f);
+ unlink(fname);
+ return -1;
+ }
+ fclose(f);
+
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP-ICON %s", fname);
+ return 0;
+}
+
+
+static void hs20_continue_icon_fetch(void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ if (wpa_s->fetch_osu_icon_in_progress)
+ hs20_next_osu_icon(wpa_s);
+}
+
+
+static void hs20_osu_icon_fetch_result(struct wpa_supplicant *wpa_s, int res)
+{
+ size_t i, j;
+ struct os_reltime now, tmp;
+ int dur;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &wpa_s->osu_icon_fetch_start, &tmp);
+ dur = tmp.sec * 1000 + tmp.usec / 1000;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Icon fetch dur=%d ms res=%d",
+ dur, res);
+
+ for (i = 0; i < wpa_s->osu_prov_count; i++) {
+ struct osu_provider *osu = &wpa_s->osu_prov[i];
+ for (j = 0; j < osu->icon_count; j++) {
+ struct osu_icon *icon = &osu->icon[j];
+ if (icon->id || icon->failed)
+ continue;
+ if (res < 0)
+ icon->failed = 1;
+ else
+ icon->id = wpa_s->osu_icon_id;
+ return;
+ }
+ }
+}
+
+
void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
const u8 *sa, const u8 *data, size_t slen)
{
@@ -142,6 +316,7 @@
u8 subtype;
struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
struct wpa_bss_anqp *anqp = NULL;
+ int ret;
if (slen < 2)
return;
@@ -207,8 +382,530 @@
wpabuf_alloc_copy(pos, slen);
}
break;
+ case HS20_STYPE_OSU_PROVIDERS_LIST:
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+ " OSU Providers list", MAC2STR(sa));
+ wpa_s->num_prov_found++;
+ if (anqp) {
+ wpabuf_free(anqp->hs20_osu_providers_list);
+ anqp->hs20_osu_providers_list =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case HS20_STYPE_ICON_BINARY_FILE:
+ ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen);
+ if (wpa_s->fetch_osu_icon_in_progress) {
+ hs20_osu_icon_fetch_result(wpa_s, ret);
+ eloop_cancel_timeout(hs20_continue_icon_fetch,
+ wpa_s, NULL);
+ eloop_register_timeout(0, 0, hs20_continue_icon_fetch,
+ wpa_s, NULL);
+ }
+ break;
default:
wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype);
break;
}
}
+
+
+void hs20_notify_parse_done(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->fetch_osu_icon_in_progress)
+ return;
+ if (eloop_is_timeout_registered(hs20_continue_icon_fetch, wpa_s, NULL))
+ return;
+ /*
+ * We are going through icon fetch, but no icon response was received.
+ * Assume this means the current AP could not provide an answer to avoid
+ * getting stuck in fetch iteration.
+ */
+ hs20_icon_fetch_failed(wpa_s);
+}
+
+
+static void hs20_free_osu_prov_entry(struct osu_provider *prov)
+{
+}
+
+
+void hs20_free_osu_prov(struct wpa_supplicant *wpa_s)
+{
+ size_t i;
+ for (i = 0; i < wpa_s->osu_prov_count; i++)
+ hs20_free_osu_prov_entry(&wpa_s->osu_prov[i]);
+ os_free(wpa_s->osu_prov);
+ wpa_s->osu_prov = NULL;
+ wpa_s->osu_prov_count = 0;
+}
+
+
+static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s)
+{
+ char fname[256];
+ FILE *f;
+ size_t i, j;
+
+ wpa_s->fetch_osu_info = 0;
+ wpa_s->fetch_osu_icon_in_progress = 0;
+
+ if (wpa_s->conf->osu_dir == NULL) {
+ hs20_free_osu_prov(wpa_s);
+ wpa_s->fetch_anqp_in_progress = 0;
+ return;
+ }
+
+ snprintf(fname, sizeof(fname), "%s/osu-providers.txt",
+ wpa_s->conf->osu_dir);
+ f = fopen(fname, "w");
+ if (f == NULL) {
+ hs20_free_osu_prov(wpa_s);
+ return;
+ }
+ for (i = 0; i < wpa_s->osu_prov_count; i++) {
+ struct osu_provider *osu = &wpa_s->osu_prov[i];
+ if (i > 0)
+ fprintf(f, "\n");
+ fprintf(f, "OSU-PROVIDER " MACSTR "\n"
+ "uri=%s\n"
+ "methods=%08x\n",
+ MAC2STR(osu->bssid), osu->server_uri, osu->osu_methods);
+ if (osu->osu_ssid_len) {
+ fprintf(f, "osu_ssid=%s\n",
+ wpa_ssid_txt(osu->osu_ssid,
+ osu->osu_ssid_len));
+ }
+ if (osu->osu_nai[0])
+ fprintf(f, "osu_nai=%s\n", osu->osu_nai);
+ for (j = 0; j < osu->friendly_name_count; j++) {
+ fprintf(f, "friendly_name=%s:%s\n",
+ osu->friendly_name[j].lang,
+ osu->friendly_name[j].text);
+ }
+ for (j = 0; j < osu->serv_desc_count; j++) {
+ fprintf(f, "desc=%s:%s\n",
+ osu->serv_desc[j].lang,
+ osu->serv_desc[j].text);
+ }
+ for (j = 0; j < osu->icon_count; j++) {
+ struct osu_icon *icon = &osu->icon[j];
+ if (icon->failed)
+ continue; /* could not fetch icon */
+ fprintf(f, "icon=%u:%u:%u:%s:%s:%s\n",
+ icon->id, icon->width, icon->height, icon->lang,
+ icon->icon_type, icon->filename);
+ }
+ }
+ fclose(f);
+ hs20_free_osu_prov(wpa_s);
+
+ wpa_msg(wpa_s, MSG_INFO, "OSU provider fetch completed");
+ wpa_s->fetch_anqp_in_progress = 0;
+}
+
+
+void hs20_next_osu_icon(struct wpa_supplicant *wpa_s)
+{
+ size_t i, j;
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: Ready to fetch next icon");
+
+ for (i = 0; i < wpa_s->osu_prov_count; i++) {
+ struct osu_provider *osu = &wpa_s->osu_prov[i];
+ for (j = 0; j < osu->icon_count; j++) {
+ struct osu_icon *icon = &osu->icon[j];
+ if (icon->id || icon->failed)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: Try to fetch icon '%s' "
+ "from " MACSTR, icon->filename,
+ MAC2STR(osu->bssid));
+ os_get_reltime(&wpa_s->osu_icon_fetch_start);
+ if (hs20_anqp_send_req(wpa_s, osu->bssid,
+ BIT(HS20_STYPE_ICON_REQUEST),
+ (u8 *) icon->filename,
+ os_strlen(icon->filename)) < 0) {
+ icon->failed = 1;
+ continue;
+ }
+ return;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: No more icons to fetch");
+ hs20_osu_fetch_done(wpa_s);
+}
+
+
+static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ const u8 *osu_ssid, u8 osu_ssid_len,
+ const u8 *pos, size_t len)
+{
+ struct osu_provider *prov;
+ const u8 *end = pos + len;
+ u16 len2;
+ const u8 *pos2;
+
+ wpa_hexdump(MSG_DEBUG, "HS 2.0: Parsing OSU Provider", pos, len);
+ prov = os_realloc_array(wpa_s->osu_prov,
+ wpa_s->osu_prov_count + 1,
+ sizeof(*prov));
+ if (prov == NULL)
+ return;
+ wpa_s->osu_prov = prov;
+ prov = &prov[wpa_s->osu_prov_count];
+ os_memset(prov, 0, sizeof(*prov));
+
+ os_memcpy(prov->bssid, bss->bssid, ETH_ALEN);
+ os_memcpy(prov->osu_ssid, osu_ssid, osu_ssid_len);
+ prov->osu_ssid_len = osu_ssid_len;
+
+ /* OSU Friendly Name Length */
+ if (pos + 2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+ "Friendly Name Length");
+ return;
+ }
+ len2 = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + len2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+ "Friendly Name Duples");
+ return;
+ }
+ pos2 = pos;
+ pos += len2;
+
+ /* OSU Friendly Name Duples */
+ while (pos2 + 4 <= pos && prov->friendly_name_count < OSU_MAX_ITEMS) {
+ struct osu_lang_string *f;
+ if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) {
+ wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name");
+ break;
+ }
+ f = &prov->friendly_name[prov->friendly_name_count++];
+ os_memcpy(f->lang, pos2 + 1, 3);
+ os_memcpy(f->text, pos2 + 1 + 3, pos2[0] - 3);
+ pos2 += 1 + pos2[0];
+ }
+
+ /* OSU Server URI */
+ if (pos + 1 > end || pos + 1 + pos[0] > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server "
+ "URI");
+ return;
+ }
+ os_memcpy(prov->server_uri, pos + 1, pos[0]);
+ pos += 1 + pos[0];
+
+ /* OSU Method list */
+ if (pos + 1 > end || pos + 1 + pos[0] > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
+ "list");
+ return;
+ }
+ pos2 = pos + 1;
+ pos += 1 + pos[0];
+ while (pos2 < pos) {
+ if (*pos2 < 32)
+ prov->osu_methods |= BIT(*pos2);
+ pos2++;
+ }
+
+ /* Icons Available Length */
+ if (pos + 2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
+ "Available Length");
+ return;
+ }
+ len2 = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + len2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
+ "Available");
+ return;
+ }
+ pos2 = pos;
+ pos += len2;
+
+ /* Icons Available */
+ while (pos2 < pos) {
+ struct osu_icon *icon = &prov->icon[prov->icon_count];
+ if (pos2 + 2 + 2 + 3 + 1 + 1 > pos) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata");
+ break;
+ }
+
+ icon->width = WPA_GET_LE16(pos2);
+ pos2 += 2;
+ icon->height = WPA_GET_LE16(pos2);
+ pos2 += 2;
+ os_memcpy(icon->lang, pos2, 3);
+ pos2 += 3;
+
+ if (pos2 + 1 + pos2[0] > pos) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type");
+ break;
+ }
+ os_memcpy(icon->icon_type, pos2 + 1, pos2[0]);
+ pos2 += 1 + pos2[0];
+
+ if (pos2 + 1 + pos2[0] > pos) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
+ "Filename");
+ break;
+ }
+ os_memcpy(icon->filename, pos2 + 1, pos2[0]);
+ pos2 += 1 + pos2[0];
+
+ prov->icon_count++;
+ }
+
+ /* OSU_NAI */
+ if (pos + 1 > end || pos + 1 + pos[0] > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
+ return;
+ }
+ os_memcpy(prov->osu_nai, pos + 1, pos[0]);
+ pos += 1 + pos[0];
+
+ /* OSU Service Description Length */
+ if (pos + 2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+ "Service Description Length");
+ return;
+ }
+ len2 = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + len2 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+ "Service Description Duples");
+ return;
+ }
+ pos2 = pos;
+ pos += len2;
+
+ /* OSU Service Description Duples */
+ while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) {
+ struct osu_lang_string *f;
+ if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) {
+ wpa_printf(MSG_DEBUG, "Invalid OSU Service "
+ "Description");
+ break;
+ }
+ f = &prov->serv_desc[prov->serv_desc_count++];
+ os_memcpy(f->lang, pos2 + 1, 3);
+ os_memcpy(f->text, pos2 + 1 + 3, pos2[0] - 3);
+ pos2 += 1 + pos2[0];
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR,
+ MAC2STR(bss->bssid));
+ wpa_s->osu_prov_count++;
+}
+
+
+void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+ struct wpabuf *prov_anqp;
+ const u8 *pos, *end;
+ u16 len;
+ const u8 *osu_ssid;
+ u8 osu_ssid_len;
+ u8 num_providers;
+
+ hs20_free_osu_prov(wpa_s);
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (bss->anqp == NULL)
+ continue;
+ prov_anqp = bss->anqp->hs20_osu_providers_list;
+ if (prov_anqp == NULL)
+ continue;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Parsing OSU Providers list from "
+ MACSTR, MAC2STR(bss->bssid));
+ wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers list",
+ prov_anqp);
+ pos = wpabuf_head(prov_anqp);
+ end = pos + wpabuf_len(prov_anqp);
+
+ /* OSU SSID */
+ if (pos + 1 > end)
+ continue;
+ if (pos + 1 + pos[0] > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
+ "OSU SSID");
+ continue;
+ }
+ osu_ssid_len = *pos++;
+ if (osu_ssid_len > 32) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID "
+ "Length %u", osu_ssid_len);
+ continue;
+ }
+ osu_ssid = pos;
+ pos += osu_ssid_len;
+
+ if (pos + 1 > end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
+ "Number of OSU Providers");
+ continue;
+ }
+ num_providers = *pos++;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Number of OSU Providers: %u",
+ num_providers);
+
+ /* OSU Providers */
+ while (pos + 2 < end && num_providers > 0) {
+ num_providers--;
+ len = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + len > end)
+ break;
+ hs20_osu_add_prov(wpa_s, bss, osu_ssid,
+ osu_ssid_len, pos, len);
+ pos += len;
+ }
+
+ if (pos != end) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Ignored %d bytes of "
+ "extra data after OSU Providers",
+ (int) (end - pos));
+ }
+ }
+
+ wpa_s->fetch_osu_icon_in_progress = 1;
+ hs20_next_osu_icon(wpa_s);
+}
+
+
+static void hs20_osu_scan_res_handler(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ wpa_printf(MSG_DEBUG, "OSU provisioning fetch scan completed");
+ wpa_s->network_select = 0;
+ wpa_s->fetch_all_anqp = 1;
+ wpa_s->fetch_osu_info = 1;
+ wpa_s->fetch_osu_icon_in_progress = 0;
+
+ interworking_start_fetch_anqp(wpa_s);
+}
+
+
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+ "interface disabled");
+ return -1;
+ }
+
+ if (wpa_s->scanning) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+ "scanning");
+ return -1;
+ }
+
+ if (wpa_s->conf->osu_dir == NULL) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+ "osu_dir not configured");
+ return -1;
+ }
+
+ if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+ "fetch in progress (%d, %d)",
+ wpa_s->fetch_anqp_in_progress,
+ wpa_s->network_select);
+ return -1;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, "Starting OSU provisioning information fetch");
+ wpa_s->num_osu_scans = 0;
+ wpa_s->num_prov_found = 0;
+ hs20_start_osu_scan(wpa_s);
+
+ return 0;
+}
+
+
+void hs20_start_osu_scan(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->num_osu_scans++;
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ wpa_s->scan_res_handler = hs20_osu_scan_res_handler;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s)
+{
+ wpa_printf(MSG_DEBUG, "Cancel OSU fetch");
+ interworking_stop_fetch_anqp(wpa_s);
+ wpa_s->network_select = 0;
+ wpa_s->fetch_osu_info = 0;
+ wpa_s->fetch_osu_icon_in_progress = 0;
+}
+
+
+void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s)
+{
+ hs20_osu_icon_fetch_result(wpa_s, -1);
+ eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
+ eloop_register_timeout(0, 0, hs20_continue_icon_fetch, wpa_s, NULL);
+}
+
+
+void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
+ const char *url, u8 osu_method)
+{
+ if (url)
+ wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION "%u %s",
+ osu_method, url);
+ else
+ wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION);
+}
+
+
+void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
+ u16 reauth_delay, const char *url)
+{
+ if (!wpa_sm_pmf_enabled(wpa_s->wpa)) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Ignore deauthentication imminent notice since PMF was not enabled");
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, HS20_DEAUTH_IMMINENT_NOTICE "%u %u %s",
+ code, reauth_delay, url);
+
+ if (code == HS20_DEAUTH_REASON_CODE_BSS) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Add BSS to blacklist");
+ wpa_blacklist_add(wpa_s, wpa_s->bssid);
+ /* TODO: For now, disable full ESS since some drivers may not
+ * support disabling per BSS. */
+ if (wpa_s->current_ssid) {
+ struct os_time now;
+ os_get_time(&now);
+ if (now.sec + reauth_delay <=
+ wpa_s->current_ssid->disabled_until.sec)
+ return;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds (BSS)",
+ reauth_delay);
+ wpa_s->current_ssid->disabled_until.sec =
+ now.sec + reauth_delay;
+ }
+ }
+
+ if (code == HS20_DEAUTH_REASON_CODE_ESS && wpa_s->current_ssid) {
+ struct os_time now;
+ os_get_time(&now);
+ if (now.sec + reauth_delay <=
+ wpa_s->current_ssid->disabled_until.sec)
+ return;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds",
+ reauth_delay);
+ wpa_s->current_ssid->disabled_until.sec =
+ now.sec + reauth_delay;
+ }
+}
diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h
index 1c8481b..88e5062 100644
--- a/wpa_supplicant/hs20_supplicant.h
+++ b/wpa_supplicant/hs20_supplicant.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -8,7 +8,7 @@
#ifndef HS20_SUPPLICANT_H
#define HS20_SUPPLICANT_H
-void wpas_hs20_add_indication(struct wpabuf *buf);
+void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id);
int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
const u8 *payload, size_t payload_len);
@@ -18,5 +18,20 @@
const u8 *sa, const u8 *data, size_t slen);
int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_bss *bss);
+int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+void hs20_notify_parse_done(struct wpa_supplicant *wpa_s);
+
+void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
+ const char *url, u8 osu_method);
+void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
+ u16 reauth_delay, const char *url);
+
+void hs20_free_osu_prov(struct wpa_supplicant *wpa_s);
+void hs20_next_osu_icon(struct wpa_supplicant *wpa_s);
+void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s);
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s);
+void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s);
+void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s);
+void hs20_start_osu_scan(struct wpa_supplicant *wpa_s);
#endif /* HS20_SUPPLICANT_H */
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index da8971d..abf5dee 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -46,9 +46,28 @@
static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
static struct wpa_cred * interworking_credentials_available_realm(
- struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded);
static struct wpa_cred * interworking_credentials_available_3gpp(
- struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded);
+
+
+static int cred_prio_cmp(const struct wpa_cred *a, const struct wpa_cred *b)
+{
+ if (a->priority > b->priority)
+ return 1;
+ if (a->priority < b->priority)
+ return -1;
+ if (a->provisioning_sp == NULL || b->provisioning_sp == NULL ||
+ os_strcmp(a->provisioning_sp, b->provisioning_sp) != 0)
+ return 0;
+ if (a->sp_priority < b->sp_priority)
+ return 1;
+ if (a->sp_priority > b->sp_priority)
+ return -1;
+ return 0;
+}
static void interworking_reconnect(struct wpa_supplicant *wpa_s)
@@ -102,6 +121,9 @@
{
struct wpa_supplicant *wpa_s = ctx;
+ wpa_printf(MSG_DEBUG, "ANQP: Response callback dst=" MACSTR
+ " dialog_token=%u result=%d status_code=%u",
+ MAC2STR(dst), dialog_token, result, status_code);
anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
status_code);
interworking_next_anqp_fetch(wpa_s);
@@ -155,13 +177,45 @@
struct wpa_cred *cred;
for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
- if (cred->domain || cred->pcsc || cred->imsi)
+ if (cred->domain || cred->pcsc || cred->imsi ||
+ cred->roaming_partner)
return 1;
}
return 0;
}
+#ifdef CONFIG_HS20
+
+static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_cred *cred;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->min_dl_bandwidth_home ||
+ cred->min_ul_bandwidth_home ||
+ cred->min_dl_bandwidth_roaming ||
+ cred->min_ul_bandwidth_roaming)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int cred_with_conn_capab(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_cred *cred;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->num_req_conn_capab)
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* CONFIG_HS20 */
+
+
static int additional_roaming_consortiums(struct wpa_bss *bss)
{
const u8 *ie;
@@ -227,13 +281,17 @@
wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
wpabuf_put_u8(extra, 0); /* Reserved */
wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
- if (all) {
+ if (all)
wpabuf_put_u8(extra,
HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+ if (all || cred_with_min_backhaul(wpa_s))
wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
+ if (all || cred_with_conn_capab(wpa_s))
wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
+ if (all)
wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
- }
+ if (all)
+ wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST);
gas_anqp_set_element_len(extra, len_pos);
}
#endif /* CONFIG_HS20 */
@@ -918,6 +976,7 @@
wpa_config_set_quoted(ssid, "password", cred->password) < 0)
goto fail;
+ wpa_s->next_ssid = ssid;
wpa_config_update_prio_list(wpa_s->conf);
interworking_reconnect(wpa_s);
@@ -1046,11 +1105,164 @@
}
+static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ int res;
+ unsigned int dl_bandwidth, ul_bandwidth;
+ const u8 *wan;
+ u8 wan_info, dl_load, ul_load;
+ u16 lmd;
+ u32 ul_speed, dl_speed;
+
+ if (!cred->min_dl_bandwidth_home &&
+ !cred->min_ul_bandwidth_home &&
+ !cred->min_dl_bandwidth_roaming &&
+ !cred->min_ul_bandwidth_roaming)
+ return 0; /* No bandwidth constraint specified */
+
+ if (bss->anqp == NULL || bss->anqp->hs20_wan_metrics == NULL)
+ return 0; /* No WAN Metrics known - ignore constraint */
+
+ wan = wpabuf_head(bss->anqp->hs20_wan_metrics);
+ wan_info = wan[0];
+ if (wan_info & BIT(3))
+ return 1; /* WAN link at capacity */
+ lmd = WPA_GET_LE16(wan + 11);
+ if (lmd == 0)
+ return 0; /* Downlink/Uplink Load was not measured */
+ dl_speed = WPA_GET_LE32(wan + 1);
+ ul_speed = WPA_GET_LE32(wan + 5);
+ dl_load = wan[9];
+ ul_load = wan[10];
+
+ if (dl_speed >= 0xffffff)
+ dl_bandwidth = dl_speed / 255 * (255 - dl_load);
+ else
+ dl_bandwidth = dl_speed * (255 - dl_load) / 255;
+
+ if (ul_speed >= 0xffffff)
+ ul_bandwidth = ul_speed / 255 * (255 - ul_load);
+ else
+ ul_bandwidth = ul_speed * (255 - ul_load) / 255;
+
+ res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+ bss->anqp->domain_name : NULL);
+ if (res > 0) {
+ if (cred->min_dl_bandwidth_home > dl_bandwidth)
+ return 1;
+ if (cred->min_ul_bandwidth_home > ul_bandwidth)
+ return 1;
+ } else {
+ if (cred->min_dl_bandwidth_roaming > dl_bandwidth)
+ return 1;
+ if (cred->min_ul_bandwidth_roaming > ul_bandwidth)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int cred_over_max_bss_load(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ const u8 *ie;
+ int res;
+
+ if (!cred->max_bss_load)
+ return 0; /* No BSS Load constraint specified */
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_BSS_LOAD);
+ if (ie == NULL || ie[1] < 3)
+ return 0; /* No BSS Load advertised */
+
+ res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+ bss->anqp->domain_name : NULL);
+ if (res <= 0)
+ return 0; /* Not a home network */
+
+ return ie[4] > cred->max_bss_load;
+}
+
+
+static int has_proto_match(const u8 *pos, const u8 *end, u8 proto)
+{
+ while (pos + 4 <= end) {
+ if (pos[0] == proto && pos[3] == 1 /* Open */)
+ return 1;
+ pos += 4;
+ }
+
+ return 0;
+}
+
+
+static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto,
+ u16 port)
+{
+ while (pos + 4 <= end) {
+ if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port &&
+ pos[3] == 1 /* Open */)
+ return 1;
+ pos += 4;
+ }
+
+ return 0;
+}
+
+
+static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ int res;
+ const u8 *capab, *end;
+ unsigned int i, j;
+ int *ports;
+
+ if (!cred->num_req_conn_capab)
+ return 0; /* No connection capability constraint specified */
+
+ if (bss->anqp == NULL || bss->anqp->hs20_connection_capability == NULL)
+ return 0; /* No Connection Capability known - ignore constraint
+ */
+
+ res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+ bss->anqp->domain_name : NULL);
+ if (res > 0)
+ return 0; /* No constraint in home network */
+
+ capab = wpabuf_head(bss->anqp->hs20_connection_capability);
+ end = capab + wpabuf_len(bss->anqp->hs20_connection_capability);
+
+ for (i = 0; i < cred->num_req_conn_capab; i++) {
+ ports = cred->req_conn_capab_port[i];
+ if (!ports) {
+ if (!has_proto_match(capab, end,
+ cred->req_conn_capab_proto[i]))
+ return 1;
+ } else {
+ for (j = 0; ports[j] > -1; j++) {
+ if (!has_proto_port_match(
+ capab, end,
+ cred->req_conn_capab_proto[i],
+ ports[j]))
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
static struct wpa_cred * interworking_credentials_available_roaming_consortium(
- struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded)
{
struct wpa_cred *cred, *selected = NULL;
const u8 *ie;
+ int is_excluded = 0;
ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
@@ -1073,16 +1285,33 @@
cred->roaming_consortium_len))
continue;
- 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;
+ if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw && cred_conn_capab_missing(wpa_s, cred, bss))
+ continue;
+ if (cred_excluded_ssid(cred, bss)) {
+ if (excluded == NULL)
+ continue;
+ if (selected == NULL) {
+ selected = cred;
+ is_excluded = 1;
+ }
+ } else {
+ if (selected == NULL || is_excluded ||
+ cred_prio_cmp(selected, cred) < 0) {
+ selected = cred;
+ is_excluded = 0;
+ }
+ }
}
+ if (excluded)
+ *excluded = is_excluded;
+
return selected;
}
@@ -1191,6 +1420,8 @@
cred->domain_suffix_match) < 0)
return -1;
+ ssid->eap.ocsp = cred->ocsp;
+
return 0;
}
@@ -1241,6 +1472,7 @@
cred->eap_method->method == EAP_TYPE_TTLS) < 0)
goto fail;
+ wpa_s->next_ssid = ssid;
wpa_config_update_prio_list(wpa_s->conf);
interworking_reconnect(wpa_s);
@@ -1253,7 +1485,8 @@
}
-int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+static int interworking_connect_helper(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, int allow_excluded)
{
struct wpa_cred *cred, *cred_rc, *cred_3gpp;
struct wpa_ssid *ssid;
@@ -1261,6 +1494,7 @@
struct nai_realm_eap *eap = NULL;
u16 count, i;
char buf[100];
+ int excluded = 0, *excl = allow_excluded ? &excluded : NULL;
if (wpa_s->conf->cred == NULL || bss == NULL)
return -1;
@@ -1271,6 +1505,10 @@
return -1;
}
+ wpa_printf(MSG_DEBUG, "Interworking: Considering BSS " MACSTR
+ " for connection (allow_excluded=%d)",
+ MAC2STR(bss->bssid), allow_excluded);
+
if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
/*
* We currently support only HS 2.0 networks and those are
@@ -1281,35 +1519,80 @@
return -1;
}
- cred_rc = interworking_credentials_available_roaming_consortium(wpa_s,
- bss);
+ cred_rc = interworking_credentials_available_roaming_consortium(
+ wpa_s, bss, 0, excl);
if (cred_rc) {
wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
- "consortium matching credential priority %d",
- cred_rc->priority);
+ "consortium matching credential priority %d "
+ "sp_priority %d",
+ cred_rc->priority, cred_rc->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
}
- cred = interworking_credentials_available_realm(wpa_s, bss);
+ cred = interworking_credentials_available_realm(wpa_s, bss, 0, excl);
if (cred) {
wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm list "
- "matching credential priority %d",
- cred->priority);
+ "matching credential priority %d sp_priority %d",
+ cred->priority, cred->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
}
- cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss);
+ cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0,
+ excl);
if (cred_3gpp) {
wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP matching "
- "credential priority %d", cred_3gpp->priority);
+ "credential priority %d sp_priority %d",
+ cred_3gpp->priority, cred_3gpp->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
+ }
+
+ if (!cred_rc && !cred && !cred_3gpp) {
+ wpa_printf(MSG_DEBUG, "Interworking: No full credential matches - consider options without BW(etc.) limits");
+ cred_rc = interworking_credentials_available_roaming_consortium(
+ wpa_s, bss, 1, excl);
+ if (cred_rc) {
+ wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
+ "consortium matching credential priority %d "
+ "sp_priority %d (ignore BW)",
+ cred_rc->priority, cred_rc->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
+ }
+
+ cred = interworking_credentials_available_realm(wpa_s, bss, 1,
+ excl);
+ if (cred) {
+ wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm "
+ "list matching credential priority %d "
+ "sp_priority %d (ignore BW)",
+ cred->priority, cred->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
+ }
+
+ cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss,
+ 1, excl);
+ if (cred_3gpp) {
+ wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP "
+ "matching credential priority %d "
+ "sp_priority %d (ignore BW)",
+ cred_3gpp->priority, cred_3gpp->sp_priority);
+ if (allow_excluded && excl && !(*excl))
+ excl = NULL;
+ }
}
if (cred_rc &&
- (cred == NULL || cred_rc->priority >= cred->priority) &&
- (cred_3gpp == NULL || cred_rc->priority >= cred_3gpp->priority))
+ (cred == NULL || cred_prio_cmp(cred_rc, cred) >= 0) &&
+ (cred_3gpp == NULL || cred_prio_cmp(cred_rc, cred_3gpp) >= 0))
return interworking_connect_roaming_consortium(wpa_s, cred_rc,
bss);
if (cred_3gpp &&
- (cred == NULL || cred_3gpp->priority >= cred->priority)) {
+ (cred == NULL || cred_prio_cmp(cred_3gpp, cred) >= 0)) {
return interworking_connect_3gpp(wpa_s, cred_3gpp, bss);
}
@@ -1443,6 +1726,7 @@
nai_realm_free(realm, count);
+ wpa_s->next_ssid = ssid;
wpa_config_update_prio_list(wpa_s->conf);
interworking_reconnect(wpa_s);
@@ -1456,13 +1740,21 @@
}
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ return interworking_connect_helper(wpa_s, bss, 1);
+}
+
+
static struct wpa_cred * interworking_credentials_available_3gpp(
- struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded)
{
struct wpa_cred *selected = NULL;
#ifdef INTERWORKING_3GPP
struct wpa_cred *cred;
int ret;
+ int is_excluded = 0;
if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
return NULL;
@@ -1534,26 +1826,49 @@
ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
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;
+ if (!ignore_bw &&
+ cred_below_min_backhaul(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw &&
+ cred_over_max_bss_load(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw &&
+ cred_conn_capab_missing(wpa_s, cred, bss))
+ continue;
+ if (cred_excluded_ssid(cred, bss)) {
+ if (excluded == NULL)
+ continue;
+ if (selected == NULL) {
+ selected = cred;
+ is_excluded = 1;
+ }
+ } else {
+ if (selected == NULL || is_excluded ||
+ cred_prio_cmp(selected, cred) < 0) {
+ selected = cred;
+ is_excluded = 0;
+ }
+ }
}
}
+
+ if (excluded)
+ *excluded = is_excluded;
#endif /* INTERWORKING_3GPP */
return selected;
}
static struct wpa_cred * interworking_credentials_available_realm(
- struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded)
{
struct wpa_cred *cred, *selected = NULL;
struct nai_realm *realm;
u16 count, i;
+ int is_excluded = 0;
if (bss->anqp == NULL || bss->anqp->nai_realm == NULL)
return NULL;
@@ -1578,13 +1893,32 @@
if (!nai_realm_match(&realm[i], cred->realm))
continue;
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;
+ if (!ignore_bw &&
+ cred_below_min_backhaul(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw &&
+ cred_over_max_bss_load(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw &&
+ cred_conn_capab_missing(wpa_s, cred, bss))
+ continue;
+ if (cred_excluded_ssid(cred, bss)) {
+ if (excluded == NULL)
+ continue;
+ if (selected == NULL) {
+ selected = cred;
+ is_excluded = 1;
+ }
+ } else {
+ if (selected == NULL || is_excluded ||
+ cred_prio_cmp(selected, cred) < 0)
+ {
+ selected = cred;
+ is_excluded = 0;
+ }
+ }
break;
}
}
@@ -1592,14 +1926,19 @@
nai_realm_free(realm, count);
+ if (excluded)
+ *excluded = is_excluded;
+
return selected;
}
-static struct wpa_cred * interworking_credentials_available(
- struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+static struct wpa_cred * interworking_credentials_available_helper(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded)
{
struct wpa_cred *cred, *cred2;
+ int excluded1, excluded2;
if (disallowed_bssid(wpa_s, bss->bssid) ||
disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
@@ -1608,26 +1947,56 @@
return NULL;
}
- cred = interworking_credentials_available_realm(wpa_s, bss);
- cred2 = interworking_credentials_available_3gpp(wpa_s, bss);
- if (cred && cred2 && cred2->priority >= cred->priority)
+ cred = interworking_credentials_available_realm(wpa_s, bss, ignore_bw,
+ &excluded1);
+ cred2 = interworking_credentials_available_3gpp(wpa_s, bss, ignore_bw,
+ &excluded2);
+ if (cred && cred2 &&
+ (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
cred = cred2;
- if (!cred)
+ excluded1 = excluded2;
+ }
+ if (!cred) {
cred = cred2;
+ excluded1 = excluded2;
+ }
- cred2 = interworking_credentials_available_roaming_consortium(wpa_s,
- bss);
- if (cred && cred2 && cred2->priority >= cred->priority)
+ cred2 = interworking_credentials_available_roaming_consortium(
+ wpa_s, bss, ignore_bw, &excluded2);
+ if (cred && cred2 &&
+ (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
cred = cred2;
- if (!cred)
+ excluded1 = excluded2;
+ }
+ if (!cred) {
cred = cred2;
+ excluded1 = excluded2;
+ }
+ if (excluded)
+ *excluded = excluded1;
return cred;
}
-static int domain_name_list_contains(struct wpabuf *domain_names,
- const char *domain)
+static struct wpa_cred * interworking_credentials_available(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int *excluded)
+{
+ struct wpa_cred *cred;
+
+ if (excluded)
+ *excluded = 0;
+ cred = interworking_credentials_available_helper(wpa_s, bss, 0,
+ excluded);
+ if (cred)
+ return cred;
+ return interworking_credentials_available_helper(wpa_s, bss, 1,
+ excluded);
+}
+
+
+int domain_name_list_contains(struct wpabuf *domain_names,
+ const char *domain, int exact_match)
{
const u8 *pos, *end;
size_t len;
@@ -1645,6 +2014,12 @@
if (pos[0] == len &&
os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
return 1;
+ if (!exact_match && pos[0] > len && pos[pos[0] - len] == '.') {
+ const char *ap = (const char *) (pos + 1);
+ int offset = pos[0] - len;
+ if (os_strncasecmp(domain, ap + offset, len) == 0)
+ return 1;
+ }
pos += 1 + pos[0];
}
@@ -1687,7 +2062,7 @@
wpa_printf(MSG_DEBUG, "Interworking: Search for match "
"with SIM/USIM domain %s", realm);
if (realm &&
- domain_name_list_contains(domain_names, realm))
+ domain_name_list_contains(domain_names, realm, 1))
return 1;
if (realm)
ret = 0;
@@ -1700,7 +2075,7 @@
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]))
+ if (domain_name_list_contains(domain_names, cred->domain[i], 1))
return 1;
}
@@ -1752,19 +2127,127 @@
}
+static int roaming_partner_match(struct wpa_supplicant *wpa_s,
+ struct roaming_partner *partner,
+ struct wpabuf *domain_names)
+{
+ wpa_printf(MSG_DEBUG, "Interworking: Comparing roaming_partner info fqdn='%s' exact_match=%d priority=%u country='%s'",
+ partner->fqdn, partner->exact_match, partner->priority,
+ partner->country);
+ wpa_hexdump_ascii(MSG_DEBUG, "Interworking: Domain names",
+ wpabuf_head(domain_names),
+ wpabuf_len(domain_names));
+ if (!domain_name_list_contains(domain_names, partner->fqdn,
+ partner->exact_match))
+ return 0;
+ /* TODO: match Country */
+ return 1;
+}
+
+
+static u8 roaming_prio(struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
+ struct wpa_bss *bss)
+{
+ size_t i;
+
+ if (bss->anqp == NULL || bss->anqp->domain_name == NULL) {
+ wpa_printf(MSG_DEBUG, "Interworking: No ANQP domain name info -> use default roaming partner priority 128");
+ return 128; /* cannot check preference with domain name */
+ }
+
+ if (interworking_home_sp_cred(wpa_s, cred, bss->anqp->domain_name) > 0)
+ {
+ wpa_printf(MSG_DEBUG, "Interworking: Determined to be home SP -> use maximum preference 0 as roaming partner priority");
+ return 0; /* max preference for home SP network */
+ }
+
+ for (i = 0; i < cred->num_roaming_partner; i++) {
+ if (roaming_partner_match(wpa_s, &cred->roaming_partner[i],
+ bss->anqp->domain_name)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Roaming partner preference match - priority %u",
+ cred->roaming_partner[i].priority);
+ return cred->roaming_partner[i].priority;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "Interworking: No roaming partner preference match - use default roaming partner priority 128");
+ return 128;
+}
+
+
+static struct wpa_bss * pick_best_roaming_partner(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected,
+ struct wpa_cred *cred)
+{
+ struct wpa_bss *bss;
+ u8 best_prio, prio;
+ struct wpa_cred *cred2;
+
+ /*
+ * Check if any other BSS is operated by a more preferred roaming
+ * partner.
+ */
+
+ best_prio = roaming_prio(wpa_s, cred, selected);
+ wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for selected BSS "
+ MACSTR " (cred=%d)", best_prio, MAC2STR(selected->bssid),
+ cred->id);
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (bss == selected)
+ continue;
+ cred2 = interworking_credentials_available(wpa_s, bss, NULL);
+ if (!cred2)
+ continue;
+ if (!wpa_bss_get_ie(bss, WLAN_EID_RSN))
+ continue;
+ prio = roaming_prio(wpa_s, cred2, bss);
+ wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for BSS "
+ MACSTR " (cred=%d)", prio, MAC2STR(bss->bssid),
+ cred2->id);
+ if (prio < best_prio) {
+ int bh1, bh2, load1, load2, conn1, conn2;
+ bh1 = cred_below_min_backhaul(wpa_s, cred, selected);
+ load1 = cred_over_max_bss_load(wpa_s, cred, selected);
+ conn1 = cred_conn_capab_missing(wpa_s, cred, selected);
+ bh2 = cred_below_min_backhaul(wpa_s, cred2, bss);
+ load2 = cred_over_max_bss_load(wpa_s, cred2, bss);
+ conn2 = cred_conn_capab_missing(wpa_s, cred2, bss);
+ wpa_printf(MSG_DEBUG, "Interworking: old: %d %d %d new: %d %d %d",
+ bh1, load1, conn1, bh2, load2, conn2);
+ if (bh1 || load1 || conn1 || !(bh2 || load2 || conn2)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Better roaming partner " MACSTR " selected", MAC2STR(bss->bssid));
+ best_prio = prio;
+ selected = bss;
+ }
+ }
+ }
+
+ return selected;
+}
+
+
static void interworking_select_network(struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
- int selected_prio = -999999, selected_home_prio = -999999;
+ struct wpa_bss *selected2 = NULL, *selected2_home = NULL;
unsigned int count = 0;
const char *type;
int res;
- struct wpa_cred *cred;
+ struct wpa_cred *cred, *selected_cred = NULL;
+ struct wpa_cred *selected_home_cred = NULL;
+ struct wpa_cred *selected2_cred = NULL;
+ struct wpa_cred *selected2_home_cred = NULL;
wpa_s->network_select = 0;
+ wpa_printf(MSG_DEBUG, "Interworking: Select network (auto_select=%d)",
+ wpa_s->auto_select);
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
- cred = interworking_credentials_available(wpa_s, bss);
+ int excluded = 0;
+ int bh, bss_load, conn_capab;
+ cred = interworking_credentials_available(wpa_s, bss,
+ &excluded);
if (!cred)
continue;
if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
@@ -1777,7 +2260,8 @@
"RSN", MAC2STR(bss->bssid));
continue;
}
- count++;
+ if (!excluded)
+ count++;
res = interworking_home_sp(wpa_s, bss->anqp ?
bss->anqp->domain_name : NULL);
if (res > 0)
@@ -1786,29 +2270,75 @@
type = "roaming";
else
type = "unknown";
- wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s",
- MAC2STR(bss->bssid), type);
+ bh = cred_below_min_backhaul(wpa_s, cred, bss);
+ bss_load = cred_over_max_bss_load(wpa_s, cred, bss);
+ conn_capab = cred_conn_capab_missing(wpa_s, cred, bss);
+ wpa_msg(wpa_s, MSG_INFO, "%s" MACSTR " type=%s%s%s%s id=%d priority=%d sp_priority=%d",
+ excluded ? INTERWORKING_BLACKLISTED : INTERWORKING_AP,
+ MAC2STR(bss->bssid), type,
+ bh ? " below_min_backhaul=1" : "",
+ bss_load ? " over_max_bss_load=1" : "",
+ conn_capab ? " conn_capab_missing=1" : "",
+ cred->id, cred->priority, cred->sp_priority);
+ if (excluded)
+ continue;
if (wpa_s->auto_select ||
(wpa_s->conf->auto_interworking &&
wpa_s->auto_network_select)) {
- if (selected == NULL ||
- cred->priority > selected_prio) {
- selected = bss;
- selected_prio = cred->priority;
- }
- if (res > 0 &&
- (selected_home == NULL ||
- cred->priority > selected_home_prio)) {
- selected_home = bss;
- selected_home_prio = cred->priority;
+ if (bh || bss_load || conn_capab) {
+ if (selected2_cred == NULL ||
+ cred_prio_cmp(cred, selected2_cred) > 0) {
+ wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2");
+ selected2 = bss;
+ selected2_cred = cred;
+ }
+ if (res > 0 &&
+ (selected2_home_cred == NULL ||
+ cred_prio_cmp(cred, selected2_home_cred) >
+ 0)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2_home");
+ selected2_home = bss;
+ selected2_home_cred = cred;
+ }
+ } else {
+ if (selected_cred == NULL ||
+ cred_prio_cmp(cred, selected_cred) > 0) {
+ wpa_printf(MSG_DEBUG, "Interworking: Mark as selected");
+ selected = bss;
+ selected_cred = cred;
+ }
+ if (res > 0 &&
+ (selected_home_cred == NULL ||
+ cred_prio_cmp(cred, selected_home_cred) >
+ 0)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Mark as selected_home");
+ selected_home = bss;
+ selected_home_cred = cred;
+ }
}
}
}
if (selected_home && selected_home != selected &&
- selected_home_prio >= selected_prio) {
+ selected_home_cred &&
+ (selected_cred == NULL ||
+ cred_prio_cmp(selected_home_cred, selected_cred) >= 0)) {
/* Prefer network operated by the Home SP */
+ wpa_printf(MSG_DEBUG, "Interworking: Overrided selected with selected_home");
selected = selected_home;
+ selected_cred = selected_home_cred;
+ }
+
+ if (!selected) {
+ if (selected2_home) {
+ wpa_printf(MSG_DEBUG, "Interworking: Use home BSS with BW limit mismatch since no other network could be selected");
+ selected = selected2_home;
+ selected_cred = selected2_home_cred;
+ } else if (selected2) {
+ wpa_printf(MSG_DEBUG, "Interworking: Use visited BSS with BW limit mismatch since no other network could be selected");
+ selected = selected2;
+ selected_cred = selected2_cred;
+ }
}
if (count == 0) {
@@ -1837,8 +2367,18 @@
"with matching credentials found");
}
- if (selected)
+ if (selected) {
+ wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR,
+ MAC2STR(selected->bssid));
+ selected = pick_best_roaming_partner(wpa_s, selected,
+ selected_cred);
+ wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR
+ " (after best roaming partner selection)",
+ MAC2STR(selected->bssid));
+ wpa_msg(wpa_s, MSG_INFO, INTERWORKING_SELECTED MACSTR,
+ MAC2STR(selected->bssid));
interworking_connect(wpa_s, selected);
+ }
}
@@ -1885,8 +2425,21 @@
int found = 0;
const u8 *ie;
- if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress)
+ wpa_printf(MSG_DEBUG, "Interworking: next_anqp_fetch - "
+ "fetch_anqp_in_progress=%d fetch_osu_icon_in_progress=%d",
+ wpa_s->fetch_anqp_in_progress,
+ wpa_s->fetch_osu_icon_in_progress);
+
+ if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress) {
+ wpa_printf(MSG_DEBUG, "Interworking: Stop next-ANQP-fetch");
return;
+ }
+
+ if (wpa_s->fetch_osu_icon_in_progress) {
+ wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)");
+ hs20_next_osu_icon(wpa_s);
+ return;
+ }
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
if (!(bss->caps & IEEE80211_CAP_ESS))
@@ -1920,6 +2473,17 @@
}
if (found == 0) {
+ if (wpa_s->fetch_osu_info) {
+ if (wpa_s->num_prov_found == 0 &&
+ wpa_s->num_osu_scans < 3) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: No OSU providers seen - try to scan again");
+ hs20_start_osu_scan(wpa_s);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "Interworking: Next icon");
+ hs20_osu_icon_fetch(wpa_s);
+ return;
+ }
wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
wpa_s->fetch_anqp_in_progress = 0;
if (wpa_s->network_select)
@@ -1947,6 +2511,7 @@
wpa_s->network_select = 0;
wpa_s->fetch_all_anqp = 1;
+ wpa_s->fetch_osu_info = 0;
interworking_start_fetch_anqp(wpa_s);
@@ -2144,14 +2709,22 @@
u16 slen;
struct wpa_bss *bss = NULL, *tmp;
- if (result != GAS_QUERY_SUCCESS)
+ wpa_printf(MSG_DEBUG, "Interworking: anqp_resp_cb dst=" MACSTR
+ " dialog_token=%u result=%d status_code=%u",
+ MAC2STR(dst), dialog_token, result, status_code);
+ if (result != GAS_QUERY_SUCCESS) {
+ if (wpa_s->fetch_osu_icon_in_progress)
+ hs20_icon_fetch_failed(wpa_s);
return;
+ }
pos = wpabuf_head(adv_proto);
if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
"Protocol in response");
+ if (wpa_s->fetch_osu_icon_in_progress)
+ hs20_icon_fetch_failed(wpa_s);
return;
}
@@ -2191,6 +2764,8 @@
slen);
pos += slen;
}
+
+ hs20_notify_parse_done(wpa_s);
}
@@ -2211,6 +2786,7 @@
wpa_s->auto_network_select = 0;
wpa_s->auto_select = !!auto_select;
wpa_s->fetch_all_anqp = 0;
+ wpa_s->fetch_osu_info = 0;
wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
"selection");
wpa_s->scan_res_handler = interworking_scan_res_handler;
diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h
index c8e7093..bb0ceb8 100644
--- a/wpa_supplicant/interworking.h
+++ b/wpa_supplicant/interworking.h
@@ -29,5 +29,7 @@
int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
struct wpa_cred *cred,
struct wpabuf *domain_names);
+int domain_name_list_contains(struct wpabuf *domain_names,
+ const char *domain, int exact_match);
#endif /* INTERWORKING_H */
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index a82fbf3..2db1d54 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -252,6 +252,8 @@
void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
+ if (wpa_s->next_ssid == ssid)
+ wpa_s->next_ssid = NULL;
if (wpa_s->wpa)
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index fa75fa5..b878198 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -722,12 +722,10 @@
changed = 1;
}
-#ifndef CONFIG_NO_CONFIG_WRITE
if (changed && wpa_s->conf->update_config &&
wpa_config_write(wpa_s->confname, wpa_s->conf)) {
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
}
-#endif /* CONFIG_NO_CONFIG_WRITE */
return s->id;
}
@@ -795,11 +793,9 @@
addr, ETH_ALEN);
}
-#ifndef CONFIG_NO_CONFIG_WRITE
if (wpa_s->parent->conf->update_config &&
wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
}
@@ -3152,11 +3148,9 @@
ssid->p2p_client_list + (i + 1) * ETH_ALEN,
(ssid->num_p2p_clients - i - 1) * ETH_ALEN);
ssid->num_p2p_clients--;
-#ifndef CONFIG_NO_CONFIG_WRITE
if (wpa_s->parent->conf->update_config &&
wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
}
@@ -6816,11 +6810,9 @@
}
dl_list_add(&persistent->psk_list, &p->list);
-#ifndef CONFIG_NO_CONFIG_WRITE
if (wpa_s->parent->conf->update_config &&
wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
}
@@ -6831,14 +6823,10 @@
int res;
res = wpas_p2p_remove_psk_entry(wpa_s, s, addr, iface_addr);
- if (res > 0) {
-#ifndef CONFIG_NO_CONFIG_WRITE
- if (wpa_s->conf->update_config &&
- wpa_config_write(wpa_s->confname, wpa_s->conf))
- wpa_dbg(wpa_s, MSG_DEBUG,
- "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
- }
+ if (res > 0 && wpa_s->conf->update_config &&
+ wpa_config_write(wpa_s->confname, wpa_s->conf))
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Failed to update configuration");
}
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 6c742d6..f7eb537 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -365,11 +365,17 @@
return;
wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB);
- wpabuf_put_u8(buf, 4);
+ wpabuf_put_u8(buf, 6);
wpabuf_put_u8(buf, 0x00);
wpabuf_put_u8(buf, 0x00);
wpabuf_put_u8(buf, 0x00);
wpabuf_put_u8(buf, 0x80); /* Bit 31 - Interworking */
+ wpabuf_put_u8(buf, 0x00);
+#ifdef CONFIG_HS20
+ wpabuf_put_u8(buf, 0x40); /* Bit 46 - WNM-Notification */
+#else /* CONFIG_HS20 */
+ wpabuf_put_u8(buf, 0x00);
+#endif /* CONFIG_HS20 */
wpabuf_put_u8(buf, WLAN_EID_INTERWORKING);
wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 :
@@ -425,7 +431,7 @@
#ifdef CONFIG_HS20
if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 7) == 0)
- wpas_hs20_add_indication(extra_ie);
+ wpas_hs20_add_indication(extra_ie, -1);
#endif /* CONFIG_HS20 */
return extra_ie;
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index e712ac4..63beaef 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -360,7 +360,8 @@
struct wpabuf *hs20;
hs20 = wpabuf_alloc(20);
if (hs20) {
- wpas_hs20_add_indication(hs20);
+ int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+ wpas_hs20_add_indication(hs20, pps_mo_id);
os_memcpy(wpa_s->sme.assoc_req_ie +
wpa_s->sme.assoc_req_ie_len,
wpabuf_head(hs20), wpabuf_len(hs20));
@@ -475,6 +476,11 @@
return;
}
+ if (radio_work_pending(wpa_s, "sme-connect")) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reject sme_authenticate() call since pending work exist");
+ return;
+ }
+
cwork = os_zalloc(sizeof(*cwork));
if (cwork == NULL)
return;
@@ -751,6 +757,10 @@
params.wpa_proto = WPA_PROTO_WPA;
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.wpa_ie - 2,
elems.wpa_ie_len + 2);
+ } else if (elems.osen) {
+ params.wpa_proto = WPA_PROTO_OSEN;
+ wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.osen - 2,
+ elems.osen_len + 2);
} else
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group)
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 65b2783..0619f6d 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -18,6 +18,7 @@
#include "ctrl_iface.h"
#include "bss.h"
#include "wnm_sta.h"
+#include "hs20_supplicant.h"
#define MAX_TFS_IE_LEN 1024
#define WNM_MAX_NEIGHBOR_REPORT 10
@@ -751,6 +752,153 @@
}
+static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *data,
+ int len)
+{
+ const u8 *pos, *end, *next;
+ u8 ie, ie_len;
+
+ pos = data;
+ end = data + len;
+
+ while (pos + 1 < end) {
+ ie = *pos++;
+ ie_len = *pos++;
+ wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
+ ie, ie_len);
+ if (ie_len > end - pos) {
+ wpa_printf(MSG_DEBUG, "WNM: Not enough room for "
+ "subelement");
+ break;
+ }
+ next = pos + ie_len;
+ if (ie_len < 4) {
+ pos = next;
+ continue;
+ }
+ wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u",
+ WPA_GET_BE24(pos), pos[3]);
+
+#ifdef CONFIG_HS20
+ if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
+ WPA_GET_BE24(pos) == OUI_WFA &&
+ pos[3] == HS20_WNM_SUB_REM_NEEDED) {
+ /* Subscription Remediation subelement */
+ const u8 *ie_end;
+ u8 url_len;
+ char *url;
+ u8 osu_method;
+
+ wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation "
+ "subelement");
+ ie_end = pos + ie_len;
+ pos += 4;
+ url_len = *pos++;
+ if (url_len == 0) {
+ wpa_printf(MSG_DEBUG, "WNM: No Server URL included");
+ url = NULL;
+ osu_method = 1;
+ } else {
+ if (pos + url_len + 1 > ie_end) {
+ wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
+ url_len,
+ (int) (ie_end - pos));
+ break;
+ }
+ url = os_malloc(url_len + 1);
+ if (url == NULL)
+ break;
+ os_memcpy(url, pos, url_len);
+ url[url_len] = '\0';
+ osu_method = pos[url_len];
+ }
+ hs20_rx_subscription_remediation(wpa_s, url,
+ osu_method);
+ os_free(url);
+ pos = next;
+ continue;
+ }
+
+ if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
+ WPA_GET_BE24(pos) == OUI_WFA &&
+ pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
+ const u8 *ie_end;
+ u8 url_len;
+ char *url;
+ u8 code;
+ u16 reauth_delay;
+
+ ie_end = pos + ie_len;
+ pos += 4;
+ code = *pos++;
+ reauth_delay = WPA_GET_LE16(pos);
+ pos += 2;
+ url_len = *pos++;
+ wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication "
+ "Imminent - Reason Code %u "
+ "Re-Auth Delay %u URL Length %u",
+ code, reauth_delay, url_len);
+ if (pos + url_len > ie_end)
+ break;
+ url = os_malloc(url_len + 1);
+ if (url == NULL)
+ break;
+ os_memcpy(url, pos, url_len);
+ url[url_len] = '\0';
+ hs20_rx_deauth_imminent_notice(wpa_s, code,
+ reauth_delay, url);
+ os_free(url);
+ pos = next;
+ continue;
+ }
+#endif /* CONFIG_HS20 */
+
+ pos = next;
+ }
+}
+
+
+static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *frm, int len)
+{
+ const u8 *pos, *end;
+ u8 dialog_token, type;
+
+ /* Dialog Token [1] | Type [1] | Subelements */
+
+ if (len < 2 || sa == NULL)
+ return;
+ end = frm + len;
+ pos = frm;
+ dialog_token = *pos++;
+ type = *pos++;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request "
+ "(dialog_token %u type %u sa " MACSTR ")",
+ dialog_token, type, MAC2STR(sa));
+ wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements",
+ pos, end - pos);
+
+ if (wpa_s->wpa_state != WPA_COMPLETED ||
+ os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
+ "from our AP - ignore it");
+ return;
+ }
+
+ switch (type) {
+ case 1:
+ ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos);
+ break;
+ default:
+ wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown "
+ "WNM-Notification type %u", type);
+ break;
+ }
+}
+
+
void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
const struct ieee80211_mgmt *mgmt, size_t len)
{
@@ -782,6 +930,9 @@
case WNM_SLEEP_MODE_RESP:
ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos);
break;
+ case WNM_NOTIFICATION_REQ:
+ ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
+ break;
default:
wpa_printf(MSG_ERROR, "WNM: Unknown request");
break;
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 6278806..0691183 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -2298,6 +2298,37 @@
return wpa_ctrl_command(ctrl, cmd);
}
+
+static int wpa_cli_cmd_hs20_icon_request(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[512];
+
+ if (argc < 2) {
+ printf("Command needs two arguments (dst mac addr and "
+ "icon name)\n");
+ return -1;
+ }
+
+ if (write_cmd(cmd, sizeof(cmd), "HS20_ICON_REQUEST", argc, argv) < 0)
+ return -1;
+
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_fetch_osu(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "FETCH_OSU");
+}
+
+
+static int wpa_cli_cmd_cancel_fetch_osu(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "CANCEL_FETCH_OSU");
+}
+
#endif /* CONFIG_HS20 */
@@ -2831,6 +2862,14 @@
{ "nai_home_realm_list", wpa_cli_cmd_get_nai_home_realm_list,
wpa_cli_complete_bss, cli_cmd_flag_none,
"<addr> <home realm> = get HS20 nai home realm list" },
+ { "hs20_icon_request", wpa_cli_cmd_hs20_icon_request,
+ wpa_cli_complete_bss, cli_cmd_flag_none,
+ "<addr> <icon name> = get Hotspot 2.0 OSU icon" },
+ { "fetch_osu", wpa_cli_cmd_fetch_osu, NULL, cli_cmd_flag_none,
+ "= fetch OSU provider information from all APs" },
+ { "cancel_fetch_osu", wpa_cli_cmd_cancel_fetch_osu, NULL,
+ cli_cmd_flag_none,
+ "= cancel fetch_osu command" },
#endif /* CONFIG_HS20 */
{ "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, NULL,
cli_cmd_flag_none,
@@ -3181,6 +3220,10 @@
wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_match(pos, ESS_DISASSOC_IMMINENT)) {
wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, HS20_SUBSCRIPTION_REMEDIATION)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, HS20_DEAUTH_IMMINENT_NOTICE)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_match(pos, WPA_EVENT_TERMINATING)) {
printf("wpa_supplicant is terminating - stop monitoring\n");
wpa_cli_quit = 1;
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index e942b62..ad1a03e 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -483,6 +483,10 @@
os_free(wpa_s->last_scan_res);
wpa_s->last_scan_res = NULL;
+
+#ifdef CONFIG_HS20
+ hs20_free_osu_prov(wpa_s);
+#endif /* CONFIG_HS20 */
}
@@ -933,13 +937,14 @@
{
struct wpa_ie_data ie;
int sel, proto;
- const u8 *bss_wpa, *bss_rsn;
+ const u8 *bss_wpa, *bss_rsn, *bss_osen;
if (bss) {
bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ bss_osen = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
} else
- bss_wpa = bss_rsn = NULL;
+ bss_wpa = bss_rsn = bss_osen = NULL;
if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) &&
wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 &&
@@ -955,11 +960,22 @@
(ie.key_mgmt & ssid->key_mgmt)) {
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
proto = WPA_PROTO_WPA;
+#ifdef CONFIG_HS20
+ } else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using OSEN");
+ /* TODO: parse OSEN element */
+ ie.group_cipher = WPA_CIPHER_CCMP;
+ ie.pairwise_cipher = WPA_CIPHER_CCMP;
+ ie.key_mgmt = WPA_KEY_MGMT_OSEN;
+ proto = WPA_PROTO_OSEN;
+#endif /* CONFIG_HS20 */
} else if (bss) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN");
return -1;
} else {
- if (ssid->proto & WPA_PROTO_RSN)
+ if (ssid->proto & WPA_PROTO_OSEN)
+ proto = WPA_PROTO_OSEN;
+ else if (ssid->proto & WPA_PROTO_RSN)
proto = WPA_PROTO_RSN;
else
proto = WPA_PROTO_WPA;
@@ -992,7 +1008,7 @@
wpa_s->wpa_proto = proto;
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED,
- !!(ssid->proto & WPA_PROTO_RSN));
+ !!(ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)));
if (bss || !wpa_s->ap_ies_from_associnfo) {
if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
@@ -1063,6 +1079,11 @@
} else if (sel & WPA_KEY_MGMT_WPA_NONE) {
wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
+#ifdef CONFIG_HS20
+ } else if (sel & WPA_KEY_MGMT_OSEN) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN;
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN");
+#endif /* CONFIG_HS20 */
} else {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select "
"authenticated key management type");
@@ -1208,6 +1229,10 @@
#endif /* CONFIG_INTERWORKING */
break;
case 5: /* Bits 40-47 */
+#ifdef CONFIG_HS20
+ if (wpa_s->conf->hs20)
+ *pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_HS20 */
break;
case 6: /* Bits 48-55 */
break;
@@ -1218,7 +1243,7 @@
int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf)
{
u8 *pos = buf;
- u8 len = 4, i;
+ u8 len = 6, i;
if (len < wpa_s->extended_capa_len)
len = wpa_s->extended_capa_len;
@@ -1366,6 +1391,11 @@
return;
}
+ if (radio_work_pending(wpa_s, "connect")) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since pending work exist");
+ return;
+ }
+
cwork = os_zalloc(sizeof(*cwork));
if (cwork == NULL)
return;
@@ -1581,7 +1611,8 @@
struct wpabuf *hs20;
hs20 = wpabuf_alloc(20);
if (hs20) {
- wpas_hs20_add_indication(hs20);
+ int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+ wpas_hs20_add_indication(hs20, pps_mo_id);
os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(hs20),
wpabuf_len(hs20));
wpa_ie_len += wpabuf_len(hs20);
@@ -3237,6 +3268,20 @@
}
+int radio_work_pending(struct wpa_supplicant *wpa_s, const char *type)
+{
+ struct wpa_radio_work *work;
+ struct wpa_radio *radio = wpa_s->radio;
+
+ dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
+ if (work->wpa_s == wpa_s && os_strcmp(work->type, type) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
static int wpas_init_driver(struct wpa_supplicant *wpa_s,
struct wpa_interface *iface)
{
@@ -4299,17 +4344,23 @@
if (ssid->auth_failures > 50)
dur = 300;
- else if (ssid->auth_failures > 20)
- dur = 120;
else if (ssid->auth_failures > 10)
- dur = 60;
+ dur = 120;
else if (ssid->auth_failures > 5)
+ dur = 90;
+ else if (ssid->auth_failures > 3)
+ dur = 60;
+ else if (ssid->auth_failures > 2)
dur = 30;
else if (ssid->auth_failures > 1)
dur = 20;
else
dur = 10;
+ if (ssid->auth_failures > 1 &&
+ wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt))
+ dur += os_random() % (ssid->auth_failures * 10);
+
os_get_reltime(&now);
if (now.sec + dur <= ssid->disabled_until.sec)
return;
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index b627632..442b44c 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -432,6 +432,57 @@
# matching with the network. Multiple entries can be used to specify more
# than one SSID.
#
+# roaming_partner: Roaming partner information
+# This optional field can be used to configure preferences between roaming
+# partners. The field is a string in following format:
+# <FQDN>,<0/1 exact match>,<priority>,<* or country code>
+# (non-exact match means any subdomain matches the entry; priority is in
+# 0..255 range with 0 being the highest priority)
+#
+# update_identifier: PPS MO ID
+# (Hotspot 2.0 PerProviderSubscription/UpdateIdentifier)
+#
+# provisioning_sp: FQDN of the SP that provisioned the credential
+# This optional field can be used to keep track of the SP that provisioned
+# the credential to find the PPS MO (./Wi-Fi/<provisioning_sp>).
+#
+# Minimum backhaul threshold (PPS/<X+>/Policy/MinBackhauldThreshold/*)
+# These fields can be used to specify minimum download/upload backhaul
+# bandwidth that is preferred for the credential. This constraint is
+# ignored if the AP does not advertise WAN Metrics information or if the
+# limit would prevent any connection. Values are in kilobits per second.
+# min_dl_bandwidth_home
+# min_ul_bandwidth_home
+# min_dl_bandwidth_roaming
+# min_ul_bandwidth_roaming
+#
+# max_bss_load: Maximum BSS Load Channel Utilization (1..255)
+# (PPS/<X+>/Policy/MaximumBSSLoadValue)
+# This value is used as the maximum channel utilization for network
+# selection purposes for home networks. If the AP does not advertise
+# BSS Load or if the limit would prevent any connection, this constraint
+# will be ignored.
+#
+# req_conn_capab: Required connection capability
+# (PPS/<X+>/Policy/RequiredProtoPortTuple)
+# This value is used to configure set of required protocol/port pairs that
+# a roaming network shall support (include explicitly in Connection
+# Capability ANQP element). This constraint is ignored if the AP does not
+# advertise Connection Capability or if this constraint would prevent any
+# network connection. This policy is not used in home networks.
+# Format: <protocol>[:<comma-separated list of ports]
+# Multiple entries can be used to list multiple requirements.
+# For example, number of common TCP protocols:
+# req_conn_capab=6,22,80,443
+# For example, IPSec/IKE:
+# req_conn_capab=17:500
+# req_conn_capab=50
+#
+# ocsp: Whether to use/require OCSP to check server certificate
+# 0 = do not use OCSP stapling (TLS certificate status extension)
+# 1 = try to use OCSP stapling, but not require response
+# 2 = require valid OCSP stapling response
+#
# for example:
#
#cred={
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index bcdb4d0..1314734 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -308,6 +308,7 @@
void radio_remove_works(struct wpa_supplicant *wpa_s,
const char *type, int remove_all);
void radio_work_check_next(struct wpa_supplicant *wpa_s);
+int radio_work_pending(struct wpa_supplicant *wpa_s, const char *type);
struct wpa_connect_work {
unsigned int sme:1;
@@ -419,6 +420,9 @@
enum { WPA_SETBAND_AUTO, WPA_SETBAND_5G, WPA_SETBAND_2G } setband;
+ /* Preferred network for the next connection attempt */
+ struct wpa_ssid *next_ssid;
+
/* previous scan was wildcard when interleaving between
* wildcard scans and specific SSID scan when max_ssids=1 */
int prev_scan_wildcard;
@@ -767,7 +771,15 @@
unsigned int auto_select:1;
unsigned int auto_network_select:1;
unsigned int fetch_all_anqp:1;
+ unsigned int fetch_osu_info:1;
+ unsigned int fetch_osu_icon_in_progress:1;
struct wpa_bss *interworking_gas_bss;
+ unsigned int osu_icon_id;
+ struct osu_provider *osu_prov;
+ size_t osu_prov_count;
+ struct os_reltime osu_icon_fetch_start;
+ unsigned int num_osu_scans;
+ unsigned int num_prov_found;
#endif /* CONFIG_INTERWORKING */
unsigned int drv_capa_known;
diff --git a/wpa_supplicant/wpas_module_tests.c b/wpa_supplicant/wpas_module_tests.c
index 4e39024..86b70a9 100644
--- a/wpa_supplicant/wpas_module_tests.c
+++ b/wpa_supplicant/wpas_module_tests.c
@@ -9,6 +9,65 @@
#include "utils/includes.h"
#include "utils/common.h"
+#include "wpa_supplicant_i.h"
+#include "blacklist.h"
+
+
+static int wpas_blacklist_module_tests(void)
+{
+ struct wpa_supplicant wpa_s;
+ int ret = -1;
+
+ os_memset(&wpa_s, 0, sizeof(wpa_s));
+
+ wpa_blacklist_clear(&wpa_s);
+
+ if (wpa_blacklist_get(NULL, NULL) != NULL ||
+ wpa_blacklist_get(NULL, (u8 *) "123456") != NULL ||
+ wpa_blacklist_get(&wpa_s, NULL) != NULL ||
+ wpa_blacklist_get(&wpa_s, (u8 *) "123456") != NULL)
+ goto fail;
+
+ if (wpa_blacklist_add(NULL, NULL) == 0 ||
+ wpa_blacklist_add(NULL, (u8 *) "123456") == 0 ||
+ wpa_blacklist_add(&wpa_s, NULL) == 0)
+ goto fail;
+
+ if (wpa_blacklist_del(NULL, NULL) == 0 ||
+ wpa_blacklist_del(NULL, (u8 *) "123456") == 0 ||
+ wpa_blacklist_del(&wpa_s, NULL) == 0 ||
+ wpa_blacklist_del(&wpa_s, (u8 *) "123456") == 0)
+ goto fail;
+
+ if (wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "222222") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "333333") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "444444") < 0 ||
+ wpa_blacklist_del(&wpa_s, (u8 *) "333333") < 0 ||
+ wpa_blacklist_del(&wpa_s, (u8 *) "xxxxxx") == 0 ||
+ wpa_blacklist_get(&wpa_s, (u8 *) "xxxxxx") != NULL ||
+ wpa_blacklist_get(&wpa_s, (u8 *) "111111") == NULL ||
+ wpa_blacklist_get(&wpa_s, (u8 *) "222222") == NULL ||
+ wpa_blacklist_get(&wpa_s, (u8 *) "444444") == NULL ||
+ wpa_blacklist_del(&wpa_s, (u8 *) "111111") < 0 ||
+ wpa_blacklist_del(&wpa_s, (u8 *) "222222") < 0 ||
+ wpa_blacklist_del(&wpa_s, (u8 *) "444444") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "222222") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "333333") < 0)
+ goto fail;
+
+ ret = 0;
+fail:
+ wpa_blacklist_clear(&wpa_s);
+
+ if (ret)
+ wpa_printf(MSG_ERROR, "blacklist module test failure");
+
+ return ret;
+}
+
int wpas_module_tests(void)
{
@@ -16,6 +75,9 @@
wpa_printf(MSG_INFO, "wpa_supplicant module tests");
+ if (wpas_blacklist_module_tests() < 0)
+ ret = -1;
+
#ifdef CONFIG_WPS
{
int wps_module_tests(void);