2019-05-27 09:54:11 +00:00
|
|
|
/* GTP Emulation in TTCN-3
|
|
|
|
*
|
|
|
|
* (C) 2018 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
|
|
|
|
*/
|
|
|
|
|
2018-02-17 19:57:02 +00:00
|
|
|
module GTP_Emulation {
|
|
|
|
|
|
|
|
import from IPL4asp_Types all;
|
|
|
|
import from General_Types all;
|
|
|
|
import from Osmocom_Types all;
|
|
|
|
import from GTPC_Types all;
|
|
|
|
import from GTPU_Types all;
|
|
|
|
import from GTP_CodecPort all;
|
|
|
|
import from GTP_CodecPort_CtrlFunct all;
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* Main Emulation Component
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
const integer GTP0_PORT := 3386;
|
|
|
|
const integer GTP1C_PORT := 2123;
|
|
|
|
const integer GTP1U_PORT := 2152;
|
|
|
|
|
|
|
|
type record GtpEmulationCfg {
|
|
|
|
HostName gtpc_bind_ip,
|
|
|
|
PortNumber gtpc_bind_port,
|
|
|
|
HostName gtpu_bind_ip,
|
|
|
|
PortNumber gtpu_bind_port,
|
|
|
|
boolean sgsn_role
|
|
|
|
};
|
|
|
|
|
|
|
|
type component GTP_Emulation_CT {
|
|
|
|
/* Communication with underlying GTP CodecPort */
|
|
|
|
port GTPC_PT GTPC;
|
|
|
|
port GTPU_PT GTPU;
|
|
|
|
|
|
|
|
/* Communication with Clients */
|
|
|
|
port GTPEM_PT CLIENT;
|
|
|
|
port GTPEM_PROC_PT CLIENT_PROC;
|
2021-05-04 13:26:56 +00:00
|
|
|
port GTPEM_PT CLIENT_DEFAULT;
|
2018-02-17 19:57:02 +00:00
|
|
|
|
|
|
|
/* Configuration by the user */
|
|
|
|
var GtpEmulationCfg g_gtp_cfg;
|
|
|
|
|
|
|
|
/* State */
|
|
|
|
var integer g_gtpc_id, g_gtpu_id;
|
|
|
|
var OCT1 g_restart_ctr;
|
|
|
|
var uint16_t g_c_seq_nr, g_u_seq_nr;
|
|
|
|
var TidTableRec TidTable[16];
|
|
|
|
var ImsiTableRec ImsiTable[16];
|
|
|
|
};
|
|
|
|
|
|
|
|
type record TidTableRec {
|
|
|
|
OCT4 teid,
|
|
|
|
GTP_ConnHdlr vc_conn
|
|
|
|
};
|
|
|
|
|
|
|
|
type record ImsiTableRec {
|
|
|
|
hexstring imsi,
|
|
|
|
GTP_ConnHdlr vc_conn
|
|
|
|
};
|
|
|
|
|
|
|
|
private function f_comp_by_teid(OCT4 teid) runs on GTP_Emulation_CT return GTP_ConnHdlr {
|
|
|
|
var integer i;
|
|
|
|
for (i := 0; i < sizeof(TidTable); i := i+1) {
|
|
|
|
if (isbound(TidTable[i].teid) and TidTable[i].teid == teid) {
|
|
|
|
return TidTable[i].vc_conn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setverdict(fail, "No Component for TEID ", teid);
|
2018-07-05 15:35:03 +00:00
|
|
|
mtc.stop;
|
2018-02-17 19:57:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private function f_comp_by_imsi(hexstring imsi) runs on GTP_Emulation_CT return GTP_ConnHdlr {
|
|
|
|
var integer i;
|
|
|
|
for (i := 0; i < sizeof(ImsiTable); i := i+1) {
|
|
|
|
if (isbound(ImsiTable[i].imsi) and ImsiTable[i].imsi == imsi) {
|
|
|
|
return ImsiTable[i].vc_conn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setverdict(fail, "No Component for IMSI ", imsi);
|
2018-07-05 15:35:03 +00:00
|
|
|
mtc.stop;
|
2018-02-17 19:57:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private function f_tid_tbl_add(OCT4 teid, GTP_ConnHdlr vc_conn) runs on GTP_Emulation_CT {
|
|
|
|
var integer i;
|
|
|
|
for (i := 0; i < sizeof(TidTable); i := i+1) {
|
|
|
|
if (not isbound(TidTable[i].teid)) {
|
|
|
|
TidTable[i].teid := teid;
|
|
|
|
TidTable[i].vc_conn := vc_conn;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-07-05 15:35:03 +00:00
|
|
|
testcase.stop("No Space in TidTable for ", teid);
|
2018-02-17 19:57:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private function f_imsi_tbl_add(hexstring imsi, GTP_ConnHdlr vc_conn) runs on GTP_Emulation_CT {
|
|
|
|
var integer i;
|
|
|
|
for (i := 0; i < sizeof(ImsiTable); i := i+1) {
|
|
|
|
if (not isbound(ImsiTable[i].imsi)) {
|
|
|
|
ImsiTable[i].imsi := imsi;
|
|
|
|
ImsiTable[i].vc_conn := vc_conn;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-07-05 15:35:03 +00:00
|
|
|
testcase.stop("No Space in IMSI Table for ", imsi);
|
2018-02-17 19:57:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function f_gtpc_extract_imsi(PDU_GTPC gtp) return template (omit) hexstring {
|
|
|
|
if (ischosen(gtp.gtpc_pdu.createPDPContextRequest)) {
|
|
|
|
return gtp.gtpc_pdu.createPDPContextRequest.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestSGSN)) {
|
|
|
|
return gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestSGSN.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestGGSN)) {
|
|
|
|
return gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestGGSN.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestCGW)) {
|
|
|
|
return gtp.gtpc_pdu.updatePDPContextRequest.updatePDPContextRequestCGW.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.pdu_NotificationRequest)) {
|
|
|
|
return gtp.gtpc_pdu.pdu_NotificationRequest.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.sendRouteingInformationForGPRSRequest)) {
|
|
|
|
return gtp.gtpc_pdu.sendRouteingInformationForGPRSRequest.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.sendRouteingInformationForGPRSResponse)) {
|
|
|
|
return gtp.gtpc_pdu.sendRouteingInformationForGPRSResponse.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.failureReportRequest)) {
|
|
|
|
return gtp.gtpc_pdu.failureReportRequest.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.noteMS_GPRSPresentRequest)) {
|
|
|
|
return gtp.gtpc_pdu.noteMS_GPRSPresentRequest.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.identificationResponse) ){
|
|
|
|
return gtp.gtpc_pdu.identificationResponse.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.sgsn_ContextRequest)) {
|
|
|
|
return gtp.gtpc_pdu.sgsn_ContextRequest.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.sgsn_ContextResponse)) {
|
|
|
|
return gtp.gtpc_pdu.sgsn_ContextResponse.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.forwardRelocationRequest)) {
|
|
|
|
return gtp.gtpc_pdu.forwardRelocationRequest.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.relocationCancelRequest)) {
|
|
|
|
return gtp.gtpc_pdu.relocationCancelRequest.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.uERegistrationQueryRequest)) {
|
|
|
|
return gtp.gtpc_pdu.uERegistrationQueryRequest.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.uERegistrationQueryResponse)) {
|
|
|
|
return gtp.gtpc_pdu.uERegistrationQueryResponse.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.mBMSNotificationRequest)) {
|
|
|
|
return gtp.gtpc_pdu.mBMSNotificationRequest.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.createMBMSContextRequest)) {
|
|
|
|
return gtp.gtpc_pdu.createMBMSContextRequest.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.deleteMBMSContextRequest)) {
|
|
|
|
return gtp.gtpc_pdu.deleteMBMSContextRequest.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.mS_InfoChangeNotificationRequest)) {
|
|
|
|
return gtp.gtpc_pdu.mS_InfoChangeNotificationRequest.imsi.digits;
|
|
|
|
} else if (ischosen(gtp.gtpc_pdu.mS_InfoChangeNotificationResponse)) {
|
|
|
|
return gtp.gtpc_pdu.mS_InfoChangeNotificationResponse.imsi.digits;
|
|
|
|
} else {
|
|
|
|
return omit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function f_init(GtpEmulationCfg cfg) runs on GTP_Emulation_CT {
|
|
|
|
var Result res;
|
|
|
|
|
|
|
|
map(self:GTPC, system:GTPC);
|
|
|
|
res := GTP_CodecPort_CtrlFunct.f_IPL4_listen(GTPC, cfg.gtpc_bind_ip,
|
|
|
|
cfg.gtpc_bind_port, {udp:={}});
|
|
|
|
g_gtpc_id := res.connId;
|
|
|
|
|
|
|
|
map(self:GTPU, system:GTPU);
|
|
|
|
res := GTP_CodecPort_CtrlFunct.f_GTPU_listen(GTPU, cfg.gtpu_bind_ip,
|
|
|
|
cfg.gtpu_bind_port, {udp:={}});
|
|
|
|
g_gtpu_id := res.connId;
|
|
|
|
|
|
|
|
g_restart_ctr := f_rnd_octstring(1);
|
|
|
|
g_c_seq_nr := f_rnd_int(65535);
|
|
|
|
g_u_seq_nr := f_rnd_int(65535);
|
|
|
|
g_gtp_cfg := cfg;
|
|
|
|
}
|
|
|
|
|
|
|
|
function main(GtpEmulationCfg cfg) runs on GTP_Emulation_CT {
|
|
|
|
var Gtp1cUnitdata g1c_ud;
|
|
|
|
var Gtp1uUnitdata g1u_ud;
|
|
|
|
var GTP_ConnHdlr vc_conn;
|
|
|
|
var hexstring imsi;
|
|
|
|
var OCT4 teid;
|
|
|
|
|
|
|
|
f_init(cfg);
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
alt {
|
|
|
|
/* route inbound GTP-C based on IMSI or TEID */
|
|
|
|
[] GTPC.receive(Gtp1cUnitdata:?) -> value g1c_ud {
|
|
|
|
var template hexstring imsi_t := f_gtpc_extract_imsi(g1c_ud.gtpc);
|
|
|
|
if (isvalue(imsi_t)) {
|
|
|
|
vc_conn := f_comp_by_imsi(valueof(imsi_t));
|
2018-07-10 16:38:17 +00:00
|
|
|
CLIENT.send(g1c_ud) to vc_conn;
|
|
|
|
} else if(g1c_ud.gtpc.teid != int2oct(0, 4)) {
|
2018-02-17 19:57:02 +00:00
|
|
|
vc_conn := f_comp_by_teid(g1c_ud.gtpc.teid);
|
2018-07-10 16:38:17 +00:00
|
|
|
CLIENT.send(g1c_ud) to vc_conn;
|
|
|
|
} else {
|
2021-05-04 13:26:56 +00:00
|
|
|
/* Check if a default port is set: */
|
|
|
|
if (CLIENT_DEFAULT.checkstate("Connected")) {
|
|
|
|
CLIENT_DEFAULT.send(g1c_ud);
|
|
|
|
} 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(g1c_ud) to TidTable[i].vc_conn;
|
|
|
|
}
|
2018-07-10 16:38:17 +00:00
|
|
|
}
|
|
|
|
}
|
2018-02-17 19:57:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
[] GTPU.receive(Gtp1uUnitdata:?) -> value g1u_ud {
|
|
|
|
vc_conn := f_comp_by_teid(g1u_ud.gtpu.teid);
|
|
|
|
CLIENT.send(g1u_ud) to vc_conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* transparently forward any GTP-C / GTP-U from clients to peer[s] */
|
|
|
|
[] CLIENT.receive(Gtp1cUnitdata:?) -> value g1c_ud sender vc_conn {
|
|
|
|
GTPC.send(g1c_ud);
|
|
|
|
}
|
|
|
|
[] CLIENT.receive(Gtp1uUnitdata:?) -> value g1u_ud sender vc_conn {
|
|
|
|
GTPU.send(g1u_ud);
|
|
|
|
}
|
2021-05-04 13:26:56 +00:00
|
|
|
[] CLIENT_DEFAULT.receive(Gtp1cUnitdata:?) -> value g1c_ud sender vc_conn {
|
|
|
|
GTPC.send(g1c_ud);
|
|
|
|
}
|
2018-02-17 19:57:02 +00:00
|
|
|
|
|
|
|
[] CLIENT_PROC.getcall(GTPEM_register_imsi:{?}) -> param(imsi) sender vc_conn {
|
|
|
|
f_imsi_tbl_add(imsi, vc_conn);
|
2018-05-31 20:17:46 +00:00
|
|
|
CLIENT_PROC.reply(GTPEM_register_imsi:{imsi}) to vc_conn;
|
2018-02-17 19:57:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
[] CLIENT_PROC.getcall(GTPEM_register_teid:{?}) -> param(teid) sender vc_conn {
|
|
|
|
f_tid_tbl_add(teid, vc_conn);
|
2018-05-31 20:17:46 +00:00
|
|
|
CLIENT_PROC.reply(GTPEM_register_teid:{teid}) to vc_conn;
|
2018-02-17 19:57:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* Interaction between Main and Client Components
|
|
|
|
***********************************************************************/
|
|
|
|
type port GTPEM_PT message {
|
|
|
|
inout Gtp1cUnitdata, Gtp1uUnitdata;
|
|
|
|
} with { extension "internal" };
|
|
|
|
|
|
|
|
signature GTPEM_register_imsi(hexstring imsi);
|
|
|
|
signature GTPEM_register_teid(OCT4 teid);
|
|
|
|
|
|
|
|
type port GTPEM_PROC_PT procedure {
|
|
|
|
inout GTPEM_register_imsi, GTPEM_register_teid;
|
|
|
|
} with { extension "internal" };
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* Client Compoennt
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
type component GTP_ConnHdlr {
|
|
|
|
port GTPEM_PT GTP;
|
|
|
|
port GTPEM_PROC_PT GTP_PROC;
|
|
|
|
};
|
|
|
|
|
|
|
|
function f_gtp_register_imsi(hexstring imsi) runs on GTP_ConnHdlr {
|
|
|
|
GTP_PROC.call(GTPEM_register_imsi:{imsi}) {
|
|
|
|
[] GTP_PROC.getreply(GTPEM_register_imsi:{imsi});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function f_gtp_register_teid(OCT4 teid) runs on GTP_ConnHdlr {
|
|
|
|
GTP_PROC.call(GTPEM_register_teid:{teid}) {
|
|
|
|
[] GTP_PROC.getreply(GTPEM_register_teid:{teid});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|