+static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ const u8 *ie;
+
+ if (cred->required_roaming_consortium_len == 0)
+ return 0;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+
+ if (ie == NULL &&
+ (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
+ return 1;
+
+ return !roaming_consortium_match(ie,
+ bss->anqp ?
+ bss->anqp->roaming_consortium : NULL,
+ cred->required_roaming_consortium,
+ cred->required_roaming_consortium_len);
+}
+
+
+static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ size_t i;
+
+ if (!cred->excluded_ssid)
+ return 0;
+
+ for (i = 0; i < cred->num_excluded_ssid; i++) {
+ struct excluded_ssid *e = &cred->excluded_ssid[i];
+ if (bss->ssid_len == e->ssid_len &&
+ os_memcmp(bss->ssid, e->ssid, e->ssid_len) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred, struct wpa_bss *bss)
+{
+#ifdef CONFIG_HS20
+ 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;
+ }
+#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;
+}
+
+