IPA/MSC/BSC Emulation: Framework for handling multiple SCCP connections
diff --git a/library/BSSMAP_Emulation.ttcn b/library/BSSMAP_Emulation.ttcn
new file mode 100644
index 0000000..bcc0e11
--- /dev/null
+++ b/library/BSSMAP_Emulation.ttcn
@@ -0,0 +1,206 @@
+module BSSMAP_Emulation {
+
+import from SCCPasp_Types all;
+import from BSSAP_Types all;
+import from BSSMAP_Templates all;
+//import from MSC_ConnectionHandler all;
+
+/* General "base class" component definition, of which specific implementations
+ * derive themselves by means of the "extends" feature */
+type component BSSAP_ConnHdlr {
+ /* port towards MSC Emulator core / SCCP connection dispatchar */
+ port BSSAP_Conn_PT BSSAP;
+}
+
+/* Auxiliary primitive that can happen on the port between per-connection client and this dispatcher */
+type enumerated BSSAP_Conn_Prim {
+ /* SCCP tell us that connection was released */
+ MSC_CONN_PRIM_DISC_IND,
+ /* we tell SCCP to release connection */
+ MSC_CONN_PRIM_DISC_REQ
+}
+
+/* port between individual per-connection components and this dispatcher */
+type port BSSAP_Conn_PT message {
+ inout PDU_BSSAP;
+ inout BSSAP_Conn_Prim;
+} with { extension "internal" };
+
+
+/* represents a single BSSAP connection over SCCP */
+type record ConnectionData {
+ /* reference to the instance of the per-connection component */
+ BSSAP_ConnHdlr comp_ref,
+ integer sccp_conn_id
+}
+
+type component BSSMAP_Emulation_CT {
+ /* SCCP port on the bottom side, using ASP primitives */
+ port SCCPasp_PT SCCP;
+ /* BSSAP port to the per-connection clients */
+ port BSSAP_Conn_PT CLIENT;
+
+ /* use 16 as this is also the number of SCCP connections that SCCP_Emulation can handle */
+ var ConnectionData ConnectionTable[16];
+};
+
+
+/* resolve component reference by connection ID */
+private function f_comp_by_conn_id(integer sccp_conn_id)
+runs on BSSMAP_Emulation_CT return BSSAP_ConnHdlr {
+ var integer i;
+ for (i := 0; i < sizeof(ConnectionTable); i := i+1) {
+ if (ConnectionTable[i].sccp_conn_id == sccp_conn_id) {
+ return ConnectionTable[i].comp_ref;
+ }
+ }
+ log("BSSMAP Connection table not found by SCCP Connection ID ", sccp_conn_id);
+ self.stop;
+}
+
+/* resolve connection ID by component reference */
+private function f_conn_id_by_comp(BSSAP_ConnHdlr client)
+runs on BSSMAP_Emulation_CT return integer {
+ for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
+ if (ConnectionTable[i].comp_ref == client) {
+ return ConnectionTable[i].sccp_conn_id;
+ }
+ }
+ log("BSSMAP Connection table not found by component ", client);
+ self.stop;
+}
+
+private function f_conn_table_init()
+runs on BSSMAP_Emulation_CT {
+ for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
+ ConnectionTable[i].comp_ref := null;
+ ConnectionTable[i].sccp_conn_id := -1;
+ }
+}
+
+private function f_conn_table_add(BSSAP_ConnHdlr comp_ref, integer sccp_conn_id)
+runs on BSSMAP_Emulation_CT {
+ for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
+ if (ConnectionTable[i].sccp_conn_id == -1) {
+ ConnectionTable[i].comp_ref := comp_ref;
+ ConnectionTable[i].sccp_conn_id := sccp_conn_id;
+ return;
+ }
+ }
+ log("BSSMAP Connection table full!");
+ self.stop;
+}
+
+private function f_conn_table_del(integer sccp_conn_id)
+runs on BSSMAP_Emulation_CT {
+ for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) {
+ if (ConnectionTable[i].sccp_conn_id == sccp_conn_id) {
+ ConnectionTable[i].sccp_conn_id := -1;
+ ConnectionTable[i].comp_ref := null;
+ }
+ }
+ log("BSSMAP Connection table attempt to delete non-existant ", sccp_conn_id);
+ self.stop;
+}
+
+/* handle (optional) userData portion of various primitives and dispatch it to the client */
+private function f_handle_userData(BSSAP_ConnHdlr client, template octetstring userdata)
+runs on BSSMAP_Emulation_CT {
+ if (not isvalue(userdata)) {
+ return;
+ }
+
+ /* decode + send decoded BSSAP to client */
+ var PDU_BSSAP bssap := dec_PDU_BSSAP(valueof(userdata));
+ CLIENT.send(bssap) to client;
+}
+
+/* call-back type, to be provided by specific implementation; called when new SCCP connection
+ * arrives */
+type function BssmapCreateCallback(ASP_SCCP_N_CONNECT_ind conn_ind)
+runs on BSSMAP_Emulation_CT return BSSAP_ConnHdlr;
+
+type function BssmapUnitdataCallback(PDU_BSSAP bssap)
+runs on BSSMAP_Emulation_CT return template PDU_BSSAP;
+
+type record BssmapOps {
+ BssmapCreateCallback create_cb,
+ BssmapUnitdataCallback unitdata_cb
+}
+
+function main(BssmapOps ops) runs on BSSMAP_Emulation_CT {
+
+ f_conn_table_init();
+
+ while (true) {
+ var ASP_SCCP_N_UNITDATA_ind ud_ind;
+ var ASP_SCCP_N_CONNECT_ind conn_ind;
+ var ASP_SCCP_N_DATA_ind data_ind;
+ var ASP_SCCP_N_DISCONNECT_ind disc_ind;
+ var BSSAP_ConnHdlr vc_conn;
+ var PDU_BSSAP bssap;
+
+ alt {
+ /* SCCP -> Client: UNIT-DATA (connectionless SCCP) from a BSC */
+ [] SCCP.receive(ASP_SCCP_N_UNITDATA_ind:?) -> value ud_ind {
+ /* Connectionless Procedures like RESET */
+ var template PDU_BSSAP resp;
+ bssap := dec_PDU_BSSAP(ud_ind.userData);
+ resp := ops.unitdata_cb.apply(bssap);
+ if (isvalue(resp)) {
+ var octetstring resp_ud := enc_PDU_BSSAP(valueof(resp));
+ SCCP.send(t_ASP_N_UNITDATA_req(ud_ind.callingAddress,
+ ud_ind.calledAddress, omit,
+ omit, resp_ud, omit));
+ }
+ }
+
+ /* SCCP -> Client: new connection from BSC */
+ [] SCCP.receive(ASP_SCCP_N_CONNECT_ind:?) -> value conn_ind {
+ vc_conn := ops.create_cb.apply(conn_ind);
+ /* store mapping between client components and SCCP connectionId */
+ f_conn_table_add(vc_conn, conn_ind.connectionId);
+ /* handle user payload */
+ f_handle_userData(vc_conn, conn_ind.userData);
+ /* confirm connection establishment */
+ SCCP.send(t_ASP_N_CONNECT_res(omit, omit, omit, omit, conn_ind.connectionId, omit));
+ }
+
+ /* SCCP -> Client: connection-oriented data in existing connection */
+ [] SCCP.receive(ASP_SCCP_N_DATA_ind:?) -> value data_ind {
+ vc_conn := f_comp_by_conn_id(data_ind.connectionId);
+ f_handle_userData(vc_conn, conn_ind.userData);
+ }
+
+ /* SCCP -> Client: disconnect of an existing connection */
+ [] SCCP.receive(ASP_SCCP_N_DISCONNECT_ind:?) -> value disc_ind {
+ vc_conn := f_comp_by_conn_id(disc_ind.connectionId);
+ f_handle_userData(vc_conn, disc_ind.userData);
+ /* notify client about termination */
+ var BSSAP_Conn_Prim prim := MSC_CONN_PRIM_DISC_IND;
+ CLIENT.send(prim) to vc_conn;
+ f_conn_table_del(disc_ind.connectionId);
+ /* TOOD: return confirm to other side? */
+ }
+
+ /* Disconnect request client -> SCCP */
+ [] CLIENT.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_REQ) -> sender vc_conn {
+ var integer conn_id := f_conn_id_by_comp(vc_conn);
+ SCCP.send(t_ASP_N_DISCONNECT_req(omit, 0, omit, conn_id, omit));
+ f_conn_table_del(conn_id);
+ }
+
+ /* BSSAP from client -> SCCP */
+ [] CLIENT.receive(PDU_BSSAP:?) -> value bssap sender vc_conn {
+ var integer conn_id := f_conn_id_by_comp(vc_conn);
+ /* encode + send to dispatcher */
+ var octetstring userdata := enc_PDU_BSSAP(bssap);
+ SCCP.send(t_ASP_N_DATA_req(userdata, conn_id, omit));
+ }
+
+ }
+ }
+}
+
+
+}
diff --git a/library/BSSMAP_Templates.ttcn b/library/BSSMAP_Templates.ttcn
index aea1e01..b030072 100644
--- a/library/BSSMAP_Templates.ttcn
+++ b/library/BSSMAP_Templates.ttcn
@@ -44,6 +44,18 @@
}
}
+template PDU_BSSAP tr_BSSMAP_Reset modifies tr_BSSAP_BSSMAP := {
+ pdu := {
+ bssmap := {
+ reset := {
+ messageType := '30'O,
+ cause := ?,
+ a_InterfaceSelectorForReset := *
+ }
+ }
+ }
+}
+
template (value) PDU_BSSAP ts_BSSMAP_ResetAck modifies ts_BSSAP_BSSMAP := {
pdu := {
bssmap := {
@@ -141,6 +153,15 @@
}
}
+template PDU_BSSAP tr_BSSMAP_ComplL3 modifies tr_BSSAP_BSSMAP := {
+ pdu := {
+ bssmap := {
+ completeLayer3Information := ?
+ }
+ }
+}
+
+
template PDU_BSSAP ts_BSSMAP_HandoReq(BssmapCause cause, BSSMAP_IE_CellIdentifierList cid_list)
modifies ts_BSSAP_BSSMAP := {
pdu := {
@@ -166,5 +187,55 @@
}
}
+const OCT1 ChRate_TCHF := '08'O;
+const OCT1 ChRate_TCHH := '09'O;
+
+template (value) BSSMAP_IE_ChannelType ts_BSSMAP_IE_ChannelType := {
+ elementIdentifier := '0B'O, /* overwritten */
+ lengthIndicator := 0, /* overwritten */
+ speechOrDataIndicator := '0001'B, /* speech */
+ spare1_4 := '0000'B,
+ channelRateAndType := ChRate_TCHF,
+ speechId_DataIndicator := '01'O /* FRv1 */
+}
+
+template (value) BSSMAP_IE_CircuitIdentityCode ts_BSSMAP_IE_CIC(uint11_t span, uint5_t ts) := {
+ elementIdentifier := '01'O, /* overwritten */
+ cicHigh := bit2oct(substr(int2bit(span, 11) << 5, 0, 8)),
+ cicLow := bit2oct((substr(int2bit(span, 11), 8, 3) << 5) & int2bit(ts, 5))
+}
+
+template (value) PDU_BSSAP ts_BSSMAP_AssignmentCmd(uint11_t span, uint5_t ts)
+modifies ts_BSSAP_BSSMAP := {
+ pdu := {
+ bssmap := {
+ assignmentRequest := {
+ messageType :='01'O, /* overwritten */
+ channelType := ts_BSSMAP_IE_ChannelType,
+ layer3HeaderInfo := omit,
+ priority := omit,
+ circuitIdentityCode := ts_BSSMAP_IE_CIC(span, ts),
+ downLinkDTX_Flag := omit,
+ interferenceBandToBeUsed := omit,
+ classmarkInformationType2 := omit,
+ groupCallReference := omit,
+ talkerFlag := omit,
+ configurationEvolutionIndication := omit,
+ lsaAccesControlSuppression := omit,
+ serviceHandover := omit,
+ encryptionInformation := omit,
+ talkerPriority := omit,
+ aoIPTransportLayer := omit,
+ codecList := omit,
+ callIdentifier := omit,
+ kC128 := omit,
+ globalCallReference := omit,
+ lCLS_Configuration := omit,
+ lCLS_ConnectionStatusControl := omit,
+ lCLS_CorrelationNotNeeded := omit
+ }
+ }
+ }
+}
}
diff --git a/library/L3_Templates.ttcn b/library/L3_Templates.ttcn
index 8f60b5c..4c182f8 100644
--- a/library/L3_Templates.ttcn
+++ b/library/L3_Templates.ttcn
@@ -33,10 +33,10 @@
var IMSI_L3 l3;
var integer len := lengthof(digits);
if (len rem 2 == 1) { /* modulo remainder */
- l3.oddevenIndicator := '0'B;
+ l3.oddevenIndicator := '1'B;
l3.fillerDigit := '1111'B;
} else {
- l3.oddevenIndicator := '1'B;
+ l3.oddevenIndicator := '0'B;
l3.fillerDigit := omit;
}
l3.digits := digits;