IPA/MSC/BSC Emulation: Framework for handling multiple SCCP connections

This commit is contained in:
Harald Welte 2017-11-23 00:00:43 +01:00
parent c76f29f094
commit 365f4edbd8
4 changed files with 365 additions and 2 deletions

View File

@ -0,0 +1,86 @@
module MSC_ConnectionHandler {
import from General_Types all;
import from Osmocom_Types all;
import from SCCPasp_Types all;
import from BSSAP_Types all;
import from BSSMAP_Emulation all;
import from BSSMAP_Templates all;
/* this component represents a single subscriber connection at the MSC.
* There is a 1:1 mapping between SCCP connections and BSSAP_ConnHdlr components.
* We inherit all component variables, ports, functions, ... from BSSAP_ConnHdlr */
type component MSC_ConnHdlr extends BSSAP_ConnHdlr {
/* E1 timeslot that is allocated to this component/connection */
var uint5_t g_e1_timeslot;
/* SCCP Connecction Identifier for the underlying SCCP connection */
var integer g_sccp_conn_id;
}
/* Callback function from general BSSMAP_Emulation whenever a new incoming
* SCCP connection arrivces. Must create + start a new component */
private function CreateCallback(ASP_SCCP_N_CONNECT_ind conn_ind)
runs on BSSMAP_Emulation_CT return BSSAP_ConnHdlr {
var MSC_ConnHdlr vc_conn;
/* Create a new BSSAP_ConnHdlr component */
vc_conn := MSC_ConnHdlr.create;
/* connect it to the port */
connect(vc_conn:BSSAP, self:CLIENT);
/* start it */
vc_conn.start(MSC_ConnectionHandler.main(conn_ind.connectionId, conn_ind.connectionId));
return vc_conn;
}
/* Callback function from general BSSMAP_Emulation whenever a connectionless
* BSSMAP message arrives. Can retunr a PDU_BSSAP that should be sent in return */
private function UnitdataCallback(PDU_BSSAP bssap)
runs on BSSMAP_Emulation_CT return template PDU_BSSAP {
var template PDU_BSSAP resp := omit;
if (match(bssap, tr_BSSMAP_Reset)) {
resp := ts_BSSMAP_ResetAck;
}
return resp;
}
const BssmapOps MSC_BssmapOps := {
create_cb := refers(CreateCallback),
unitdata_cb := refers(UnitdataCallback)
}
/* main function processing various incoming events */
function main(integer connection_id, integer timeslot) runs on MSC_ConnHdlr {
g_sccp_conn_id := connection_id;
g_e1_timeslot := timeslot
while (true) {
var PDU_BSSAP bssap;
alt {
/* new SCCP-level connection indication from BSC */
[] BSSAP.receive(tr_BSSMAP_ComplL3) -> value bssap {
/* respond with ASSIGNMENT CMD */
BSSAP.send(ts_BSSMAP_AssignmentCmd(0, g_e1_timeslot));
/* FIXME: Send MGCP */
}
/*
[] BSSAP.receive(tr_BSSMAP_AssignmentCompl) {
}
[] BSSAP.receive(tr_BSSMAP_AssignmentFail) {
}
*/
/* TODO: CLEAR REQUEST from BSS */
/* TODO: CLEAR COMPLETE from BSS (response to CLEAR COMMAND) */
[] BSSAP.receive(BSSAP_Conn_Prim:MSC_CONN_PRIM_DISC_IND) {
self.stop;
}
[] BSSAP.receive(PDU_BSSAP:?) -> value bssap {
log("Received unhandled SCCP-CO: ", bssap);
}
}
}
}
}

View File

@ -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));
}
}
}
}
}

View File

@ -44,6 +44,18 @@ template (value) PDU_BSSAP ts_BSSMAP_Reset(BssmapCause cause) modifies ts_BSSAP_
}
}
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 @@ modifies ts_BSSAP_BSSMAP := {
}
}
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 @@ modifies ts_BSSAP_BSSMAP := {
}
}
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
}
}
}
}
}

View File

@ -33,10 +33,10 @@ private function f_enc_IMSI_L3(hexstring digits) return IMSI_L3 {
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;