HS 2.0R2: Allow excluded network to be selected based on user override

Move excluded SSID filtering step to the end of credential validation
process and return list of BSSes that would otherwise have matching
credentials, but have an excluded SSID. Automatic network selection will
not select such a network, but interworking_connect command can be used
to pick excluded networks.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2013-08-09 20:37:28 +03:00 committed by Jouni Malinen
parent 33fb8c526c
commit 28f2a7c407
2 changed files with 138 additions and 47 deletions

View file

@ -159,6 +159,7 @@ extern "C" {
#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP " #define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP "
#define INTERWORKING_AP "INTERWORKING-AP " #define INTERWORKING_AP "INTERWORKING-AP "
#define INTERWORKING_BLACKLISTED "INTERWORKING-BLACKLISTED "
#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH " #define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
#define INTERWORKING_ALREADY_CONNECTED "INTERWORKING-ALREADY-CONNECTED " #define INTERWORKING_ALREADY_CONNECTED "INTERWORKING-ALREADY-CONNECTED "

View file

@ -46,9 +46,11 @@
static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s); static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
static struct wpa_cred * interworking_credentials_available_realm( static struct wpa_cred * interworking_credentials_available_realm(
struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw); struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
int *excluded);
static struct wpa_cred * interworking_credentials_available_3gpp( static struct wpa_cred * interworking_credentials_available_3gpp(
struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw); struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
int *excluded);
static void interworking_reconnect(struct wpa_supplicant *wpa_s) static void interworking_reconnect(struct wpa_supplicant *wpa_s)
@ -1238,10 +1240,12 @@ static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s,
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,
int *excluded)
{ {
struct wpa_cred *cred, *selected = NULL; struct wpa_cred *cred, *selected = NULL;
const u8 *ie; const u8 *ie;
int is_excluded = 0;
ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
@ -1264,8 +1268,6 @@ static struct wpa_cred * interworking_credentials_available_roaming_consortium(
cred->roaming_consortium_len)) cred->roaming_consortium_len))
continue; continue;
if (cred_excluded_ssid(cred, bss))
continue;
if (cred_no_required_oi_match(cred, bss)) if (cred_no_required_oi_match(cred, bss))
continue; continue;
if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss)) if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss))
@ -1274,12 +1276,25 @@ static struct wpa_cred * interworking_credentials_available_roaming_consortium(
continue; continue;
if (!ignore_bw && cred_conn_capab_missing(wpa_s, cred, bss)) if (!ignore_bw && cred_conn_capab_missing(wpa_s, cred, bss))
continue; continue;
if (cred_excluded_ssid(cred, bss)) {
if (selected == NULL || if (excluded == NULL)
selected->priority < cred->priority) continue;
selected = cred; if (selected == NULL) {
selected = cred;
is_excluded = 1;
}
} else {
if (selected == NULL || is_excluded ||
selected->priority < cred->priority) {
selected = cred;
is_excluded = 0;
}
}
} }
if (excluded)
*excluded = is_excluded;
return selected; return selected;
} }
@ -1451,7 +1466,8 @@ fail:
} }
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_cred *cred, *cred_rc, *cred_3gpp;
struct wpa_ssid *ssid; struct wpa_ssid *ssid;
@ -1459,6 +1475,7 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
struct nai_realm_eap *eap = NULL; struct nai_realm_eap *eap = NULL;
u16 count, i; u16 count, i;
char buf[100]; char buf[100];
int excluded = 0, *excl = allow_excluded ? &excluded : NULL;
if (wpa_s->conf->cred == NULL || bss == NULL) if (wpa_s->conf->cred == NULL || bss == NULL)
return -1; return -1;
@ -1479,50 +1496,64 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
return -1; return -1;
} }
cred_rc = interworking_credentials_available_roaming_consortium(wpa_s, cred_rc = interworking_credentials_available_roaming_consortium(
bss, 0); wpa_s, bss, 0, excl);
if (cred_rc) { if (cred_rc) {
wpa_printf(MSG_DEBUG, "Interworking: Highest roaming " wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
"consortium matching credential priority %d", "consortium matching credential priority %d",
cred_rc->priority); cred_rc->priority);
if (allow_excluded && excl && !(*excl))
excl = NULL;
} }
cred = interworking_credentials_available_realm(wpa_s, bss, 0); cred = interworking_credentials_available_realm(wpa_s, bss, 0, excl);
if (cred) { if (cred) {
wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm list " wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm list "
"matching credential priority %d", "matching credential priority %d",
cred->priority); cred->priority);
if (allow_excluded && excl && !(*excl))
excl = NULL;
} }
cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0); cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0,
excl);
if (cred_3gpp) { if (cred_3gpp) {
wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP matching " wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP matching "
"credential priority %d", cred_3gpp->priority); "credential priority %d", cred_3gpp->priority);
if (allow_excluded && excl && !(*excl))
excl = NULL;
} }
if (!cred_rc && !cred && !cred_3gpp) { if (!cred_rc && !cred && !cred_3gpp) {
cred_rc = interworking_credentials_available_roaming_consortium( cred_rc = interworking_credentials_available_roaming_consortium(
wpa_s, bss, 1); wpa_s, bss, 1, excl);
if (cred_rc) { if (cred_rc) {
wpa_printf(MSG_DEBUG, "Interworking: Highest roaming " wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
"consortium matching credential priority %d " "consortium matching credential priority %d "
"(ignore BW)", "(ignore BW)",
cred_rc->priority); cred_rc->priority);
if (allow_excluded && excl && !(*excl))
excl = NULL;
} }
cred = interworking_credentials_available_realm(wpa_s, bss, 1); cred = interworking_credentials_available_realm(wpa_s, bss, 1,
excl);
if (cred) { if (cred) {
wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm " wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm "
"list matching credential priority %d " "list matching credential priority %d "
"(ignore BW)", cred->priority); "(ignore BW)", cred->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,
1); 1, excl);
if (cred_3gpp) { if (cred_3gpp) {
wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP " wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP "
"matching credential priority %d (ignore BW)", "matching credential priority %d (ignore BW)",
cred_3gpp->priority); cred_3gpp->priority);
if (allow_excluded && excl && !(*excl))
excl = NULL;
} }
} }
@ -1681,13 +1712,21 @@ fail:
} }
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( static struct wpa_cred * interworking_credentials_available_3gpp(
struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw) struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
int *excluded)
{ {
struct wpa_cred *selected = NULL; struct wpa_cred *selected = NULL;
#ifdef INTERWORKING_3GPP #ifdef INTERWORKING_3GPP
struct wpa_cred *cred; struct wpa_cred *cred;
int ret; int ret;
int is_excluded = 0;
if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
return NULL; return NULL;
@ -1759,8 +1798,6 @@ static struct wpa_cred * interworking_credentials_available_3gpp(
ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len); ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not "); wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
if (ret) { if (ret) {
if (cred_excluded_ssid(cred, bss))
continue;
if (cred_no_required_oi_match(cred, bss)) if (cred_no_required_oi_match(cred, bss))
continue; continue;
if (!ignore_bw && if (!ignore_bw &&
@ -1772,22 +1809,38 @@ static struct wpa_cred * interworking_credentials_available_3gpp(
if (!ignore_bw && if (!ignore_bw &&
cred_conn_capab_missing(wpa_s, cred, bss)) cred_conn_capab_missing(wpa_s, cred, bss))
continue; continue;
if (selected == NULL || if (cred_excluded_ssid(cred, bss)) {
selected->priority < cred->priority) if (excluded == NULL)
selected = cred; continue;
if (selected == NULL) {
selected = cred;
is_excluded = 1;
}
} else {
if (selected == NULL || is_excluded ||
selected->priority < cred->priority) {
selected = cred;
is_excluded = 0;
}
}
} }
} }
if (excluded)
*excluded = is_excluded;
#endif /* INTERWORKING_3GPP */ #endif /* INTERWORKING_3GPP */
return selected; return selected;
} }
static struct wpa_cred * interworking_credentials_available_realm( static struct wpa_cred * interworking_credentials_available_realm(
struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw) struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
int *excluded)
{ {
struct wpa_cred *cred, *selected = NULL; struct wpa_cred *cred, *selected = NULL;
struct nai_realm *realm; struct nai_realm *realm;
u16 count, i; u16 count, i;
int is_excluded = 0;
if (bss->anqp == NULL || bss->anqp->nai_realm == NULL) if (bss->anqp == NULL || bss->anqp->nai_realm == NULL)
return NULL; return NULL;
@ -1812,8 +1865,6 @@ static struct wpa_cred * interworking_credentials_available_realm(
if (!nai_realm_match(&realm[i], cred->realm)) if (!nai_realm_match(&realm[i], cred->realm))
continue; continue;
if (nai_realm_find_eap(cred, &realm[i])) { if (nai_realm_find_eap(cred, &realm[i])) {
if (cred_excluded_ssid(cred, bss))
continue;
if (cred_no_required_oi_match(cred, bss)) if (cred_no_required_oi_match(cred, bss))
continue; continue;
if (!ignore_bw && if (!ignore_bw &&
@ -1825,9 +1876,21 @@ static struct wpa_cred * interworking_credentials_available_realm(
if (!ignore_bw && if (!ignore_bw &&
cred_conn_capab_missing(wpa_s, cred, bss)) cred_conn_capab_missing(wpa_s, cred, bss))
continue; continue;
if (selected == NULL || if (cred_excluded_ssid(cred, bss)) {
selected->priority < cred->priority) if (excluded == NULL)
selected = cred; continue;
if (selected == NULL) {
selected = cred;
is_excluded = 1;
}
} else {
if (selected == NULL || is_excluded ||
selected->priority <
cred->priority) {
selected = cred;
is_excluded = 0;
}
}
break; break;
} }
} }
@ -1835,14 +1898,19 @@ static struct wpa_cred * interworking_credentials_available_realm(
nai_realm_free(realm, count); nai_realm_free(realm, count);
if (excluded)
*excluded = is_excluded;
return selected; return selected;
} }
static struct wpa_cred * interworking_credentials_available_helper( static struct wpa_cred * interworking_credentials_available_helper(
struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw) struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
int *excluded)
{ {
struct wpa_cred *cred, *cred2; struct wpa_cred *cred, *cred2;
int excluded1, excluded2;
if (disallowed_bssid(wpa_s, bss->bssid) || if (disallowed_bssid(wpa_s, bss->bssid) ||
disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) { disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
@ -1851,34 +1919,51 @@ static struct wpa_cred * interworking_credentials_available_helper(
return NULL; return NULL;
} }
cred = interworking_credentials_available_realm(wpa_s, bss, ignore_bw); cred = interworking_credentials_available_realm(wpa_s, bss, ignore_bw,
cred2 = interworking_credentials_available_3gpp(wpa_s, bss, ignore_bw); &excluded1);
if (cred && cred2 && cred2->priority >= cred->priority) cred2 = interworking_credentials_available_3gpp(wpa_s, bss, ignore_bw,
&excluded2);
if (cred && cred2 &&
(cred2->priority >= cred->priority || (!excluded2 && excluded1))) {
cred = cred2; cred = cred2;
if (!cred) excluded1 = excluded2;
}
if (!cred) {
cred = cred2; cred = cred2;
excluded1 = excluded2;
}
cred2 = interworking_credentials_available_roaming_consortium(wpa_s, cred2 = interworking_credentials_available_roaming_consortium(
bss, wpa_s, bss, ignore_bw, &excluded2);
ignore_bw); if (cred && cred2 &&
if (cred && cred2 && cred2->priority >= cred->priority) (cred2->priority >= cred->priority || (!excluded2 && excluded1))) {
cred = cred2; cred = cred2;
if (!cred) excluded1 = excluded2;
}
if (!cred) {
cred = cred2; cred = cred2;
excluded1 = excluded2;
}
if (excluded)
*excluded = excluded1;
return cred; return cred;
} }
static struct wpa_cred * interworking_credentials_available( static struct wpa_cred * interworking_credentials_available(
struct wpa_supplicant *wpa_s, struct wpa_bss *bss) struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int *excluded)
{ {
struct wpa_cred *cred; struct wpa_cred *cred;
cred = interworking_credentials_available_helper(wpa_s, bss, 0); if (excluded)
*excluded = 0;
cred = interworking_credentials_available_helper(wpa_s, bss, 0,
excluded);
if (cred) if (cred)
return cred; return cred;
return interworking_credentials_available_helper(wpa_s, bss, 1); return interworking_credentials_available_helper(wpa_s, bss, 1,
excluded);
} }
@ -2064,7 +2149,7 @@ static struct wpa_bss * pick_best_roaming_partner(struct wpa_supplicant *wpa_s,
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
if (bss == selected) if (bss == selected)
continue; continue;
cred = interworking_credentials_available(wpa_s, bss); cred = interworking_credentials_available(wpa_s, bss, NULL);
if (!cred) if (!cred)
continue; continue;
if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) if (!wpa_bss_get_ie(bss, WLAN_EID_RSN))
@ -2094,7 +2179,9 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s)
wpa_s->network_select = 0; wpa_s->network_select = 0;
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
cred = interworking_credentials_available(wpa_s, bss); int excluded = 0;
cred = interworking_credentials_available(wpa_s, bss,
&excluded);
if (!cred) if (!cred)
continue; continue;
if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
@ -2107,7 +2194,8 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s)
"RSN", MAC2STR(bss->bssid)); "RSN", MAC2STR(bss->bssid));
continue; continue;
} }
count++; if (!excluded)
count++;
res = interworking_home_sp(wpa_s, bss->anqp ? res = interworking_home_sp(wpa_s, bss->anqp ?
bss->anqp->domain_name : NULL); bss->anqp->domain_name : NULL);
if (res > 0) if (res > 0)
@ -2116,8 +2204,8 @@ 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 wpa_msg(wpa_s, MSG_INFO, "%s" MACSTR " type=%s%s%s%s",
" type=%s%s%s%s", excluded ? INTERWORKING_BLACKLISTED : INTERWORKING_AP,
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" : "",
@ -2125,6 +2213,8 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s)
" over_max_bss_load=1" : "", " over_max_bss_load=1" : "",
cred_conn_capab_missing(wpa_s, cred, bss) ? cred_conn_capab_missing(wpa_s, cred, bss) ?
" conn_capab_missing=1" : ""); " conn_capab_missing=1" : "");
if (excluded)
continue;
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)) {