[MERGED] osmo-ttcn3-hacks[master]: MNCC_Emulation: Similar to BSSMAP_Emulation but for MNCC

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Harald Welte gerrit-no-reply at lists.osmocom.org
Sun Jan 21 18:34:24 UTC 2018


Harald Welte has submitted this change and it was merged.

Change subject: MNCC_Emulation: Similar to BSSMAP_Emulation but for MNCC
......................................................................


MNCC_Emulation: Similar to BSSMAP_Emulation but for MNCC

Dispatches MNCC to individual ConnHldr's based on IMSI.

Change-Id: I850b49ce6a6c894b413b8905008452ce91d2cdb0
---
A library/MNCC_Emulation.ttcn
1 file changed, 384 insertions(+), 0 deletions(-)

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



diff --git a/library/MNCC_Emulation.ttcn b/library/MNCC_Emulation.ttcn
new file mode 100644
index 0000000..4bf516d
--- /dev/null
+++ b/library/MNCC_Emulation.ttcn
@@ -0,0 +1,384 @@
+module MNCC_Emulation {
+
+/* MNCC Emulation, runs on top of MNCC_CodecPort.  It multiplexes/demultiplexes
+ * the individual calls, so there can be separate TTCN-3 components handling
+ * each of the calls
+ *
+ * The MNCC_Emulation.main() function processes MNCC primitives from the MNCC
+ * socket via the MNCC_CodecPort, and dispatches them to the per-connection components.
+ *
+ * Outbound MNCC connections are initiated by sending a MNCC_Call_Req primitive
+ * to the component running the MNCC_Emulation.main() function.
+ *
+ * For each new inbound connections, the MnccOps.create_cb() is called.  It can create
+ * or resolve a TTCN-3 component, and returns a component reference to which that inbound
+ * connection is routed/dispatched.
+ *
+ * If a pre-existing component wants to register to handle a future inbound call, it can
+ * do so by registering an "expect" with the expected destination phone number.  This is e.g. useful
+ * if you are simulating BSC + MNCC, and first trigger a connection from BSC side in a
+ * component which then subsequently should also handle the MNCC emulation.
+ *
+ * Inbound Unit Data messages (such as are dispatched to the MnccOps.unitdata_cb() callback,
+ * which is registered with an argument to the main() function below.
+ *
+ * (C) 2018 by Harald Welte <laforge at gnumonks.org>
+ * All rights reserved.
+ *
+ * Released under the terms of GNU General Public License, Version 2 or
+ * (at your option) any later version.
+ */
+
+
+import from Osmocom_Types all;
+import from MNCC_CodecPort all;
+import from MNCC_Types all;
+import from UD_Types all;
+
+/* General "base class" component definition, of which specific implementations
+ * derive themselves by means of the "extends" feature */
+type component MNCC_ConnHdlr {
+	/* ports towards MNCC Emulator core / call dispatchar */
+	port MNCC_Conn_PT MNCC;
+	port MNCCEM_PROC_PT MNCC_PROC;
+}
+
+/* Auxiliary primitive that can happen on the port between per-connection client and this dispatcher */
+type enumerated MNCC_Conn_Prim {
+	/* MNCC tell us that connection was released */
+	MNCC_CONN_PRIM_DISC_IND,
+	/* we tell MNCC to release connection */
+	MNCC_CONN_PRIM_DISC_REQ
+}
+
+type record MNCC_Conn_Req {
+	MNCC_PDU		mncc
+}
+
+/* port between individual per-connection components and this dispatcher */
+type port MNCC_Conn_PT message {
+	inout MNCC_PDU, MNCC_Conn_Prim, MNCC_Conn_Req;
+} with { extension "internal" };
+
+
+/* represents a single MNCC call */
+type record ConnectionData {
+	/* reference to the instance of the per-connection component */
+	MNCC_ConnHdlr	comp_ref,
+	integer		mncc_call_id
+}
+
+type component MNCC_Emulation_CT {
+	/* UNIX DOMAIN socket on the bottom side, using primitives */
+	port MNCC_CODEC_PT MNCC;
+	/* MNCC port to the per-connection clients */
+	port MNCC_Conn_PT MNCC_CLIENT;
+
+	/* use 16 as this is also the number of SCCP connections that SCCP_Emulation can handle */
+	var ConnectionData MnccCallTable[16];
+
+	/* pending expected incoming connections */
+	var ExpectData MnccExpectTable[8];
+	/* procedure based port to register for incoming connections */
+	port MNCCEM_PROC_PT MNCC_PROC;
+
+	var integer g_mncc_ud_id;
+};
+
+private function f_call_id_known(uint32_t mncc_call_id)
+runs on MNCC_Emulation_CT return boolean {
+	var integer i;
+	for (i := 0; i < sizeof(MnccCallTable); i := i+1) {
+		if (MnccCallTable[i].mncc_call_id == mncc_call_id){
+			return true;
+		}
+	}
+	return false;
+}
+
+private function f_comp_known(MNCC_ConnHdlr client)
+runs on MNCC_Emulation_CT return boolean {
+	var integer i;
+	for (i := 0; i < sizeof(MnccCallTable); i := i+1) {
+		if (MnccCallTable[i].comp_ref == client) {
+			return true;
+		}
+	}
+	return false;
+}
+
+/* resolve component reference by connection ID */
+private function f_comp_by_call_id(uint32_t mncc_call_id)
+runs on MNCC_Emulation_CT return MNCC_ConnHdlr {
+	var integer i;
+	for (i := 0; i < sizeof(MnccCallTable); i := i+1) {
+		if (MnccCallTable[i].mncc_call_id == mncc_call_id) {
+			return MnccCallTable[i].comp_ref;
+		}
+	}
+	log("MNCC Call table not found by MNCC Call ID ", mncc_call_id);
+	setverdict(fail);
+	self.stop;
+}
+
+/* resolve connection ID by component reference */
+private function f_call_id_by_comp(MNCC_ConnHdlr client)
+runs on MNCC_Emulation_CT return integer {
+	for (var integer i := 0; i < sizeof(MnccCallTable); i := i+1) {
+		if (MnccCallTable[i].comp_ref == client) {
+			return MnccCallTable[i].mncc_call_id;
+		}
+	}
+	log("MNCC Call table not found by component ", client);
+	setverdict(fail);
+	self.stop;
+}
+
+private function f_gen_call_id()
+runs on MNCC_Emulation_CT return integer {
+	var uint32_t call_id;
+
+	do {
+		call_id := float2int(rnd()*4294967296.0);
+	} while (f_call_id_known(call_id) == true);
+
+	return call_id;
+}
+
+private function f_call_table_init()
+runs on MNCC_Emulation_CT {
+	for (var integer i := 0; i < sizeof(MnccCallTable); i := i+1) {
+		MnccCallTable[i].comp_ref := null;
+		MnccCallTable[i].mncc_call_id := -1;
+	}
+}
+
+private function f_call_table_add(MNCC_ConnHdlr comp_ref, uint32_t mncc_call_id)
+runs on MNCC_Emulation_CT {
+	for (var integer i := 0; i < sizeof(MnccCallTable); i := i+1) {
+		if (MnccCallTable[i].mncc_call_id == -1) {
+			MnccCallTable[i].comp_ref := comp_ref;
+			MnccCallTable[i].mncc_call_id := mncc_call_id;
+			log("Added conn table entry ", i, comp_ref, mncc_call_id);
+			return;
+		}
+	}
+	log("MNCC Call table full!");
+	setverdict(fail);
+	self.stop;
+}
+
+private function f_call_table_del(uint32_t mncc_call_id)
+runs on MNCC_Emulation_CT {
+	for (var integer i := 0; i < sizeof(MnccCallTable); i := i+1) {
+		if (MnccCallTable[i].mncc_call_id == mncc_call_id) {
+			log("Deleted conn table entry ", i,
+			    MnccCallTable[i].comp_ref, mncc_call_id);
+			MnccCallTable[i].mncc_call_id := -1;
+			MnccCallTable[i].comp_ref := null;
+			return
+		}
+	}
+	log("MNCC Call table attempt to delete non-existant ", mncc_call_id);
+	setverdict(fail);
+	self.stop;
+}
+
+
+function f_connect(charstring sock) runs on MNCC_Emulation_CT {
+	var UD_connect_result res;
+	timer T := 5.0;
+
+	T.start;
+	MNCC.send(UD_connect:{sock, -1});
+	alt {
+	[] MNCC.receive(UD_connect_result:?) -> value res {
+		if (ispresent(res.result) and ispresent(res.result.result_code) and res.result.result_code == ERROR) {
+			setverdict(fail, "Error connecting to MNCC socket", res);
+			self.stop;
+		} else {
+			g_mncc_ud_id := res.id;
+		}
+		}
+	[] T.timeout {
+		setverdict(fail, "Timeout connecting to MNCC socket");
+		self.stop;
+		}
+	}
+}
+
+/* call-back type, to be provided by specific implementation; called when new SCCP connection
+ * arrives */
+type function MnccCreateCallback(MNCC_PDU conn_ind, charstring id)
+runs on MNCC_Emulation_CT return MNCC_ConnHdlr;
+
+type function MnccUnitdataCallback(MNCC_PDU mncc)
+runs on MNCC_Emulation_CT return template MNCC_PDU;
+
+type record MnccOps {
+	MnccCreateCallback create_cb,
+	MnccUnitdataCallback unitdata_cb
+}
+
+function main(MnccOps ops, charstring id, charstring sock) runs on MNCC_Emulation_CT {
+
+	f_connect(sock);
+	f_call_table_init();
+
+	while (true) {
+		var MNCC_send_data sd;
+		var MNCC_Conn_Req creq;
+		var MNCC_ConnHdlr vc_conn;
+		var MNCC_PDU mncc;
+		var MNCC_ConnHdlr vc_hdlr;
+		var charstring dest_nr;
+
+		alt {
+		/* MNCC -> Client: UNIT-DATA (connectionless SCCP) from a BSC */
+		[] MNCC.receive(t_SD_MNCC_MSGT(g_mncc_ud_id, MNCC_SOCKET_HELLO)) -> value sd {
+			/* Connectionless Procedures like HELLO */
+			var template MNCC_PDU resp;
+			resp := ops.unitdata_cb.apply(sd.data);
+			if (isvalue(resp)) {
+				MNCC.send(t_SD_MNCC(g_mncc_ud_id, resp));
+			}
+			}
+
+		/* MNCC -> Client: Release Indication / confirmation */
+		[] MNCC.receive(t_SD_MNCC_MSGT(g_mncc_ud_id, (MNCC_REL_IND, MNCC_REL_CNF))) -> value sd {
+			var uint32_t call_id := f_mncc_get_call_id(sd.data);
+			/* forward to respective client */
+			vc_conn := f_comp_by_call_id(call_id);
+			MNCC_CLIENT.send(sd.data) to vc_conn;
+			/* remove from call table */
+			f_call_table_del(call_id);
+			}
+
+		/* MNCC -> Client: call related messages */
+		[] MNCC.receive(t_SD_MNCC_MSGT(g_mncc_ud_id, ?)) -> value sd {
+			var uint32_t call_id := f_mncc_get_call_id(sd.data);
+
+			if (f_call_id_known(call_id)) {
+				vc_conn := f_comp_by_call_id(call_id);
+				MNCC_CLIENT.send(sd.data) to vc_conn;
+			} else {
+				/* TODO: Only accept this for SETUP.req? */
+				vc_conn := ops.create_cb.apply(sd.data, id)
+				/* store mapping between client components and SCCP connectionId */
+				f_call_table_add(vc_conn, call_id);
+				/* handle user payload */
+				MNCC_CLIENT.send(sd.data) to vc_conn;
+			}
+			}
+
+		/* Client -> MNCC Socket: RELEASE.ind or RELEASE.cnf: forward + drop call table entry */
+		[] MNCC_CLIENT.receive(MNCC_PDU:{msg_type := (MNCC_REL_IND, MNCC_REL_CNF), u:=?}) -> value mncc sender vc_conn {
+			var integer call_id := f_call_id_by_comp(vc_conn);
+			/* forward to MNCC socket */
+			MNCC.send(t_SD_MNCC(g_mncc_ud_id, mncc));
+			/* remove from call table */
+			f_call_table_del(call_id);
+			}
+
+		/* Client -> MNCC Socket: Normal message */
+		[] MNCC_CLIENT.receive(MNCC_PDU:?) -> value mncc sender vc_conn {
+			/* forward to MNCC socket */
+			MNCC.send(t_SD_MNCC(g_mncc_ud_id, mncc));
+			}
+
+
+		/* Client -> us: procedure call to register expect */
+		[] MNCC_PROC.getcall(MNCCEM_register:{?,?}) -> param(dest_nr, vc_hdlr) {
+			f_create_expect(dest_nr, vc_hdlr);
+			MNCC_PROC.reply(MNCCEM_register:{dest_nr, vc_hdlr});
+			}
+
+		}
+	}
+}
+
+private function f_mgcp_ep_extract_cic(charstring inp) return integer {
+	var charstring local_part := regexp(inp, "(*)@*", 0);
+	return hex2int(str2hex(local_part));
+
+}
+
+/***********************************************************************
+ * "Expect" Handling (mapping for expected incoming MNCC calls from IUT)
+ ***********************************************************************/
+
+/* data about an expected future incoming connection */
+type record ExpectData {
+	/* destination number based on which we can match it */
+	charstring dest_number optional,
+	/* component reference for this connection */
+	MNCC_ConnHdlr vc_conn
+}
+
+/* procedure based port to register for incoming calls */
+signature MNCCEM_register(in charstring dest_nr, in MNCC_ConnHdlr hdlr);
+
+type port MNCCEM_PROC_PT procedure {
+	inout MNCCEM_register;
+} with { extension "internal" };
+
+/* CreateCallback that can be used as create_cb and will use the expectation table */
+function ExpectedCreateCallback(MNCC_PDU conn_ind, charstring id)
+runs on MNCC_Emulation_CT return MNCC_ConnHdlr {
+	var MNCC_ConnHdlr ret := null;
+	var charstring dest_number;
+	var integer i;
+
+	if (not ischosen(conn_ind.u.signal) or conn_ind.msg_type != MNCC_SETUP_IND) {
+		setverdict(fail, "MNCC ExpectedCreateCallback needs MNCC_SETUP_IND");
+		return ret;
+	}
+	dest_number := conn_ind.u.signal.called.number;
+
+	for (i := 0; i < sizeof(MnccExpectTable); i:= i+1) {
+		if (not ispresent(MnccExpectTable[i].dest_number)) {
+			continue;
+		}
+		if (dest_number == MnccExpectTable[i].dest_number) {
+			ret := MnccExpectTable[i].vc_conn;
+			/* release this entry to be used again */
+			MnccExpectTable[i].dest_number := omit;
+			MnccExpectTable[i].vc_conn := null;
+			log("Found MnccExpect[", i, "] for ", dest_number, " handled at ", ret);
+			/* return the component reference */
+			return ret;
+		}
+	}
+	setverdict(fail, "Couldn't find MnccExpect for incoming call ", dest_number);
+	return ret;
+}
+
+/* server/emulation side function to create expect */
+private function f_create_expect(charstring dest_number, MNCC_ConnHdlr hdlr)
+runs on MNCC_Emulation_CT {
+	var integer i;
+	for (i := 0; i < sizeof(MnccExpectTable); i := i+1) {
+		if (not ispresent(MnccExpectTable[i].dest_number)) {
+			MnccExpectTable[i].dest_number := dest_number;
+			MnccExpectTable[i].vc_conn := hdlr;
+			log("Created MnccExpect[", i, "] for ", dest_number, " to be handled at ", hdlr);
+			return;
+		}
+	}
+	setverdict(fail, "No space left in MnccMnccExpectTable");
+}
+
+/* client/conn_hdlr side function to use procedure port to create expect in emulation */
+function f_create_mncc_expect(charstring dest_number) runs on MNCC_ConnHdlr {
+	MNCC_PROC.call(MNCCEM_register:{dest_number, self}) {
+		[] MNCC_PROC.getreply(MNCCEM_register:{?,?}) {};
+	}
+}
+
+function DummyUnitdataCallback(MNCC_PDU mncc)
+runs on MNCC_Emulation_CT return template MNCC_PDU {
+	log("Ignoring MNCC ", mncc);
+	return omit;
+}
+
+}

-- 
To view, visit https://gerrit.osmocom.org/5960
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I850b49ce6a6c894b413b8905008452ce91d2cdb0
Gerrit-PatchSet: 1
Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-Owner: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder



More information about the gerrit-log mailing list