HS 2.0R2: Add support for Policy/RequiredProtoPortTuple
The new credential parameter req_conn_capab can be used to specify restrictions on roaming networks providing connectivity for a set of protocols/ports. Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
parent
a45b2dc5dc
commit
33fb8c526c
5 changed files with 202 additions and 3 deletions
|
@ -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
|
# BSS Load or if the limit would prevent any connection, this constraint
|
||||||
# will be ignored.
|
# 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:
|
# for example:
|
||||||
#
|
#
|
||||||
#cred={
|
#cred={
|
||||||
|
|
|
@ -1931,6 +1931,10 @@ void wpa_config_free_cred(struct wpa_cred *cred)
|
||||||
os_free(cred->excluded_ssid);
|
os_free(cred->excluded_ssid);
|
||||||
os_free(cred->roaming_partner);
|
os_free(cred->roaming_partner);
|
||||||
os_free(cred->provisioning_sp);
|
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);
|
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,
|
int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
|
||||||
const char *value, int line)
|
const char *value, int line)
|
||||||
{
|
{
|
||||||
|
@ -2478,6 +2545,9 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
|
||||||
return 0;
|
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);
|
val = wpa_config_parse_string(value, &len);
|
||||||
if (val == NULL) {
|
if (val == NULL) {
|
||||||
wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
|
wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
|
||||||
|
|
|
@ -265,6 +265,10 @@ struct wpa_cred {
|
||||||
* constraint will be ignored.
|
* constraint will be ignored.
|
||||||
*/
|
*/
|
||||||
unsigned int max_bss_load;
|
unsigned int max_bss_load;
|
||||||
|
|
||||||
|
unsigned int num_req_conn_capab;
|
||||||
|
u8 *req_conn_capab_proto;
|
||||||
|
int **req_conn_capab_port;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,7 @@ static int cred_with_domain(struct wpa_supplicant *wpa_s)
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_HS20
|
#ifdef CONFIG_HS20
|
||||||
|
|
||||||
static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
|
static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
|
||||||
{
|
{
|
||||||
struct wpa_cred *cred;
|
struct wpa_cred *cred;
|
||||||
|
@ -180,6 +181,19 @@ static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
|
||||||
}
|
}
|
||||||
return 0;
|
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 */
|
#endif /* CONFIG_HS20 */
|
||||||
|
|
||||||
|
|
||||||
|
@ -253,7 +267,7 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
|
||||||
HS20_STYPE_OPERATOR_FRIENDLY_NAME);
|
HS20_STYPE_OPERATOR_FRIENDLY_NAME);
|
||||||
if (all || cred_with_min_backhaul(wpa_s))
|
if (all || cred_with_min_backhaul(wpa_s))
|
||||||
wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
|
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);
|
wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
|
||||||
if (all)
|
if (all)
|
||||||
wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
|
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(
|
static struct wpa_cred * interworking_credentials_available_roaming_consortium(
|
||||||
struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw)
|
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;
|
continue;
|
||||||
if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
|
if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
|
||||||
continue;
|
continue;
|
||||||
|
if (!ignore_bw && cred_conn_capab_missing(wpa_s, cred, bss))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (selected == NULL ||
|
if (selected == NULL ||
|
||||||
selected->priority < cred->priority)
|
selected->priority < cred->priority)
|
||||||
|
@ -1683,6 +1769,9 @@ static struct wpa_cred * interworking_credentials_available_3gpp(
|
||||||
if (!ignore_bw &&
|
if (!ignore_bw &&
|
||||||
cred_over_max_bss_load(wpa_s, cred, bss))
|
cred_over_max_bss_load(wpa_s, cred, bss))
|
||||||
continue;
|
continue;
|
||||||
|
if (!ignore_bw &&
|
||||||
|
cred_conn_capab_missing(wpa_s, cred, bss))
|
||||||
|
continue;
|
||||||
if (selected == NULL ||
|
if (selected == NULL ||
|
||||||
selected->priority < cred->priority)
|
selected->priority < cred->priority)
|
||||||
selected = cred;
|
selected = cred;
|
||||||
|
@ -1733,6 +1822,9 @@ static struct wpa_cred * interworking_credentials_available_realm(
|
||||||
if (!ignore_bw &&
|
if (!ignore_bw &&
|
||||||
cred_over_max_bss_load(wpa_s, cred, bss))
|
cred_over_max_bss_load(wpa_s, cred, bss))
|
||||||
continue;
|
continue;
|
||||||
|
if (!ignore_bw &&
|
||||||
|
cred_conn_capab_missing(wpa_s, cred, bss))
|
||||||
|
continue;
|
||||||
if (selected == NULL ||
|
if (selected == NULL ||
|
||||||
selected->priority < cred->priority)
|
selected->priority < cred->priority)
|
||||||
selected = cred;
|
selected = cred;
|
||||||
|
@ -2024,12 +2116,15 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s)
|
||||||
type = "roaming";
|
type = "roaming";
|
||||||
else
|
else
|
||||||
type = "unknown";
|
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,
|
MAC2STR(bss->bssid), type,
|
||||||
cred_below_min_backhaul(wpa_s, cred, bss) ?
|
cred_below_min_backhaul(wpa_s, cred, bss) ?
|
||||||
" below_min_backhaul=1" : "",
|
" below_min_backhaul=1" : "",
|
||||||
cred_over_max_bss_load(wpa_s, cred, bss) ?
|
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 ||
|
if (wpa_s->auto_select ||
|
||||||
(wpa_s->conf->auto_interworking &&
|
(wpa_s->conf->auto_interworking &&
|
||||||
wpa_s->auto_network_select)) {
|
wpa_s->auto_network_select)) {
|
||||||
|
|
|
@ -463,6 +463,21 @@ fast_reauth=1
|
||||||
# BSS Load or if the limit would prevent any connection, this constraint
|
# BSS Load or if the limit would prevent any connection, this constraint
|
||||||
# will be ignored.
|
# 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:
|
# for example:
|
||||||
#
|
#
|
||||||
#cred={
|
#cred={
|
||||||
|
|
Loading…
Reference in a new issue