A5/n Ciph: request Classmark Update if missing

When the VLR requests a Ciphering Mode with vlr_ops.set_ciph_mode(), and if we
need a ciph algo flag from a Classmark information that is not yet known
(usually CM 2 during LU), send a BSSMAP Classmark Request to get it.

To manage the intermission of the Classmark Request, add
- msc_classmark_request_then_cipher_mode_cmd(),
- state SUBSCR_CONN_S_WAIT_CLASSMARK_UPDATE,
- event SUBSCR_CONN_E_CLASSMARK_UPDATE.

From state AUTH_CIPH, switch to state WAIT_CLASSMARK_UPDATE. Once the BSSMAP
Classmark Response, is received, switch back to SUBSCR_CONN_S_AUTH_CIPH and
re-initiate Ciphering Mode.

To be able to re-enter the Ciphering Mode algo decision, factor it out into
msc_geran_set_cipher_mode().

Rationale:

In the following commit, essentially we stopped supporting A5/3 ciphering:

commit 71330720b6efdda2fcfd3e9c0cb45f89e32e5670
"MSC: Intersect configured A5 algorithms with MS-supported ones"
Change-Id: Id124923ee52a357cb7d3e04d33f585214774f3a3

A5/3 was no longer supported because from that commit on, we strictly checked
the MS-supported ciphers, but we did not have Classmark 2 available during
Location Updating.

This patch changes that: when Classmark 2 is missing, actively request it by a
BSSMAP Classmark Request; continue Ciphering only after the Response. Always
request missing Classmark, even if a lesser cipher were configured available.

If the Classmark Update response fails to come in, cause an attach failure.
Instead, we could attempt to use a lesser cipher that is also enabled. That is
left as a future feature, should that become relevant. I think it's unlikely.

Technically, we could now end up requesting a Classmark Updating both during LU
(vlr_lu_fsm) and CM Service/Paging Response (proc_arq_fsm), but in practice the
only time we lack a Classmark is: during Location Updating with A5/3 enabled.
A5/1 support is indicated in CM1 which is always available, and A5/3 support is
indicated in CM2, which is always available during CM Service Request as well
as Paging Response. So this patch has practical relevance only for Location
Updating. For networks that permit only A5/3, this patch fixes Location
Updating. For networks that support A5/3 and A5/1, so far we always used A5/1
during LU, and after this patch we request CM2 and likely use A5/3 instead.

In msc_vlr_test_gsm_ciph, verify that requesting Classmark 2 for A5/3 works
during LU. Also verify that the lack of a Classmark Response results in attach
failure.

In msc_vlr_test_gsm_ciph, a hacky unit test fakes a situation where a CM2 is
missing during proc_arq_fsm and proves that that code path works, even though
the practical relevance is currently zero. It would only become interesting if
ciphering algorithms A5/4 and higher became relevant, because support of those
would be indicated in Classmark 3, which would always require a Classmark
Request.

Related: OS#3043
Depends: I4a2e1d3923e33912579c4180aa1ff8e8f5abb7e7 (libosmocore)
Change-Id: I73c7cb6a86624695bd9c0f59abb72e2fdc655131
diff --git a/tests/msc_vlr/msc_vlr_tests.c b/tests/msc_vlr/msc_vlr_tests.c
index bf1b200..1192cf2 100644
--- a/tests/msc_vlr/msc_vlr_tests.c
+++ b/tests/msc_vlr/msc_vlr_tests.c
@@ -35,6 +35,7 @@
 #include <osmocom/msc/debug.h>
 #include <osmocom/msc/gsm_04_08.h>
 #include <osmocom/msc/transaction.h>
+#include <osmocom/msc/a_iface_bssap.h>
 
 #if BUILD_IU
 #include <osmocom/msc/iucs_ranap.h>
@@ -111,10 +112,10 @@
 
 struct msgb *msgb_from_hex(const char *label, uint16_t size, const char *hex)
 {
-	struct msgb *msg = msgb_alloc(size, label);
+	struct msgb *msg = msgb_alloc_headroom(size, 4, label);
 	unsigned char *rc;
-	msg->l2h = msg->head;
-	rc = msgb_put(msg, osmo_hexparse(hex, msg->head, msgb_tailroom(msg)));
+	msg->l2h = msg->data;
+	rc = msgb_put(msg, osmo_hexparse(hex, msg->data, msgb_tailroom(msg)));
 	OSMO_ASSERT(rc == msg->l2h);
 	return msg;
 }
@@ -240,7 +241,32 @@
 	msg = msgb_from_hex("ms_sends_msg", 1024, hex);
 	msg->l1h = msg->l2h = msg->l3h = msg->data;
 	rx_from_ms(msg);
-	talloc_free(msg);
+	msgb_free(msg);
+}
+
+void bss_sends_bssap_mgmt(const char *hex)
+{
+	struct msgb *msg;
+	struct bssmap_header *bh;
+	struct a_conn_info a_conn_info;
+
+	msg = msgb_from_hex("bss_sends_bssap_mgmt", 1024, hex);
+	msg->l3h = msg->data;
+
+	msg->l2h = msgb_push(msg, sizeof(*bh));
+	bh = (void*)msg->l2h;
+	bh->type = BSSAP_MSG_BSS_MANAGEMENT;
+	bh->length = msgb_l3len(msg);
+
+	if (g_conn && !conn_exists(g_conn))
+		g_conn = NULL;
+
+	OSMO_ASSERT(g_conn);
+	a_conn_info.network = net;
+	a_conn_info.conn_id = g_conn->a.conn_id;
+
+	a_sccp_rx_dt((struct osmo_sccp_user*)0x1, &a_conn_info, msg);
+	msgb_free(msg);
 }
 
 static int ms_sends_msg_fake(uint8_t pdisc, uint8_t msg_type)
@@ -363,6 +389,33 @@
 	paging_stopped = true;
 }
 
+
+/* override, requires '-Wl,--wrap=osmo_sccp_tx_data_msg' */
+int __real_osmo_sccp_tx_data_msg(struct osmo_sccp_user *scu, uint32_t conn_id,
+				 struct msgb *msg);
+int __wrap_osmo_sccp_tx_data_msg(struct osmo_sccp_user *scu, uint32_t conn_id,
+				 struct msgb *msg)
+{
+	const char *proto_str;
+	const char *msg_str = gsm0808_bssmap_name(msg->l3h[2]);
+	switch (*msg->l3h) {
+	case BSSAP_MSG_BSS_MANAGEMENT:
+		proto_str = "BSSAP-BSS-MANAGEMENT";
+		break;
+	case BSSAP_MSG_DTAP:
+		proto_str = "BSSAP-DTAP";
+		break;
+	default:
+		proto_str = "";
+		msg_str = "";
+		break;
+	}
+
+	log("BSC <--%s-- MSC: %s %s", proto_str, msg_str, msgb_hexdump(msg));
+	msgb_free(msg);
+	return 0;
+}
+
 void clear_vlr()
 {
 	struct vlr_subscr *vsub, *n;