summaryrefslogtreecommitdiffstats
path: root/bsc-nat/MSC_ConnectionHandler.ttcn
blob: 8635a29697e90e9fcf4925bb0386a16d96fd2dd3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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 BSSAP_CodecPort all;
import from RAN_Emulation all;
import from BSSMAP_Templates all;

import from MGCP_Types all;
import from MGCP_Templates all;
import from SDP_Types all;

/* this component represents a single subscriber connection at the MSC.
 * There is a 1:1 mapping between SCCP connections and RAN_ConnHdlr components.
 * We inherit all component variables, ports, functions, ... from RAN_ConnHdlr */
type component MSC_ConnHdlr extends RAN_ConnHdlr {
	/* SCCP Connecction Identifier for the underlying SCCP connection */
	var integer g_sccp_conn_id;

	var MSC_State g_state := MSC_STATE_NONE;
	var MgcpEndpoint g_ep_name;
	var MgcpCallId g_call_id;
	var MgcpConnectionId g_mgcp_conn_id;
}

/* Callback function from general BSSMAP_Emulation whenever a new incoming
 * SCCP connection arrivces. Must create + start a new component */
private function CreateCallback(BSSAP_N_CONNECT_ind conn_ind, charstring id)
runs on RAN_Emulation_CT return RAN_ConnHdlr {
	var MSC_ConnHdlr vc_conn;
	/* Create a new RAN_ConnHdlr component */
	vc_conn := MSC_ConnHdlr.create(g_ran_id & "-Conn-" & int2str(conn_ind.connectionId));
	/* connect it to the port */
	connect(vc_conn:BSSAP, self:CLIENT);
	/* start it */
	vc_conn.start(MSC_ConnectionHandler.main(conn_ind.connectionId, g_next_e1_ts));
	/* increment next E1 timeslot */
	g_next_e1_ts := g_next_e1_ts + 1;
	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 RAN_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 RanOps MSC_RanOps := {
	create_cb := refers(CreateCallback),
	unitdata_cb := refers(UnitdataCallback),
	decode_dtap := false,
	role_ms := false,
	protocol := RAN_PROTOCOL_BSSAP,
	sccp_addr_local := omit,
	sccp_addr_peer := omit
}

type enumerated MSC_State {
	MSC_STATE_NONE,
	MSC_STATE_WAIT_ASS_COMPL,
	MSC_STATE_WAIT_CRCX_ACK,
	MSC_STATE_WAIT_MDCX_ACK,
	MSC_STATE_WAIT_CLEAR_COMPL,
	MSC_STATE_WAIT_DLCX_ACK
}

/* main function processing various incoming events */
function main(integer connection_id, integer e1_timeslot) runs on MSC_ConnHdlr {
	var MgcpResponse mgcp_rsp;
	timer T := 5.0;

	g_sccp_conn_id := connection_id;
	g_call_id := f_mgcp_alloc_call_id();
	g_ep_name := hex2str(int2hex(e1_timeslot, 1)) & "@mgw";

	/* we just accepted an incoming SCCP connection, start guard timer */
	T.start;

	while (true) {
		var PDU_BSSAP bssap;
		alt {
		/* new SCCP-level connection indication from BSC */
		[g_state == MSC_STATE_NONE] BSSAP.receive(tr_BSSMAP_ComplL3) -> value bssap {
			/* respond with ASSIGNMENT CMD  */
			g_state := MSC_STATE_WAIT_ASS_COMPL;
			BSSAP.send(ts_BSSMAP_AssignmentReq(ts_BSSMAP_IE_CIC(0, e1_timeslot)));
			}
		[g_state == MSC_STATE_WAIT_ASS_COMPL] BSSAP.receive(tr_BSSMAP_AssignmentComplete(?,*)) {
			/* FIXME: Send MGCP CRCX */
			g_state := MSC_STATE_WAIT_CRCX_ACK;
			var MgcpTransId trans_id := f_mgcp_alloc_tid();
			//template SDP_Message sdp := omit;
			BSSAP.send(ts_CRCX(trans_id, g_ep_name, "recvonly", g_call_id)); //, sdp));
			}
		/*
		[] BSSAP.receive(tr_BSSMAP_AssignmentFail) {
		}
		*/

		/* receive CRCX ACK: transmit MDCX */
		[g_state == MSC_STATE_WAIT_CRCX_ACK] BSSAP.receive(tr_CRCX_ACK) -> value mgcp_rsp {
			/* extract connection ID */
			g_mgcp_conn_id := f_MgcpResp_extract_conn_id(mgcp_rsp);
			g_state := MSC_STATE_WAIT_MDCX_ACK;
			var MgcpTransId trans_id := f_mgcp_alloc_tid();
			BSSAP.send(ts_MDCX(trans_id, g_ep_name, "sendrecv", g_call_id, g_mgcp_conn_id));
			}

		/* receive MDCX ACK: wait + transmit CLEAR COMMAND */
		[g_state == MSC_STATE_WAIT_MDCX_ACK] BSSAP.receive(tr_CRCX_ACK) -> value mgcp_rsp {
			g_state := MSC_STATE_WAIT_CLEAR_COMPL
			BSSAP.send(ts_BSSMAP_ClearCommand(9)); /* Cause: call control */
			}

		/* CLEAR COMPLETE from BSS (response to CLEAR COMMAND) */
		[g_state == MSC_STATE_WAIT_CLEAR_COMPL] BSSAP.receive(tr_BSSMAP_ClearComplete) {
			/* send DLCX */
			g_state := MSC_STATE_WAIT_DLCX_ACK;
			var MgcpTransId trans_id := f_mgcp_alloc_tid();
			BSSAP.send(ts_DLCX(trans_id, g_ep_name, g_call_id));
			}

		[g_state == MSC_STATE_WAIT_DLCX_ACK] BSSAP.receive(tr_DLCX_ACK) {
			BSSAP.send(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_REQ);
			setverdict(pass);
			self.stop;
			}

		/* TODO: CLEAR REQUEST from BSS */

		[] BSSAP.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_IND) {
			setverdict(fail);
			self.stop;
			}

		[] BSSAP.receive(PDU_BSSAP:?) -> value bssap {
			log("Received unhandled SCCP-CO: ", bssap);
			}

		/* Guard timer has expired, close connection */
		[] T.timeout {
			BSSAP.send(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_REQ);
			setverdict(fail, "Timeout of guard timer");
			self.stop;
			}

		}
	}
}

}