library: Add S1AP CodecPort/Emulation

Change-Id: I9bfba3ab2a3830e590b203c44c03b9c9383fff99
This commit is contained in:
Harald Welte 2019-07-02 14:26:39 +08:00 committed by laforge
parent d01fc8fefb
commit 3549811565
6 changed files with 871 additions and 1 deletions

View File

@ -0,0 +1,82 @@
module S1AP_CodecPort {
/* Simple S1AP Codec Port, translating between raw SCTP primitives with
* octetstring payload towards the IPL4asp provider, and S1AP primitives
* which carry the decoded S1AP data types as payload.
*
* (C) 2019 by Harald Welte <laforge@gnumonks.org>
* All rights reserved.
*
* Released under the terms of GNU General Public License, Version 2 or
* (at your option) any later version.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import from IPL4asp_PortType all;
import from IPL4asp_Types all;
import from S1AP_PDU_Descriptions all;
import from S1AP_Types all;
type record S1AP_RecvFrom {
ConnectionId connId,
HostName remName,
PortNumber remPort,
HostName locName,
PortNumber locPort,
S1AP_PDU msg
};
template S1AP_RecvFrom t_S1AP_RecvFrom(template S1AP_PDU msg) := {
connId := ?,
remName := ?,
remPort := ?,
locName := ?,
locPort := ?,
msg := msg
}
type record S1AP_Send {
ConnectionId connId,
S1AP_PDU msg
}
template S1AP_Send t_S1AP_Send(template ConnectionId connId, template S1AP_PDU msg) := {
connId := connId,
msg := msg
}
private function IPL4_to_S1AP_RecvFrom(in ASP_RecvFrom pin, out S1AP_RecvFrom pout) {
pout.connId := pin.connId;
pout.remName := pin.remName;
pout.remPort := pin.remPort;
pout.locName := pin.locName;
pout.locPort := pin.locPort;
pout.msg := dec_S1AP_PDU(pin.msg);
} with { extension "prototype(fast)" };
private function S1AP_to_IPL4_Send(in S1AP_Send pin, out ASP_Send pout) {
pout.connId := pin.connId;
pout.proto := {
sctp := {
sinfo_stream := omit,
sinfo_ppid := 18,
remSocks := omit,
assocId := omit
}
};
pout.msg := enc_S1AP_PDU(pin.msg);
} with { extension "prototype(fast)" };
type port S1AP_CODEC_PT message {
out S1AP_Send;
in S1AP_RecvFrom,
ASP_ConnId_ReadyToRelease,
ASP_Event;
} with { extension "user IPL4asp_PT
out(S1AP_Send -> ASP_Send:function(S1AP_to_IPL4_Send))
in(ASP_RecvFrom -> S1AP_RecvFrom: function(IPL4_to_S1AP_RecvFrom);
ASP_ConnId_ReadyToRelease -> ASP_ConnId_ReadyToRelease: simple;
ASP_Event -> ASP_Event: simple)"
}
}

View File

@ -0,0 +1,44 @@
module S1AP_CodecPort_CtrlFunct {
import from S1AP_CodecPort all;
import from IPL4asp_Types all;
external function f_IPL4_listen(
inout S1AP_CODEC_PT portRef,
in HostName locName,
in PortNumber locPort,
in ProtoTuple proto,
in OptionList options := {}
) return Result;
external function f_IPL4_connect(
inout S1AP_CODEC_PT portRef,
in HostName remName,
in PortNumber remPort,
in HostName locName,
in PortNumber locPort,
in ConnectionId connId,
in ProtoTuple proto,
in OptionList options := {}
) return Result;
external function f_IPL4_close(
inout S1AP_CODEC_PT portRef,
in ConnectionId id,
in ProtoTuple proto := { unspecified := {} }
) return Result;
external function f_IPL4_setUserData(
inout S1AP_CODEC_PT portRef,
in ConnectionId id,
in UserData userData
) return Result;
external function f_IPL4_getUserData(
inout S1AP_CODEC_PT portRef,
in ConnectionId id,
out UserData userData
) return Result;
}

View File

@ -0,0 +1,56 @@
#include "IPL4asp_PortType.hh"
#include "S1AP_CodecPort.hh"
#include "IPL4asp_PT.hh"
namespace S1AP__CodecPort__CtrlFunct {
IPL4asp__Types::Result f__IPL4__listen(
S1AP__CodecPort::S1AP__CODEC__PT& portRef,
const IPL4asp__Types::HostName& locName,
const IPL4asp__Types::PortNumber& locPort,
const IPL4asp__Types::ProtoTuple& proto,
const IPL4asp__Types::OptionList& options)
{
return f__IPL4__PROVIDER__listen(portRef, locName, locPort, proto, options);
}
IPL4asp__Types::Result f__IPL4__connect(
S1AP__CodecPort::S1AP__CODEC__PT& portRef,
const IPL4asp__Types::HostName& remName,
const IPL4asp__Types::PortNumber& remPort,
const IPL4asp__Types::HostName& locName,
const IPL4asp__Types::PortNumber& locPort,
const IPL4asp__Types::ConnectionId& connId,
const IPL4asp__Types::ProtoTuple& proto,
const IPL4asp__Types::OptionList& options)
{
return f__IPL4__PROVIDER__connect(portRef, remName, remPort,
locName, locPort, connId, proto, options);
}
IPL4asp__Types::Result f__IPL4__close(
S1AP__CodecPort::S1AP__CODEC__PT& portRef,
const IPL4asp__Types::ConnectionId& connId,
const IPL4asp__Types::ProtoTuple& proto)
{
return f__IPL4__PROVIDER__close(portRef, connId, proto);
}
IPL4asp__Types::Result f__IPL4__setUserData(
S1AP__CodecPort::S1AP__CODEC__PT& portRef,
const IPL4asp__Types::ConnectionId& connId,
const IPL4asp__Types::UserData& userData)
{
return f__IPL4__PROVIDER__setUserData(portRef, connId, userData);
}
IPL4asp__Types::Result f__IPL4__getUserData(
S1AP__CodecPort::S1AP__CODEC__PT& portRef,
const IPL4asp__Types::ConnectionId& connId,
IPL4asp__Types::UserData& userData)
{
return f__IPL4__PROVIDER__getUserData(portRef, connId, userData);
}
}

681
library/S1AP_Emulation.ttcn Normal file
View File

@ -0,0 +1,681 @@
module S1AP_Emulation {
/* S1AP Emulation, runs on top of S1AP_CodecPort. It multiplexes/demultiplexes
* the individual IMSIs/subscribers, so there can be separate TTCN-3 components handling
* each of them.
*
* The S1AP_Emulation.main() function processes S1AP primitives from the S1AP
* socket via the S1AP_CodecPort, and dispatches them to the per-IMSI components.
*
* For each new IMSI, the S1apOps.create_cb() is called. It can create
* or resolve a TTCN-3 component, and returns a component reference to which that IMSI
* is routed/dispatched.
*
* If a pre-existing component wants to register to handle a future inbound IMSI, it can
* do so by registering an "expect" with the expected IMSI.
*
* Inbound non-UE related S1AP messages (such as RESET, SETUP, OVERLOAD) are dispatched to
* the S1apOps.unitdata_cb() callback, which is registered with an argument to the
* main() function below.
*
* (C) 2019 by Harald Welte <laforge@gnumonks.org>
* All rights reserved.
*
* Released under the terms of GNU General Public License, Version 2 or
* (at your option) any later version.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import from S1AP_CodecPort all;
import from S1AP_CodecPort_CtrlFunct all;
import from S1AP_Types all;
import from S1AP_Constants all;
import from S1AP_PDU_Contents all;
import from S1AP_PDU_Descriptions all;
import from S1AP_IEs all;
import from S1AP_Templates all;
import from NAS_EPS_Types all;
import from NAS_Templates all;
import from LTE_CryptoFunctions all;
import from General_Types all;
import from Osmocom_Types all;
import from IPL4asp_Types all;
import from DNS_Helpers all;
type component S1AP_ConnHdlr {
port S1AP_Conn_PT S1AP;
/* procedure based port to register for incoming connections */
port S1APEM_PROC_PT S1AP_PROC;
}
/* port between individual per-connection components and this dispatcher */
type port S1AP_Conn_PT message {
inout S1AP_PDU, PDU_NAS_EPS, S1APEM_Config;
} with { extension "internal" };
type record NAS_Keys {
octetstring k_nas_int,
octetstring k_nas_enc
};
type union S1APEM_Config {
NAS_Keys set_nas_keys
};
type enumerated S1APEM_EventUpDown {
S1APEM_EVENT_DOWN,
S1APEM_EVENT_UP
}
/* an event indicating us whether or not a connection is physically up or down,
* and whether we have received an ID_ACK */
type union S1APEM_Event {
S1APEM_EventUpDown up_down
}
/* global test port e.g. for non-imsi/conn specific messages */
type port S1AP_PT message {
inout S1AP_PDU, S1APEM_Event;
} with { extension "internal" };
/* represents a single S1AP Association */
type record AssociationData {
S1AP_ConnHdlr comp_ref, /* component handling this UE connection */
uint24_t enb_ue_s1ap_id optional, /* eNB side S1AP ID */
uint32_t mme_ue_s1ap_id optional, /* MME side S1AP ID */
EUTRAN_CGI cgi optional,
TAI tai optional,
NAS_UE_State nus
//hexstring imsi optional
};
type component S1AP_Emulation_CT {
/* Port facing to the UDP SUT */
port S1AP_CODEC_PT S1AP;
/* All S1AP_ConnHdlr S1AP ports connect here
* S1AP_Emulation_CT.main needs to figure out what messages
* to send where with CLIENT.send() to vc_conn */
port S1AP_Conn_PT S1AP_CLIENT;
/* currently tracked connections */
var AssociationData S1apAssociationTable[16];
/* pending expected CRCX */
var ExpectData S1apExpectTable[8];
/* procedure based port to register for incoming connections */
port S1APEM_PROC_PT S1AP_PROC;
/* test port for unit data messages */
port S1AP_PT S1AP_UNIT;
var S1AP_conn_parameters g_pars;
var charstring g_s1ap_id;
var integer g_s1ap_conn_id := -1;
}
type function S1APCreateCallback(S1AP_PDU msg, template (omit) MME_UE_S1AP_ID mme_id,
template (omit) ENB_UE_S1AP_ID enb_id, charstring id)
runs on S1AP_Emulation_CT return S1AP_ConnHdlr;
type function S1APUnitdataCallback(S1AP_PDU msg)
runs on S1AP_Emulation_CT return template S1AP_PDU;
type record S1APOps {
S1APCreateCallback create_cb,
S1APUnitdataCallback unitdata_cb
}
type record S1AP_conn_parameters {
HostName remote_ip,
PortNumber remote_sctp_port,
HostName local_ip,
PortNumber local_sctp_port,
NAS_Role role
}
function tr_S1AP_RecvFrom_R(template S1AP_PDU msg)
runs on S1AP_Emulation_CT return template S1AP_RecvFrom {
var template S1AP_RecvFrom mrf := {
connId := g_s1ap_conn_id,
remName := ?,
remPort := ?,
locName := ?,
locPort := ?,
msg := msg
}
return mrf;
}
private function f_s1ap_ids_known(template (omit) MME_UE_S1AP_ID mme_id,
template (omit) ENB_UE_S1AP_ID enb_id)
runs on S1AP_Emulation_CT return boolean {
var integer i;
log("f_s1ap_ids_known(",mme_id,", ",enb_id,")");
for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
log("tbl[",i,"]: mme=", S1apAssociationTable[i].mme_ue_s1ap_id,
", enb=", S1apAssociationTable[i].enb_ue_s1ap_id);
/* skip empty records */
if (S1apAssociationTable[i].mme_ue_s1ap_id == omit and
S1apAssociationTable[i].enb_ue_s1ap_id == omit) {
log("skipping empty ", i);
continue;
}
if (S1apAssociationTable[i].mme_ue_s1ap_id == omit) {
log("entry ", i, " has no MME ID yet (enb=", S1apAssociationTable[i].enb_ue_s1ap_id);
/* Table doesn't yet know the MME side ID, let's look-up only
* based on the eNB side ID */
if (match(S1apAssociationTable[i].enb_ue_s1ap_id, enb_id)) {
/* update table with MME side ID */
S1apAssociationTable[i].mme_ue_s1ap_id := valueof(mme_id);
return true;
}
} else if (match(S1apAssociationTable[i].enb_ue_s1ap_id, enb_id) and
match(S1apAssociationTable[i].mme_ue_s1ap_id, mme_id)) {
return true;
}
}
return false;
}
private function f_comp_known(S1AP_ConnHdlr client)
runs on S1AP_Emulation_CT return boolean {
var integer i;
for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
if (S1apAssociationTable[i].comp_ref == client) {
return true;
}
}
return false;
}
private function f_assoc_id_by_s1ap_ids(template (omit) MME_UE_S1AP_ID mme_id,
template (omit) ENB_UE_S1AP_ID enb_id)
runs on S1AP_Emulation_CT return integer {
var integer i;
for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
if (match(S1apAssociationTable[i].enb_ue_s1ap_id, enb_id)) {
if (istemplatekind(mme_id, "omit")) {
return i;
} else {
if (match(S1apAssociationTable[i].mme_ue_s1ap_id, mme_id)) {
return i;
}
}
}
}
setverdict(fail, "S1AP Association Table not found by ENB-ID=", enb_id, " MME-ID=", mme_id);
mtc.stop;
}
private function f_assoc_id_by_comp(S1AP_ConnHdlr client)
runs on S1AP_Emulation_CT return integer {
var integer i;
for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
if (S1apAssociationTable[i].comp_ref == client) {
return i;
}
}
setverdict(fail, "S1AP Association Table not found by component ", client);
mtc.stop;
}
private function f_assoc_by_comp(S1AP_ConnHdlr client)
runs on S1AP_Emulation_CT return AssociationData {
var integer i := f_assoc_id_by_comp(client);
return S1apAssociationTable[i];
}
private function f_s1ap_id_table_add(S1AP_ConnHdlr comp_ref,
template (omit) MME_UE_S1AP_ID mme_id, ENB_UE_S1AP_ID enb_id)
runs on S1AP_Emulation_CT return integer {
var integer i;
for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
if (not isvalue(S1apAssociationTable[i].enb_ue_s1ap_id)) {
S1apAssociationTable[i].enb_ue_s1ap_id := enb_id;
if (istemplatekind(mme_id, "omit")) {
S1apAssociationTable[i].mme_ue_s1ap_id := omit;
} else {
S1apAssociationTable[i].mme_ue_s1ap_id := valueof(mme_id);
}
S1apAssociationTable[i].comp_ref := comp_ref;
return i;
}
}
testcase.stop("S1AP Association Table full!");
return -1;
}
private function f_s1ap_id_table_del(S1AP_ConnHdlr comp_ref, ENB_UE_S1AP_ID enb_id)
runs on S1AP_Emulation_CT {
var integer i;
for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
if (S1apAssociationTable[i].comp_ref == comp_ref and
S1apAssociationTable[i].mme_ue_s1ap_id == enb_id) {
S1apAssociationTable[i].enb_ue_s1ap_id := omit;
S1apAssociationTable[i].mme_ue_s1ap_id := omit;
S1apAssociationTable[i].comp_ref := null;
return;
}
}
setverdict(fail, "S1AP Association Table: Couldn't find to-be-deleted entry!");
mtc.stop;
}
private function f_s1ap_id_table_init()
runs on S1AP_Emulation_CT {
for (var integer i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
S1apAssociationTable[i].mme_ue_s1ap_id := omit;
S1apAssociationTable[i].enb_ue_s1ap_id := omit;
S1apAssociationTable[i].cgi := omit;
S1apAssociationTable[i].tai := omit;
S1apAssociationTable[i].nus := valueof(t_NAS_UE_State(g_pars.role));
}
}
private template (value) SctpTuple ts_SCTP(template (omit) integer ppid := 18) := {
sinfo_stream := omit,
sinfo_ppid := ppid,
remSocks := omit,
assocId := omit
};
private template PortEvent tr_SctpAssocChange := {
sctpEvent := {
sctpAssocChange := ?
}
}
private template PortEvent tr_SctpPeerAddrChange := {
sctpEvent := {
sctpPeerAddrChange := ?
}
}
private function f_s1ap_xceive(template (value) S1AP_PDU tx,
template S1AP_PDU rx_t := ?)
runs on S1AP_Emulation_CT return S1AP_PDU {
timer T := 10.0;
var S1AP_RecvFrom mrf;
S1AP.send(t_S1AP_Send(g_s1ap_conn_id, tx));
alt {
[] S1AP.receive(tr_S1AP_RecvFrom_R(rx_t)) -> value mrf { }
[] S1AP.receive(tr_SctpAssocChange) { repeat; }
[] S1AP.receive(tr_SctpPeerAddrChange) { repeat; }
[] T.timeout {
setverdict(fail, "Timeout waiting for ", rx_t);
mtc.stop;
}
}
return mrf.msg;
}
/*
private function f_nas_try_decaps(PDU_NAS_EPS nas) return PDU_NAS_EPS
{
var PDU_NAS_EPS_SecurityProtectedNASMessage secp_nas;
if (not match(nas, tr_NAS_EMM_SecurityProtected)) {
return nas;
}
secp_nas := nas.ePS_messages.ePS_MobilityManagement.pDU_NAS_EPS_SecurityProtectedNASMessage;
select (secp_nas.securityHeaderType) {
case ('0011'B) {
var octetstring knas_int := '530ce32318f26264eab26bc116870b86'O;
var octetstring data_with_seq := int2oct(secp_nas.sequenceNumber, 1) & secp_nas.nAS_Message;
var OCT4 exp_mac := f_snow_3g_f9(knas_int, secp_nas.sequenceNumber, 0,
is_downlink:=true, data:=data_with_seq);
if (exp_mac != secp_nas.messageAuthenticationCode) {
setverdict(fail, "Received NAS MAC ", secp_nas.messageAuthenticationCode,
" doesn't match expected MAC ", exp_mac, ": ", nas);
mtc.stop;
}
return dec_PDU_NAS_EPS(secp_nas.nAS_Message);
}
case else {
setverdict(fail, "Implement SecHdrType for ", secp_nas);
mtc.stop;
}
}
}
*/
function main(S1APOps ops, S1AP_conn_parameters p, charstring id) runs on S1AP_Emulation_CT {
var Result res;
g_pars := p;
g_s1ap_id := id;
f_s1ap_id_table_init();
f_expect_table_init();
map(self:S1AP, system:S1AP_CODEC_PT);
if (p.remote_sctp_port == -1) {
res := S1AP_CodecPort_CtrlFunct.f_IPL4_listen(S1AP, p.local_ip, p.local_sctp_port, { sctp := valueof(ts_SCTP) });
} else {
res := S1AP_CodecPort_CtrlFunct.f_IPL4_connect(S1AP, p.remote_ip, p.remote_sctp_port,
p.local_ip, p.local_sctp_port, -1, { sctp := valueof(ts_SCTP) });
}
if (not ispresent(res.connId)) {
setverdict(fail, "Could not connect S1AP socket, check your configuration");
mtc.stop;
}
g_s1ap_conn_id := res.connId;
/* notify user about SCTP establishment */
if (p.remote_sctp_port != -1) {
S1AP_UNIT.send(S1APEM_Event:{up_down:=S1APEM_EVENT_UP})
}
while (true) {
var S1AP_ConnHdlr vc_conn;
var PDU_NAS_EPS nas;
var hexstring imsi;
var S1AP_RecvFrom mrf;
var S1AP_PDU msg;
var S1APEM_Config s1cfg;
var charstring vlr_name, mme_name;
var integer ai;
alt {
/* Configuration primitive from client */
[] S1AP_CLIENT.receive(S1APEM_Config:{set_nas_keys:=?}) -> value s1cfg sender vc_conn {
var integer assoc_id := f_assoc_id_by_comp(vc_conn);
S1apAssociationTable[assoc_id].nus.k_nas_int := s1cfg.set_nas_keys.k_nas_int;
S1apAssociationTable[assoc_id].nus.k_nas_enc := s1cfg.set_nas_keys.k_nas_enc;
}
/* S1AP from client: InitialUE */
[] S1AP_CLIENT.receive(tr_S1AP_InitialUE) -> value msg sender vc_conn {
/* create a table entry about this connection */
ai := f_s1ap_id_table_add(vc_conn, omit, valueof(f_S1AP_get_ENB_UE_S1AP_ID(msg)));
/* Store CGI + TAI so we can use it for generating UlNasTransport from NAS */
S1apAssociationTable[ai].tai := msg.initiatingMessage.value_.InitialUEMessage.protocolIEs[2].value_.TAI;
S1apAssociationTable[ai].cgi := msg.initiatingMessage.value_.InitialUEMessage.protocolIEs[3].value_.EUTRAN_CGI;
/* Pass message through */
S1AP.send(t_S1AP_Send(g_s1ap_conn_id, msg));
}
/* NAS from client: Wrap in S1AP Uplink NAS Transport */
[] S1AP_CLIENT.receive(PDU_NAS_EPS:?) -> value nas sender vc_conn {
var integer assoc_id := f_assoc_id_by_comp(vc_conn);
var AssociationData ad := S1apAssociationTable[assoc_id];
nas := f_nas_encaps(S1apAssociationTable[assoc_id].nus, nas, new_ctx := false);
var octetstring nas_enc := enc_PDU_NAS_EPS(nas);
S1AP.send(t_S1AP_Send(g_s1ap_conn_id,
ts_S1AP_UlNasTransport(ad.mme_ue_s1ap_id,
ad.enb_ue_s1ap_id,
nas_enc, ad.cgi, ad.tai)));
}
/* S1AP from client: pass on transparently */
[] S1AP_CLIENT.receive(S1AP_PDU:?) -> value msg sender vc_conn {
/* Pass message through */
/* FIXME: validate S1AP_IDs ? */
S1AP.send(t_S1AP_Send(g_s1ap_conn_id, msg));
}
/* non-UE related S1AP: pass through unmodified/unverified */
[] S1AP_UNIT.receive(S1AP_PDU:?) -> value msg sender vc_conn {
/* Pass message through */
S1AP.send(t_S1AP_Send(g_s1ap_conn_id, msg));
}
/* S1AP received from peer (MME) */
[] S1AP.receive(tr_S1AP_RecvFrom_R(?)) -> value mrf {
if (match(mrf.msg, tr_S1AP_nonUErelated)) {
/* non-UE-related S1AP message */
var template S1AP_PDU resp := ops.unitdata_cb.apply(mrf.msg);
if (isvalue(resp)) {
S1AP.send(t_S1AP_Send(g_s1ap_conn_id, valueof(resp)));
}
} else if (match(mrf.msg, tr_S1AP_UeContextReleaseCmd)) {
/* TODO: special handling, as it contains multiple eNB or MME IDs */
setverdict(fail, "implement UeContextReleaseCmd handling");
mtc.stop;
} else {
/* Ue-related S1AP message */
/* obtain MME + ENB UE S1AP ID */
var template (omit) MME_UE_S1AP_ID mme_ue_id := f_S1AP_get_MME_UE_S1AP_ID(mrf.msg);
var template (omit) ENB_UE_S1AP_ID enb_ue_id := f_S1AP_get_ENB_UE_S1AP_ID(mrf.msg);
/* check if those IDs are known in our table */
if (f_s1ap_ids_known(mme_ue_id, enb_ue_id)) {
/* if yes, dispatch to the ConnHdlr for this Ue-Connection */
var template (omit) octetstring nas_enc;
var integer assoc_id := f_assoc_id_by_s1ap_ids(mme_ue_id, enb_ue_id);
vc_conn := S1apAssociationTable[assoc_id].comp_ref;
nas_enc := f_S1AP_get_NAS_PDU(mrf.msg);
if (isvalue(nas_enc)) {
nas := dec_PDU_NAS_EPS(valueof(nas_enc));
if (match(nas, tr_NAS_EMM_SecurityProtected)) {
nas := f_nas_try_decaps(S1apAssociationTable[assoc_id].nus, nas);
}
/* send decoded NAS */
S1AP_CLIENT.send(nas) to vc_conn;
} else {
/* send raw S1AP */
S1AP_CLIENT.send(mrf.msg) to vc_conn;
}
} else {
/* if not, call create_cb so it can create new ConnHdlr */
vc_conn := ops.create_cb.apply(mrf.msg, mme_ue_id, enb_ue_id, id);
f_s1ap_id_table_add(vc_conn, mme_ue_id, valueof(enb_ue_id));
S1AP_CLIENT.send(mrf.msg) to vc_conn;
}
}
}
[] S1AP.receive(tr_SctpAssocChange) { }
[] S1AP.receive(tr_SctpPeerAddrChange) { }
[] S1AP_PROC.getcall(S1APEM_register:{?,?}) -> param(imsi, vc_conn) {
f_create_expect(imsi, vc_conn);
S1AP_PROC.reply(S1APEM_register:{imsi, vc_conn}) to vc_conn;
}
}
}
}
/* "Expect" Handling */
type record ExpectData {
hexstring imsi optional,
S1AP_ConnHdlr vc_conn
}
signature S1APEM_register(in hexstring imsi, in S1AP_ConnHdlr hdlr);
type port S1APEM_PROC_PT procedure {
inout S1APEM_register;
} with { extension "internal" };
/* Function that can be used as create_cb and will usse the expect table */
function ExpectedCreateCallback(S1AP_PDU msg, hexstring imsi, charstring id)
runs on S1AP_Emulation_CT return S1AP_ConnHdlr {
var S1AP_ConnHdlr ret := null;
var integer i;
for (i := 0; i < sizeof(S1apExpectTable); i := i+1) {
if (not ispresent(S1apExpectTable[i].imsi)) {
continue;
}
if (imsi == S1apExpectTable[i].imsi) {
ret := S1apExpectTable[i].vc_conn;
/* Release this entry */
S1apExpectTable[i].imsi := omit;
S1apExpectTable[i].vc_conn := null;
log("Found Expect[", i, "] for ", msg, " handled at ", ret);
return ret;
}
}
setverdict(fail, "Couldn't find Expect for ", msg);
mtc.stop;
}
private function f_create_expect(hexstring imsi, S1AP_ConnHdlr hdlr)
runs on S1AP_Emulation_CT {
var integer i;
/* Check an entry like this is not already presnt */
for (i := 0; i < sizeof(S1apExpectTable); i := i+1) {
if (imsi == S1apExpectTable[i].imsi) {
setverdict(fail, "IMSI already present", imsi);
mtc.stop;
}
}
for (i := 0; i < sizeof(S1apExpectTable); i := i+1) {
if (not ispresent(S1apExpectTable[i].imsi)) {
S1apExpectTable[i].imsi := imsi;
S1apExpectTable[i].vc_conn := hdlr;
log("Created Expect[", i, "] for ", imsi, " to be handled at ", hdlr);
return;
}
}
testcase.stop("No space left in S1apExpectTable")
}
/* client/conn_hdlr side function to use procedure port to create expect in emulation */
function f_create_s1ap_expect(hexstring imsi) runs on S1AP_ConnHdlr {
S1AP_PROC.call(S1APEM_register:{imsi, self}) {
[] S1AP_PROC.getreply(S1APEM_register:{?,?}) {};
}
}
private function f_expect_table_init()
runs on S1AP_Emulation_CT {
var integer i;
for (i := 0; i < sizeof(S1apExpectTable); i := i + 1) {
S1apExpectTable[i].imsi := omit;
}
}
function DummyUnitdataCallback(S1AP_PDU msg)
runs on S1AP_Emulation_CT return template S1AP_PDU {
log("Ignoring S1AP ", msg);
return omit;
}
function f_S1AP_get_ENB_UE_S1AP_ID(S1AP_PDU s1ap) return template (omit) ENB_UE_S1AP_ID
{
if (ischosen(s1ap.initiatingMessage)) {
var InitiatingMessage im := s1ap.initiatingMessage;
select (s1ap) {
case (tr_S1AP_InitialUE) {
return im.value_.InitialUEMessage.protocolIEs[0].value_.ENB_UE_S1AP_ID;
}
case (tr_S1AP_DlNasTransport) {
return im.value_.DownlinkNASTransport.protocolIEs[1].value_.ENB_UE_S1AP_ID;
}
case (tr_S1AP_UlNasTransport) {
return im.value_.UplinkNASTransport.protocolIEs[1].value_.ENB_UE_S1AP_ID;
}
case (tr_S1AP_IntialCtxSetupReq) {
return im.value_.initialContextSetupRequest.protocolIEs[1].value_.ENB_UE_S1AP_ID;
}
case (tr_S1AP_UeContextReleaseReq) {
return im.value_.UEContextReleaseRequest.protocolIEs[1].value_.ENB_UE_S1AP_ID;
}
/* UeContextReleaseCmd needs special handling; it can contain any number of MME/UE IDs */
case (tr_S1AP_ConnEstInd) {
return im.value_.ConnectionEstablishmentIndication.protocolIEs[1].value_.ENB_UE_S1AP_ID;
}
/* TODO */
}
} else if (ischosen(s1ap.successfulOutcome)) {
var SuccessfulOutcome so := s1ap.successfulOutcome;
select (s1ap) {
case (tr_S1AP_InitialCtxSetupResp) {
return so.value_.initialContextSetupResponse.protocolIEs[1].value_.ENB_UE_S1AP_ID;
}
case (tr_S1AP_UeContextReleaseCompl) {
return so.value_.UEContextReleaseComplete.protocolIEs[1].value_.ENB_UE_S1AP_ID;
}
/* TODO */
}
} else if (ischosen(s1ap.unsuccessfulOutcome)) {
var UnsuccessfulOutcome uo := s1ap.unsuccessfulOutcome;
select (s1ap) {
case (tr_S1AP_InitialCtxSetupFail) {
return uo.value_.initialContextSetupFailure.protocolIEs[1].value_.ENB_UE_S1AP_ID;
}
/* TODO */
}
}
return omit;
}
function f_S1AP_get_MME_UE_S1AP_ID(S1AP_PDU s1ap) return template (omit) MME_UE_S1AP_ID
{
if (ischosen(s1ap.initiatingMessage)) {
var InitiatingMessage im := s1ap.initiatingMessage;
select (s1ap) {
case (tr_S1AP_DlNasTransport) {
return im.value_.DownlinkNASTransport.protocolIEs[0].value_.MME_UE_S1AP_ID;
}
case (tr_S1AP_UlNasTransport) {
return im.value_.UplinkNASTransport.protocolIEs[0].value_.MME_UE_S1AP_ID;
}
case (tr_S1AP_IntialCtxSetupReq) {
return im.value_.initialContextSetupRequest.protocolIEs[0].value_.MME_UE_S1AP_ID;
}
case (tr_S1AP_UeContextReleaseReq) {
return im.value_.UEContextReleaseRequest.protocolIEs[0].value_.MME_UE_S1AP_ID;
}
/* UeContextReleaseCmd needs special handling; it can contain any number of MME/UE IDs */
case (tr_S1AP_ConnEstInd) {
return im.value_.ConnectionEstablishmentIndication.protocolIEs[0].value_.MME_UE_S1AP_ID;
}
/* TODO */
}
} else if (ischosen(s1ap.successfulOutcome)) {
var SuccessfulOutcome so := s1ap.successfulOutcome;
select (s1ap) {
case (tr_S1AP_InitialCtxSetupResp) {
return so.value_.initialContextSetupResponse.protocolIEs[0].value_.MME_UE_S1AP_ID;
}
case (tr_S1AP_UeContextReleaseCompl) {
return so.value_.UEContextReleaseComplete.protocolIEs[0].value_.MME_UE_S1AP_ID;
}
/* TODO */
}
} else if (ischosen(s1ap.unsuccessfulOutcome)) {
var UnsuccessfulOutcome uo := s1ap.unsuccessfulOutcome;
select (s1ap) {
case (tr_S1AP_InitialCtxSetupFail) {
return uo.value_.initialContextSetupFailure.protocolIEs[0].value_.MME_UE_S1AP_ID;
}
/* TODO */
}
}
return omit;
}
function f_S1AP_get_NAS_PDU(S1AP_PDU s1ap) return template (omit) NAS_PDU
{
var integer i;
if (ischosen(s1ap.initiatingMessage)) {
var InitiatingMessage im := s1ap.initiatingMessage;
select (s1ap) {
case (tr_S1AP_DlNasTransport) {
var DownlinkNASTransport msg := im.value_.DownlinkNASTransport;
for (i := 0; i < lengthof(msg.protocolIEs); i := i+1) {
if (msg.protocolIEs[i].id == id_NAS_PDU) {
return msg.protocolIEs[i].value_.NAS_PDU;
}
}
}
case (tr_S1AP_UlNasTransport) {
var UplinkNASTransport msg := im.value_.UplinkNASTransport;
for (i := 0; i < lengthof(msg.protocolIEs); i := i+1) {
if (msg.protocolIEs[i].id == id_NAS_PDU) {
return msg.protocolIEs[i].value_.NAS_PDU;
}
}
}
}
}
return omit;
}
}

View File

@ -31,10 +31,17 @@ DIR=$BASEDIR/titan.ProtocolModules.SGsAP_13.2.0/src
FILES="SGsAP_Types.ttcn"
gen_links $DIR $FILES
DIR=../library/s1ap
FILES="S1AP_CommonDataTypes.asn S1AP_Constants.asn S1AP_Containers.asn S1AP_IEs.asn S1AP_PDU_Contents.asn
S1AP_PDU_Descriptions.asn "
FILES+="S1AP_EncDec.cc S1AP_Types.ttcn "
gen_links $DIR $FILES
DIR=../library
FILES="Misc_Helpers.ttcn General_Types.ttcn GSM_Types.ttcn Osmocom_Types.ttcn Native_Functions.ttcn Native_FunctionDefs.cc "
FILES+="SGsAP_Templates.ttcn SGsAP_CodecPort.ttcn SGsAP_CodecPort_CtrlFunct.ttcn SGsAP_CodecPort_CtrlFunctDef.cc SGsAP_Emulation.ttcn DNS_Helpers.ttcn "
FILES+="L3_Templates.ttcn "
FILES+="S1AP_CodecPort.ttcn "
gen_links $DIR $FILES
ignore_pp_results

View File

@ -1,6 +1,6 @@
#!/bin/sh
FILES="*.ttcn IPL4asp_PT.cc IPL4asp_discovery.cc Native_FunctionDefs.cc SGsAP_CodecPort_CtrlFunctDef.cc TCCConversion.cc TCCEncoding.cc TCCInterface.cc TELNETasp_PT.cc "
FILES="*.ttcn *.asn IPL4asp_PT.cc IPL4asp_discovery.cc Native_FunctionDefs.cc SGsAP_CodecPort_CtrlFunctDef.cc TCCConversion.cc TCCEncoding.cc TCCInterface.cc TELNETasp_PT.cc S1AP_EncDec.cc "
export CPPFLAGS_TTCN3=""