RAN_Emulation: Modularize protocol support
The RAN_Emulation currently unconditionally provides BSSAP and MGCP support.
Let's re-structure the code so that support for those protocols is now
possible to enable/disable at compile time.
This patch is in preparation of introducing RANAP support in RAN_Emulation.
Change-Id: Id53ba3ff05f9946230e0e4a759245de14a0f9fbd
Related: OS#2856
diff --git a/library/RAN_Adapter.ttcn b/library/RAN_Adapter.ttcnpp
similarity index 98%
rename from library/RAN_Adapter.ttcn
rename to library/RAN_Adapter.ttcnpp
index 294f747..43b4988 100644
--- a/library/RAN_Adapter.ttcn
+++ b/library/RAN_Adapter.ttcnpp
@@ -88,6 +88,7 @@
ba.vc_RAN := RAN_Emulation_CT.create(id & "-RAN");
}
select (cfg.transport) {
+#ifdef RAN_EMULATION_BSSAP
case (BSSAP_TRANSPORT_AoIP) {
ba.vc_M3UA := M3UA_CT.create(id & "-M3UA");
map(ba.vc_M3UA:SCTP_PORT, system:sctp);
@@ -128,6 +129,7 @@
ba.vc_WAIT.done;
disconnect(ba.vc_IPA:IPA_SP_PORT, ba.vc_WAIT:IPA_SP_PORT);
}
+#endif
case else {
setverdict(fail, "Unsuppored RAN_Transport");
mtc.stop;
@@ -139,8 +141,10 @@
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);
+#endif
if (cfg.transport == BSSAP_TRANSPORT_SCCPlite_SERVER or
cfg.transport == BSSAP_TRANSPORT_SCCPlite_CLIENT) {
/* connect IPA MGCP port with BSSMAP MGCP port */
diff --git a/library/RAN_Emulation.ttcn b/library/RAN_Emulation.ttcnpp
similarity index 92%
rename from library/RAN_Emulation.ttcn
rename to library/RAN_Emulation.ttcnpp
index 72e2733..e091133 100644
--- a/library/RAN_Emulation.ttcn
+++ b/library/RAN_Emulation.ttcnpp
@@ -22,7 +22,7 @@
* Inbound Unit Data messages (such as are dispatched to the RanOps.unitdata_cb() callback,
* which is registered with an argument to the main() function below.
*
- * (C) 2017-2018 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2017-2019 by Harald Welte <laforge@gnumonks.org>
* All rights reserved.
*
* Released under the terms of GNU General Public License, Version 2 or
@@ -34,13 +34,19 @@
import from Osmocom_Types all;
import from SCCP_Emulation all;
import from SCCPasp_Types all;
+import from IPA_Emulation all;
+import from MobileL3_Types all;
+
+#ifdef RAN_EMULATION_BSSAP
import from BSSAP_Types all;
import from BSSAP_CodecPort all;
import from BSSMAP_Templates all;
+#endif
+
+#ifdef RAN_EMULATION_MGCP
import from MGCP_Types all;
import from MGCP_Templates all;
-import from IPA_Emulation all;
-import from MobileL3_Types all;
+#endif
/* General "base class" component definition, of which specific implementations
* derive themselves by means of the "extends" feature */
@@ -61,12 +67,6 @@
MSC_CONN_PRIM_CONF_IND
}
-type record BSSAP_Conn_Req {
- SCCP_PAR_Address addr_peer,
- SCCP_PAR_Address addr_own,
- PDU_BSSAP bssap
-}
-
/* similar to PDU_BSSAP with DTAP, but DTAP is already decoded! */
type record PDU_DTAP_MO {
OCT1 dlci optional,
@@ -102,23 +102,22 @@
dtap := dtap
}
-template BSSAP_Conn_Req ts_BSSAP_Conn_Req(SCCP_PAR_Address peer, SCCP_PAR_Address own, PDU_BSSAP bssap) := {
- addr_peer := peer,
- addr_own := own,
- bssap := bssap
-};
-
-
/* port between individual per-connection components and this dispatcher */
type port RAN_Conn_PT message {
- /* BSSAP or direct DTAP messages from/to clients */
- inout PDU_BSSAP, PDU_DTAP_MO, PDU_DTAP_MT,
- /* misc indications / requests between SCCP and client */
- RAN_Conn_Prim,
+ inout
+#ifdef RAN_EMULATION_BSSAP
+ PDU_BSSAP,
/* Client requests us to create SCCP Connection */
BSSAP_Conn_Req,
+#endif
+#ifdef RAN_EMULATION_MGCP
/* MGCP, only used for IPA SCCPlite (MGCP in IPA mux) */
- MgcpCommand, MgcpResponse;
+ MgcpCommand, MgcpResponse,
+#endif
+ /* direct DTAP messages from/to clients */
+ PDU_DTAP_MO, PDU_DTAP_MT,
+ /* misc indications / requests between SCCP and client */
+ RAN_Conn_Prim;
} with { extension "internal" };
@@ -127,8 +126,10 @@
/* reference to the instance of the per-connection component */
RAN_ConnHdlr comp_ref,
integer sccp_conn_id,
+#ifdef RAN_EMULATION_MGCP
/* most recent MGCP transaction ID (Used on MSC side) */
MgcpTransId mgcp_trans_id optional,
+#endif
/* CIC that has been used for voice of this channel (BSC side) */
integer cic optional,
/* array of N(SD) values for MO DTAP messages, indexed by discriminator */
@@ -142,12 +143,16 @@
}
type component RAN_Emulation_CT {
- /* SCCP port on the bottom side, using ASP primitives */
+ /* SCCP ports on the bottom side, using ASP primitives */
+#ifdef RAN_EMULATION_BSSAP
port BSSAP_CODEC_PT BSSAP;
+#endif
/* BSSAP port to the per-connection clients */
port RAN_Conn_PT CLIENT;
+#ifdef RAN_EMULATION_MGCP
/* MGCP port */
port IPA_MGCP_PT MGCP;
+#endif
/* use 16 as this is also the number of SCCP connections that SCCP_Emulation can handle */
var ConnectionData ConnectionTable[16];
@@ -212,6 +217,8 @@
mtc.stop;
}
+
+#ifdef RAN_EMULATION_MGCP
/* resolve component reference by CIC */
private function f_comp_by_mgcp_tid(MgcpTransId tid)
runs on RAN_Emulation_CT return RAN_ConnHdlr {
@@ -237,6 +244,7 @@
setverdict(fail, "RAN Connection table not found by component ", client);
mtc.stop;
}
+#endif
private function f_comp_by_cic(integer cic)
runs on RAN_Emulation_CT return RAN_ConnHdlr {
@@ -303,7 +311,9 @@
for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
ConnectionTable[i].comp_ref := null;
ConnectionTable[i].sccp_conn_id := -1;
+#ifdef RAN_EMULATION_MGCP
ConnectionTable[i].mgcp_trans_id := omit;
+#endif
ConnectionTable[i].cic := omit;
ConnectionTable[i].n_sd := { 0, 0, 0, 0 };
}
@@ -354,6 +364,18 @@
return null;
}
+#ifdef RAN_EMULATION_BSSAP
+type record BSSAP_Conn_Req {
+ SCCP_PAR_Address addr_peer,
+ SCCP_PAR_Address addr_own,
+ PDU_BSSAP bssap
+}
+template BSSAP_Conn_Req ts_BSSAP_Conn_Req(SCCP_PAR_Address peer, SCCP_PAR_Address own, PDU_BSSAP bssap) := {
+ addr_peer := peer,
+ addr_own := own,
+ bssap := bssap
+};
+
/* handle (optional) userData portion of various primitives and dispatch it to the client */
private function f_handle_userData(RAN_ConnHdlr client, PDU_BSSAP bssap)
runs on RAN_Emulation_CT {
@@ -420,69 +442,6 @@
return g_ran_ops.unitdata_cb.apply(bssap);
}
-type record RanOps {
- BssmapCreateCallback create_cb,
- BssmapUnitdataCallback unitdata_cb,
- boolean decode_dtap,
- boolean role_ms,
- /* needed for performing BSSMAP RESET */
- SCCP_PAR_Address sccp_addr_local optional,
- SCCP_PAR_Address sccp_addr_peer optional
-}
-
-template BIT4 t_ML3_DISC_CC_MM_SS := ('0011'B, '0101'B, '1011'B);
-
-/* patch N(SD) into enc_l3, according to 24.007 11.2.3.2 */
-function f_ML3_patch_seq(inout ConnectionData cd, in PDU_ML3_MS_NW dtap, inout octetstring enc_l3) {
- var uint2_t seq_nr;
- if (ischosen(dtap.msgs.cc) or ischosen(dtap.msgs.mm) or ischosen(dtap.msgs.ss)) {
- seq_nr := cd.n_sd[0];
- cd.n_sd[0] := (cd.n_sd[0] + 1) mod 4;
- } else if (ischosen(dtap.msgs.gcc)) {
- seq_nr := cd.n_sd[1];
- cd.n_sd[1] := (cd.n_sd[1] + 1) mod 2;
- } else if (ischosen(dtap.msgs.bcc)) {
- seq_nr := cd.n_sd[2];
- cd.n_sd[2] := (cd.n_sd[2] + 1) mod 2;
- } else if (ischosen(dtap.msgs.loc)) {
- seq_nr := cd.n_sd[3];
- cd.n_sd[3] := (cd.n_sd[3] + 1) mod 2;
- } else {
- /* no sequence number to patch */
- return;
- }
- log("patching N(SD)=", seq_nr, " into dtap ", enc_l3);
- enc_l3[1] := (enc_l3[1] and4b '3f'O) or4b bit2oct(int2bit(seq_nr, 8) << 6);
- log("patched enc_l3: ", enc_l3);
-}
-
-private function f_bssap_l3_is_rr(PDU_BSSAP bssap) return boolean {
- var template octetstring l3 := f_bssap_extract_l3(bssap);
- if (not isvalue(l3)) {
- return false;
- }
- var octetstring l3v := valueof(l3);
- if (lengthof(l3v) < 1) {
- return false;
- }
- /* lower 4 bits of first octet are protocol discriminator */
- if ((oct2bit(l3v[0]) and4b '00001111'B) == '00000110'B) {
- return true;
- }
- return false;
-}
-
-private altstep as_reset_ack() runs on RAN_Emulation_CT {
- var BSSAP_N_UNITDATA_ind ud_ind;
- [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(?, ?, tr_BSSMAP_Reset)) -> value ud_ind {
- log("Respoding to inbound RESET with RESET-ACK");
- BSSAP.send(ts_BSSAP_UNITDATA_req(ud_ind.callingAddress, ud_ind.calledAddress,
- ts_BSSMAP_ResetAck));
- repeat;
- }
-}
-
-
private function f_bssap_wait_for_reset() runs on RAN_Emulation_CT {
var BSSAP_N_UNITDATA_ind ud_ind;
timer T := 20.0;
@@ -522,37 +481,98 @@
}
}
-function main(RanOps ops, charstring id) runs on RAN_Emulation_CT {
+private function f_bssap_l3_is_rr(PDU_BSSAP bssap) return boolean {
+ var template octetstring l3 := f_bssap_extract_l3(bssap);
+ return f_L3_is_rr(l3);
+}
+#endif
- g_ran_id := id;
- g_ran_ops := ops;
- f_conn_table_init();
- f_expect_table_init();
- 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);
+
+type enumerated RanProtocol {
+ RAN_PROTOCOL_BSSAP
+}
+
+type record RanOps {
+#ifdef RAN_EMULATION_BSSAP
+ BssmapCreateCallback create_cb optional,
+ BssmapUnitdataCallback unitdata_cb optional,
+#endif
+ boolean decode_dtap,
+ boolean role_ms,
+ RanProtocol protocol,
+ /* needed for performing BSSMAP RESET */
+ SCCP_PAR_Address sccp_addr_local optional,
+ SCCP_PAR_Address sccp_addr_peer optional
+}
+
+template BIT4 t_ML3_DISC_CC_MM_SS := ('0011'B, '0101'B, '1011'B);
+
+private function f_L3_is_rr(template octetstring l3) return boolean {
+ if (not isvalue(l3)) {
+ return false;
}
+ var octetstring l3v := valueof(l3);
+ if (lengthof(l3v) < 1) {
+ return false;
+ }
+ /* lower 4 bits of first octet are protocol discriminator */
+ if ((oct2bit(l3v[0]) and4b '00001111'B) == '00000110'B) {
+ return true;
+ }
+ return false;
+}
- while (true) {
+/* patch N(SD) into enc_l3, according to 24.007 11.2.3.2 */
+function f_ML3_patch_seq(inout ConnectionData cd, in PDU_ML3_MS_NW dtap, inout octetstring enc_l3) {
+ var uint2_t seq_nr;
+ if (ischosen(dtap.msgs.cc) or ischosen(dtap.msgs.mm) or ischosen(dtap.msgs.ss)) {
+ seq_nr := cd.n_sd[0];
+ cd.n_sd[0] := (cd.n_sd[0] + 1) mod 4;
+ } else if (ischosen(dtap.msgs.gcc)) {
+ seq_nr := cd.n_sd[1];
+ cd.n_sd[1] := (cd.n_sd[1] + 1) mod 2;
+ } else if (ischosen(dtap.msgs.bcc)) {
+ seq_nr := cd.n_sd[2];
+ cd.n_sd[2] := (cd.n_sd[2] + 1) mod 2;
+ } else if (ischosen(dtap.msgs.loc)) {
+ seq_nr := cd.n_sd[3];
+ cd.n_sd[3] := (cd.n_sd[3] + 1) mod 2;
+ } else {
+ /* no sequence number to patch */
+ return;
+ }
+ log("patching N(SD)=", seq_nr, " into dtap ", enc_l3);
+ enc_l3[1] := (enc_l3[1] and4b '3f'O) or4b bit2oct(int2bit(seq_nr, 8) << 6);
+ log("patched enc_l3: ", enc_l3);
+}
+
+private altstep as_reset_ack() runs on RAN_Emulation_CT {
+#ifdef RAN_EMULATION_BSSAP
+ var BSSAP_N_UNITDATA_ind ud_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");
+ BSSAP.send(ts_BSSAP_UNITDATA_req(ud_ind.callingAddress, ud_ind.calledAddress,
+ ts_BSSMAP_ResetAck));
+ repeat;
+ }
+#endif
+}
+
+
+private altstep as_main_bssap() runs on RAN_Emulation_CT {
+#ifdef RAN_EMULATION_BSSAP
var BSSAP_N_UNITDATA_ind ud_ind;
var BSSAP_N_CONNECT_ind conn_ind;
var BSSAP_N_CONNECT_cfm conn_cfm;
var BSSAP_N_DATA_ind data_ind;
var BSSAP_N_DISCONNECT_ind disc_ind;
var BSSAP_Conn_Req creq;
- var RAN_ConnHdlr vc_conn;
var PDU_BSSAP bssap;
- var PDU_DTAP_MO dtap_mo;
- var PDU_DTAP_MT dtap_mt;
- var MgcpCommand mgcp_req;
- var MgcpResponse mgcp_resp;
- var RAN_ConnHdlr vc_hdlr;
- var octetstring l3_info;
- var hexstring imsi;
- var OCT4 tmsi;
+ var RAN_ConnHdlr vc_conn;
- alt {
/* SCCP -> Client: UNIT-DATA (connectionless SCCP) from a BSC */
[] BSSAP.receive(BSSAP_N_UNITDATA_ind:?) -> value ud_ind {
/* Connectionless Procedures like RESET */
@@ -563,10 +583,9 @@
ud_ind.calledAddress, resp));
}
}
-
/* SCCP -> Client: new connection from BSC */
[] BSSAP.receive(BSSAP_N_CONNECT_ind:?) -> value conn_ind {
- vc_conn := ops.create_cb.apply(conn_ind, id);
+ vc_conn := g_ran_ops.create_cb.apply(conn_ind, g_ran_id);
/* store mapping between client components and SCCP connectionId */
f_conn_table_add(vc_conn, conn_ind.connectionId);
/* handle user payload */
@@ -574,7 +593,6 @@
/* confirm connection establishment */
BSSAP.send(ts_BSSAP_CONNECT_res(conn_ind.connectionId, omit));
}
-
/* SCCP -> Client: connection-oriented data in existing connection */
[] BSSAP.receive(BSSAP_N_DATA_ind:?) -> value data_ind {
vc_conn := f_comp_by_conn_id(data_ind.connectionId);
@@ -582,7 +600,6 @@
f_handle_userData(vc_conn, data_ind.userData);
}
}
-
/* SCCP -> Client: disconnect of an existing connection */
[] BSSAP.receive(BSSAP_N_DISCONNECT_ind:?) -> value disc_ind {
vc_conn := f_comp_by_conn_id(disc_ind.connectionId);
@@ -595,7 +612,6 @@
f_conn_table_del(disc_ind.connectionId);
/* TOOD: return confirm to other side? */
}
-
/* SCCP -> Client: connection confirm for outbound connection */
[] BSSAP.receive(BSSAP_N_CONNECT_cfm:?) -> value conn_cfm {
vc_conn := f_comp_by_conn_id(conn_cfm.connectionId);
@@ -606,6 +622,11 @@
f_handle_userData(vc_conn, conn_cfm.userData);
}
}
+ [] CLIENT.receive(PDU_BSSAP:?) -> value bssap sender vc_conn {
+ var integer conn_id := f_conn_id_by_comp(vc_conn);
+ /* send it to dispatcher */
+ BSSAP.send(ts_BSSAP_DATA_req(conn_id, bssap));
+ }
/* Disconnect request client -> SCCP */
[] CLIENT.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_REQ) -> sender vc_conn {
@@ -644,33 +665,16 @@
}
}
+#else
+ [false] CLIENT.receive(false) {}
+#endif
+}
- [] CLIENT.receive(PDU_BSSAP:?) -> value bssap sender vc_conn {
- var integer conn_id := f_conn_id_by_comp(vc_conn);
- /* send it to dispatcher */
- BSSAP.send(ts_BSSAP_DATA_req(conn_id, bssap));
- }
-
- [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);
- /* convert from decoded DTAP to encoded DTAP */
- var octetstring l3_enc := enc_PDU_ML3_MS_NW(dtap_mo.dtap);
- /* patch correct L3 send sequence number N(SD) into l3_enc */
- if (dtap_mo.skip_seq_patching == false) {
- f_ML3_patch_seq(ConnectionTable[idx], dtap_mo.dtap, l3_enc);
- }
- bssap := valueof(ts_BSSAP_DTAP(l3_enc, dtap_mo.dlci));
- BSSAP.send(ts_BSSAP_DATA_req(ConnectionTable[idx].sccp_conn_id, bssap));
- }
-
- [not g_ran_ops.role_ms] CLIENT.receive(PDU_DTAP_MT:?) -> value dtap_mt sender vc_conn {
- var integer conn_id := f_conn_id_by_comp(vc_conn);
- /* convert from decoded DTAP to encoded DTAP */
- var octetstring l3_enc := enc_PDU_ML3_NW_MS(dtap_mt.dtap);
- bssap := valueof(ts_BSSAP_DTAP(l3_enc, dtap_mo.dlci));
- BSSAP.send(ts_BSSAP_DATA_req(conn_id, bssap));
- }
-
+private altstep as_main_mgcp() runs on RAN_Emulation_CT {
+#ifdef RAN_EMULATION_MGCP
+ var MgcpCommand mgcp_req;
+ var MgcpResponse mgcp_resp;
+ var RAN_ConnHdlr vc_conn;
/* Handling of MGCP in IPA SCCPLite case. This predates 3GPP AoIP
* and uses a MGCP session in parallel to BSSAP. BSSAP uses CIC
@@ -710,7 +714,68 @@
vc_conn := f_comp_by_mgcp_tid(mgcp_resp.line.trans_id);
CLIENT.send(mgcp_resp) to vc_conn;
}
+#else
+ [false] CLIENT.receive {}
+#endif
+}
+/* send a raw (encoded) L3 message over given SCCP connection */
+private function f_xmit_raw_l3(integer sccp_conn_id, OCT1 dlci, octetstring l3_enc) runs on RAN_Emulation_CT
+{
+ select (g_ran_ops.protocol) {
+#ifdef RAN_EMULATION_BSSAP
+ case (RAN_PROTOCOL_BSSAP) {
+ var PDU_BSSAP bssap;
+ bssap := valueof(ts_BSSAP_DTAP(l3_enc, dlci));
+ BSSAP.send(ts_BSSAP_DATA_req(sccp_conn_id, bssap));
+ }
+#endif
+ }
+}
+
+function main(RanOps ops, charstring id) runs on RAN_Emulation_CT {
+
+ g_ran_id := id;
+ g_ran_ops := ops;
+ f_conn_table_init();
+ f_expect_table_init();
+
+ 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);
+ }
+
+ while (true) {
+ var RAN_ConnHdlr vc_conn;
+ var PDU_DTAP_MO dtap_mo;
+ var PDU_DTAP_MT dtap_mt;
+ var RAN_ConnHdlr vc_hdlr;
+ var octetstring l3_info;
+ var hexstring imsi;
+ var OCT4 tmsi;
+
+ alt {
+ [g_ran_ops.protocol == RAN_PROTOCOL_BSSAP] as_main_bssap();
+
+ [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);
+ /* convert from decoded DTAP to encoded DTAP */
+ var octetstring l3_enc := enc_PDU_ML3_MS_NW(dtap_mo.dtap);
+ /* patch correct L3 send sequence number N(SD) into l3_enc */
+ if (dtap_mo.skip_seq_patching == false) {
+ f_ML3_patch_seq(ConnectionTable[idx], dtap_mo.dtap, l3_enc);
+ }
+ f_xmit_raw_l3(ConnectionTable[idx].sccp_conn_id, dtap_mo.dlci, l3_enc);
+ }
+
+ [not g_ran_ops.role_ms] CLIENT.receive(PDU_DTAP_MT:?) -> value dtap_mt sender vc_conn {
+ var integer idx := f_idx_by_comp(vc_conn);
+ /* convert from decoded DTAP to encoded DTAP */
+ var octetstring l3_enc := enc_PDU_ML3_NW_MS(dtap_mt.dtap);
+ f_xmit_raw_l3(ConnectionTable[idx].sccp_conn_id, dtap_mt.dlci, l3_enc);
+ }
+
+ [] as_main_mgcp();
[] PROC.getcall(RAN_register:{?,?}) -> param(l3_info, vc_hdlr) {
f_create_expect(l3_info, vc_hdlr);
@@ -727,11 +792,13 @@
}
}
+#ifdef RAN_EMULATION_MGCP
private function f_mgcp_ep_extract_cic(charstring inp) return integer {
var charstring local_part := regexp(inp, "(*)@*", 0);
return hex2int(str2hex(local_part));
}
+#endif
/***********************************************************************
* "Expect" Handling (mapping for expected incoming SCCP connections)