HS 2.0R2: Add support for Policy/MinBackhaulThreshold
authorJouni Malinen <jouni@qca.qualcomm.com>
Mon, 5 Aug 2013 22:06:44 +0000 (01:06 +0300)
committerJouni Malinen <j@w1.fi>
Tue, 25 Feb 2014 23:24:23 +0000 (01:24 +0200)
The new credential parameters min_{dl,ul}_bandwidth_{home,roaming} can
be used to specify restrictions on available backhaul bandwidth.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>

wpa_supplicant/README-HS20
wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/config_file.c
wpa_supplicant/interworking.c
wpa_supplicant/wpa_supplicant.conf

index ec576b9..57abdbf 100644 (file)
@@ -227,6 +227,16 @@ Credentials can be pre-configured for automatic network selection:
 #      This optional field can be used to keep track of the SP that provisioned
 #      the credential to find the PPS MO (./Wi-Fi/<provisioning_sp>).
 #
+# Minimum backhaul threshold (PPS/<X+>/Policy/MinBackhauldThreshold/*)
+#      These fields can be used to specify minimum download/upload backhaul
+#      bandwidth that is preferred for the credential. This constraint is
+#      ignored if the AP does not advertise WAN Metrics information or if the
+#      limit would prevent any connection. Values are in kilobits per second.
+# min_dl_bandwidth_home
+# min_ul_bandwidth_home
+# min_dl_bandwidth_roaming
+# min_ul_bandwidth_roaming
+#
 # for example:
 #
 #cred={
index b540b00..ab7a680 100644 (file)
@@ -2453,6 +2453,26 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
                return 0;
        }
 
+       if (os_strcmp(var, "min_dl_bandwidth_home") == 0) {
+               cred->min_dl_bandwidth_home = atoi(value);
+               return 0;
+       }
+
+       if (os_strcmp(var, "min_ul_bandwidth_home") == 0) {
+               cred->min_ul_bandwidth_home = atoi(value);
+               return 0;
+       }
+
+       if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0) {
+               cred->min_dl_bandwidth_roaming = atoi(value);
+               return 0;
+       }
+
+       if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0) {
+               cred->min_ul_bandwidth_roaming = atoi(value);
+               return 0;
+       }
+
        val = wpa_config_parse_string(value, &len);
        if (val == NULL) {
                wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
index 6c9b753..b37a974 100644 (file)
@@ -251,6 +251,11 @@ struct wpa_cred {
         * provisioning_sp - FQDN of the SP that provisioned the credential
         */
        char *provisioning_sp;
+
+       unsigned int min_dl_bandwidth_home;
+       unsigned int min_ul_bandwidth_home;
+       unsigned int min_dl_bandwidth_roaming;
+       unsigned int min_ul_bandwidth_roaming;
 };
 
 
index d8bd3c9..e50c86a 100644 (file)
@@ -809,6 +809,19 @@ static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred)
 
        if (cred->provisioning_sp)
                fprintf(f, "\tprovisioning_sp=%s\n", cred->provisioning_sp);
+
+       if (cred->min_dl_bandwidth_home)
+               fprintf(f, "\tmin_dl_bandwidth_home=%u\n",
+                       cred->min_dl_bandwidth_home);
+       if (cred->min_ul_bandwidth_home)
+               fprintf(f, "\tmin_ul_bandwidth_home=%u\n",
+                       cred->min_ul_bandwidth_home);
+       if (cred->min_dl_bandwidth_roaming)
+               fprintf(f, "\tmin_dl_bandwidth_roaming=%u\n",
+                       cred->min_dl_bandwidth_roaming);
+       if (cred->min_ul_bandwidth_roaming)
+               fprintf(f, "\tmin_ul_bandwidth_roaming=%u\n",
+                       cred->min_ul_bandwidth_roaming);
 }
 
 
index 3bc6fae..7f42e77 100644 (file)
@@ -46,9 +46,9 @@
 
 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
 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, int ignore_bw);
 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, int ignore_bw);
 
 
 static void interworking_reconnect(struct wpa_supplicant *wpa_s)
@@ -166,6 +166,23 @@ static int cred_with_domain(struct wpa_supplicant *wpa_s)
 }
 
 
+#ifdef CONFIG_HS20
+static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_cred *cred;
+
+       for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+               if (cred->min_dl_bandwidth_home ||
+                   cred->min_ul_bandwidth_home ||
+                   cred->min_dl_bandwidth_roaming ||
+                   cred->min_ul_bandwidth_roaming)
+                       return 1;
+       }
+       return 0;
+}
+#endif /* CONFIG_HS20 */
+
+
 static int additional_roaming_consortiums(struct wpa_bss *bss)
 {
        const u8 *ie;
@@ -231,14 +248,17 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
                wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
                wpabuf_put_u8(extra, 0); /* Reserved */
                wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
-               if (all) {
+               if (all)
                        wpabuf_put_u8(extra,
                                      HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+               if (all || cred_with_min_backhaul(wpa_s))
                        wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
+               if (all)
                        wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
+               if (all)
                        wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
+               if (all)
                        wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST);
-               }
                gas_anqp_set_element_len(extra, len_pos);
        }
 #endif /* CONFIG_HS20 */
@@ -1052,8 +1072,67 @@ static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss)
 }
 
 
+static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s,
+                                  struct wpa_cred *cred, struct wpa_bss *bss)
+{
+       int res;
+       unsigned int dl_bandwidth, ul_bandwidth;
+       const u8 *wan;
+       u8 wan_info, dl_load, ul_load;
+       u16 lmd;
+       u32 ul_speed, dl_speed;
+
+       if (!cred->min_dl_bandwidth_home &&
+           !cred->min_ul_bandwidth_home &&
+           !cred->min_dl_bandwidth_roaming &&
+           !cred->min_ul_bandwidth_roaming)
+               return 0; /* No bandwidth constraint specified */
+
+       if (bss->anqp == NULL || bss->anqp->hs20_wan_metrics == NULL)
+               return 0; /* No WAN Metrics known - ignore constraint */
+
+       wan = wpabuf_head(bss->anqp->hs20_wan_metrics);
+       wan_info = wan[0];
+       if (wan_info & BIT(3))
+               return 1; /* WAN link at capacity */
+       lmd = WPA_GET_LE16(wan + 11);
+       if (lmd == 0)
+               return 0; /* Downlink/Uplink Load was not measured */
+       dl_speed = WPA_GET_LE32(wan + 1);
+       ul_speed = WPA_GET_LE32(wan + 5);
+       dl_load = wan[9];
+       ul_load = wan[10];
+
+       if (dl_speed >= 0xffffff)
+               dl_bandwidth = dl_speed / 255 * (255 - dl_load);
+       else
+               dl_bandwidth = dl_speed * (255 - dl_load) / 255;
+
+       if (ul_speed >= 0xffffff)
+               ul_bandwidth = ul_speed / 255 * (255 - ul_load);
+       else
+               ul_bandwidth = ul_speed * (255 - ul_load) / 255;
+
+       res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+                                       bss->anqp->domain_name : NULL);
+       if (res > 0) {
+               if (cred->min_dl_bandwidth_home > dl_bandwidth)
+                       return 1;
+               if (cred->min_ul_bandwidth_home > ul_bandwidth)
+                       return 1;
+       } else {
+               if (cred->min_dl_bandwidth_roaming > dl_bandwidth)
+                       return 1;
+               if (cred->min_ul_bandwidth_roaming > ul_bandwidth)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
 static struct wpa_cred * interworking_credentials_available_roaming_consortium(
-       struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw)
 {
        struct wpa_cred *cred, *selected = NULL;
        const u8 *ie;
@@ -1083,6 +1162,8 @@ static struct wpa_cred * interworking_credentials_available_roaming_consortium(
                        continue;
                if (cred_no_required_oi_match(cred, bss))
                        continue;
+               if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss))
+                       continue;
 
                if (selected == NULL ||
                    selected->priority < cred->priority)
@@ -1289,26 +1370,52 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
        }
 
        cred_rc = interworking_credentials_available_roaming_consortium(wpa_s,
-                                                                       bss);
+                                                                       bss, 0);
        if (cred_rc) {
                wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
                           "consortium matching credential priority %d",
                           cred_rc->priority);
        }
 
-       cred = interworking_credentials_available_realm(wpa_s, bss);
+       cred = interworking_credentials_available_realm(wpa_s, bss, 0);
        if (cred) {
                wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm list "
                           "matching credential priority %d",
                           cred->priority);
        }
 
-       cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss);
+       cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0);
        if (cred_3gpp) {
                wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP matching "
                           "credential priority %d", cred_3gpp->priority);
        }
 
+       if (!cred_rc && !cred && !cred_3gpp) {
+               cred_rc = interworking_credentials_available_roaming_consortium(
+                       wpa_s, bss, 1);
+               if (cred_rc) {
+                       wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
+                                  "consortium matching credential priority %d "
+                                  "(ignore BW)",
+                                  cred_rc->priority);
+               }
+
+               cred = interworking_credentials_available_realm(wpa_s, bss, 1);
+               if (cred) {
+                       wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm "
+                                  "list matching credential priority %d "
+                                  "(ignore BW)", cred->priority);
+               }
+
+               cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss,
+                                                                   1);
+               if (cred_3gpp) {
+                       wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP "
+                                  "matching credential priority %d (ignore BW)",
+                                  cred_3gpp->priority);
+               }
+       }
+
        if (cred_rc &&
            (cred == NULL || cred_rc->priority >= cred->priority) &&
            (cred_3gpp == NULL || cred_rc->priority >= cred_3gpp->priority))
@@ -1465,7 +1572,7 @@ fail:
 
 
 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, int ignore_bw)
 {
        struct wpa_cred *selected = NULL;
 #ifdef INTERWORKING_3GPP
@@ -1546,6 +1653,9 @@ static struct wpa_cred * interworking_credentials_available_3gpp(
                                continue;
                        if (cred_no_required_oi_match(cred, bss))
                                continue;
+                       if (!ignore_bw &&
+                           cred_below_min_backhaul(wpa_s, cred, bss))
+                               continue;
                        if (selected == NULL ||
                            selected->priority < cred->priority)
                                selected = cred;
@@ -1557,7 +1667,7 @@ static struct wpa_cred * interworking_credentials_available_3gpp(
 
 
 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, int ignore_bw)
 {
        struct wpa_cred *cred, *selected = NULL;
        struct nai_realm *realm;
@@ -1590,6 +1700,9 @@ static struct wpa_cred * interworking_credentials_available_realm(
                                        continue;
                                if (cred_no_required_oi_match(cred, bss))
                                        continue;
+                               if (!ignore_bw &&
+                                   cred_below_min_backhaul(wpa_s, cred, bss))
+                                       continue;
                                if (selected == NULL ||
                                    selected->priority < cred->priority)
                                        selected = cred;
@@ -1604,8 +1717,8 @@ static struct wpa_cred * interworking_credentials_available_realm(
 }
 
 
-static struct wpa_cred * interworking_credentials_available(
-       struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+static struct wpa_cred * interworking_credentials_available_helper(
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw)
 {
        struct wpa_cred *cred, *cred2;
 
@@ -1616,15 +1729,16 @@ static struct wpa_cred * interworking_credentials_available(
                return NULL;
        }
 
-       cred = interworking_credentials_available_realm(wpa_s, bss);
-       cred2 = interworking_credentials_available_3gpp(wpa_s, bss);
+       cred = interworking_credentials_available_realm(wpa_s, bss, ignore_bw);
+       cred2 = interworking_credentials_available_3gpp(wpa_s, bss, ignore_bw);
        if (cred && cred2 && cred2->priority >= cred->priority)
                cred = cred2;
        if (!cred)
                cred = cred2;
 
        cred2 = interworking_credentials_available_roaming_consortium(wpa_s,
-                                                                     bss);
+                                                                     bss,
+                                                                     ignore_bw);
        if (cred && cred2 && cred2->priority >= cred->priority)
                cred = cred2;
        if (!cred)
@@ -1634,6 +1748,18 @@ 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_cred *cred;
+
+       cred = interworking_credentials_available_helper(wpa_s, bss, 0);
+       if (cred)
+               return cred;
+       return interworking_credentials_available_helper(wpa_s, bss, 1);
+}
+
+
 int domain_name_list_contains(struct wpabuf *domain_names,
                              const char *domain, int exact_match)
 {
@@ -1868,8 +1994,10 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s)
                        type = "roaming";
                else
                        type = "unknown";
-               wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s",
-                       MAC2STR(bss->bssid), type);
+               wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s%s",
+                       MAC2STR(bss->bssid), type,
+                       cred_below_min_backhaul(wpa_s, cred, bss) ?
+                       " below_min_backhaul=1" : "");
                if (wpa_s->auto_select ||
                    (wpa_s->conf->auto_interworking &&
                     wpa_s->auto_network_select)) {
index 3c90362..42954e6 100644 (file)
@@ -446,6 +446,16 @@ fast_reauth=1
 #      This optional field can be used to keep track of the SP that provisioned
 #      the credential to find the PPS MO (./Wi-Fi/<provisioning_sp>).
 #
+# Minimum backhaul threshold (PPS/<X+>/Policy/MinBackhauldThreshold/*)
+#      These fields can be used to specify minimum download/upload backhaul
+#      bandwidth that is preferred for the credential. This constraint is
+#      ignored if the AP does not advertise WAN Metrics information or if the
+#      limit would prevent any connection. Values are in kilobits per second.
+# min_dl_bandwidth_home
+# min_ul_bandwidth_home
+# min_dl_bandwidth_roaming
+# min_ul_bandwidth_roaming
+#
 # for example:
 #
 #cred={