Change in osmo-ttcn3-hacks[master]: RAN_Emulation: Add RANAP support

Harald Welte gerrit-no-reply at lists.osmocom.org
Thu Apr 25 20:07:13 UTC 2019


Harald Welte has submitted this change and it was merged. ( https://gerrit.osmocom.org/13653 )

Change subject: RAN_Emulation: Add RANAP support
......................................................................

RAN_Emulation: Add RANAP support

So far, RAN_Emulation only handled BSSAP and hence could be used
to emulate BSCs towards the MSC.  Let's extend it with RANAP support
so we can also emulate RNCs towards the MSC.

We try to share as much code and logic as possible betweeb the two.

Related: OS#2856, OS#2857
Change-Id: Ie79bda764162e5c5a42608bde5c5f486ea531f33
---
M library/RAN_Adapter.ttcnpp
M library/RAN_Emulation.ttcnpp
2 files changed, 345 insertions(+), 10 deletions(-)

Approvals:
  Harald Welte: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/library/RAN_Adapter.ttcnpp b/library/RAN_Adapter.ttcnpp
index ae7934e..53c8bac 100644
--- a/library/RAN_Adapter.ttcnpp
+++ b/library/RAN_Adapter.ttcnpp
@@ -45,7 +45,8 @@
 type enumerated RAN_Transport {
 	BSSAP_TRANSPORT_AoIP,	/* 3GPP AoIP: SCCP over M3UA over SCTP */
 	BSSAP_TRANSPORT_SCCPlite_SERVER, /* SCCPlite: SCCP over IPA over TCP */
-	BSSAP_TRANSPORT_SCCPlite_CLIENT  /* SCCPlite: SCCP over IPA over TCP */
+	BSSAP_TRANSPORT_SCCPlite_CLIENT, /* SCCPlite: SCCP over IPA over TCP */
+	RANAP_TRANSPORT_IuCS	/* 3GPP IuCS: SCCP over M3UA over SCTP */
 };
 
 type record RAN_Configuration {
@@ -90,8 +91,7 @@
 		ba.vc_RAN := RAN_Emulation_CT.create(id & "-RAN");
 	}
 	select (cfg.transport) {
-#ifdef RAN_EMULATION_BSSAP
-	case (BSSAP_TRANSPORT_AoIP) {
+	case (BSSAP_TRANSPORT_AoIP, RANAP_TRANSPORT_IuCS) {
 		ba.vc_M3UA := M3UA_CT.create(id & "-M3UA");
 		map(ba.vc_M3UA:SCTP_PORT, system:sctp);
 		/* connect MTP3 service provider (M3UA) to lower side of SCCP */
@@ -133,7 +133,6 @@
 		disconnect(ba.vc_IPA:IPA_SP_PORT, ba.vc_WAIT:IPA_SP_PORT);
 		}
 #endif /* SCCP */
-#endif /* BSSAP */
 	case else {
 		setverdict(fail, "Unsuppored RAN_Transport");
 		mtc.stop;
@@ -145,10 +144,17 @@
 		T.start;
 		//T.timeout;
 		log("Connecting BSSMAP Emulation to SCCP_SP_PORT and starting emulation");
-#if RAN_EMULATION_BSSAP
 		/* connect BSSNAP component to upper side of SCCP */
-		connect(ba.vc_RAN:BSSAP, ba.vc_SCCP:SCCP_SP_PORT);
+		if (cfg.transport == RANAP_TRANSPORT_IuCS) {
+#ifdef RAN_EMULATION_RANAP
+			ops.protocol := RAN_PROTOCOL_RANAP
+			connect(ba.vc_RAN:RANAP, ba.vc_SCCP:SCCP_SP_PORT);
 #endif
+		} else {
+#ifdef RAN_EMULATION_BSSAP
+			connect(ba.vc_RAN:BSSAP, ba.vc_SCCP:SCCP_SP_PORT);
+#endif
+		}
 		if (cfg.transport == BSSAP_TRANSPORT_SCCPlite_SERVER or
 		    cfg.transport == BSSAP_TRANSPORT_SCCPlite_CLIENT) {
 #ifdef IPA_EMULATION_MGCP
@@ -156,7 +162,6 @@
 			connect(ba.vc_IPA:IPA_MGCP_PORT, ba.vc_RAN:MGCP);
 #endif
 		}
-		/* start the BSSMAP emulation */
 		ba.vc_RAN.start(RAN_Emulation.main(valueof(ops), ""));
 	}
 
diff --git a/library/RAN_Emulation.ttcnpp b/library/RAN_Emulation.ttcnpp
index e091133..a74b6de 100644
--- a/library/RAN_Emulation.ttcnpp
+++ b/library/RAN_Emulation.ttcnpp
@@ -48,6 +48,14 @@
 import from MGCP_Templates all;
 #endif
 
+#ifdef RAN_EMULATION_RANAP
+import from RANAP_CodecPort all;
+import from RANAP_PDU_Descriptions all;
+import from RANAP_Constants all;
+import from RANAP_IEs all;
+import from RANAP_Templates all;
+#endif
+
 /* General "base class" component definition, of which specific implementations
  * derive themselves by means of the "extends" feature */
 type component RAN_ConnHdlr {
@@ -110,6 +118,11 @@
 		/* Client requests us to create SCCP Connection */
 		BSSAP_Conn_Req,
 #endif
+#ifdef RAN_EMULATION_RANAP
+		RANAP_PDU,
+		/* Client requests us to create SCCP Connection */
+		RANAP_Conn_Req,
+#endif
 #ifdef RAN_EMULATION_MGCP
 		/* MGCP, only used for IPA SCCPlite (MGCP in IPA mux) */
 		MgcpCommand, MgcpResponse,
@@ -147,6 +160,9 @@
 #ifdef RAN_EMULATION_BSSAP
 	port BSSAP_CODEC_PT BSSAP;
 #endif
+#ifdef RAN_EMULATION_RANAP
+	port RANAP_CODEC_PT RANAP;
+#endif
 	/* BSSAP port to the per-connection clients */
 	port RAN_Conn_PT CLIENT;
 #ifdef RAN_EMULATION_MGCP
@@ -487,10 +503,137 @@
 }
 #endif
 
+#ifdef RAN_EMULATION_RANAP
+type record RANAP_Conn_Req {
+	SCCP_PAR_Address	addr_peer,
+	SCCP_PAR_Address	addr_own,
+	RANAP_PDU		ranap
+}
+template (value) RANAP_Conn_Req ts_RANAP_Conn_Req(SCCP_PAR_Address peer, SCCP_PAR_Address own, RANAP_PDU ranap) := {
+	addr_peer := peer,
+	addr_own := own,
+	ranap := ranap
+};
+
+private function fake_dlci_from_sapi(template (omit) SAPI sapi) return template (omit) OCT1
+{
+	if (istemplatekind(sapi, "omit")) {
+		return omit;
+	} else if (valueof(sapi) == sapi_3) {
+		return '03'O;
+	}
+	return '00'O;
+}
+
+private function f_handle_userData_RANAP(RAN_ConnHdlr client, RANAP_PDU ranap)
+runs on RAN_Emulation_CT {
+	/* decode + send decoded RANAP to client */
+	var template (omit) octetstring l3 := f_ranap_extract_l3(ranap);
+	if (istemplatekind(l3, "omit")) {
+		CLIENT.send(ranap) to client;
+	} else {
+		var template (omit) SAPI sapi := f_ranap_extract_sapi(ranap);
+		var template (omit) OCT1 dlci := fake_dlci_from_sapi(sapi);
+		if (g_ran_ops.role_ms) {
+			/* we are the MS, so any message to us must be MT */
+			var PDU_DTAP_MT mt := {
+				dlci := omit,
+				dtap := dec_PDU_ML3_NW_MS(valueof(l3))
+			};
+			if (isvalue(dlci)) {
+				mt.dlci := valueof(dlci)
+			}
+			CLIENT.send(mt) to client;
+		} else {
+			/* we are the Network, so any message to us must be MO */
+			var PDU_DTAP_MO mo := {
+				dlci := omit,
+				dtap := dec_PDU_ML3_MS_NW(valueof(l3))
+			};
+			if (isvalue(dlci)) {
+				mo.dlci := valueof(dlci)
+			}
+			CLIENT.send(mo) to client;
+		}
+	}
+}
+
+/* call-back type, to be provided by specific implementation; called when new SCCP connection
+ * arrives */
+type function RanapCreateCallback(RANAP_N_CONNECT_ind conn_ind, charstring id)
+runs on RAN_Emulation_CT return RAN_ConnHdlr;
+
+type function RanapUnitdataCallback(RANAP_PDU ranap)
+runs on RAN_Emulation_CT return template RANAP_PDU;
+
+private function CommonRanapUnitdataCallback(RANAP_PDU ranap)
+runs on RAN_Emulation_CT return template RANAP_PDU {
+	if (match(ranap, tr_RANAP_Paging(?, ?))) {
+		var RAN_ConnHdlr client := null;
+		/* extract IMSI and (if present) TMSI */
+		var IMSI imsi := ranap.initiatingMessage.value_.paging.protocolIEs[1].value_.permanentNAS_UE_ID.iMSI;
+		var template OCT4 tmsi := omit;
+		if (lengthof(ranap.initiatingMessage.value_.paging.protocolIEs) > 2 and
+		    ranap.initiatingMessage.value_.paging.protocolIEs[2].id == id_TemporaryUE_ID) {
+			var TemporaryUE_ID ue_id;
+			ue_id := ranap.initiatingMessage.value_.paging.protocolIEs[2].value_.temporaryUE_ID;
+			if (ischosen(ue_id.tMSI)) {
+				tmsi := ue_id.tMSI;
+			} else {
+				tmsi := ue_id.p_TMSI;
+			}
+		}
+		client := f_imsi_table_find(oct2hex(imsi), tmsi);
+		if (isvalue(client)) {
+			log("CommonRanapUnitdataCallback: IMSI/TMSI found in table, dispatching to ",
+				client);
+			CLIENT.send(ranap) to client;
+			return omit;
+		}
+		log("CommonRanapUnitdataCallback: IMSI/TMSI not found in table");
+	} else {
+		log("CommonRanapUnitdataCallback: Not a paging message");
+	}
+
+	/* ELSE: handle in user callback */
+	return g_ran_ops.ranap_unitdata_cb.apply(ranap);
+}
+
+private function f_ranap_l3_is_rr(RANAP_PDU ranap) return boolean {
+	var template (omit) SAPI sapi;
+	var template octetstring l3 := f_ranap_extract_l3(ranap);
+	return f_L3_is_rr(l3);
+}
+
+function f_ranap_reset(SCCP_PAR_Address peer, SCCP_PAR_Address own) runs on RAN_Emulation_CT {
+	timer T := 5.0;
+	var CN_DomainIndicator dom;
+	if (g_ran_ops.ps_domain) {
+		dom := ps_domain;
+	} else {
+		dom := cs_domain;
+	}
+
+	RANAP.send(ts_RANAP_UNITDATA_req(peer, own, ts_RANAP_Reset(ts_RanapCause_om_intervention, dom)));
+	T.start;
+	alt {
+	[] RANAP.receive(tr_RANAP_UNITDATA_ind(own, peer, tr_RANAP_ResetAck)) {
+		log("Received RESET-ACK in response to RESET, we're ready to go!");
+		}
+	[] as_reset_ack();
+	[] RANAP.receive { repeat };
+	[] T.timeout {
+		setverdict(fail, "Timeout waiting for RESET-ACK after sending RESET");
+		mtc.stop;
+		}
+	}
+}
+#endif
 
 
 type enumerated RanProtocol {
-	RAN_PROTOCOL_BSSAP
+	RAN_PROTOCOL_BSSAP,
+	RAN_PROTOCOL_RANAP
 }
 
 type record RanOps {
@@ -498,6 +641,11 @@
 	BssmapCreateCallback create_cb optional,
 	BssmapUnitdataCallback unitdata_cb optional,
 #endif
+#ifdef RAN_EMULATION_RANAP
+	RanapCreateCallback ranap_create_cb optional,
+	RanapUnitdataCallback ranap_unitdata_cb optional,
+	boolean ps_domain,
+#endif
 	boolean decode_dtap,
 	boolean role_ms,
 	RanProtocol protocol,
@@ -551,6 +699,9 @@
 #ifdef RAN_EMULATION_BSSAP
 	var BSSAP_N_UNITDATA_ind ud_ind;
 #endif
+#ifdef RAN_EMULATION_RANAP
+	var RANAP_N_UNITDATA_ind rud_ind;
+#endif
 #ifdef RAN_EMULATION_BSSAP
 	[] BSSAP.receive(tr_BSSAP_UNITDATA_ind(?, ?, tr_BSSMAP_Reset)) -> value ud_ind {
 		log("Respoding to inbound RESET with RESET-ACK");
@@ -559,6 +710,15 @@
 		repeat;
 	}
 #endif
+#ifdef RAN_EMULATION_RANAP
+	[] RANAP.receive(tr_RANAP_UNITDATA_ind(?, ?, tr_RANAP_Reset)) -> value rud_ind {
+		log("Respoding to inbound IuRESET with IuRESET-ACK");
+		var CN_DomainIndicator dom;
+		dom := rud_ind.userData.initiatingMessage.value_.Reset.protocolIEs[1].value_.cN_DomainIndicator;
+		RANAP.send(ts_RANAP_UNITDATA_req(rud_ind.callingAddress, rud_ind.calledAddress,
+			   ts_RANAP_ResetAck(dom)));
+	}
+#endif
 }
 
 
@@ -666,7 +826,116 @@
 
 			}
 #else
-		[false] CLIENT.receive(false) {}
+		[false] CLIENT.receive {}
+#endif
+}
+
+private altstep as_main_ranap() runs on RAN_Emulation_CT {
+#ifdef RAN_EMULATION_RANAP
+		var RANAP_N_UNITDATA_ind rud_ind;
+		var RANAP_N_CONNECT_ind rconn_ind;
+		var RANAP_N_CONNECT_cfm rconn_cfm;
+		var RANAP_N_DATA_ind rdata_ind;
+		var RANAP_N_DISCONNECT_ind rdisc_ind;
+		var RANAP_Conn_Req creq;
+		var RANAP_PDU ranap;
+		var RAN_ConnHdlr vc_conn;
+
+		/* SCCP -> Client: UNIT-DATA (connectionless SCCP) from a BSC */
+		[] RANAP.receive(RANAP_N_UNITDATA_ind:?) -> value rud_ind {
+			/* Connectionless Procedures like RESET */
+			var template RANAP_PDU resp;
+			resp := CommonRanapUnitdataCallback(rud_ind.userData);
+			if (isvalue(resp)) {
+				RANAP.send(ts_RANAP_UNITDATA_req(rud_ind.callingAddress,
+								 rud_ind.calledAddress, resp));
+			}
+			}
+		/* SCCP -> Client: new connection from BSC */
+		[] RANAP.receive(RANAP_N_CONNECT_ind:?) -> value rconn_ind {
+			vc_conn := g_ran_ops.ranap_create_cb.apply(rconn_ind, g_ran_id);
+			/* store mapping between client components and SCCP connectionId */
+			f_conn_table_add(vc_conn, rconn_ind.connectionId);
+			/* handle user payload */
+			f_handle_userData_RANAP(vc_conn, rconn_ind.userData);
+			/* confirm connection establishment */
+			RANAP.send(ts_RANAP_CONNECT_res(rconn_ind.connectionId, omit));
+			}
+		/* SCCP -> Client: connection-oriented data in existing connection */
+		[] RANAP.receive(RANAP_N_DATA_ind:?) -> value rdata_ind {
+			vc_conn := f_comp_by_conn_id(rdata_ind.connectionId);
+			if (ispresent(rdata_ind.userData)) {
+				f_handle_userData_RANAP(vc_conn, rdata_ind.userData);
+			}
+			}
+		/* SCCP -> Client: disconnect of an existing connection */
+		[] RANAP.receive(RANAP_N_DISCONNECT_ind:?) -> value rdisc_ind {
+			vc_conn := f_comp_by_conn_id(rdisc_ind.connectionId);
+			if (ispresent(rdisc_ind.userData)) {
+				f_handle_userData_RANAP(vc_conn, rdisc_ind.userData);
+			}
+			/* notify client about termination */
+			var RAN_Conn_Prim prim := MSC_CONN_PRIM_DISC_IND;
+			CLIENT.send(prim) to vc_conn;
+			f_conn_table_del(rdisc_ind.connectionId);
+			/* TOOD: return confirm to other side? */
+			}
+		/* SCCP -> Client: connection confirm for outbound connection */
+		[] RANAP.receive(RANAP_N_CONNECT_cfm:?) -> value rconn_cfm {
+			vc_conn := f_comp_by_conn_id(rconn_cfm.connectionId);
+			var RAN_Conn_Prim prim := MSC_CONN_PRIM_CONF_IND;
+			CLIENT.send(prim) to vc_conn;
+			/* handle user payload */
+			if (ispresent(rconn_cfm.userData)) {
+				f_handle_userData_RANAP(vc_conn, rconn_cfm.userData);
+			}
+			}
+
+		[] CLIENT.receive(RANAP_PDU:?) -> value ranap sender vc_conn {
+			var integer conn_id := f_conn_id_by_comp(vc_conn);
+			/* send it to dispatcher */
+			RANAP.send(ts_RANAP_DATA_req(conn_id, ranap));
+			}
+
+		/* Disconnect request client -> SCCP */
+		[] CLIENT.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_REQ) -> sender vc_conn {
+			var integer conn_id := f_conn_id_by_comp(vc_conn);
+			RANAP.send(ts_RANAP_DISC_req(conn_id, 0));
+			f_conn_table_del(conn_id);
+			}
+
+		/* BSSAP from client -> SCCP */
+		[] CLIENT.receive(RANAP_Conn_Req:?) -> value creq sender vc_conn {
+			var integer conn_id;
+			/* send to dispatcher */
+
+			if (f_comp_known(vc_conn) == false) {
+				/* unknown client, create new connection */
+				conn_id := f_gen_conn_id();
+
+				/* store mapping between client components and SCCP connectionId */
+				f_conn_table_add(vc_conn, conn_id);
+
+				RANAP.send(ts_RANAP_CONNECT_req(creq.addr_peer, creq.addr_own, conn_id,
+								creq.ranap));
+			} else {
+				/* known client, send via existing connection */
+				conn_id := f_conn_id_by_comp(vc_conn);
+				RANAP.send(ts_RANAP_DATA_req(conn_id, creq.ranap));
+			}
+
+			/* InitialL3 contains RR (PAG RESP) or MM (CM SRV REQ), we must increment
+			 * counter only on MM/CC/SS, but not on RR */
+			if (g_ran_ops.role_ms and not f_ranap_l3_is_rr(creq.ranap)) {
+				/* we have just sent the first MM message, increment the counter */
+				var integer idx := f_idx_by_comp(vc_conn);
+				ConnectionTable[idx].n_sd[0] := 1;
+				log("patch: N(SD) for ConnIdx ", idx, " set to 1");
+			}
+			}
+
+#else
+		[false] CLIENT.receive {}
 #endif
 }
 
@@ -730,6 +999,18 @@
 		BSSAP.send(ts_BSSAP_DATA_req(sccp_conn_id, bssap));
 		}
 #endif
+#ifdef RAN_EMULATION_RANAP
+	case (RAN_PROTOCOL_RANAP) {
+		var RANAP_PDU ranap;
+		if (false /* SAPI */) {
+			var RANAP_IEs.SAPI sapi := sapi_0;
+			ranap := valueof(ts_RANAP_DirectTransferSAPI(l3_enc, sapi));
+		} else {
+			ranap := valueof(ts_RANAP_DirectTransfer(l3_enc));
+		}
+		RANAP.send(ts_RANAP_DATA_req(sccp_conn_id, ranap));
+		}
+#endif
 	}
 }
 
@@ -742,7 +1023,18 @@
 
 	if (isvalue(ops.sccp_addr_peer) and isvalue(ops.sccp_addr_local)) {
 		f_sleep(1.0);	/* HACK to wait for M3UA/ASP to be ACTIVE */
-		f_bssap_reset(ops.sccp_addr_peer, ops.sccp_addr_local);
+		select (g_ran_ops.protocol) {
+#ifdef RAN_EMULATION_BSSAP
+		case (RAN_PROTOCOL_BSSAP) {
+			f_bssap_reset(ops.sccp_addr_peer, ops.sccp_addr_local);
+			}
+#endif
+#ifdef RAN_EMULATION_RANAP
+		case (RAN_PROTOCOL_RANAP) {
+			f_ranap_reset(ops.sccp_addr_peer, ops.sccp_addr_local);
+			}
+#endif
+		}
 	}
 
 	while (true) {
@@ -756,6 +1048,7 @@
 
 		alt {
 		[g_ran_ops.protocol == RAN_PROTOCOL_BSSAP] as_main_bssap();
+		[g_ran_ops.protocol == RAN_PROTOCOL_RANAP] as_main_ranap();
 
 		[g_ran_ops.role_ms] CLIENT.receive(PDU_DTAP_MO:?) -> value dtap_mo sender vc_conn {
 			var integer idx := f_idx_by_comp(vc_conn);
@@ -822,6 +1115,7 @@
 	inout RAN_register, RAN_register_imsi;
 } with { extension "internal" };
 
+#ifdef RAN_EMULATION_BSSAP
 /* CreateCallback that can be used as create_cb and will use the expectation table */
 function ExpectedCreateCallback(BSSAP_N_CONNECT_ind conn_ind, charstring id)
 runs on RAN_Emulation_CT return RAN_ConnHdlr {
@@ -854,6 +1148,42 @@
 	mtc.stop;
 	return ret;
 }
+#endif
+
+#ifdef RAN_EMULATION_RANAP
+/* CreateCallback that can be used as create_cb and will use the expectation table */
+function RanapExpectedCreateCallback(RANAP_N_CONNECT_ind conn_ind, charstring id)
+runs on RAN_Emulation_CT return RAN_ConnHdlr {
+	var RAN_ConnHdlr ret := null;
+	var template (omit) octetstring l3_info;
+	var integer i;
+
+	l3_info := f_ranap_extract_l3(conn_ind.userData);
+	if (istemplatekind(l3_info, "omit")) {
+		setverdict(fail, "N-CONNECT.ind without NAS payload");
+		mtc.stop;
+		return ret;
+	}
+
+	for (i := 0; i < sizeof(ExpectTable); i:= i+1) {
+		if (not ispresent(ExpectTable[i].l3_payload)) {
+			continue;
+		}
+		if (valueof(l3_info) == ExpectTable[i].l3_payload) {
+			ret := ExpectTable[i].vc_conn;
+			/* release this entry to be used again */
+			ExpectTable[i].l3_payload := omit;
+			ExpectTable[i].vc_conn := null;
+			log("Found Expect[", i, "] for ", l3_info, " handled at ", ret);
+			/* return the component reference */
+			return ret;
+		}
+	}
+	setverdict(fail, "Couldn't find Expect for incoming connection ", conn_ind);
+	mtc.stop;
+	return ret;
+}
+#endif
 
 private function f_create_expect(octetstring l3, RAN_ConnHdlr hdlr)
 runs on RAN_Emulation_CT {

-- 
To view, visit https://gerrit.osmocom.org/13653
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: Ie79bda764162e5c5a42608bde5c5f486ea531f33
Gerrit-Change-Number: 13653
Gerrit-PatchSet: 8
Gerrit-Owner: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder (1000002)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20190425/a111245a/attachment.html>


More information about the gerrit-log mailing list