Interworking: Relax 3GPP info PLMN matching for MNC
authorJouni Malinen <jouni@qca.qualcomm.com>
Mon, 8 Jul 2013 13:53:05 +0000 (16:53 +0300)
committerJouni Malinen <j@w1.fi>
Mon, 8 Jul 2013 13:53:05 +0000 (16:53 +0300)
3GPP TS 24.232 Annex A.3 allows network operator to advertise only two
digits of MNC even if MNC has three digits. Allow such matches in
network selection. In addition, allow three digit matches of MNC even if
MNC length was assumed to be two to avoid missing networks if MNC length
cannot be determined reliably. Remove the '-' separator from simulated
SIM/USIM cases to allow the new matching rules to work.

Fix the PLMN List information element parsing loop to use the length of
the PLMN List instead of the length of the full 3GPP Cellular Info to
avoid unexpected matches should a new element ever be added by 3GPP.

Finally, add more debug prints from PLMN matching to make the logs
easier to understand.

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

wpa_supplicant/interworking.c

index 2f35240..36f75a1 100644 (file)
@@ -599,19 +599,29 @@ static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred,
 
 static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
 {
-       u8 plmn[3];
+       u8 plmn[3], plmn2[3];
        const u8 *pos, *end;
        u8 udhl;
 
-       /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */
+       /*
+        * See Annex A of 3GPP TS 24.234 v8.1.0 for description. The network
+        * operator is allowed to include only two digits of the MNC, so allow
+        * matches based on both two and three digit MNC assumptions. Since some
+        * SIM/USIM cards may not expose MNC length conveniently, we may be
+        * provided the default MNC length 3 here and as such, checking with MNC
+        * length 2 is justifiable even though 3GPP TS 24.234 does not mention
+        * that case. Anyway, MCC/MNC pair where both 2 and 3 digit MNC is used
+        * with otherwise matching values would not be good idea in general, so
+        * this should not result in selecting incorrect networks.
+        */
+       /* Match with 3 digit MNC */
        plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
-       plmn[1] = imsi[2] - '0';
-       /* default to MNC length 3 if unknown */
-       if (mnc_len != 2)
-               plmn[1] |= (imsi[5] - '0') << 4;
-       else
-               plmn[1] |= 0xf0;
+       plmn[1] = (imsi[2] - '0') | ((imsi[5] - '0') << 4);
        plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
+       /* Match with 2 digit MNC */
+       plmn2[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
+       plmn2[1] = (imsi[2] - '0') | 0xf0;
+       plmn2[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
 
        if (anqp == NULL)
                return 0;
@@ -631,6 +641,10 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
        }
        end = pos + udhl;
 
+       wpa_printf(MSG_DEBUG, "Interworking: Matching against MCC/MNC alternatives: %02x:%02x:%02x or %02x:%02x:%02x (IMSI %s, MNC length %d)",
+                  plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2],
+                  imsi, mnc_len);
+
        while (pos + 2 <= end) {
                u8 iei, len;
                const u8 *l_end;
@@ -643,14 +657,20 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
                if (iei == 0 && len > 0) {
                        /* PLMN List */
                        u8 num, i;
+                       wpa_hexdump(MSG_DEBUG, "Interworking: PLMN List information element",
+                                   pos, len);
                        num = *pos++;
                        for (i = 0; i < num; i++) {
-                               if (pos + 3 > end)
+                               if (pos + 3 > l_end)
                                        break;
-                               if (os_memcmp(pos, plmn, 3) == 0)
+                               if (os_memcmp(pos, plmn, 3) == 0 ||
+                                   os_memcmp(pos, plmn2, 3) == 0)
                                        return 1; /* Found matching PLMN */
                                pos += 3;
                        }
+               } else {
+                       wpa_hexdump(MSG_DEBUG, "Interworking: Unrecognized 3GPP information element",
+                                   pos, len);
                }
 
                pos = l_end;
@@ -1337,6 +1357,8 @@ static struct wpa_cred * interworking_credentials_available_3gpp(
                char *sep;
                const char *imsi;
                int mnc_len;
+               char imsi_buf[16];
+               size_t msin_len;
 
 #ifdef PCSC_FUNCS
                if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
@@ -1363,7 +1385,14 @@ static struct wpa_cred * interworking_credentials_available_3gpp(
                    (sep - cred->imsi != 5 && sep - cred->imsi != 6))
                        continue;
                mnc_len = sep - cred->imsi - 3;
-               imsi = cred->imsi;
+               os_memcpy(imsi_buf, cred->imsi, 3 + mnc_len);
+               sep++;
+               msin_len = os_strlen(cred->imsi);
+               if (3 + mnc_len + msin_len >= sizeof(imsi_buf) - 1)
+                       msin_len = sizeof(imsi_buf) - 3 - mnc_len - 1;
+               os_memcpy(&imsi_buf[3 + mnc_len], sep, msin_len);
+               imsi_buf[3 + mnc_len + msin_len] = '\0';
+               imsi = imsi_buf;
 
 #if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY)
        compare: