blob: 1f23b66cdc0ee07ffd5055da17ab3016df26879a [file] [log] [blame]
Harald Welte35498112019-07-02 14:26:39 +08001module S1AP_Emulation {
2
3/* S1AP Emulation, runs on top of S1AP_CodecPort. It multiplexes/demultiplexes
4 * the individual IMSIs/subscribers, so there can be separate TTCN-3 components handling
5 * each of them.
6 *
7 * The S1AP_Emulation.main() function processes S1AP primitives from the S1AP
8 * socket via the S1AP_CodecPort, and dispatches them to the per-IMSI components.
9 *
10 * For each new IMSI, the S1apOps.create_cb() is called. It can create
11 * or resolve a TTCN-3 component, and returns a component reference to which that IMSI
12 * is routed/dispatched.
13 *
14 * If a pre-existing component wants to register to handle a future inbound IMSI, it can
15 * do so by registering an "expect" with the expected IMSI.
16 *
17 * Inbound non-UE related S1AP messages (such as RESET, SETUP, OVERLOAD) are dispatched to
18 * the S1apOps.unitdata_cb() callback, which is registered with an argument to the
19 * main() function below.
20 *
21 * (C) 2019 by Harald Welte <laforge@gnumonks.org>
22 * All rights reserved.
23 *
24 * Released under the terms of GNU General Public License, Version 2 or
25 * (at your option) any later version.
26 *
27 * SPDX-License-Identifier: GPL-2.0-or-later
28 */
29
30import from S1AP_CodecPort all;
31import from S1AP_CodecPort_CtrlFunct all;
32import from S1AP_Types all;
33import from S1AP_Constants all;
34import from S1AP_PDU_Contents all;
35import from S1AP_PDU_Descriptions all;
36import from S1AP_IEs all;
37import from S1AP_Templates all;
38
39import from NAS_EPS_Types all;
40import from NAS_Templates all;
41
42import from LTE_CryptoFunctions all;
43
44import from General_Types all;
45import from Osmocom_Types all;
46import from IPL4asp_Types all;
47import from DNS_Helpers all;
48
49
50type component S1AP_ConnHdlr {
51 port S1AP_Conn_PT S1AP;
52 /* procedure based port to register for incoming connections */
53 port S1APEM_PROC_PT S1AP_PROC;
54}
55
56/* port between individual per-connection components and this dispatcher */
57type port S1AP_Conn_PT message {
58 inout S1AP_PDU, PDU_NAS_EPS, S1APEM_Config;
59} with { extension "internal" };
60
61type record NAS_Keys {
62 octetstring k_nas_int,
63 octetstring k_nas_enc
64};
65type union S1APEM_Config {
66 NAS_Keys set_nas_keys
67};
68
69type enumerated S1APEM_EventUpDown {
70 S1APEM_EVENT_DOWN,
71 S1APEM_EVENT_UP
72}
73
74/* an event indicating us whether or not a connection is physically up or down,
75 * and whether we have received an ID_ACK */
76type union S1APEM_Event {
77 S1APEM_EventUpDown up_down
78}
79
80/* global test port e.g. for non-imsi/conn specific messages */
81type port S1AP_PT message {
82 inout S1AP_PDU, S1APEM_Event;
83} with { extension "internal" };
84
85
86/* represents a single S1AP Association */
87type record AssociationData {
88 S1AP_ConnHdlr comp_ref, /* component handling this UE connection */
89 uint24_t enb_ue_s1ap_id optional, /* eNB side S1AP ID */
90 uint32_t mme_ue_s1ap_id optional, /* MME side S1AP ID */
91 EUTRAN_CGI cgi optional,
92 TAI tai optional,
93 NAS_UE_State nus
94
95 //hexstring imsi optional
96};
97
98type component S1AP_Emulation_CT {
99 /* Port facing to the UDP SUT */
100 port S1AP_CODEC_PT S1AP;
101 /* All S1AP_ConnHdlr S1AP ports connect here
102 * S1AP_Emulation_CT.main needs to figure out what messages
103 * to send where with CLIENT.send() to vc_conn */
104 port S1AP_Conn_PT S1AP_CLIENT;
105 /* currently tracked connections */
106 var AssociationData S1apAssociationTable[16];
107 /* pending expected CRCX */
108 var ExpectData S1apExpectTable[8];
109 /* procedure based port to register for incoming connections */
110 port S1APEM_PROC_PT S1AP_PROC;
111 /* test port for unit data messages */
112 port S1AP_PT S1AP_UNIT;
113
114 var S1AP_conn_parameters g_pars;
115 var charstring g_s1ap_id;
116 var integer g_s1ap_conn_id := -1;
117}
118
119type function S1APCreateCallback(S1AP_PDU msg, template (omit) MME_UE_S1AP_ID mme_id,
120 template (omit) ENB_UE_S1AP_ID enb_id, charstring id)
121runs on S1AP_Emulation_CT return S1AP_ConnHdlr;
122
123type function S1APUnitdataCallback(S1AP_PDU msg)
124runs on S1AP_Emulation_CT return template S1AP_PDU;
125
126type record S1APOps {
127 S1APCreateCallback create_cb,
128 S1APUnitdataCallback unitdata_cb
129}
130
131type record S1AP_conn_parameters {
132 HostName remote_ip,
133 PortNumber remote_sctp_port,
134 HostName local_ip,
135 PortNumber local_sctp_port,
136 NAS_Role role
137}
138
139function tr_S1AP_RecvFrom_R(template S1AP_PDU msg)
140runs on S1AP_Emulation_CT return template S1AP_RecvFrom {
141 var template S1AP_RecvFrom mrf := {
142 connId := g_s1ap_conn_id,
143 remName := ?,
144 remPort := ?,
145 locName := ?,
146 locPort := ?,
147 msg := msg
148 }
149 return mrf;
150}
151
152private function f_s1ap_ids_known(template (omit) MME_UE_S1AP_ID mme_id,
153 template (omit) ENB_UE_S1AP_ID enb_id)
154runs on S1AP_Emulation_CT return boolean {
155 var integer i;
156 log("f_s1ap_ids_known(",mme_id,", ",enb_id,")");
157 for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
158 log("tbl[",i,"]: mme=", S1apAssociationTable[i].mme_ue_s1ap_id,
159 ", enb=", S1apAssociationTable[i].enb_ue_s1ap_id);
160 /* skip empty records */
161 if (S1apAssociationTable[i].mme_ue_s1ap_id == omit and
162 S1apAssociationTable[i].enb_ue_s1ap_id == omit) {
163 log("skipping empty ", i);
164 continue;
165 }
166 if (S1apAssociationTable[i].mme_ue_s1ap_id == omit) {
167 log("entry ", i, " has no MME ID yet (enb=", S1apAssociationTable[i].enb_ue_s1ap_id);
168 /* Table doesn't yet know the MME side ID, let's look-up only
169 * based on the eNB side ID */
170 if (match(S1apAssociationTable[i].enb_ue_s1ap_id, enb_id)) {
171 /* update table with MME side ID */
172 S1apAssociationTable[i].mme_ue_s1ap_id := valueof(mme_id);
173 return true;
174 }
175 } else if (match(S1apAssociationTable[i].enb_ue_s1ap_id, enb_id) and
176 match(S1apAssociationTable[i].mme_ue_s1ap_id, mme_id)) {
177 return true;
178 }
179 }
180 return false;
181}
182
183private function f_comp_known(S1AP_ConnHdlr client)
184runs on S1AP_Emulation_CT return boolean {
185 var integer i;
186 for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
187 if (S1apAssociationTable[i].comp_ref == client) {
188 return true;
189 }
190 }
191 return false;
192}
193
194private function f_assoc_id_by_s1ap_ids(template (omit) MME_UE_S1AP_ID mme_id,
195 template (omit) ENB_UE_S1AP_ID enb_id)
196runs on S1AP_Emulation_CT return integer {
197 var integer i;
198 for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
199 if (match(S1apAssociationTable[i].enb_ue_s1ap_id, enb_id)) {
200 if (istemplatekind(mme_id, "omit")) {
201 return i;
202 } else {
203 if (match(S1apAssociationTable[i].mme_ue_s1ap_id, mme_id)) {
204 return i;
205 }
206 }
207 }
208 }
209 setverdict(fail, "S1AP Association Table not found by ENB-ID=", enb_id, " MME-ID=", mme_id);
210 mtc.stop;
211}
212
213private function f_assoc_id_by_comp(S1AP_ConnHdlr client)
214runs on S1AP_Emulation_CT return integer {
215 var integer i;
216 for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
217 if (S1apAssociationTable[i].comp_ref == client) {
218 return i;
219 }
220 }
221 setverdict(fail, "S1AP Association Table not found by component ", client);
222 mtc.stop;
223}
224
225private function f_assoc_by_comp(S1AP_ConnHdlr client)
226runs on S1AP_Emulation_CT return AssociationData {
227 var integer i := f_assoc_id_by_comp(client);
228 return S1apAssociationTable[i];
229}
230
231private function f_s1ap_id_table_add(S1AP_ConnHdlr comp_ref,
232 template (omit) MME_UE_S1AP_ID mme_id, ENB_UE_S1AP_ID enb_id)
233runs on S1AP_Emulation_CT return integer {
234 var integer i;
235 for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
236 if (not isvalue(S1apAssociationTable[i].enb_ue_s1ap_id)) {
237 S1apAssociationTable[i].enb_ue_s1ap_id := enb_id;
238 if (istemplatekind(mme_id, "omit")) {
239 S1apAssociationTable[i].mme_ue_s1ap_id := omit;
240 } else {
241 S1apAssociationTable[i].mme_ue_s1ap_id := valueof(mme_id);
242 }
243 S1apAssociationTable[i].comp_ref := comp_ref;
244 return i;
245 }
246 }
247 testcase.stop("S1AP Association Table full!");
248 return -1;
249}
250
251private function f_s1ap_id_table_del(S1AP_ConnHdlr comp_ref, ENB_UE_S1AP_ID enb_id)
252runs on S1AP_Emulation_CT {
253 var integer i;
254 for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
255 if (S1apAssociationTable[i].comp_ref == comp_ref and
Philipp Maier0ce67ab2023-07-07 13:02:11 +0200256 S1apAssociationTable[i].enb_ue_s1ap_id == enb_id) {
Harald Welte35498112019-07-02 14:26:39 +0800257 S1apAssociationTable[i].enb_ue_s1ap_id := omit;
258 S1apAssociationTable[i].mme_ue_s1ap_id := omit;
259 S1apAssociationTable[i].comp_ref := null;
260 return;
261 }
262 }
263 setverdict(fail, "S1AP Association Table: Couldn't find to-be-deleted entry!");
264 mtc.stop;
265}
266
267
268private function f_s1ap_id_table_init()
269runs on S1AP_Emulation_CT {
270 for (var integer i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
271 S1apAssociationTable[i].mme_ue_s1ap_id := omit;
272 S1apAssociationTable[i].enb_ue_s1ap_id := omit;
273 S1apAssociationTable[i].cgi := omit;
274 S1apAssociationTable[i].tai := omit;
275 S1apAssociationTable[i].nus := valueof(t_NAS_UE_State(g_pars.role));
276 }
277}
278
279private template (value) SctpTuple ts_SCTP(template (omit) integer ppid := 18) := {
280 sinfo_stream := omit,
281 sinfo_ppid := ppid,
282 remSocks := omit,
283 assocId := omit
284};
285
286private template PortEvent tr_SctpAssocChange := {
287 sctpEvent := {
288 sctpAssocChange := ?
289 }
290}
291private template PortEvent tr_SctpPeerAddrChange := {
292 sctpEvent := {
293 sctpPeerAddrChange := ?
294 }
295}
296
297private function f_s1ap_xceive(template (value) S1AP_PDU tx,
298 template S1AP_PDU rx_t := ?)
299runs on S1AP_Emulation_CT return S1AP_PDU {
300 timer T := 10.0;
301 var S1AP_RecvFrom mrf;
302
303 S1AP.send(t_S1AP_Send(g_s1ap_conn_id, tx));
304 alt {
305 [] S1AP.receive(tr_S1AP_RecvFrom_R(rx_t)) -> value mrf { }
306 [] S1AP.receive(tr_SctpAssocChange) { repeat; }
307 [] S1AP.receive(tr_SctpPeerAddrChange) { repeat; }
308 [] T.timeout {
309 setverdict(fail, "Timeout waiting for ", rx_t);
310 mtc.stop;
311 }
312 }
313 return mrf.msg;
314}
315
316/*
317private function f_nas_try_decaps(PDU_NAS_EPS nas) return PDU_NAS_EPS
318{
319 var PDU_NAS_EPS_SecurityProtectedNASMessage secp_nas;
320 if (not match(nas, tr_NAS_EMM_SecurityProtected)) {
321 return nas;
322 }
323 secp_nas := nas.ePS_messages.ePS_MobilityManagement.pDU_NAS_EPS_SecurityProtectedNASMessage;
324 select (secp_nas.securityHeaderType) {
325 case ('0011'B) {
326 var octetstring knas_int := '530ce32318f26264eab26bc116870b86'O;
327 var octetstring data_with_seq := int2oct(secp_nas.sequenceNumber, 1) & secp_nas.nAS_Message;
328 var OCT4 exp_mac := f_snow_3g_f9(knas_int, secp_nas.sequenceNumber, 0,
329 is_downlink:=true, data:=data_with_seq);
330 if (exp_mac != secp_nas.messageAuthenticationCode) {
331 setverdict(fail, "Received NAS MAC ", secp_nas.messageAuthenticationCode,
332 " doesn't match expected MAC ", exp_mac, ": ", nas);
333 mtc.stop;
334 }
335 return dec_PDU_NAS_EPS(secp_nas.nAS_Message);
336 }
337 case else {
338 setverdict(fail, "Implement SecHdrType for ", secp_nas);
339 mtc.stop;
340 }
341 }
342}
343*/
344
Philipp Maierbb8f05d2023-07-07 14:16:08 +0200345function handle_S1AP_UeContextReleaseCmd(template (present) S1AP_PDU rel_cmd) runs on S1AP_Emulation_CT {
346 if (ispresent(rel_cmd.initiatingMessage.value_.uEContextReleaseCommand.protocolIEs[0].value_.uE_S1AP_IDs.uE_S1AP_ID_pair)) {
347 var template MME_UE_S1AP_ID mme_ue_id;
348 var template ENB_UE_S1AP_ID enb_ue_id;
349 var integer assoc_id;
350 var S1AP_ConnHdlr vc_conn
351
352 mme_ue_id := rel_cmd.initiatingMessage.value_.uEContextReleaseCommand.protocolIEs[0].value_.uE_S1AP_IDs.uE_S1AP_ID_pair.mME_UE_S1AP_ID;
353 enb_ue_id := rel_cmd.initiatingMessage.value_.uEContextReleaseCommand.protocolIEs[0].value_.uE_S1AP_IDs.uE_S1AP_ID_pair.eNB_UE_S1AP_ID;
354
355 assoc_id := f_assoc_id_by_s1ap_ids(mme_ue_id, enb_ue_id);
356 vc_conn := S1apAssociationTable[assoc_id].comp_ref;
357
358 f_s1ap_id_table_del(vc_conn, valueof(enb_ue_id));
359 } else {
360 /* TODO: The UE CONTEXT RELEASE COMMAND (see also: 3GPP TS 36.413, section 9.1.4.6), may identify the
361 * context by either an uE_S1AP_ID_pair (MME_UE_S1AP_ID and ENB_UE_S1AP_ID) or an MME_UE_S1AP_ID alone.
362 * The latter case is not implemented here yet. */
363 setverdict(fail, "complete implementation of UeContextReleaseCmd handling");
364 mtc.stop;
365 }
366}
367
Harald Welte35498112019-07-02 14:26:39 +0800368function main(S1APOps ops, S1AP_conn_parameters p, charstring id) runs on S1AP_Emulation_CT {
369 var Result res;
370 g_pars := p;
371 g_s1ap_id := id;
372 f_s1ap_id_table_init();
373 f_expect_table_init();
374
375 map(self:S1AP, system:S1AP_CODEC_PT);
376 if (p.remote_sctp_port == -1) {
377 res := S1AP_CodecPort_CtrlFunct.f_IPL4_listen(S1AP, p.local_ip, p.local_sctp_port, { sctp := valueof(ts_SCTP) });
378 } else {
379 res := S1AP_CodecPort_CtrlFunct.f_IPL4_connect(S1AP, p.remote_ip, p.remote_sctp_port,
380 p.local_ip, p.local_sctp_port, -1, { sctp := valueof(ts_SCTP) });
381 }
382 if (not ispresent(res.connId)) {
383 setverdict(fail, "Could not connect S1AP socket, check your configuration");
384 mtc.stop;
385 }
386 g_s1ap_conn_id := res.connId;
387
388 /* notify user about SCTP establishment */
389 if (p.remote_sctp_port != -1) {
390 S1AP_UNIT.send(S1APEM_Event:{up_down:=S1APEM_EVENT_UP})
391 }
392
393 while (true) {
394 var S1AP_ConnHdlr vc_conn;
395 var PDU_NAS_EPS nas;
396 var hexstring imsi;
397 var S1AP_RecvFrom mrf;
398 var S1AP_PDU msg;
399 var S1APEM_Config s1cfg;
400 var charstring vlr_name, mme_name;
401 var integer ai;
402
403 alt {
404 /* Configuration primitive from client */
405 [] S1AP_CLIENT.receive(S1APEM_Config:{set_nas_keys:=?}) -> value s1cfg sender vc_conn {
406 var integer assoc_id := f_assoc_id_by_comp(vc_conn);
407 S1apAssociationTable[assoc_id].nus.k_nas_int := s1cfg.set_nas_keys.k_nas_int;
408 S1apAssociationTable[assoc_id].nus.k_nas_enc := s1cfg.set_nas_keys.k_nas_enc;
409 }
410 /* S1AP from client: InitialUE */
411 [] S1AP_CLIENT.receive(tr_S1AP_InitialUE) -> value msg sender vc_conn {
412 /* create a table entry about this connection */
413 ai := f_s1ap_id_table_add(vc_conn, omit, valueof(f_S1AP_get_ENB_UE_S1AP_ID(msg)));
414 /* Store CGI + TAI so we can use it for generating UlNasTransport from NAS */
415 S1apAssociationTable[ai].tai := msg.initiatingMessage.value_.InitialUEMessage.protocolIEs[2].value_.TAI;
416 S1apAssociationTable[ai].cgi := msg.initiatingMessage.value_.InitialUEMessage.protocolIEs[3].value_.EUTRAN_CGI;
417 /* Pass message through */
418 S1AP.send(t_S1AP_Send(g_s1ap_conn_id, msg));
419 }
420 /* NAS from client: Wrap in S1AP Uplink NAS Transport */
421 [] S1AP_CLIENT.receive(PDU_NAS_EPS:?) -> value nas sender vc_conn {
422 var integer assoc_id := f_assoc_id_by_comp(vc_conn);
423 var AssociationData ad := S1apAssociationTable[assoc_id];
424 nas := f_nas_encaps(S1apAssociationTable[assoc_id].nus, nas, new_ctx := false);
425 var octetstring nas_enc := enc_PDU_NAS_EPS(nas);
426 S1AP.send(t_S1AP_Send(g_s1ap_conn_id,
427 ts_S1AP_UlNasTransport(ad.mme_ue_s1ap_id,
428 ad.enb_ue_s1ap_id,
429 nas_enc, ad.cgi, ad.tai)));
430 }
431 /* S1AP from client: pass on transparently */
432 [] S1AP_CLIENT.receive(S1AP_PDU:?) -> value msg sender vc_conn {
433 /* Pass message through */
434 /* FIXME: validate S1AP_IDs ? */
435 S1AP.send(t_S1AP_Send(g_s1ap_conn_id, msg));
436 }
437
438 /* non-UE related S1AP: pass through unmodified/unverified */
439 [] S1AP_UNIT.receive(S1AP_PDU:?) -> value msg sender vc_conn {
440 /* Pass message through */
441 S1AP.send(t_S1AP_Send(g_s1ap_conn_id, msg));
442 }
443
444 /* S1AP received from peer (MME) */
445 [] S1AP.receive(tr_S1AP_RecvFrom_R(?)) -> value mrf {
446 if (match(mrf.msg, tr_S1AP_nonUErelated)) {
447 /* non-UE-related S1AP message */
448 var template S1AP_PDU resp := ops.unitdata_cb.apply(mrf.msg);
449 if (isvalue(resp)) {
450 S1AP.send(t_S1AP_Send(g_s1ap_conn_id, valueof(resp)));
451 }
452 } else if (match(mrf.msg, tr_S1AP_UeContextReleaseCmd)) {
Philipp Maierbb8f05d2023-07-07 14:16:08 +0200453 handle_S1AP_UeContextReleaseCmd(mrf.msg);
Harald Welte35498112019-07-02 14:26:39 +0800454 } else {
455 /* Ue-related S1AP message */
456 /* obtain MME + ENB UE S1AP ID */
457 var template (omit) MME_UE_S1AP_ID mme_ue_id := f_S1AP_get_MME_UE_S1AP_ID(mrf.msg);
458 var template (omit) ENB_UE_S1AP_ID enb_ue_id := f_S1AP_get_ENB_UE_S1AP_ID(mrf.msg);
459 /* check if those IDs are known in our table */
460 if (f_s1ap_ids_known(mme_ue_id, enb_ue_id)) {
461 /* if yes, dispatch to the ConnHdlr for this Ue-Connection */
462 var template (omit) octetstring nas_enc;
463 var integer assoc_id := f_assoc_id_by_s1ap_ids(mme_ue_id, enb_ue_id);
464 vc_conn := S1apAssociationTable[assoc_id].comp_ref;
465 nas_enc := f_S1AP_get_NAS_PDU(mrf.msg);
466 if (isvalue(nas_enc)) {
467 nas := dec_PDU_NAS_EPS(valueof(nas_enc));
468 if (match(nas, tr_NAS_EMM_SecurityProtected)) {
469 nas := f_nas_try_decaps(S1apAssociationTable[assoc_id].nus, nas);
470 }
471 /* send decoded NAS */
472 S1AP_CLIENT.send(nas) to vc_conn;
473 } else {
474 /* send raw S1AP */
475 S1AP_CLIENT.send(mrf.msg) to vc_conn;
476 }
477 } else {
478 /* if not, call create_cb so it can create new ConnHdlr */
479 vc_conn := ops.create_cb.apply(mrf.msg, mme_ue_id, enb_ue_id, id);
480 f_s1ap_id_table_add(vc_conn, mme_ue_id, valueof(enb_ue_id));
481 S1AP_CLIENT.send(mrf.msg) to vc_conn;
482 }
483 }
484 }
485 [] S1AP.receive(tr_SctpAssocChange) { }
486 [] S1AP.receive(tr_SctpPeerAddrChange) { }
487 [] S1AP_PROC.getcall(S1APEM_register:{?,?}) -> param(imsi, vc_conn) {
488 f_create_expect(imsi, vc_conn);
489 S1AP_PROC.reply(S1APEM_register:{imsi, vc_conn}) to vc_conn;
490 }
491 }
492
493 }
494}
495
496/* "Expect" Handling */
497
498type record ExpectData {
499 hexstring imsi optional,
500 S1AP_ConnHdlr vc_conn
501}
502
503signature S1APEM_register(in hexstring imsi, in S1AP_ConnHdlr hdlr);
504
505type port S1APEM_PROC_PT procedure {
506 inout S1APEM_register;
507} with { extension "internal" };
508
Oliver Smith23e192e2023-02-13 15:00:46 +0100509/* Function that can be used as create_cb and will use the expect table */
Harald Welte35498112019-07-02 14:26:39 +0800510function ExpectedCreateCallback(S1AP_PDU msg, hexstring imsi, charstring id)
511runs on S1AP_Emulation_CT return S1AP_ConnHdlr {
512 var S1AP_ConnHdlr ret := null;
513 var integer i;
514
515 for (i := 0; i < sizeof(S1apExpectTable); i := i+1) {
516 if (not ispresent(S1apExpectTable[i].imsi)) {
517 continue;
518 }
519 if (imsi == S1apExpectTable[i].imsi) {
520 ret := S1apExpectTable[i].vc_conn;
521 /* Release this entry */
522 S1apExpectTable[i].imsi := omit;
523 S1apExpectTable[i].vc_conn := null;
524 log("Found Expect[", i, "] for ", msg, " handled at ", ret);
525 return ret;
526 }
527 }
528 setverdict(fail, "Couldn't find Expect for ", msg);
529 mtc.stop;
530}
531
532private function f_create_expect(hexstring imsi, S1AP_ConnHdlr hdlr)
533runs on S1AP_Emulation_CT {
534 var integer i;
535
536 /* Check an entry like this is not already presnt */
537 for (i := 0; i < sizeof(S1apExpectTable); i := i+1) {
538 if (imsi == S1apExpectTable[i].imsi) {
539 setverdict(fail, "IMSI already present", imsi);
540 mtc.stop;
541 }
542 }
543 for (i := 0; i < sizeof(S1apExpectTable); i := i+1) {
544 if (not ispresent(S1apExpectTable[i].imsi)) {
545 S1apExpectTable[i].imsi := imsi;
546 S1apExpectTable[i].vc_conn := hdlr;
547 log("Created Expect[", i, "] for ", imsi, " to be handled at ", hdlr);
548 return;
549 }
550 }
551 testcase.stop("No space left in S1apExpectTable")
552}
553
554/* client/conn_hdlr side function to use procedure port to create expect in emulation */
555function f_create_s1ap_expect(hexstring imsi) runs on S1AP_ConnHdlr {
556 S1AP_PROC.call(S1APEM_register:{imsi, self}) {
557 [] S1AP_PROC.getreply(S1APEM_register:{?,?}) {};
558 }
559}
560
561
562private function f_expect_table_init()
563runs on S1AP_Emulation_CT {
564 var integer i;
565 for (i := 0; i < sizeof(S1apExpectTable); i := i + 1) {
566 S1apExpectTable[i].imsi := omit;
567 }
568}
569
570function DummyUnitdataCallback(S1AP_PDU msg)
571runs on S1AP_Emulation_CT return template S1AP_PDU {
572 log("Ignoring S1AP ", msg);
573 return omit;
574}
575
576
577function f_S1AP_get_ENB_UE_S1AP_ID(S1AP_PDU s1ap) return template (omit) ENB_UE_S1AP_ID
578{
579 if (ischosen(s1ap.initiatingMessage)) {
580 var InitiatingMessage im := s1ap.initiatingMessage;
581 select (s1ap) {
582 case (tr_S1AP_InitialUE) {
583 return im.value_.InitialUEMessage.protocolIEs[0].value_.ENB_UE_S1AP_ID;
584 }
585 case (tr_S1AP_DlNasTransport) {
586 return im.value_.DownlinkNASTransport.protocolIEs[1].value_.ENB_UE_S1AP_ID;
587 }
588 case (tr_S1AP_UlNasTransport) {
589 return im.value_.UplinkNASTransport.protocolIEs[1].value_.ENB_UE_S1AP_ID;
590 }
591 case (tr_S1AP_IntialCtxSetupReq) {
592 return im.value_.initialContextSetupRequest.protocolIEs[1].value_.ENB_UE_S1AP_ID;
593 }
594 case (tr_S1AP_UeContextReleaseReq) {
595 return im.value_.UEContextReleaseRequest.protocolIEs[1].value_.ENB_UE_S1AP_ID;
596 }
597 /* UeContextReleaseCmd needs special handling; it can contain any number of MME/UE IDs */
598 case (tr_S1AP_ConnEstInd) {
599 return im.value_.ConnectionEstablishmentIndication.protocolIEs[1].value_.ENB_UE_S1AP_ID;
600 }
601 /* TODO */
602 }
603 } else if (ischosen(s1ap.successfulOutcome)) {
604 var SuccessfulOutcome so := s1ap.successfulOutcome;
605 select (s1ap) {
606 case (tr_S1AP_InitialCtxSetupResp) {
607 return so.value_.initialContextSetupResponse.protocolIEs[1].value_.ENB_UE_S1AP_ID;
608 }
609 case (tr_S1AP_UeContextReleaseCompl) {
610 return so.value_.UEContextReleaseComplete.protocolIEs[1].value_.ENB_UE_S1AP_ID;
611 }
612 /* TODO */
613 }
614 } else if (ischosen(s1ap.unsuccessfulOutcome)) {
615 var UnsuccessfulOutcome uo := s1ap.unsuccessfulOutcome;
616 select (s1ap) {
617 case (tr_S1AP_InitialCtxSetupFail) {
618 return uo.value_.initialContextSetupFailure.protocolIEs[1].value_.ENB_UE_S1AP_ID;
619 }
620 /* TODO */
621 }
622 }
623 return omit;
624}
625
626function f_S1AP_get_MME_UE_S1AP_ID(S1AP_PDU s1ap) return template (omit) MME_UE_S1AP_ID
627{
628 if (ischosen(s1ap.initiatingMessage)) {
629 var InitiatingMessage im := s1ap.initiatingMessage;
630 select (s1ap) {
631 case (tr_S1AP_DlNasTransport) {
632 return im.value_.DownlinkNASTransport.protocolIEs[0].value_.MME_UE_S1AP_ID;
633 }
634 case (tr_S1AP_UlNasTransport) {
635 return im.value_.UplinkNASTransport.protocolIEs[0].value_.MME_UE_S1AP_ID;
636 }
637 case (tr_S1AP_IntialCtxSetupReq) {
638 return im.value_.initialContextSetupRequest.protocolIEs[0].value_.MME_UE_S1AP_ID;
639 }
640 case (tr_S1AP_UeContextReleaseReq) {
641 return im.value_.UEContextReleaseRequest.protocolIEs[0].value_.MME_UE_S1AP_ID;
642 }
643 /* UeContextReleaseCmd needs special handling; it can contain any number of MME/UE IDs */
644 case (tr_S1AP_ConnEstInd) {
645 return im.value_.ConnectionEstablishmentIndication.protocolIEs[0].value_.MME_UE_S1AP_ID;
646 }
647 /* TODO */
648 }
649 } else if (ischosen(s1ap.successfulOutcome)) {
650 var SuccessfulOutcome so := s1ap.successfulOutcome;
651 select (s1ap) {
652 case (tr_S1AP_InitialCtxSetupResp) {
653 return so.value_.initialContextSetupResponse.protocolIEs[0].value_.MME_UE_S1AP_ID;
654 }
655 case (tr_S1AP_UeContextReleaseCompl) {
656 return so.value_.UEContextReleaseComplete.protocolIEs[0].value_.MME_UE_S1AP_ID;
657 }
658 /* TODO */
659 }
660 } else if (ischosen(s1ap.unsuccessfulOutcome)) {
661 var UnsuccessfulOutcome uo := s1ap.unsuccessfulOutcome;
662 select (s1ap) {
663 case (tr_S1AP_InitialCtxSetupFail) {
664 return uo.value_.initialContextSetupFailure.protocolIEs[0].value_.MME_UE_S1AP_ID;
665 }
666 /* TODO */
667 }
668 }
669 return omit;
670}
671
672function f_S1AP_get_NAS_PDU(S1AP_PDU s1ap) return template (omit) NAS_PDU
673{
674 var integer i;
675
676 if (ischosen(s1ap.initiatingMessage)) {
677 var InitiatingMessage im := s1ap.initiatingMessage;
678 select (s1ap) {
679 case (tr_S1AP_DlNasTransport) {
680 var DownlinkNASTransport msg := im.value_.DownlinkNASTransport;
681 for (i := 0; i < lengthof(msg.protocolIEs); i := i+1) {
682 if (msg.protocolIEs[i].id == id_NAS_PDU) {
683 return msg.protocolIEs[i].value_.NAS_PDU;
684 }
685 }
686 }
687 case (tr_S1AP_UlNasTransport) {
688 var UplinkNASTransport msg := im.value_.UplinkNASTransport;
689 for (i := 0; i < lengthof(msg.protocolIEs); i := i+1) {
690 if (msg.protocolIEs[i].id == id_NAS_PDU) {
691 return msg.protocolIEs[i].value_.NAS_PDU;
692 }
693 }
694 }
695 }
696 }
697 return omit;
698}
699
700
701
702}