neels submitted this change.

View Change


Approvals: Jenkins Builder: Verified neels: Looks good to me, approved
fix VLR evil twin on LU with unknown TMSI

When a subscriber first attaches by TMSI only, and later tells the IMSI
via ID Response, it may turn out that this IMSI already exists in the
VLR database. If this happens, the TMSI that the subscriber issued was
not known in the existing VLR entry, indicating that the subscriber has
in the meantime camped on a different core. Which means we can assume
that there cannot be any active connections, and the old subscriber can
be discarded, for the benefit of the new one.

(We could also discard the new one, but it is more complex to reparent
the ongoing FSMs for Compl L3 than to copy some dormant VLR state.)

In vlr_subscr_set_imsi(), check for an existing IMSI entry in the VLR.

If such exists, copy any pending Paging and auth tuple state to the new
subscriber, and discard the old one from the VLR.

In order to safely discard a vlr subscriber by force, add a new vlr_ops
function: subscr_inval(), to tell the MSC that a vlr_subscr is no longer
valid.

Upcoming patch I583682d1a35a70b008d7bb2d89ba7c3109a60b21 better clears
TMSI state from the VLR, making it more likely to hit the evil twin
situation this patch fixes; hence this is, sort of, preparation.

Related: SYS#6860 OS#4721
Change-Id: Ifdabe0b65bffafbf7b8e5cc10e2d225d1ed1cecd
---
M include/osmocom/msc/paging.h
M include/osmocom/msc/vlr.h
M src/libmsc/gsm_04_08.c
M src/libmsc/paging.c
M src/libvlr/vlr.c
M tests/msc_vlr/msc_vlr_test_rest.err
6 files changed, 139 insertions(+), 2 deletions(-)

diff --git a/include/osmocom/msc/paging.h b/include/osmocom/msc/paging.h
index 4de679d..f8ebf9e 100644
--- a/include/osmocom/msc/paging.h
+++ b/include/osmocom/msc/paging.h
@@ -40,6 +40,7 @@
struct paging_request *paging_request_start(struct vlr_subscr *vsub, enum paging_cause cause,
paging_cb_t paging_cb, struct gsm_trans *trans,
const char *label);
+void paging_request_join_vsub(struct vlr_subscr *keep_vsub, struct vlr_subscr *discarding_vsub);
void paging_request_remove(struct paging_request *pr);

void paging_response(struct msc_a *msc_a);
diff --git a/include/osmocom/msc/vlr.h b/include/osmocom/msc/vlr.h
index 86a72f2..a7707fd 100644
--- a/include/osmocom/msc/vlr.h
+++ b/include/osmocom/msc/vlr.h
@@ -257,6 +257,8 @@
/* notify MSC/SGSN that the given subscriber has been associated
* with this msc_conn_ref */
int (*subscr_assoc)(void *msc_conn_ref, struct vlr_subscr *vsub);
+ /* notify MSC that the given subscriber is no longer valid */
+ void (*subscr_inval)(void *msc_conn_ref, struct vlr_subscr *vsub);
};

/* An instance of the VLR codebase */
diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c
index 70faf95..17350fa 100644
--- a/src/libmsc/gsm_04_08.c
+++ b/src/libmsc/gsm_04_08.c
@@ -1572,6 +1572,24 @@
return 0;
}

+static void msc_vlr_subscr_inval(void *msc_conn_ref, struct vlr_subscr *vsub)
+{
+ /* Search vsub backwards to make sure msc_conn_ref is a valid msc_a instance. */
+ struct msub *msub;
+ OSMO_ASSERT(vsub);
+ llist_for_each_entry(msub, &msub_list, entry) {
+ struct msc_a *msc_a;
+ if (msub->vsub != vsub)
+ continue;
+
+ msc_a = msub_msc_a(msub);
+ if (msc_a)
+ msc_a_release_cn(msc_a);
+
+ msub->vsub = NULL;
+ }
+}
+
/* operations that we need to implement for libvlr */
const struct vlr_ops msc_vlr_ops = {
.tx_auth_req = msc_vlr_tx_auth_req,
@@ -1586,6 +1604,7 @@
.tx_mm_info = msc_vlr_tx_mm_info,
.subscr_update = msc_vlr_subscr_update,
.subscr_assoc = msc_vlr_subscr_assoc,
+ .subscr_inval = msc_vlr_subscr_inval,
};

struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value)
diff --git a/src/libmsc/paging.c b/src/libmsc/paging.c
index 9845f99..9b3dad5 100644
--- a/src/libmsc/paging.c
+++ b/src/libmsc/paging.c
@@ -120,6 +120,34 @@
return pr;
}

+/* Two subscribers (e.g. an old TMSI and a new TMSI) turn out to have the same identity, so in order to discard one of
+ * them, transfer any pending Paging requests to the vsub that will survive. */
+void paging_request_join_vsub(struct vlr_subscr *keep_vsub, struct vlr_subscr *discarding_vsub)
+{
+ struct paging_request *pr;
+
+ if (!discarding_vsub->cs.is_paging)
+ return;
+
+ /* transfer all Paging Response callbacks */
+ while ((pr = llist_first_entry_or_null(&discarding_vsub->cs.requests, struct paging_request, entry))) {
+ llist_del(&pr->entry);
+ talloc_steal(keep_vsub, pr);
+ llist_add_tail(&pr->entry, &keep_vsub->cs.requests);
+ }
+
+ /* make sure a Paging use count is present on keep_vsub, if needed */
+ if (!keep_vsub->cs.is_paging && !llist_empty(&keep_vsub->cs.requests)) {
+ vlr_subscr_get(keep_vsub, VSUB_USE_PAGING);
+ keep_vsub->cs.is_paging = true;
+ }
+
+ /* Already made sure at the top of this function that discarding_vsub->cs.is_paging == true */
+ discarding_vsub->cs.is_paging = false;
+ osmo_timer_del(&discarding_vsub->cs.paging_response_timer);
+ vlr_subscr_put(discarding_vsub, VSUB_USE_PAGING);
+}
+
void paging_request_remove(struct paging_request *pr)
{
struct gsm_trans *trans = pr->trans;
diff --git a/src/libvlr/vlr.c b/src/libvlr/vlr.c
index e2e02bf..cb5794f 100644
--- a/src/libvlr/vlr.c
+++ b/src/libvlr/vlr.c
@@ -36,6 +36,7 @@
#include <osmocom/msc/vlr.h>
#include <osmocom/msc/debug.h>
#include <osmocom/msc/gsup_client_mux.h>
+#include <osmocom/msc/paging.h>

#include <netinet/in.h>
#include <arpa/inet.h>
@@ -272,7 +273,8 @@

llist_for_each_entry(vsub, &vlr->subscribers, list) {
if (vlr_subscr_matches_imsi(vsub, imsi)) {
- vlr_subscr_get_src(vsub, use, file, line);
+ if (use)
+ vlr_subscr_get_src(vsub, use, file, line);
return vsub;
}
}
@@ -577,11 +579,63 @@
return vsub;
}

+static void dedup_vsub(struct vlr_subscr *exists, struct vlr_subscr *vsub)
+{
+ struct vlr_instance *vlr = exists->vlr;
+ int i;
+ int j;
+ LOGP(DVLR, LOGL_NOTICE,
+ "There is an existing subscriber for IMSI %s used by %s, replacing with new VLR subscr: %s used by %s\n",
+ exists->imsi, osmo_use_count_to_str_c(OTC_SELECT, &exists->use_count),
+ vlr_subscr_name(vsub),
+ osmo_use_count_to_str_c(OTC_SELECT, &vsub->use_count));
+
+ /* Take over some state from the previous vsub */
+ paging_request_join_vsub(vsub, exists);
+ if (!vsub->msisdn[0])
+ OSMO_STRLCPY_ARRAY(vsub->msisdn, exists->msisdn);
+ if (!vsub->name[0])
+ OSMO_STRLCPY_ARRAY(vsub->name, exists->name);
+ /* Copy valid auth tuples we may already have, to reduce the need to ask for new ones from the HLR */
+ for (i = 0; i < ARRAY_SIZE(exists->auth_tuples); i++) {
+ if (exists->auth_tuples[i].key_seq == VLR_KEY_SEQ_INVAL)
+ continue;
+ for (j = 0; j < ARRAY_SIZE(vsub->auth_tuples); j++) {
+ if (vsub->auth_tuples[j].key_seq != VLR_KEY_SEQ_INVAL)
+ continue;
+ vsub->auth_tuples[j] = exists->auth_tuples[i];
+ }
+ }
+
+ if (exists->msc_conn_ref)
+ LOGVSUBP(LOGL_ERROR, vsub,
+ "There is an existing VLR entry for this same subscriber with an active connection."
+ " That should not be possible. Discarding old subscriber entry %s.\n",
+ exists->imsi);
+
+ if (vlr->ops.subscr_inval)
+ vlr->ops.subscr_inval(exists->msc_conn_ref, exists);
+ vlr_subscr_free(exists);
+}
+
void vlr_subscr_set_imsi(struct vlr_subscr *vsub, const char *imsi)
{
+ struct vlr_subscr *exists;
if (!vsub)
return;

+ /* If the same IMSI is already set, nothing changes. */
+ if (!strcmp(vsub->imsi, imsi))
+ return;
+
+ /* We've just learned about this new IMSI, our primary key in the VLR. make sure to invalidate any prior VLR
+ * entries for this IMSI. */
+ exists = vlr_subscr_find_by_imsi(vsub->vlr, imsi, NULL);
+
+ if (exists)
+ dedup_vsub(exists, vsub);
+
+ /* Set the IMSI on the new subscriber, here. */
if (OSMO_STRLCPY_ARRAY(vsub->imsi, imsi) >= sizeof(vsub->imsi)) {
LOGP(DVLR, LOGL_NOTICE, "IMSI was truncated: full IMSI=%s, truncated IMSI=%s\n",
imsi, vsub->imsi);
diff --git a/tests/msc_vlr/msc_vlr_test_rest.err b/tests/msc_vlr/msc_vlr_test_rest.err
index e649325..fae302f 100644
--- a/tests/msc_vlr/msc_vlr_test_rest.err
+++ b/tests/msc_vlr/msc_vlr_test_rest.err
@@ -467,7 +467,6 @@
DMM IDENTITY RESPONSE: IMSI-901700000004620
DVLR set IMSI on subscriber; IMSI=901700000004620 id=901700000004620
DVLR vlr_lu_fsm(TMSI-0x23422342:GERAN-A:LU){VLR_ULA_S_WAIT_IMSI}: Received Event VLR_ULA_E_ID_IMSI
-DVLR set IMSI on subscriber; IMSI=901700000004620 id=901700000004620
DVLR vlr_lu_fsm(TMSI-0x23422342:GERAN-A:LU){VLR_ULA_S_WAIT_IMSI}: vlr_loc_upd_node1_pre()
DVLR vlr_lu_fsm(TMSI-0x23422342:GERAN-A:LU){VLR_ULA_S_WAIT_IMSI}: vlr_loc_upd_node1()
DVLR vlr_lu_fsm(TMSI-0x23422342:GERAN-A:LU){VLR_ULA_S_WAIT_IMSI}: vlr_loc_upd_post_auth()

To view, visit change 36452. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: osmo-msc
Gerrit-Branch: master
Gerrit-Change-Id: Ifdabe0b65bffafbf7b8e5cc10e2d225d1ed1cecd
Gerrit-Change-Number: 36452
Gerrit-PatchSet: 3
Gerrit-Owner: neels <nhofmeyr@sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de>
Gerrit-Reviewer: laforge <laforge@osmocom.org>
Gerrit-Reviewer: neels <nhofmeyr@sysmocom.de>
Gerrit-Reviewer: pespin <pespin@sysmocom.de>
Gerrit-MessageType: merged