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
, const char *imsi
, char prefix
)
512 const char *sep
, *msin
;
514 size_t msin_len
, plmn_len
;
517 * TS 23.003, Clause 14 (3GPP to WLAN Interworking)
519 * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org
520 * <MNC> is zero-padded to three digits in case two-digit MNC is used
523 if (imsi
== NULL
|| os_strlen(imsi
) > 16) {
524 wpa_printf(MSG_DEBUG
, "No valid IMSI available");
527 sep
= os_strchr(imsi
, '-');
530 plmn_len
= sep
- imsi
;
531 if (plmn_len
!= 5 && plmn_len
!= 6)
534 msin_len
= os_strlen(msin
);
537 end
= pos
+ sizeof(nai
);
540 os_memcpy(pos
, imsi
, plmn_len
);
542 os_memcpy(pos
, msin
, msin_len
);
544 pos
+= os_snprintf(pos
, end
- pos
, "@wlan.mnc");
554 pos
+= os_snprintf(pos
, end
- pos
, ".mcc%c%c%c.3gppnetwork.org",
555 imsi
[0], imsi
[1], imsi
[2]);
561 static int set_root_nai(struct wpa_ssid
*ssid
, const char *imsi
, char prefix
)
564 if (build_root_nai(nai
, imsi
, prefix
) < 0)
566 return wpa_config_set_quoted(ssid
, "identity", nai
);
569 #endif /* INTERWORKING_3GPP */
572 static int interworking_connect_3gpp(struct wpa_supplicant
*wpa_s
,
575 #ifdef INTERWORKING_3GPP
576 struct wpa_cred
*cred
;
577 struct wpa_ssid
*ssid
;
580 if (bss
->anqp_3gpp
== NULL
)
583 for (cred
= wpa_s
->conf
->cred
; cred
; cred
= cred
->next
) {
584 if (cred
->imsi
== NULL
|| !cred
->imsi
[0] ||
585 cred
->milenage
== NULL
|| !cred
->milenage
[0])
587 if (plmn_id_match(bss
->anqp_3gpp
, cred
->imsi
))
593 ie
= wpa_bss_get_ie(bss
, WLAN_EID_SSID
);
596 wpa_printf(MSG_DEBUG
, "Interworking: Connect with " MACSTR
" (3GPP)",
597 MAC2STR(bss
->bssid
));
599 ssid
= wpa_config_add_network(wpa_s
->conf
);
603 wpas_notify_network_added(wpa_s
, ssid
);
604 wpa_config_set_network_defaults(ssid
);
605 ssid
->priority
= cred
->priority
;
607 ssid
->ssid
= os_zalloc(ie
[1] + 1);
608 if (ssid
->ssid
== NULL
)
610 os_memcpy(ssid
->ssid
, ie
+ 2, ie
[1]);
611 ssid
->ssid_len
= ie
[1];
613 /* TODO: figure out whether to use EAP-SIM, EAP-AKA, or EAP-AKA' */
614 if (wpa_config_set(ssid
, "eap", "SIM", 0) < 0) {
615 wpa_printf(MSG_DEBUG
, "EAP-SIM not supported");
618 if (set_root_nai(ssid
, cred
->imsi
, '1') < 0) {
619 wpa_printf(MSG_DEBUG
, "Failed to set Root NAI");
623 if (cred
->milenage
&& cred
->milenage
[0]) {
624 if (wpa_config_set_quoted(ssid
, "password",
629 if (wpa_config_set_quoted(ssid
, "pcsc", "") < 0)
633 if (cred
->password
&& cred
->password
[0] &&
634 wpa_config_set_quoted(ssid
, "password", cred
->password
) < 0)
637 wpa_config_update_prio_list(wpa_s
->conf
);
638 wpa_s
->disconnected
= 0;
639 wpa_s
->reassociate
= 1;
640 wpa_supplicant_req_scan(wpa_s
, 0, 0);
645 wpas_notify_network_removed(wpa_s
, ssid
);
646 wpa_config_remove_network(wpa_s
->conf
, ssid
->id
);
647 #endif /* INTERWORKING_3GPP */
652 int interworking_connect(struct wpa_supplicant
*wpa_s
, struct wpa_bss
*bss
)
654 struct wpa_cred
*cred
;
655 struct wpa_ssid
*ssid
;
656 struct nai_realm
*realm
;
657 struct nai_realm_eap
*eap
= NULL
;
662 if (wpa_s
->conf
->cred
== NULL
|| bss
== NULL
)
664 ie
= wpa_bss_get_ie(bss
, WLAN_EID_SSID
);
665 if (ie
== NULL
|| ie
[1] == 0) {
666 wpa_printf(MSG_DEBUG
, "Interworking: No SSID known for "
667 MACSTR
, MAC2STR(bss
->bssid
));
671 realm
= nai_realm_parse(bss
->anqp_nai_realm
, &count
);
673 wpa_printf(MSG_DEBUG
, "Interworking: Could not parse NAI "
674 "Realm list from " MACSTR
, MAC2STR(bss
->bssid
));
678 for (cred
= wpa_s
->conf
->cred
; cred
; cred
= cred
->next
) {
679 for (i
= 0; i
< count
; i
++) {
680 if (!nai_realm_match(&realm
[i
], cred
->realm
))
682 eap
= nai_realm_find_eap(cred
, &realm
[i
]);
691 if (interworking_connect_3gpp(wpa_s
, bss
) == 0) {
693 nai_realm_free(realm
, count
);
697 wpa_printf(MSG_DEBUG
, "Interworking: No matching credentials "
698 "and EAP method found for " MACSTR
,
699 MAC2STR(bss
->bssid
));
700 nai_realm_free(realm
, count
);
704 wpa_printf(MSG_DEBUG
, "Interworking: Connect with " MACSTR
,
705 MAC2STR(bss
->bssid
));
707 ssid
= wpa_config_add_network(wpa_s
->conf
);
709 nai_realm_free(realm
, count
);
712 wpas_notify_network_added(wpa_s
, ssid
);
713 wpa_config_set_network_defaults(ssid
);
714 ssid
->priority
= cred
->priority
;
716 ssid
->ssid
= os_zalloc(ie
[1] + 1);
717 if (ssid
->ssid
== NULL
)
719 os_memcpy(ssid
->ssid
, ie
+ 2, ie
[1]);
720 ssid
->ssid_len
= ie
[1];
722 if (wpa_config_set(ssid
, "eap", eap_get_name(EAP_VENDOR_IETF
,
723 eap
->method
), 0) < 0)
726 if (eap
->method
== EAP_TYPE_TTLS
&&
727 cred
->username
&& cred
->username
[0]) {
730 /* Use anonymous NAI in Phase 1 */
731 pos
= os_strchr(cred
->username
, '@');
733 size_t buflen
= 9 + os_strlen(pos
) + 1;
734 anon
= os_malloc(buflen
);
737 os_snprintf(anon
, buflen
, "anonymous%s", pos
);
739 anon
= os_strdup("anonymous");
743 if (wpa_config_set_quoted(ssid
, "anonymous_identity", anon
) <
751 if (cred
->username
&& cred
->username
[0] &&
752 wpa_config_set_quoted(ssid
, "identity", cred
->username
) < 0)
755 if (cred
->password
&& cred
->password
[0] &&
756 wpa_config_set_quoted(ssid
, "password", cred
->password
) < 0)
759 switch (eap
->method
) {
761 if (eap
->inner_method
) {
762 os_snprintf(buf
, sizeof(buf
), "\"autheap=%s\"",
763 eap_get_name(EAP_VENDOR_IETF
,
765 if (wpa_config_set(ssid
, "phase2", buf
, 0) < 0)
769 switch (eap
->inner_non_eap
) {
770 case NAI_REALM_INNER_NON_EAP_PAP
:
771 if (wpa_config_set(ssid
, "phase2", "\"auth=PAP\"", 0) <
775 case NAI_REALM_INNER_NON_EAP_CHAP
:
776 if (wpa_config_set(ssid
, "phase2", "\"auth=CHAP\"", 0)
780 case NAI_REALM_INNER_NON_EAP_MSCHAP
:
781 if (wpa_config_set(ssid
, "phase2", "\"auth=MSCHAP\"",
785 case NAI_REALM_INNER_NON_EAP_MSCHAPV2
:
786 if (wpa_config_set(ssid
, "phase2", "\"auth=MSCHAPV2\"",
793 os_snprintf(buf
, sizeof(buf
), "\"auth=%s\"",
794 eap_get_name(EAP_VENDOR_IETF
, eap
->inner_method
));
795 if (wpa_config_set(ssid
, "phase2", buf
, 0) < 0)
800 if (cred
->ca_cert
&& cred
->ca_cert
[0] &&
801 wpa_config_set_quoted(ssid
, "ca_cert", cred
->ca_cert
) < 0)
804 nai_realm_free(realm
, count
);
806 wpa_config_update_prio_list(wpa_s
->conf
);
807 wpa_s
->disconnected
= 0;
808 wpa_s
->reassociate
= 1;
809 wpa_supplicant_req_scan(wpa_s
, 0, 0);
814 wpas_notify_network_removed(wpa_s
, ssid
);
815 wpa_config_remove_network(wpa_s
->conf
, ssid
->id
);
816 nai_realm_free(realm
, count
);
821 static struct wpa_cred
* interworking_credentials_available_3gpp(
822 struct wpa_supplicant
*wpa_s
, struct wpa_bss
*bss
)
824 struct wpa_cred
*cred
, *selected
= NULL
;
827 #ifdef INTERWORKING_3GPP
828 if (bss
->anqp_3gpp
== NULL
)
831 for (cred
= wpa_s
->conf
->cred
; cred
; cred
= cred
->next
) {
832 if (cred
->imsi
== NULL
|| !cred
->imsi
[0] ||
833 cred
->milenage
== NULL
|| !cred
->milenage
[0])
836 wpa_printf(MSG_DEBUG
, "Interworking: Parsing 3GPP info from "
837 MACSTR
, MAC2STR(bss
->bssid
));
838 ret
= plmn_id_match(bss
->anqp_3gpp
, cred
->imsi
);
839 wpa_printf(MSG_DEBUG
, "PLMN match %sfound", ret
? "" : "not ");
841 if (selected
== NULL
||
842 selected
->priority
< cred
->priority
)
846 #endif /* INTERWORKING_3GPP */
851 static struct wpa_cred
* interworking_credentials_available_realm(
852 struct wpa_supplicant
*wpa_s
, struct wpa_bss
*bss
)
854 struct wpa_cred
*cred
, *selected
= NULL
;
855 struct nai_realm
*realm
;
858 if (bss
->anqp_nai_realm
== NULL
)
861 if (wpa_s
->conf
->cred
== NULL
)
864 wpa_printf(MSG_DEBUG
, "Interworking: Parsing NAI Realm list from "
865 MACSTR
, MAC2STR(bss
->bssid
));
866 realm
= nai_realm_parse(bss
->anqp_nai_realm
, &count
);
868 wpa_printf(MSG_DEBUG
, "Interworking: Could not parse NAI "
869 "Realm list from " MACSTR
, MAC2STR(bss
->bssid
));
873 for (cred
= wpa_s
->conf
->cred
; cred
; cred
= cred
->next
) {
874 if (cred
->realm
== NULL
)
877 for (i
= 0; i
< count
; i
++) {
878 if (!nai_realm_match(&realm
[i
], cred
->realm
))
880 if (nai_realm_find_eap(cred
, &realm
[i
])) {
881 if (selected
== NULL
||
882 selected
->priority
< cred
->priority
)
889 nai_realm_free(realm
, count
);
895 static struct wpa_cred
* interworking_credentials_available(
896 struct wpa_supplicant
*wpa_s
, struct wpa_bss
*bss
)
898 struct wpa_cred
*cred
, *cred2
;
900 cred
= interworking_credentials_available_realm(wpa_s
, bss
);
901 cred2
= interworking_credentials_available_3gpp(wpa_s
, bss
);
902 if (cred
&& cred2
&& cred2
->priority
>= cred
->priority
)
911 static int domain_name_list_contains(struct wpabuf
*domain_names
,
917 len
= os_strlen(domain
);
918 pos
= wpabuf_head(domain_names
);
919 end
= pos
+ wpabuf_len(domain_names
);
921 while (pos
+ 1 < end
) {
922 if (pos
+ 1 + pos
[0] > end
)
925 wpa_hexdump_ascii(MSG_DEBUG
, "Interworking: AP domain name",
928 os_strncasecmp(domain
, (const char *) (pos
+ 1), len
) == 0)
938 static int interworking_home_sp(struct wpa_supplicant
*wpa_s
,
939 struct wpabuf
*domain_names
)
941 struct wpa_cred
*cred
;
942 #ifdef INTERWORKING_3GPP
943 char nai
[100], *realm
;
944 #endif /* INTERWORKING_3GPP */
946 if (domain_names
== NULL
|| wpa_s
->conf
->cred
== NULL
)
949 for (cred
= wpa_s
->conf
->cred
; cred
; cred
= cred
->next
) {
950 #ifdef INTERWORKING_3GPP
952 build_root_nai(nai
, cred
->imsi
, 0) == 0) {
953 realm
= os_strchr(nai
, '@');
956 wpa_printf(MSG_DEBUG
, "Interworking: Search for match "
957 "with SIM/USIM domain %s", realm
);
959 domain_name_list_contains(domain_names
, realm
))
962 #endif /* INTERWORKING_3GPP */
964 if (cred
->domain
== NULL
)
967 wpa_printf(MSG_DEBUG
, "Interworking: Search for match with "
968 "home SP FQDN %s", cred
->domain
);
969 if (domain_name_list_contains(domain_names
, cred
->domain
))
977 static int interworking_find_network_match(struct wpa_supplicant
*wpa_s
)
980 struct wpa_ssid
*ssid
;
982 dl_list_for_each(bss
, &wpa_s
->bss
, struct wpa_bss
, list
) {
983 for (ssid
= wpa_s
->conf
->ssid
; ssid
; ssid
= ssid
->next
) {
984 if (ssid
->disabled
|| ssid
->mode
!= WPAS_MODE_INFRA
)
986 if (ssid
->ssid_len
!= bss
->ssid_len
||
987 os_memcmp(ssid
->ssid
, bss
->ssid
, ssid
->ssid_len
) !=
991 * TODO: Consider more accurate matching of security
992 * configuration similarly to what is done in events.c
1002 static void interworking_select_network(struct wpa_supplicant
*wpa_s
)
1004 struct wpa_bss
*bss
, *selected
= NULL
, *selected_home
= NULL
;
1005 int selected_prio
= -999999, selected_home_prio
= -999999;
1006 unsigned int count
= 0;
1009 struct wpa_cred
*cred
;
1011 wpa_s
->network_select
= 0;
1013 dl_list_for_each(bss
, &wpa_s
->bss
, struct wpa_bss
, list
) {
1014 cred
= interworking_credentials_available(wpa_s
, bss
);
1018 res
= interworking_home_sp(wpa_s
, bss
->anqp_domain_name
);
1025 wpa_msg(wpa_s
, MSG_INFO
, INTERWORKING_AP MACSTR
" type=%s",
1026 MAC2STR(bss
->bssid
), type
);
1027 if (wpa_s
->auto_select
) {
1028 if (selected
== NULL
||
1029 cred
->priority
> selected_prio
) {
1031 selected_prio
= cred
->priority
;
1034 (selected_home
== NULL
||
1035 cred
->priority
> selected_home_prio
)) {
1036 selected_home
= bss
;
1037 selected_home_prio
= cred
->priority
;
1042 if (selected_home
&& selected_home
!= selected
&&
1043 selected_home_prio
>= selected_prio
) {
1044 /* Prefer network operated by the Home SP */
1045 selected
= selected_home
;
1050 * No matching network was found based on configured
1051 * credentials. Check whether any of the enabled network blocks
1052 * have matching APs.
1054 if (interworking_find_network_match(wpa_s
)) {
1055 wpa_printf(MSG_DEBUG
, "Interworking: Possible BSS "
1056 "match for enabled network configurations");
1057 wpa_s
->disconnected
= 0;
1058 wpa_s
->reassociate
= 1;
1059 wpa_supplicant_req_scan(wpa_s
, 0, 0);
1063 wpa_msg(wpa_s
, MSG_INFO
, INTERWORKING_NO_MATCH
"No network "
1064 "with matching credentials found");
1068 interworking_connect(wpa_s
, selected
);
1072 static void interworking_next_anqp_fetch(struct wpa_supplicant
*wpa_s
)
1074 struct wpa_bss
*bss
;
1078 if (!wpa_s
->fetch_anqp_in_progress
)
1081 dl_list_for_each(bss
, &wpa_s
->bss
, struct wpa_bss
, list
) {
1082 if (!(bss
->caps
& IEEE80211_CAP_ESS
))
1084 ie
= wpa_bss_get_ie(bss
, WLAN_EID_EXT_CAPAB
);
1085 if (ie
== NULL
|| ie
[1] < 4 || !(ie
[5] & 0x80))
1086 continue; /* AP does not support Interworking */
1088 if (!(bss
->flags
& WPA_BSS_ANQP_FETCH_TRIED
)) {
1090 bss
->flags
|= WPA_BSS_ANQP_FETCH_TRIED
;
1091 wpa_msg(wpa_s
, MSG_INFO
, "Starting ANQP fetch for "
1092 MACSTR
, MAC2STR(bss
->bssid
));
1093 interworking_anqp_send_req(wpa_s
, bss
);
1099 wpa_msg(wpa_s
, MSG_INFO
, "ANQP fetch completed");
1100 wpa_s
->fetch_anqp_in_progress
= 0;
1101 if (wpa_s
->network_select
)
1102 interworking_select_network(wpa_s
);
1107 static void interworking_start_fetch_anqp(struct wpa_supplicant
*wpa_s
)
1109 struct wpa_bss
*bss
;
1111 dl_list_for_each(bss
, &wpa_s
->bss
, struct wpa_bss
, list
)
1112 bss
->flags
&= ~WPA_BSS_ANQP_FETCH_TRIED
;
1114 wpa_s
->fetch_anqp_in_progress
= 1;
1115 interworking_next_anqp_fetch(wpa_s
);
1119 int interworking_fetch_anqp(struct wpa_supplicant
*wpa_s
)
1121 if (wpa_s
->fetch_anqp_in_progress
|| wpa_s
->network_select
)
1124 wpa_s
->network_select
= 0;
1126 interworking_start_fetch_anqp(wpa_s
);
1132 void interworking_stop_fetch_anqp(struct wpa_supplicant
*wpa_s
)
1134 if (!wpa_s
->fetch_anqp_in_progress
)
1137 wpa_s
->fetch_anqp_in_progress
= 0;
1141 int anqp_send_req(struct wpa_supplicant
*wpa_s
, const u8
*dst
,
1142 u16 info_ids
[], size_t num_ids
)
1147 struct wpa_bss
*bss
;
1150 freq
= wpa_s
->assoc_freq
;
1151 bss
= wpa_bss_get_bssid(wpa_s
, dst
);
1157 wpa_printf(MSG_DEBUG
, "ANQP: Query Request to " MACSTR
" for %u id(s)",
1158 MAC2STR(dst
), (unsigned int) num_ids
);
1160 buf
= anqp_build_req(info_ids
, num_ids
, NULL
);
1164 res
= gas_query_req(wpa_s
->gas
, dst
, freq
, buf
, anqp_resp_cb
, wpa_s
);
1166 wpa_printf(MSG_DEBUG
, "ANQP: Failed to send Query Request");
1169 wpa_printf(MSG_DEBUG
, "ANQP: Query started with dialog token "
1177 static void interworking_parse_rx_anqp_resp(struct wpa_supplicant
*wpa_s
,
1178 const u8
*sa
, u16 info_id
,
1179 const u8
*data
, size_t slen
)
1181 const u8
*pos
= data
;
1182 struct wpa_bss
*bss
= wpa_bss_get_bssid(wpa_s
, sa
);
1185 case ANQP_CAPABILITY_LIST
:
1186 wpa_msg(wpa_s
, MSG_INFO
, "RX-ANQP " MACSTR
1187 " ANQP Capability list", MAC2STR(sa
));
1189 case ANQP_VENUE_NAME
:
1190 wpa_msg(wpa_s
, MSG_INFO
, "RX-ANQP " MACSTR
1191 " Venue Name", MAC2STR(sa
));
1192 wpa_hexdump_ascii(MSG_DEBUG
, "ANQP: Venue Name", pos
, slen
);
1194 wpabuf_free(bss
->anqp_venue_name
);
1195 bss
->anqp_venue_name
= wpabuf_alloc_copy(pos
, slen
);
1198 case ANQP_NETWORK_AUTH_TYPE
:
1199 wpa_msg(wpa_s
, MSG_INFO
, "RX-ANQP " MACSTR
1200 " Network Authentication Type information",
1202 wpa_hexdump_ascii(MSG_DEBUG
, "ANQP: Network Authentication "
1205 wpabuf_free(bss
->anqp_network_auth_type
);
1206 bss
->anqp_network_auth_type
=
1207 wpabuf_alloc_copy(pos
, slen
);
1210 case ANQP_ROAMING_CONSORTIUM
:
1211 wpa_msg(wpa_s
, MSG_INFO
, "RX-ANQP " MACSTR
1212 " Roaming Consortium list", MAC2STR(sa
));
1213 wpa_hexdump_ascii(MSG_DEBUG
, "ANQP: Roaming Consortium",
1216 wpabuf_free(bss
->anqp_roaming_consortium
);
1217 bss
->anqp_roaming_consortium
=
1218 wpabuf_alloc_copy(pos
, slen
);
1221 case ANQP_IP_ADDR_TYPE_AVAILABILITY
:
1222 wpa_msg(wpa_s
, MSG_INFO
, "RX-ANQP " MACSTR
1223 " IP Address Type Availability information",
1225 wpa_hexdump(MSG_MSGDUMP
, "ANQP: IP Address Availability",
1228 wpabuf_free(bss
->anqp_ip_addr_type_availability
);
1229 bss
->anqp_ip_addr_type_availability
=
1230 wpabuf_alloc_copy(pos
, slen
);
1233 case ANQP_NAI_REALM
:
1234 wpa_msg(wpa_s
, MSG_INFO
, "RX-ANQP " MACSTR
1235 " NAI Realm list", MAC2STR(sa
));
1236 wpa_hexdump_ascii(MSG_DEBUG
, "ANQP: NAI Realm", pos
, slen
);
1238 wpabuf_free(bss
->anqp_nai_realm
);
1239 bss
->anqp_nai_realm
= wpabuf_alloc_copy(pos
, slen
);
1242 case ANQP_3GPP_CELLULAR_NETWORK
:
1243 wpa_msg(wpa_s
, MSG_INFO
, "RX-ANQP " MACSTR
1244 " 3GPP Cellular Network information", MAC2STR(sa
));
1245 wpa_hexdump_ascii(MSG_DEBUG
, "ANQP: 3GPP Cellular Network",
1248 wpabuf_free(bss
->anqp_3gpp
);
1249 bss
->anqp_3gpp
= wpabuf_alloc_copy(pos
, slen
);
1252 case ANQP_DOMAIN_NAME
:
1253 wpa_msg(wpa_s
, MSG_INFO
, "RX-ANQP " MACSTR
1254 " Domain Name list", MAC2STR(sa
));
1255 wpa_hexdump_ascii(MSG_MSGDUMP
, "ANQP: Domain Name", pos
, slen
);
1257 wpabuf_free(bss
->anqp_domain_name
);
1258 bss
->anqp_domain_name
= wpabuf_alloc_copy(pos
, slen
);
1261 case ANQP_VENDOR_SPECIFIC
:
1265 switch (WPA_GET_BE24(pos
)) {
1267 wpa_printf(MSG_DEBUG
, "Interworking: Unsupported "
1268 "vendor-specific ANQP OUI %06x",
1274 wpa_printf(MSG_DEBUG
, "Interworking: Unsupported ANQP Info ID "
1281 void anqp_resp_cb(void *ctx
, const u8
*dst
, u8 dialog_token
,
1282 enum gas_query_result result
,
1283 const struct wpabuf
*adv_proto
,
1284 const struct wpabuf
*resp
, u16 status_code
)
1286 struct wpa_supplicant
*wpa_s
= ctx
;
1292 if (result
!= GAS_QUERY_SUCCESS
)
1295 pos
= wpabuf_head(adv_proto
);
1296 if (wpabuf_len(adv_proto
) < 4 || pos
[0] != WLAN_EID_ADV_PROTO
||
1297 pos
[1] < 2 || pos
[3] != ACCESS_NETWORK_QUERY_PROTOCOL
) {
1298 wpa_printf(MSG_DEBUG
, "ANQP: Unexpected Advertisement "
1299 "Protocol in response");
1303 pos
= wpabuf_head(resp
);
1304 end
= pos
+ wpabuf_len(resp
);
1307 if (pos
+ 4 > end
) {
1308 wpa_printf(MSG_DEBUG
, "ANQP: Invalid element");
1311 info_id
= WPA_GET_LE16(pos
);
1313 slen
= WPA_GET_LE16(pos
);
1315 if (pos
+ slen
> end
) {
1316 wpa_printf(MSG_DEBUG
, "ANQP: Invalid element length "
1317 "for Info ID %u", info_id
);
1320 interworking_parse_rx_anqp_resp(wpa_s
, dst
, info_id
, pos
,
1327 static void interworking_scan_res_handler(struct wpa_supplicant
*wpa_s
,
1328 struct wpa_scan_results
*scan_res
)
1330 wpa_printf(MSG_DEBUG
, "Interworking: Scan results available - start "
1332 interworking_start_fetch_anqp(wpa_s
);
1336 int interworking_select(struct wpa_supplicant
*wpa_s
, int auto_select
)
1338 interworking_stop_fetch_anqp(wpa_s
);
1339 wpa_s
->network_select
= 1;
1340 wpa_s
->auto_select
= !!auto_select
;
1341 wpa_printf(MSG_DEBUG
, "Interworking: Start scan for network "
1343 wpa_s
->scan_res_handler
= interworking_scan_res_handler
;
1344 wpa_s
->scan_req
= 2;
1345 wpa_supplicant_req_scan(wpa_s
, 0, 0);