2 * Interworking (IEEE 802.11u)
3 * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
12 #include "common/ieee802_11_defs.h"
13 #include "common/gas.h"
14 #include "common/wpa_ctrl.h"
15 #include "drivers/driver.h"
16 #include "eap_common/eap_defs.h"
17 #include "eap_peer/eap_methods.h"
18 #include "wpa_supplicant_i.h"
20 #include "config_ssid.h"
24 #include "gas_query.h"
25 #include "interworking.h"
28 #if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC)
29 #define INTERWORKING_3GPP
31 #if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC)
32 #define INTERWORKING_3GPP
34 #if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC)
35 #define INTERWORKING_3GPP
40 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
43 static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
50 buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
51 (extra ? wpabuf_len(extra) : 0));
55 len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
56 for (i = 0; i < num_ids; i++)
57 wpabuf_put_le16(buf, info_ids[i]);
58 gas_anqp_set_element_len(buf, len_pos);
60 wpabuf_put_buf(buf, extra);
62 gas_anqp_set_len(buf);
68 static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
70 enum gas_query_result result,
71 const struct wpabuf *adv_proto,
72 const struct wpabuf *resp,
75 struct wpa_supplicant *wpa_s = ctx;
77 anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
79 interworking_next_anqp_fetch(wpa_s);
83 static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
92 ANQP_NETWORK_AUTH_TYPE,
93 ANQP_ROAMING_CONSORTIUM,
94 ANQP_IP_ADDR_TYPE_AVAILABILITY,
96 ANQP_3GPP_CELLULAR_NETWORK,
99 struct wpabuf *extra = NULL;
101 wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
102 MAC2STR(bss->bssid));
104 buf = anqp_build_req(info_ids, sizeof(info_ids) / sizeof(info_ids[0]),
110 res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
111 interworking_anqp_resp_cb, wpa_s);
113 wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
116 wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
124 struct nai_realm_eap {
127 enum nai_realm_eap_auth_inner_non_eap inner_non_eap;
129 u8 tunneled_cred_type;
136 struct nai_realm_eap *eap;
140 static void nai_realm_free(struct nai_realm *realms, u16 count)
146 for (i = 0; i < count; i++) {
147 os_free(realms[i].eap);
148 os_free(realms[i].realm);
154 static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos,
157 u8 elen, auth_count, a;
161 wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
166 if (pos + elen > end || elen < 2) {
167 wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
173 wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u",
174 elen, e->method, auth_count);
176 for (a = 0; a < auth_count; a++) {
179 if (pos + 2 > end || pos + 2 + pos[1] > end) {
180 wpa_printf(MSG_DEBUG, "No room for Authentication "
181 "Parameter subfield");
189 case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
192 e->inner_non_eap = *pos;
193 if (e->method != EAP_TYPE_TTLS)
196 case NAI_REALM_INNER_NON_EAP_PAP:
197 wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP");
199 case NAI_REALM_INNER_NON_EAP_CHAP:
200 wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP");
202 case NAI_REALM_INNER_NON_EAP_MSCHAP:
203 wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP");
205 case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
206 wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
210 case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
213 e->inner_method = *pos;
214 wpa_printf(MSG_DEBUG, "Inner EAP method: %u",
217 case NAI_REALM_EAP_AUTH_CRED_TYPE:
221 wpa_printf(MSG_DEBUG, "Credential Type: %u",
224 case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE:
227 e->tunneled_cred_type = *pos;
228 wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential "
229 "Type: %u", e->tunneled_cred_type);
232 wpa_printf(MSG_DEBUG, "Unsupported Authentication "
233 "Parameter: id=%u len=%u", id, len);
234 wpa_hexdump(MSG_DEBUG, "Authentication Parameter "
246 static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos,
254 wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
259 len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
261 if (pos + len > end || len < 3) {
262 wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
264 len, (unsigned int) (end - pos));
269 r->encoding = *pos++;
271 if (pos + realm_len > f_end) {
272 wpa_printf(MSG_DEBUG, "No room for NAI Realm "
274 realm_len, (unsigned int) (f_end - pos));
277 wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
278 r->realm = os_malloc(realm_len + 1);
279 if (r->realm == NULL)
281 os_memcpy(r->realm, pos, realm_len);
282 r->realm[realm_len] = '\0';
285 if (pos + 1 > f_end) {
286 wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
289 r->eap_count = *pos++;
290 wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
291 if (pos + r->eap_count * 3 > f_end) {
292 wpa_printf(MSG_DEBUG, "No room for EAP Methods");
295 r->eap = os_zalloc(r->eap_count * sizeof(struct nai_realm_eap));
299 for (e = 0; e < r->eap_count; e++) {
300 pos = nai_realm_parse_eap(&r->eap[e], pos, f_end);
309 static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count)
311 struct nai_realm *realm;
315 if (anqp == NULL || wpabuf_len(anqp) < 2)
318 pos = wpabuf_head_u8(anqp);
319 end = pos + wpabuf_len(anqp);
320 num = WPA_GET_LE16(pos);
321 wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
324 if (num * 5 > end - pos) {
325 wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
326 "enough data (%u octets) for that many realms",
327 num, (unsigned int) (end - pos));
331 realm = os_zalloc(num * sizeof(struct nai_realm));
335 for (i = 0; i < num; i++) {
336 pos = nai_realm_parse_realm(&realm[i], pos, end);
338 nai_realm_free(realm, num);
348 static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
350 char *tmp, *pos, *end;
353 if (realm->realm == NULL || home_realm == NULL)
356 if (os_strchr(realm->realm, ';') == NULL)
357 return os_strcasecmp(realm->realm, home_realm) == 0;
359 tmp = os_strdup(realm->realm);
365 end = os_strchr(pos, ';');
368 if (os_strcasecmp(pos, home_realm) == 0) {
383 static int nai_realm_cred_username(struct nai_realm_eap *eap)
385 if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
386 return 0; /* method not supported */
388 if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) {
389 /* Only tunneled methods with username/password supported */
393 if (eap->method == EAP_TYPE_PEAP &&
394 eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
397 if (eap->method == EAP_TYPE_TTLS) {
398 if (eap->inner_method == 0 && eap->inner_non_eap == 0)
400 if (eap->inner_method &&
401 eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
403 if (eap->inner_non_eap &&
404 eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
405 eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
406 eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
407 eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2)
411 if (eap->inner_method &&
412 eap->inner_method != EAP_TYPE_GTC &&
413 eap->inner_method != EAP_TYPE_MSCHAPV2)
420 static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred,
421 struct nai_realm *realm)
426 cred->username == NULL ||
427 cred->username[0] == '\0' ||
428 cred->password == NULL ||
429 cred->password[0] == '\0')
432 for (e = 0; e < realm->eap_count; e++) {
433 struct nai_realm_eap *eap = &realm->eap[e];
434 if (nai_realm_cred_username(eap))
442 #ifdef INTERWORKING_3GPP
444 static int plmn_id_match(struct wpabuf *anqp, const char *imsi)
451 sep = os_strchr(imsi, '-');
452 if (sep == NULL || (sep - imsi != 5 && sep - imsi != 6))
455 /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */
456 plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
457 plmn[1] = imsi[2] - '0';
459 plmn[1] |= (imsi[5] - '0') << 4;
462 plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
466 pos = wpabuf_head_u8(anqp);
467 end = pos + wpabuf_len(anqp);
471 wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
476 if (pos + udhl > end) {
477 wpa_printf(MSG_DEBUG, "Invalid UDHL");
482 while (pos + 2 <= end) {
491 if (iei == 0 && len > 0) {
495 for (i = 0; i < num; i++) {
498 if (os_memcmp(pos, plmn, 3) == 0)
499 return 1; /* Found matching PLMN */
510 static int build_root_nai(char *nai, size_t nai_len, const char *imsi,
513 const char *sep, *msin;
515 size_t msin_len, plmn_len;
518 * TS 23.003, Clause 14 (3GPP to WLAN Interworking)
520 * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org
521 * <MNC> is zero-padded to three digits in case two-digit MNC is used
524 if (imsi == NULL || os_strlen(imsi) > 16) {
525 wpa_printf(MSG_DEBUG, "No valid IMSI available");
528 sep = os_strchr(imsi, '-');
531 plmn_len = sep - imsi;
532 if (plmn_len != 5 && plmn_len != 6)
535 msin_len = os_strlen(msin);
541 os_memcpy(pos, imsi, plmn_len);
543 os_memcpy(pos, msin, msin_len);
545 pos += os_snprintf(pos, end - pos, "@wlan.mnc");
555 pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
556 imsi[0], imsi[1], imsi[2]);
562 static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
565 if (build_root_nai(nai, sizeof(nai), imsi, prefix) < 0)
567 return wpa_config_set_quoted(ssid, "identity", nai);
570 #endif /* INTERWORKING_3GPP */
573 static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
576 #ifdef INTERWORKING_3GPP
577 struct wpa_cred *cred;
578 struct wpa_ssid *ssid;
581 if (bss->anqp_3gpp == NULL)
584 for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
585 if (cred->imsi == NULL || !cred->imsi[0] ||
586 cred->milenage == NULL || !cred->milenage[0])
588 if (plmn_id_match(bss->anqp_3gpp, cred->imsi))
594 ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
597 wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)",
598 MAC2STR(bss->bssid));
600 ssid = wpa_config_add_network(wpa_s->conf);
604 wpas_notify_network_added(wpa_s, ssid);
605 wpa_config_set_network_defaults(ssid);
606 ssid->priority = cred->priority;
608 ssid->ssid = os_zalloc(ie[1] + 1);
609 if (ssid->ssid == NULL)
611 os_memcpy(ssid->ssid, ie + 2, ie[1]);
612 ssid->ssid_len = ie[1];
614 /* TODO: figure out whether to use EAP-SIM, EAP-AKA, or EAP-AKA' */
615 if (wpa_config_set(ssid, "eap", "SIM", 0) < 0) {
616 wpa_printf(MSG_DEBUG, "EAP-SIM not supported");
619 if (set_root_nai(ssid, cred->imsi, '1') < 0) {
620 wpa_printf(MSG_DEBUG, "Failed to set Root NAI");
624 if (cred->milenage && cred->milenage[0]) {
625 if (wpa_config_set_quoted(ssid, "password",
630 if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
634 if (cred->password && cred->password[0] &&
635 wpa_config_set_quoted(ssid, "password", cred->password) < 0)
638 wpa_config_update_prio_list(wpa_s->conf);
639 wpa_s->disconnected = 0;
640 wpa_s->reassociate = 1;
641 wpa_supplicant_req_scan(wpa_s, 0, 0);
646 wpas_notify_network_removed(wpa_s, ssid);
647 wpa_config_remove_network(wpa_s->conf, ssid->id);
648 #endif /* INTERWORKING_3GPP */
653 int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
655 struct wpa_cred *cred;
656 struct wpa_ssid *ssid;
657 struct nai_realm *realm;
658 struct nai_realm_eap *eap = NULL;
663 if (wpa_s->conf->cred == NULL || bss == NULL)
665 ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
666 if (ie == NULL || ie[1] == 0) {
667 wpa_printf(MSG_DEBUG, "Interworking: No SSID known for "
668 MACSTR, MAC2STR(bss->bssid));
672 realm = nai_realm_parse(bss->anqp_nai_realm, &count);
674 wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
675 "Realm list from " MACSTR, MAC2STR(bss->bssid));
679 for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
680 for (i = 0; i < count; i++) {
681 if (!nai_realm_match(&realm[i], cred->realm))
683 eap = nai_realm_find_eap(cred, &realm[i]);
692 if (interworking_connect_3gpp(wpa_s, bss) == 0) {
694 nai_realm_free(realm, count);
698 wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
699 "and EAP method found for " MACSTR,
700 MAC2STR(bss->bssid));
701 nai_realm_free(realm, count);
705 wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR,
706 MAC2STR(bss->bssid));
708 ssid = wpa_config_add_network(wpa_s->conf);
710 nai_realm_free(realm, count);
713 wpas_notify_network_added(wpa_s, ssid);
714 wpa_config_set_network_defaults(ssid);
715 ssid->priority = cred->priority;
717 ssid->ssid = os_zalloc(ie[1] + 1);
718 if (ssid->ssid == NULL)
720 os_memcpy(ssid->ssid, ie + 2, ie[1]);
721 ssid->ssid_len = ie[1];
723 if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
724 eap->method), 0) < 0)
727 if (eap->method == EAP_TYPE_TTLS &&
728 cred->username && cred->username[0]) {
731 /* Use anonymous NAI in Phase 1 */
732 pos = os_strchr(cred->username, '@');
734 size_t buflen = 9 + os_strlen(pos) + 1;
735 anon = os_malloc(buflen);
738 os_snprintf(anon, buflen, "anonymous%s", pos);
740 anon = os_strdup("anonymous");
744 if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) <
752 if (cred->username && cred->username[0] &&
753 wpa_config_set_quoted(ssid, "identity", cred->username) < 0)
756 if (cred->password && cred->password[0] &&
757 wpa_config_set_quoted(ssid, "password", cred->password) < 0)
760 switch (eap->method) {
762 if (eap->inner_method) {
763 os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
764 eap_get_name(EAP_VENDOR_IETF,
766 if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
770 switch (eap->inner_non_eap) {
771 case NAI_REALM_INNER_NON_EAP_PAP:
772 if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) <
776 case NAI_REALM_INNER_NON_EAP_CHAP:
777 if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0)
781 case NAI_REALM_INNER_NON_EAP_MSCHAP:
782 if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"",
786 case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
787 if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
794 os_snprintf(buf, sizeof(buf), "\"auth=%s\"",
795 eap_get_name(EAP_VENDOR_IETF, eap->inner_method));
796 if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
801 if (cred->ca_cert && cred->ca_cert[0] &&
802 wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0)
805 nai_realm_free(realm, count);
807 wpa_config_update_prio_list(wpa_s->conf);
808 wpa_s->disconnected = 0;
809 wpa_s->reassociate = 1;
810 wpa_supplicant_req_scan(wpa_s, 0, 0);
815 wpas_notify_network_removed(wpa_s, ssid);
816 wpa_config_remove_network(wpa_s->conf, ssid->id);
817 nai_realm_free(realm, count);
822 static struct wpa_cred * interworking_credentials_available_3gpp(
823 struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
825 struct wpa_cred *cred, *selected = NULL;
828 #ifdef INTERWORKING_3GPP
829 if (bss->anqp_3gpp == NULL)
832 for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
833 if (cred->imsi == NULL || !cred->imsi[0] ||
834 cred->milenage == NULL || !cred->milenage[0])
837 wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from "
838 MACSTR, MAC2STR(bss->bssid));
839 ret = plmn_id_match(bss->anqp_3gpp, cred->imsi);
840 wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
842 if (selected == NULL ||
843 selected->priority < cred->priority)
847 #endif /* INTERWORKING_3GPP */
852 static struct wpa_cred * interworking_credentials_available_realm(
853 struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
855 struct wpa_cred *cred, *selected = NULL;
856 struct nai_realm *realm;
859 if (bss->anqp_nai_realm == NULL)
862 if (wpa_s->conf->cred == NULL)
865 wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
866 MACSTR, MAC2STR(bss->bssid));
867 realm = nai_realm_parse(bss->anqp_nai_realm, &count);
869 wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
870 "Realm list from " MACSTR, MAC2STR(bss->bssid));
874 for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
875 if (cred->realm == NULL)
878 for (i = 0; i < count; i++) {
879 if (!nai_realm_match(&realm[i], cred->realm))
881 if (nai_realm_find_eap(cred, &realm[i])) {
882 if (selected == NULL ||
883 selected->priority < cred->priority)
890 nai_realm_free(realm, count);
896 static struct wpa_cred * interworking_credentials_available(
897 struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
899 struct wpa_cred *cred, *cred2;
901 cred = interworking_credentials_available_realm(wpa_s, bss);
902 cred2 = interworking_credentials_available_3gpp(wpa_s, bss);
903 if (cred && cred2 && cred2->priority >= cred->priority)
912 static int domain_name_list_contains(struct wpabuf *domain_names,
918 len = os_strlen(domain);
919 pos = wpabuf_head(domain_names);
920 end = pos + wpabuf_len(domain_names);
922 while (pos + 1 < end) {
923 if (pos + 1 + pos[0] > end)
926 wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name",
929 os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
939 static int interworking_home_sp(struct wpa_supplicant *wpa_s,
940 struct wpabuf *domain_names)
942 struct wpa_cred *cred;
943 #ifdef INTERWORKING_3GPP
944 char nai[100], *realm;
945 #endif /* INTERWORKING_3GPP */
947 if (domain_names == NULL || wpa_s->conf->cred == NULL)
950 for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
951 #ifdef INTERWORKING_3GPP
953 build_root_nai(nai, sizeof(nai), cred->imsi, 0) == 0) {
954 realm = os_strchr(nai, '@');
957 wpa_printf(MSG_DEBUG, "Interworking: Search for match "
958 "with SIM/USIM domain %s", realm);
960 domain_name_list_contains(domain_names, realm))
963 #endif /* INTERWORKING_3GPP */
965 if (cred->domain == NULL)
968 wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
969 "home SP FQDN %s", cred->domain);
970 if (domain_name_list_contains(domain_names, cred->domain))
978 static int interworking_find_network_match(struct wpa_supplicant *wpa_s)
981 struct wpa_ssid *ssid;
983 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
984 for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
985 if (ssid->disabled || ssid->mode != WPAS_MODE_INFRA)
987 if (ssid->ssid_len != bss->ssid_len ||
988 os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) !=
992 * TODO: Consider more accurate matching of security
993 * configuration similarly to what is done in events.c
1003 static void interworking_select_network(struct wpa_supplicant *wpa_s)
1005 struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
1006 int selected_prio = -999999, selected_home_prio = -999999;
1007 unsigned int count = 0;
1010 struct wpa_cred *cred;
1012 wpa_s->network_select = 0;
1014 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1015 cred = interworking_credentials_available(wpa_s, bss);
1019 res = interworking_home_sp(wpa_s, bss->anqp_domain_name);
1026 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s",
1027 MAC2STR(bss->bssid), type);
1028 if (wpa_s->auto_select) {
1029 if (selected == NULL ||
1030 cred->priority > selected_prio) {
1032 selected_prio = cred->priority;
1035 (selected_home == NULL ||
1036 cred->priority > selected_home_prio)) {
1037 selected_home = bss;
1038 selected_home_prio = cred->priority;
1043 if (selected_home && selected_home != selected &&
1044 selected_home_prio >= selected_prio) {
1045 /* Prefer network operated by the Home SP */
1046 selected = selected_home;
1051 * No matching network was found based on configured
1052 * credentials. Check whether any of the enabled network blocks
1053 * have matching APs.
1055 if (interworking_find_network_match(wpa_s)) {
1056 wpa_printf(MSG_DEBUG, "Interworking: Possible BSS "
1057 "match for enabled network configurations");
1058 wpa_s->disconnected = 0;
1059 wpa_s->reassociate = 1;
1060 wpa_supplicant_req_scan(wpa_s, 0, 0);
1064 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
1065 "with matching credentials found");
1069 interworking_connect(wpa_s, selected);
1073 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
1075 struct wpa_bss *bss;
1079 if (!wpa_s->fetch_anqp_in_progress)
1082 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1083 if (!(bss->caps & IEEE80211_CAP_ESS))
1085 ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
1086 if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
1087 continue; /* AP does not support Interworking */
1089 if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
1091 bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
1092 wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
1093 MACSTR, MAC2STR(bss->bssid));
1094 interworking_anqp_send_req(wpa_s, bss);
1100 wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
1101 wpa_s->fetch_anqp_in_progress = 0;
1102 if (wpa_s->network_select)
1103 interworking_select_network(wpa_s);
1108 static void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
1110 struct wpa_bss *bss;
1112 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
1113 bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
1115 wpa_s->fetch_anqp_in_progress = 1;
1116 interworking_next_anqp_fetch(wpa_s);
1120 int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
1122 if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select)
1125 wpa_s->network_select = 0;
1127 interworking_start_fetch_anqp(wpa_s);
1133 void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
1135 if (!wpa_s->fetch_anqp_in_progress)
1138 wpa_s->fetch_anqp_in_progress = 0;
1142 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
1143 u16 info_ids[], size_t num_ids)
1148 struct wpa_bss *bss;
1151 freq = wpa_s->assoc_freq;
1152 bss = wpa_bss_get_bssid(wpa_s, dst);
1158 wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
1159 MAC2STR(dst), (unsigned int) num_ids);
1161 buf = anqp_build_req(info_ids, num_ids, NULL);
1165 res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
1167 wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
1170 wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
1178 static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
1179 const u8 *sa, u16 info_id,
1180 const u8 *data, size_t slen)
1182 const u8 *pos = data;
1183 struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
1186 case ANQP_CAPABILITY_LIST:
1187 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1188 " ANQP Capability list", MAC2STR(sa));
1190 case ANQP_VENUE_NAME:
1191 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1192 " Venue Name", MAC2STR(sa));
1193 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
1195 wpabuf_free(bss->anqp_venue_name);
1196 bss->anqp_venue_name = wpabuf_alloc_copy(pos, slen);
1199 case ANQP_NETWORK_AUTH_TYPE:
1200 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1201 " Network Authentication Type information",
1203 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
1206 wpabuf_free(bss->anqp_network_auth_type);
1207 bss->anqp_network_auth_type =
1208 wpabuf_alloc_copy(pos, slen);
1211 case ANQP_ROAMING_CONSORTIUM:
1212 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1213 " Roaming Consortium list", MAC2STR(sa));
1214 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
1217 wpabuf_free(bss->anqp_roaming_consortium);
1218 bss->anqp_roaming_consortium =
1219 wpabuf_alloc_copy(pos, slen);
1222 case ANQP_IP_ADDR_TYPE_AVAILABILITY:
1223 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1224 " IP Address Type Availability information",
1226 wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
1229 wpabuf_free(bss->anqp_ip_addr_type_availability);
1230 bss->anqp_ip_addr_type_availability =
1231 wpabuf_alloc_copy(pos, slen);
1234 case ANQP_NAI_REALM:
1235 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1236 " NAI Realm list", MAC2STR(sa));
1237 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
1239 wpabuf_free(bss->anqp_nai_realm);
1240 bss->anqp_nai_realm = wpabuf_alloc_copy(pos, slen);
1243 case ANQP_3GPP_CELLULAR_NETWORK:
1244 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1245 " 3GPP Cellular Network information", MAC2STR(sa));
1246 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
1249 wpabuf_free(bss->anqp_3gpp);
1250 bss->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
1253 case ANQP_DOMAIN_NAME:
1254 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1255 " Domain Name list", MAC2STR(sa));
1256 wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
1258 wpabuf_free(bss->anqp_domain_name);
1259 bss->anqp_domain_name = wpabuf_alloc_copy(pos, slen);
1262 case ANQP_VENDOR_SPECIFIC:
1266 switch (WPA_GET_BE24(pos)) {
1268 wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
1269 "vendor-specific ANQP OUI %06x",
1275 wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID "
1282 void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
1283 enum gas_query_result result,
1284 const struct wpabuf *adv_proto,
1285 const struct wpabuf *resp, u16 status_code)
1287 struct wpa_supplicant *wpa_s = ctx;
1293 if (result != GAS_QUERY_SUCCESS)
1296 pos = wpabuf_head(adv_proto);
1297 if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
1298 pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
1299 wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
1300 "Protocol in response");
1304 pos = wpabuf_head(resp);
1305 end = pos + wpabuf_len(resp);
1308 if (pos + 4 > end) {
1309 wpa_printf(MSG_DEBUG, "ANQP: Invalid element");
1312 info_id = WPA_GET_LE16(pos);
1314 slen = WPA_GET_LE16(pos);
1316 if (pos + slen > end) {
1317 wpa_printf(MSG_DEBUG, "ANQP: Invalid element length "
1318 "for Info ID %u", info_id);
1321 interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos,
1328 static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
1329 struct wpa_scan_results *scan_res)
1331 wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start "
1333 interworking_start_fetch_anqp(wpa_s);
1337 int interworking_select(struct wpa_supplicant *wpa_s, int auto_select)
1339 interworking_stop_fetch_anqp(wpa_s);
1340 wpa_s->network_select = 1;
1341 wpa_s->auto_select = !!auto_select;
1342 wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
1344 wpa_s->scan_res_handler = interworking_scan_res_handler;
1345 wpa_s->scan_req = 2;
1346 wpa_supplicant_req_scan(wpa_s, 0, 0);