+ 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;
+ }
+#endif /* CONFIG_HS20 */
+
+ return 0;
+}
+
+
+static int cred_over_max_bss_load(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ const u8 *ie;
+ int res;
+
+ if (!cred->max_bss_load)
+ return 0; /* No BSS Load constraint specified */
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_BSS_LOAD);
+ if (ie == NULL || ie[1] < 3)
+ return 0; /* No BSS Load advertised */
+
+ res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+ bss->anqp->domain_name : NULL);
+ if (res <= 0)
+ return 0; /* Not a home network */
+
+ return ie[4] > cred->max_bss_load;
+}
+
+
+#ifdef CONFIG_HS20
+
+static int has_proto_match(const u8 *pos, const u8 *end, u8 proto)
+{
+ while (end - pos >= 4) {
+ 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 (end - pos >= 4) {
+ if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port &&
+ pos[3] == 1 /* Open */)
+ return 1;
+ pos += 4;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_HS20 */
+
+
+static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred, struct wpa_bss *bss)
+{
+#ifdef CONFIG_HS20
+ 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;
+ }
+ }
+ }
+#endif /* CONFIG_HS20 */
+
+ return 0;
+}
+
+
+static struct wpa_cred * interworking_credentials_available_roaming_consortium(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+ int *excluded)
+{
+ struct wpa_cred *cred, *selected = NULL;
+ const u8 *ie;
+ int is_excluded = 0;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+
+ if (ie == NULL &&
+ (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
+ return NULL;
+
+ if (wpa_s->conf->cred == NULL)
+ return NULL;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->roaming_consortium_len == 0)
+ continue;
+
+ if (!roaming_consortium_match(ie,
+ bss->anqp ?
+ bss->anqp->roaming_consortium :
+ NULL,
+ cred->roaming_consortium,
+ cred->roaming_consortium_len))
+ continue;
+
+ if (cred_no_required_oi_match(cred, bss))
+ continue;
+ if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
+ continue;
+ if (!ignore_bw && cred_conn_capab_missing(wpa_s, cred, bss))
+ continue;
+ if (cred_excluded_ssid(cred, bss)) {
+ if (excluded == NULL)
+ continue;
+ if (selected == NULL) {
+ selected = cred;
+ is_excluded = 1;
+ }
+ } else {
+ if (selected == NULL || is_excluded ||
+ cred_prio_cmp(selected, cred) < 0) {
+ selected = cred;
+ is_excluded = 0;
+ }
+ }
+ }
+
+ if (excluded)
+ *excluded = is_excluded;
+
+ return selected;
+}
+
+
+static int interworking_set_eap_params(struct wpa_ssid *ssid,
+ struct wpa_cred *cred, int ttls)
+{
+ if (cred->eap_method) {
+ ttls = cred->eap_method->vendor == EAP_VENDOR_IETF &&
+ cred->eap_method->method == EAP_TYPE_TTLS;
+
+ os_free(ssid->eap.eap_methods);
+ ssid->eap.eap_methods =
+ os_malloc(sizeof(struct eap_method_type) * 2);
+ if (ssid->eap.eap_methods == NULL)
+ return -1;
+ os_memcpy(ssid->eap.eap_methods, cred->eap_method,
+ sizeof(*cred->eap_method));
+ ssid->eap.eap_methods[1].vendor = EAP_VENDOR_IETF;
+ ssid->eap.eap_methods[1].method = EAP_TYPE_NONE;