From 1a712d2fc1f0fbc24debfb7901cd983ead31d245 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 16 Feb 2012 16:37:21 +0200 Subject: [PATCH] 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 --- wpa_supplicant/config.c | 5 +++ wpa_supplicant/config.h | 12 ++++++ wpa_supplicant/config_file.c | 2 + wpa_supplicant/interworking.c | 75 +++++++++++++++++++++++------------ 4 files changed, 69 insertions(+), 25 deletions(-) diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 1ccce440c..9a0e1e2a9 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -2217,6 +2217,11 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, char *val; size_t len; + if (os_strcmp(var, "priority") == 0) { + cred->priority = atoi(value); + return 0; + } + val = wpa_config_parse_string(value, &len); if (val == NULL) return -1; diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 35fdc9431..72906fb7f 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -48,6 +48,18 @@ struct wpa_cred { */ 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 */ diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index a7e64bb31..f8f4ff14d 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -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) { + if (cred->priority) + fprintf(f, "\tpriority=%d\n", cred->priority); if (cred->realm) fprintf(f, "\trealm=\"%s\"\n", cred->realm); if (cred->username) diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index 37bca7f64..46a65a031 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -1,6 +1,6 @@ /* * 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. * 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); wpa_config_set_network_defaults(ssid); + ssid->priority = cred->priority; ssid->temporary = 1; ssid->ssid = os_zalloc(ie[1] + 1); 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) goto fail; + wpa_config_update_prio_list(wpa_s->conf); wpa_s->disconnected = 0; wpa_s->reassociate = 1; 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); wpa_config_set_network_defaults(ssid); + ssid->priority = cred->priority; ssid->temporary = 1; ssid->ssid = os_zalloc(ie[1] + 1); 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); + wpa_config_update_prio_list(wpa_s->conf); wpa_s->disconnected = 0; wpa_s->reassociate = 1; 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_cred *cred; + struct wpa_cred *cred, *selected = NULL; int ret; #ifdef INTERWORKING_3GPP if (bss->anqp_3gpp == NULL) - return 0; + return NULL; for (cred = wpa_s->conf->cred; cred; cred = cred->next) { if (cred->imsi == NULL || !cred->imsi[0] || @@ -832,27 +836,29 @@ static int interworking_credentials_available_3gpp( MACSTR, MAC2STR(bss->bssid)); ret = plmn_id_match(bss->anqp_3gpp, cred->imsi); wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not "); - if (ret) - return 1; + if (ret) { + if (selected == NULL || + selected->priority < cred->priority) + selected = cred; + } } #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_cred *cred; + struct wpa_cred *cred, *selected = NULL; struct nai_realm *realm; u16 count, i; - int found = 0; if (bss->anqp_nai_realm == NULL) - return 0; + return NULL; if (wpa_s->conf->cred == NULL) - return 0; + return NULL; wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from " MACSTR, MAC2STR(bss->bssid)); @@ -860,7 +866,7 @@ static int interworking_credentials_available_realm( if (realm == NULL) { wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI " "Realm list from " MACSTR, MAC2STR(bss->bssid)); - return 0; + return NULL; } 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)) continue; if (nai_realm_find_eap(cred, &realm[i])) { - found++; + if (selected == NULL || + selected->priority < cred->priority) + selected = cred; break; } } - if (found) - break; } nai_realm_free(realm, count); - return found; + return selected; } -static int interworking_credentials_available(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss) +static struct wpa_cred * interworking_credentials_available( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { - return interworking_credentials_available_realm(wpa_s, bss) || - interworking_credentials_available_3gpp(wpa_s, bss); + struct wpa_cred *cred, *cred2; + + 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) { struct wpa_bss *bss, *selected = NULL, *selected_home = NULL; + int selected_prio = -999999, selected_home_prio = -999999; unsigned int count = 0; const char *type; int res; + struct wpa_cred *cred; wpa_s->network_select = 0; 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; count++; 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", MAC2STR(bss->bssid), type); if (wpa_s->auto_select) { - if (selected == NULL) + if (selected == NULL || + cred->priority > selected_prio) { 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_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 */ selected = selected_home; }