diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20 index c79b05ef2..e65efeea4 100644 --- a/wpa_supplicant/README-HS20 +++ b/wpa_supplicant/README-HS20 @@ -244,6 +244,21 @@ Credentials can be pre-configured for automatic network selection: # BSS Load or if the limit would prevent any connection, this constraint # will be ignored. # +# req_conn_capab: Required connection capability +# (PPS//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: [: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); } @@ -2402,6 +2406,69 @@ void wpa_config_update_psk(struct wpa_ssid *ssid) } +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) { @@ -2478,6 +2545,9 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, return 0; } + if (os_strcmp(var, "req_conn_capab") == 0) + return wpa_config_set_cred_req_conn_capab(cred, value); + val = wpa_config_parse_string(value, &len); if (val == NULL) { wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string " diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index f2da234d4..55c5dc68f 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -265,6 +265,10 @@ struct wpa_cred { * 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; }; diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index 860cddca5..89dd0abe4 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -167,6 +167,7 @@ static int cred_with_domain(struct wpa_supplicant *wpa_s) #ifdef CONFIG_HS20 + static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s) { struct wpa_cred *cred; @@ -180,6 +181,19 @@ static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s) } 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 */ @@ -253,7 +267,7 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, HS20_STYPE_OPERATOR_FRIENDLY_NAME); if (all || cred_with_min_backhaul(wpa_s)) wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS); - if (all) + 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); @@ -1153,6 +1167,76 @@ static int cred_over_max_bss_load(struct wpa_supplicant *wpa_s, } +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, int ignore_bw) { @@ -1188,6 +1272,8 @@ static struct wpa_cred * interworking_credentials_available_roaming_consortium( 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 (selected == NULL || selected->priority < cred->priority) @@ -1683,6 +1769,9 @@ static struct wpa_cred * interworking_credentials_available_3gpp( 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 (selected == NULL || selected->priority < cred->priority) selected = cred; @@ -1733,6 +1822,9 @@ static struct wpa_cred * interworking_credentials_available_realm( 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 (selected == NULL || selected->priority < cred->priority) selected = cred; @@ -2024,12 +2116,15 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s) type = "roaming"; else type = "unknown"; - wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s%s%s", + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR + " type=%s%s%s%s", MAC2STR(bss->bssid), type, cred_below_min_backhaul(wpa_s, cred, bss) ? " below_min_backhaul=1" : "", cred_over_max_bss_load(wpa_s, cred, bss) ? - " over_max_bss_load=1" : ""); + " over_max_bss_load=1" : "", + cred_conn_capab_missing(wpa_s, cred, bss) ? + " conn_capab_missing=1" : ""); if (wpa_s->auto_select || (wpa_s->conf->auto_interworking && wpa_s->auto_network_select)) { diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 3b0ea717f..78dbd0939 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -463,6 +463,21 @@ fast_reauth=1 # BSS Load or if the limit would prevent any connection, this constraint # will be ignored. # +# req_conn_capab: Required connection capability +# (PPS//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: [: