# 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
+#
# for example:
#
#cred={
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);
}
}
+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)
{
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 "
#ifdef CONFIG_HS20
+
static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
{
struct wpa_cred *cred;
}
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 */
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);
}
+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)
{
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)
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;
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;
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)) {
# 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
+#
# for example:
#
#cred={