Interworking: Add support for credential priorities

This allows credentials to be set with a specific priority to allow
the automatic network selection behavior to be controlled with user
preferences. The priority values are configured to the network block
and BSS selection will select the network based on priorities from
both pre-configured network blocks and credentials.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2012-02-16 16:37:21 +02:00 committed by Jouni Malinen
parent d94c9ee6ad
commit 1a712d2fc1
4 changed files with 69 additions and 25 deletions

View File

@ -2217,6 +2217,11 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
char *val; char *val;
size_t len; size_t len;
if (os_strcmp(var, "priority") == 0) {
cred->priority = atoi(value);
return 0;
}
val = wpa_config_parse_string(value, &len); val = wpa_config_parse_string(value, &len);
if (val == NULL) if (val == NULL)
return -1; return -1;

View File

@ -48,6 +48,18 @@ struct wpa_cred {
*/ */
int id; int id;
/**
* priority - Priority group
*
* By default, all networks and credentials get the same priority group
* (0). This field can be used to give higher priority for credentials
* (and similarly in struct wpa_ssid for network blocks) to change the
* Interworking automatic networking selection behavior. The matching
* network (based on either an enabled network block or a credential)
* with the highest priority value will be selected.
*/
int priority;
/** /**
* realm - Home Realm for Interworking * realm - Home Realm for Interworking
*/ */

View File

@ -657,6 +657,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred) static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred)
{ {
if (cred->priority)
fprintf(f, "\tpriority=%d\n", cred->priority);
if (cred->realm) if (cred->realm)
fprintf(f, "\trealm=\"%s\"\n", cred->realm); fprintf(f, "\trealm=\"%s\"\n", cred->realm);
if (cred->username) if (cred->username)

View File

@ -1,6 +1,6 @@
/* /*
* Interworking (IEEE 802.11u) * Interworking (IEEE 802.11u)
* Copyright (c) 2011, Qualcomm Atheros * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
* *
* This software may be distributed under the terms of the BSD license. * This software may be distributed under the terms of the BSD license.
* See README for more details. * See README for more details.
@ -601,6 +601,7 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
wpas_notify_network_added(wpa_s, ssid); wpas_notify_network_added(wpa_s, ssid);
wpa_config_set_network_defaults(ssid); wpa_config_set_network_defaults(ssid);
ssid->priority = cred->priority;
ssid->temporary = 1; ssid->temporary = 1;
ssid->ssid = os_zalloc(ie[1] + 1); ssid->ssid = os_zalloc(ie[1] + 1);
if (ssid->ssid == NULL) if (ssid->ssid == NULL)
@ -632,6 +633,7 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
wpa_config_set_quoted(ssid, "password", cred->password) < 0) wpa_config_set_quoted(ssid, "password", cred->password) < 0)
goto fail; goto fail;
wpa_config_update_prio_list(wpa_s->conf);
wpa_s->disconnected = 0; wpa_s->disconnected = 0;
wpa_s->reassociate = 1; wpa_s->reassociate = 1;
wpa_supplicant_req_scan(wpa_s, 0, 0); wpa_supplicant_req_scan(wpa_s, 0, 0);
@ -708,6 +710,7 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
} }
wpas_notify_network_added(wpa_s, ssid); wpas_notify_network_added(wpa_s, ssid);
wpa_config_set_network_defaults(ssid); wpa_config_set_network_defaults(ssid);
ssid->priority = cred->priority;
ssid->temporary = 1; ssid->temporary = 1;
ssid->ssid = os_zalloc(ie[1] + 1); ssid->ssid = os_zalloc(ie[1] + 1);
if (ssid->ssid == NULL) if (ssid->ssid == NULL)
@ -799,6 +802,7 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
nai_realm_free(realm, count); nai_realm_free(realm, count);
wpa_config_update_prio_list(wpa_s->conf);
wpa_s->disconnected = 0; wpa_s->disconnected = 0;
wpa_s->reassociate = 1; wpa_s->reassociate = 1;
wpa_supplicant_req_scan(wpa_s, 0, 0); wpa_supplicant_req_scan(wpa_s, 0, 0);
@ -813,15 +817,15 @@ fail:
} }
static int interworking_credentials_available_3gpp( 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)
{ {
struct wpa_cred *cred; struct wpa_cred *cred, *selected = NULL;
int ret; int ret;
#ifdef INTERWORKING_3GPP #ifdef INTERWORKING_3GPP
if (bss->anqp_3gpp == NULL) if (bss->anqp_3gpp == NULL)
return 0; return NULL;
for (cred = wpa_s->conf->cred; cred; cred = cred->next) { for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
if (cred->imsi == NULL || !cred->imsi[0] || if (cred->imsi == NULL || !cred->imsi[0] ||
@ -832,27 +836,29 @@ static int interworking_credentials_available_3gpp(
MACSTR, MAC2STR(bss->bssid)); MACSTR, MAC2STR(bss->bssid));
ret = plmn_id_match(bss->anqp_3gpp, cred->imsi); ret = plmn_id_match(bss->anqp_3gpp, cred->imsi);
wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not "); wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
if (ret) if (ret) {
return 1; if (selected == NULL ||
selected->priority < cred->priority)
selected = cred;
}
} }
#endif /* INTERWORKING_3GPP */ #endif /* INTERWORKING_3GPP */
return 0; return selected;
} }
static int interworking_credentials_available_realm( 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)
{ {
struct wpa_cred *cred; struct wpa_cred *cred, *selected = NULL;
struct nai_realm *realm; struct nai_realm *realm;
u16 count, i; u16 count, i;
int found = 0;
if (bss->anqp_nai_realm == NULL) if (bss->anqp_nai_realm == NULL)
return 0; return NULL;
if (wpa_s->conf->cred == NULL) if (wpa_s->conf->cred == NULL)
return 0; return NULL;
wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from " wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
MACSTR, MAC2STR(bss->bssid)); MACSTR, MAC2STR(bss->bssid));
@ -860,7 +866,7 @@ static int interworking_credentials_available_realm(
if (realm == NULL) { if (realm == NULL) {
wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI " wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
"Realm list from " MACSTR, MAC2STR(bss->bssid)); "Realm list from " MACSTR, MAC2STR(bss->bssid));
return 0; return NULL;
} }
for (cred = wpa_s->conf->cred; cred; cred = cred->next) { for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
@ -871,25 +877,33 @@ static int 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])) {
found++; if (selected == NULL ||
selected->priority < cred->priority)
selected = cred;
break; break;
} }
} }
if (found)
break;
} }
nai_realm_free(realm, count); nai_realm_free(realm, count);
return found; return selected;
} }
static int interworking_credentials_available(struct wpa_supplicant *wpa_s, static struct wpa_cred * interworking_credentials_available(
struct wpa_bss *bss) struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
{ {
return interworking_credentials_available_realm(wpa_s, bss) || struct wpa_cred *cred, *cred2;
interworking_credentials_available_3gpp(wpa_s, bss);
cred = interworking_credentials_available_realm(wpa_s, bss);
cred2 = interworking_credentials_available_3gpp(wpa_s, bss);
if (cred && cred2 && cred2->priority >= cred->priority)
cred = cred2;
if (!cred)
cred = cred2;
return cred;
} }
@ -962,14 +976,17 @@ static int interworking_home_sp(struct wpa_supplicant *wpa_s,
static void interworking_select_network(struct wpa_supplicant *wpa_s) static void interworking_select_network(struct wpa_supplicant *wpa_s)
{ {
struct wpa_bss *bss, *selected = NULL, *selected_home = NULL; struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
int selected_prio = -999999, selected_home_prio = -999999;
unsigned int count = 0; unsigned int count = 0;
const char *type; const char *type;
int res; int res;
struct wpa_cred *cred;
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) {
if (!interworking_credentials_available(wpa_s, bss)) cred = interworking_credentials_available(wpa_s, bss);
if (!cred)
continue; continue;
count++; count++;
res = interworking_home_sp(wpa_s, bss->anqp_domain_name); res = interworking_home_sp(wpa_s, bss->anqp_domain_name);
@ -982,14 +999,22 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s)
wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s", wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s",
MAC2STR(bss->bssid), type); MAC2STR(bss->bssid), type);
if (wpa_s->auto_select) { if (wpa_s->auto_select) {
if (selected == NULL) if (selected == NULL ||
cred->priority > selected_prio) {
selected = bss; selected = bss;
if (selected_home == NULL && res > 0) selected_prio = cred->priority;
}
if (res > 0 &&
(selected_home == NULL ||
cred->priority > selected_home_prio)) {
selected_home = bss; selected_home = bss;
selected_home_prio = cred->priority;
}
} }
} }
if (selected_home && selected_home != selected) { if (selected_home && selected_home != selected &&
selected_home_prio >= selected_prio) {
/* Prefer network operated by the Home SP */ /* Prefer network operated by the Home SP */
selected = selected_home; selected = selected_home;
} }