diff options
Diffstat (limited to 'library/GTPv2_Emulation.ttcn')
-rw-r--r-- | library/GTPv2_Emulation.ttcn | 260 |
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}); |