aboutsummaryrefslogtreecommitdiffstats
path: root/library/GTPv2_Emulation.ttcn
diff options
context:
space:
mode:
Diffstat (limited to 'library/GTPv2_Emulation.ttcn')
-rw-r--r--library/GTPv2_Emulation.ttcn260
1 files changed, 205 insertions, 55 deletions
diff --git a/library/GTPv2_Emulation.ttcn b/library/GTPv2_Emulation.ttcn
index 80742694..15720f99 100644
--- a/library/GTPv2_Emulation.ttcn
+++ b/library/GTPv2_Emulation.ttcn
@@ -14,10 +14,14 @@ module GTPv2_Emulation {
import from IPL4asp_Types all;
import from General_Types all;
import from Osmocom_Types all;
+import from GTPU_Types all;
+import from GTPv1U_CodecPort all;
+import from GTPv1U_CodecPort_CtrlFunct all;
import from GTPv2_Types all;
import from GTPv2_Templates all;
import from GTPv2_CodecPort all;
import from GTPv2_CodecPort_CtrlFunct all;
+import from SCTP_Templates all;
import from UECUPS_Types all;
import from UECUPS_CodecPort all;
@@ -40,8 +44,8 @@ type record Gtp2EmulationCfg {
IPL4asp_Types.PortNumber gtpc_bind_port,
HostName gtpc_remote_ip,
IPL4asp_Types.PortNumber gtpc_remote_port,
- //HostName gtpu_bind_ip,
- //PortNumber gtpu_bind_port,
+ HostName gtpu_bind_ip optional,
+ IPL4asp_Types.PortNumber gtpu_bind_port optional,
boolean sgw_role,
boolean use_gtpu_daemon
};
@@ -49,6 +53,7 @@ type record Gtp2EmulationCfg {
type component GTPv2_Emulation_CT {
/* Communication with underlying GTP CodecPort */
port GTPv2C_PT GTP2C;
+ port GTPU_PT GTPU;
/* Control port to GTP-U Daemon */
port UECUPS_CODEC_PT UECUPS;
@@ -62,14 +67,16 @@ type component GTPv2_Emulation_CT {
var Gtp2EmulationCfg g_gtp2_cfg;
/* State */
- var GtpPeer g_peer;
- var integer g_gtp2c_id;
+ var Gtp2cPeer g_peer;
+ var integer g_gtp2c_id, g_gtp1u_id;
var OCT1 g_restart_ctr;
var uint16_t g_c_seq_nr;
var TidTableRec TidTable[256];
var SeqTableRec SeqTable[256];
var ImsiTableRec ImsiTable[256];
+ var UdMsgTableRec UdMsgTable[256];
var PidTableRec PidTable[256];
+
var integer g_uecups_conn_id;
};
@@ -91,6 +98,12 @@ type record ImsiTableRec {
GTP2_ConnHdlr vc_conn
};
+/* Unit data message type <-> ConnHdlr mapping */
+type record UdMsgTableRec {
+ OCT1 messageType,
+ GTP2_ConnHdlr vc_conn
+};
+
/* pid <-> ConnHdlr mapping (for UECUPS process termination indication) */
type record PidTableRec {
/* process ID of the running process */
@@ -99,6 +112,16 @@ type record PidTableRec {
GTP2_ConnHdlr vc_conn
};
+private function f_teid_known(OCT4 teid) runs on GTPv2_Emulation_CT return boolean {
+ var integer i;
+ for (i := 0; i < sizeof(TidTable); i := i+1) {
+ if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
+ return true;
+ }
+ }
+ return false;
+}
+
private function f_comp_by_teid(OCT4 teid) runs on GTPv2_Emulation_CT return GTP2_ConnHdlr {
var integer i;
for (i := 0; i < sizeof(TidTable); i := i+1) {
@@ -131,6 +154,16 @@ private function f_comp_by_seq(OCT3 seq) runs on GTPv2_Emulation_CT return GTP2_
mtc.stop;
}
+private function f_imsi_known(hexstring imsi) runs on GTPv2_Emulation_CT return boolean {
+ var integer i;
+ for (i := 0; i < sizeof(ImsiTable); i := i+1) {
+ if (isbound(ImsiTable[i].imsi) and ImsiTable[i].imsi == imsi) {
+ return true;
+ }
+ }
+ return false;
+}
+
private function f_comp_by_imsi(hexstring imsi) runs on GTPv2_Emulation_CT return GTP2_ConnHdlr {
var integer i;
for (i := 0; i < sizeof(ImsiTable); i := i+1) {
@@ -202,6 +235,18 @@ private function f_imsi_tbl_add(hexstring imsi, GTP2_ConnHdlr vc_conn) runs on G
testcase.stop("No Space in IMSI Table for ", imsi);
}
+private function f_udmsg_tbl_add(OCT1 messageType, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
+ var integer i;
+ for (i := 0; i < sizeof(UdMsgTable); i := i+1) {
+ if (not isbound(UdMsgTable[i].messageType)) {
+ UdMsgTable[i].messageType := messageType;
+ UdMsgTable[i].vc_conn := vc_conn;
+ return;
+ }
+ }
+ testcase.stop("No Space in UdMsg Table for messateType ", messageType);
+}
+
private function f_pid_tbl_add(integer pid, GTP2_ConnHdlr vc_conn) runs on GTPv2_Emulation_CT {
var integer i;
for (i := 0; i < sizeof(PidTable); i := i+1) {
@@ -320,12 +365,72 @@ function f_gtp2c_extract_imsi(PDU_GTPCv2 gtp) return template (omit) hexstring {
return omit;
}
-private template (value) SctpTuple ts_SCTP(template (omit) integer ppid := omit) := {
- sinfo_stream := omit,
- sinfo_ppid := ppid,
- remSocks := omit,
- assocId := omit
-};
+private function f_gtp2c_is_initial_msg(PDU_GTPCv2 msg) return boolean
+{
+ if (ischosen(msg.gtpcv2_pdu.echoRequest) or
+ ischosen(msg.gtpcv2_pdu.versionNotSupported) or
+ ischosen(msg.gtpcv2_pdu.createSessionRequest) or
+ ischosen(msg.gtpcv2_pdu.createBearerRequest) or
+ ischosen(msg.gtpcv2_pdu.bearerResourceCommand) or
+ ischosen(msg.gtpcv2_pdu.bearerResourceFailureIndication) or
+ ischosen(msg.gtpcv2_pdu.modifyBearerRequest) or
+ ischosen(msg.gtpcv2_pdu.deleteSessionRequest) or
+ ischosen(msg.gtpcv2_pdu.deleteBearerRequest) or
+ ischosen(msg.gtpcv2_pdu.downlinkDataNotification) or
+ ischosen(msg.gtpcv2_pdu.downlinkDataNotificationAcknowledgement) or
+ ischosen(msg.gtpcv2_pdu.downlinkDataNotificationFailureIndication) or
+ ischosen(msg.gtpcv2_pdu.deleteIndirectDataForwardingTunnelRequest) or
+ ischosen(msg.gtpcv2_pdu.modifyBearerCommand) or
+ ischosen(msg.gtpcv2_pdu.modifyBearerFailureIndication) or
+ ischosen(msg.gtpcv2_pdu.updateBearerRequest) or
+ ischosen(msg.gtpcv2_pdu.deleteBearerCommand) or
+ ischosen(msg.gtpcv2_pdu.createIndirectDataForwardingTunnelRequest) or
+ ischosen(msg.gtpcv2_pdu.releaseAccessBearersRequest) or
+ ischosen(msg.gtpcv2_pdu.stopPagingIndication) or
+ ischosen(msg.gtpcv2_pdu.modifyAccessBearersRequest) or
+ ischosen(msg.gtpcv2_pdu.remoteUEReportNotification) or
+ ischosen(msg.gtpcv2_pdu.remoteUEReportAcknowledge) or
+ ischosen(msg.gtpcv2_pdu.forwardRelocationRequest) or
+ ischosen(msg.gtpcv2_pdu.forwardRelocationCompleteNotification) or
+ ischosen(msg.gtpcv2_pdu.forwardRelocationCompleteAcknowledge) or
+ ischosen(msg.gtpcv2_pdu.contextRequest) or
+ ischosen(msg.gtpcv2_pdu.contextAcknowledge) or
+ ischosen(msg.gtpcv2_pdu.identificationRequest) or
+ ischosen(msg.gtpcv2_pdu.forwardAccessContextNotification) or
+ ischosen(msg.gtpcv2_pdu.forwardAccessContextAcknowledge) or
+ ischosen(msg.gtpcv2_pdu.detachNotification) or
+ ischosen(msg.gtpcv2_pdu.detachAcknowledge) or
+ ischosen(msg.gtpcv2_pdu.changeNotificationRequest) or
+ ischosen(msg.gtpcv2_pdu.relocationCancelRequest) or
+ ischosen(msg.gtpcv2_pdu.configurationTransferTunnel) or
+ ischosen(msg.gtpcv2_pdu.rAN_InformationRelay) or
+ ischosen(msg.gtpcv2_pdu.suspendNotification) or
+ ischosen(msg.gtpcv2_pdu.suspendAcknowledge) or
+ ischosen(msg.gtpcv2_pdu.resumeNotification) or
+ ischosen(msg.gtpcv2_pdu.resumeAcknowledge) or
+ ischosen(msg.gtpcv2_pdu.cSPagingIndication) or
+ ischosen(msg.gtpcv2_pdu.createForwardingTunnelRequest) or
+ ischosen(msg.gtpcv2_pdu.deletePDN_ConnectionSetRequest) or
+ ischosen(msg.gtpcv2_pdu.traceSessionActivation) or
+ ischosen(msg.gtpcv2_pdu.traceSessionDeactivation) or
+ ischosen(msg.gtpcv2_pdu.updatePDN_ConnectionSetRequest) or
+ ischosen(msg.gtpcv2_pdu.pGW_RestartNotification) or
+ ischosen(msg.gtpcv2_pdu.pGW_RestartNotificationAcknowledge) or
+ ischosen(msg.gtpcv2_pdu.pGW_DownlinkTriggeringNotification) or
+ ischosen(msg.gtpcv2_pdu.pGW_DownlinkTriggeringAcknowledge) or
+ ischosen(msg.gtpcv2_pdu.alertMMENotification) or
+ ischosen(msg.gtpcv2_pdu.alertMMEAcknowledge) or
+ ischosen(msg.gtpcv2_pdu.uEActivityNotification) or
+ ischosen(msg.gtpcv2_pdu.uEActivityAcknowledge) or
+ ischosen(msg.gtpcv2_pdu.mBMSSessionStartRequest) or
+ ischosen(msg.gtpcv2_pdu.mBMSSessionUpdateRequest) or
+ ischosen(msg.gtpcv2_pdu.mBMSSessionStopRequest) or
+ ischosen(msg.gtpcv2_pdu.iSR_StatusIndication) or
+ ischosen(msg.gtpcv2_pdu.uE_RegistrationQueryRequest)) {
+ return true;
+ }
+ return false;
+}
function tr_UECUPS_RecvFrom_R(template PDU_UECUPS msg)
runs on GTPv2_Emulation_CT return template UECUPS_RecvFrom {
@@ -341,28 +446,18 @@ runs on GTPv2_Emulation_CT return template UECUPS_RecvFrom {
}
-private template PortEvent tr_SctpAssocChange := {
- sctpEvent := {
- sctpAssocChange := ?
- }
-}
-private template PortEvent tr_SctpPeerAddrChange := {
- sctpEvent := {
- sctpPeerAddrChange := ?
- }
-}
-
private function f_uecups_xceive(template (value) PDU_UECUPS tx,
- template PDU_UECUPS rx_t := ?)
+ template PDU_UECUPS rx_t := ?, float time_out := 10.0)
runs on GTPv2_Emulation_CT return PDU_UECUPS {
- timer T := 10.0;
+ timer T := time_out;
var UECUPS_RecvFrom mrf;
UECUPS.send(t_UECUPS_Send(g_uecups_conn_id, tx));
+ T.start;
alt {
[] UECUPS.receive(tr_UECUPS_RecvFrom_R(rx_t)) -> value mrf { }
[] UECUPS.receive(tr_SctpAssocChange) { repeat; }
- [] UECUPS.receive(tr_SctpPeerAddrChange) { repeat; }
+ [] UECUPS.receive(tr_SctpPeerAddrChange) { repeat; }
[] T.timeout {
setverdict(fail, "Timeout waiting for ", rx_t);
mtc.stop;
@@ -388,17 +483,24 @@ private function f_init(Gtp2EmulationCfg cfg) runs on GTPv2_Emulation_CT {
remPort := g_gtp2_cfg.gtpc_remote_port
}
+ g_uecups_conn_id := res.connId;
+
if (g_gtp2_cfg.use_gtpu_daemon) {
map(self:UECUPS, system:UECUPS);
- res := UECUPS_CodecPort_CtrlFunct.f_IPL4_connect(UECUPS, mp_uecups_host, mp_uecups_port, "", -1, -1, { sctp := valueof(ts_SCTP) });
+ res := UECUPS_CodecPort_CtrlFunct.f_IPL4_connect(UECUPS, mp_uecups_host, mp_uecups_port, "", -1, -1,
+ { sctp := valueof(ts_SctpTuple) });
if (not ispresent(res.connId)) {
setverdict(fail, "Could not connect UECUPS socket, check your configuration");
testcase.stop;
}
- g_uecups_conn_id := res.connId;
/* clear all tunnel state in the daemon at start */
- f_uecups_xceive({reset_all_state := {}}, {reset_all_state_res:=?});
+ f_uecups_xceive({reset_all_state := {}}, {reset_all_state_res:=?}, 30.0);
+ } else if (isvalue(cfg.gtpu_bind_ip) and isvalue(cfg.gtpu_bind_port)) {
+ map(self:GTPU, system:GTPU);
+ res := GTPv1U_CodecPort_CtrlFunct.f_GTPU_listen(GTPU, cfg.gtpu_bind_ip,
+ cfg.gtpu_bind_port, {udp:={}});
+ g_gtp1u_id := res.connId;
}
/* make sure we always pass incoming UECUPS indications whenever receiving fom the UECUPS port */
@@ -417,11 +519,28 @@ var GTP2_ConnHdlr vc_conn;
}
}
+private function SendToUdMsgTable(Gtp2cUnitdata g2c_ud) runs on GTPv2_Emulation_CT {
+ var GTP2_ConnHdlr vc_conn;
+
+ for (var integer i := 0; i < sizeof(UdMsgTable); i := i + 1) {
+ if (isbound(UdMsgTable[i].messageType)) {
+ if (UdMsgTable[i].messageType == g2c_ud.gtpc.messageType) {
+ vc_conn := UdMsgTable[i].vc_conn;
+ CLIENT.send(g2c_ud.gtpc) to vc_conn;
+ }
+ }
+ }
+
+ return;
+}
+
function main(Gtp2EmulationCfg cfg) runs on GTPv2_Emulation_CT {
var Gtp2cUnitdata g2c_ud;
+ var Gtp1uUnitdata g1u_ud;
var PDU_GTPCv2 g2c;
var GTP2_ConnHdlr vc_conn;
var hexstring imsi;
+ var OCT1 messageType;
var OCT4 teid;
var PDU_UECUPS rx_uecups;
var UECUPS_CreateTun gtc;
@@ -435,28 +554,26 @@ function main(Gtp2EmulationCfg cfg) runs on GTPv2_Emulation_CT {
/* route inbound GTP2-C based on TEID, SEQ or IMSI */
[] GTP2C.receive(Gtp2cUnitdata:?) -> value g2c_ud {
var template hexstring imsi_t := f_gtp2c_extract_imsi(g2c_ud.gtpc);
- if (not ispresent(g2c_ud.gtpc.tEID) or g2c_ud.gtpc.tEID == int2oct(0, 4)) {
- /* if this is a response, route by SEQ */
- if (match(g2c_ud.gtpc, tr_PDU_GTP2C_msgtypes(gtp2_responses))
- and f_seq_known(g2c_ud.gtpc.sequenceNumber)) {
- vc_conn := f_comp_by_seq(g2c_ud.gtpc.sequenceNumber);
- CLIENT.send(g2c_ud.gtpc) to vc_conn;
- } else {
- TEID0.send(g2c_ud.gtpc);
- }
- } else if (ispresent(g2c_ud.gtpc.tEID) and g2c_ud.gtpc.tEID != int2oct(0, 4)) {
- vc_conn := f_comp_by_teid(g2c_ud.gtpc.tEID);
+ /* if this is a response, route by SEQ: */
+ if (match(g2c_ud.gtpc, tr_PDU_GTP2C_msgtypes(gtp2_responses))
+ and f_seq_known(g2c_ud.gtpc.sequenceNumber)) {
+ vc_conn := f_comp_by_seq(g2c_ud.gtpc.sequenceNumber);
CLIENT.send(g2c_ud.gtpc) to vc_conn;
- } else if (isvalue(imsi_t)) {
+ }else if (isvalue(imsi_t) and f_imsi_known(valueof(imsi_t))) {
vc_conn := f_comp_by_imsi(valueof(imsi_t));
CLIENT.send(g2c_ud.gtpc) to vc_conn;
+ } else if ((ispresent(g2c_ud.gtpc.tEID) and g2c_ud.gtpc.tEID != '00000000'O)
+ and f_teid_known(g2c_ud.gtpc.tEID)) {
+ vc_conn := f_comp_by_teid(g2c_ud.gtpc.tEID);
+ CLIENT.send(g2c_ud.gtpc) to vc_conn;
+ } else if ((not ispresent(g2c_ud.gtpc.tEID) or g2c_ud.gtpc.tEID == '00000000'O)
+ and f_teid_known('00000000'O)) {
+ vc_conn := f_comp_by_teid(g2c_ud.gtpc.tEID);
+ CLIENT.send(g2c_ud.gtpc) to vc_conn;
} else {
- /* Send to all clients */
- var integer i;
- for (i := 0; i < sizeof(TidTable); i := i+1) {
- if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
- CLIENT.send(g2c_ud.gtpc) to TidTable[i].vc_conn;
- }
+ SendToUdMsgTable(g2c_ud);
+ if (not ispresent(g2c_ud.gtpc.tEID) or g2c_ud.gtpc.tEID == '00000000'O) {
+ TEID0.send(g2c_ud.gtpc);
}
}
@@ -466,12 +583,23 @@ function main(Gtp2EmulationCfg cfg) runs on GTPv2_Emulation_CT {
}
}
+ [] GTPU.receive(Gtp1uUnitdata:?) -> value g1u_ud {
+ if (f_teid_known(g1u_ud.gtpu.teid)) {
+ vc_conn := f_comp_by_teid(g1u_ud.gtpu.teid);
+ CLIENT.send(g1u_ud) to vc_conn;
+ } else if (g1u_ud.gtpu.teid == '00000000'O) {
+ TEID0.send(g1u_ud);
+ } else {
+ log("No client registered for TEID=", g1u_ud.gtpu.teid, "!");
+ }
+ }
[] TEID0.receive(PDU_GTPCv2:?) -> value g2c sender vc_conn {
- /* patch in the next sequence number */
- /* FIXME: do this only for outbound requests */
- g2c.sequenceNumber := int2oct(g_c_seq_nr, 3);
- g_c_seq_nr := g_c_seq_nr + 1;
+ /* patch in the next sequence number on outbound Initial message */
+ if (f_gtp2c_is_initial_msg(g2c)) {
+ g2c.sequenceNumber := int2oct(g_c_seq_nr, 3);
+ g_c_seq_nr := g_c_seq_nr + 1;
+ }
/* build Gtp2cUnitdata */
g2c_ud := { peer := g_peer, gtpc := g2c };
GTP2C.send(g2c_ud);
@@ -479,12 +607,16 @@ function main(Gtp2EmulationCfg cfg) runs on GTPv2_Emulation_CT {
f_seq_tbl_add(g2c.sequenceNumber, vc_conn);
}
}
+ [] TEID0.receive(Gtp1uUnitdata:?) -> value g1u_ud sender vc_conn {
+ GTPU.send(g1u_ud);
+ }
[] CLIENT.receive(PDU_GTPCv2:?) -> value g2c sender vc_conn {
- /* patch in the next sequence number */
- /* FIXME: do this only for outbound requests */
- g2c.sequenceNumber := int2oct(g_c_seq_nr, 3);
- g_c_seq_nr := g_c_seq_nr + 1;
+ /* patch in the next sequence number on outbound Initial message */
+ if (f_gtp2c_is_initial_msg(g2c)) {
+ g2c.sequenceNumber := int2oct(g_c_seq_nr, 3);
+ g_c_seq_nr := g_c_seq_nr + 1;
+ }
/* build Gtp2cUnitdata */
g2c_ud := { peer := g_peer, gtpc := g2c };
GTP2C.send(g2c_ud);
@@ -492,11 +624,18 @@ function main(Gtp2EmulationCfg cfg) runs on GTPv2_Emulation_CT {
f_seq_tbl_add(g2c.sequenceNumber, vc_conn);
}
}
+ [] CLIENT.receive(Gtp1uUnitdata:?) -> value g1u_ud sender vc_conn {
+ GTPU.send(g1u_ud);
+ }
[] CLIENT_PROC.getcall(GTP2EM_register_imsi:{?}) -> param(imsi) sender vc_conn {
f_imsi_tbl_add(imsi, vc_conn);
CLIENT_PROC.reply(GTP2EM_register_imsi:{imsi}) to vc_conn;
}
+ [] CLIENT_PROC.getcall(GTP2EM_register_udmsg:{?}) -> param(messageType) sender vc_conn {
+ f_udmsg_tbl_add(messageType, vc_conn);
+ CLIENT_PROC.reply(GTP2EM_register_udmsg:{messageType}) to vc_conn;
+ }
[] CLIENT_PROC.getcall(GTP2EM_register_teid:{?}) -> param(teid) sender vc_conn {
f_tid_tbl_add(teid, vc_conn);
@@ -533,10 +672,11 @@ function main(Gtp2EmulationCfg cfg) runs on GTPv2_Emulation_CT {
* Interaction between Main and Client Components
***********************************************************************/
type port GTP2EM_PT message {
- inout PDU_GTPCv2, UECUPS_ProgramTermInd;
+ inout PDU_GTPCv2, Gtp1uUnitdata, UECUPS_ProgramTermInd;
} with { extension "internal" };
signature GTP2EM_register_imsi(hexstring imsi);
+signature GTP2EM_register_udmsg(OCT1 messageType);
signature GTP2EM_register_teid(OCT4 teid);
signature GTP2EM_allocate_teid() return OCT4;
signature GTP2EM_create_tunnel(UECUPS_CreateTun gtc);
@@ -544,7 +684,7 @@ signature GTP2EM_destroy_tunnel(UECUPS_DestroyTun gtd);
signature GTP2EM_start_program(UECUPS_StartProgram sprog) return UECUPS_StartProgramRes;
type port GTP2EM_PROC_PT procedure {
- inout GTP2EM_register_imsi, GTP2EM_register_teid, GTP2EM_allocate_teid,
+ inout GTP2EM_register_imsi, GTP2EM_register_udmsg, GTP2EM_register_teid, GTP2EM_allocate_teid,
GTP2EM_create_tunnel, GTP2EM_destroy_tunnel, GTP2EM_start_program;
} with { extension "internal" };
@@ -558,11 +698,21 @@ type component GTP2_ConnHdlr {
};
function f_gtp2_register_imsi(hexstring imsi) runs on GTP2_ConnHdlr {
+ /* 15-digit IMSIs are len(imsi)=15, but decoded messages are
+ * octet-aligned, hence the hexstring in messages is len(imsi)=16, where
+ * the last hex char is a padding 'F'H */
+ imsi := f_pad_bcd_number(imsi);
GTP2_PROC.call(GTP2EM_register_imsi:{imsi}) {
[] GTP2_PROC.getreply(GTP2EM_register_imsi:{imsi});
}
}
+function f_gtp2_register_udmsg(OCT1 messageType) runs on GTP2_ConnHdlr {
+ GTP2_PROC.call(GTP2EM_register_udmsg:{messageType}) {
+ [] GTP2_PROC.getreply(GTP2EM_register_udmsg:{messageType});
+ }
+}
+
function f_gtp2_register_teid(OCT4 teid) runs on GTP2_ConnHdlr {
GTP2_PROC.call(GTP2EM_register_teid:{teid}) {
[] GTP2_PROC.getreply(GTP2EM_register_teid:{teid});