This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
Harald Welte gerrit-no-reply at lists.osmocom.orgHarald Welte has submitted this change and it was merged. Change subject: First actual SGSN test case ...................................................................... First actual SGSN test case Change-Id: Id66ddf8dbe1c5cfa96a087235588ba67763b7f05 --- D gprs_gb/LLC_Types.ttcn M gprs_gb/Test.ttcn M gprs_gb/gen_links.sh M gprs_gb/regen_makefile.sh M library/BSSGP_Emulation.ttcn A library/LLC_Templates.ttcn M sgsn/SGSN_Tests.ttcn M sgsn/gen_links.sh M sgsn/regen_makefile.sh 9 files changed, 857 insertions(+), 301 deletions(-) Approvals: Harald Welte: Looks good to me, approved Jenkins Builder: Verified diff --git a/gprs_gb/LLC_Types.ttcn b/gprs_gb/LLC_Types.ttcn deleted file mode 100644 index 8ca8f39..0000000 --- a/gprs_gb/LLC_Types.ttcn +++ /dev/null @@ -1,165 +0,0 @@ -module LLC_Types { - import from General_Types all; - import from Osmocom_Types all; - - /* TS 44.064 Chapter 6.2 */ - type record LlcAddressField { - BIT1 pd ('0'B), - boolean c_r, - BIT2 reserved, - LlcSapi sapi - } with { - variant (c_r) "FIELDLENGTH(1)" - }; - - template LlcAddressField t_LLC_Addr(template boolean c_r, template LlcSapi sapi) := { - pd := '0'B, - c_r := c_r, - reserved := '00'B, - sapi := sapi - }; - - const boolean LLC_CR_DL_CMD := true; - const boolean LLC_CR_DL_RSP := false; - const boolean LLC_CR_UL_CMD := false; - const boolean LLC_CR_UL_RSP := true; - - template LlcAddressField t_LLC_Addr_DlCmd(template LlcSapi sapi) := t_LLC_Addr(true, sapi); - template LlcAddressField t_LLC_Addr_DlRsp(template LlcSapi sapi) := t_LLC_Addr(false, sapi); - template LlcAddressField t_LLC_Addr_UlCmd(template LlcSapi sapi) := t_LLC_Addr(false, sapi); - template LlcAddressField t_LLC_Addr_UlRsp(template LlcSapi sapi) := t_LLC_Addr(true, sapi); - - type enumerated LlcSapi { - LLC_SAPI_RESERVED_0 ('0000'B), - LLC_SAPI_GMM ('0001'B), - LLC_SAPI_TOM2 ('0010'B), - LLC_SAPI_LL3 ('0011'B), - LLC_SAPI_RESERVED_4 ('0100'B), - LLC_SAPI_LL5 ('0101'B), - LLC_SAPI_RESERVED_6 ('0110'B), - LLC_SAPI_SMS ('0111'B), - LLC_SAPI_TOM8 ('1000'B), - LLC_SAPI_LL9 ('1001'B), - LLC_SAPI_RESERVED_10 ('1010'B), - LLC_SAPI_LL11 ('1011'B), - LLC_SAPI_RESERVED_12 ('1100'B), - LLC_SAPI_RESERVED_13 ('1101'B), - LLC_SAPI_RESERVED_14 ('1110'B), - LLC_SAPI_RESERVED_15 ('1111'B) - } with { variant "FIELDLENGTH(4)" }; - - /* TS 44.064 Chapter 6.3 */ - type record LlcCtrlFieldI { - BIT1 presence ('0'B), - boolean a, - BIT1 spare, - uint9_t n_s, - BIT1 spare2, - uint9_t n_r, - LlcCtrlS s - } with { variant - (a) "FIELDLENGTH(1)" - }; - - /* TS 44.064 Chapter 6.3 */ - type record LlcCtrlFieldS { - BIT2 presence ('10'B), - boolean a, - BIT2 spare, - uint9_t n_r, - LlcCtrlS s - } with { - variant (a) "FIELDLENGTH(1)" - }; - - /* TS 44.064 Chapter 6.3 */ - type record LlcCtrlFieldUI { - BIT3 presence ('110'B), - BIT2 spare, - uint9_t n_u, - boolean e, - boolean pm - } with { - variant (e) "FIELDLENGTH(1)" - variant (pm) "FIELDLENGTH(1)" - }; - - template LlcCtrlFieldUI t_LlcCtrlUI(template uint8_t n_u) := { - presence := '110'B, - spare := '00'B, - n_u := n_u, - e := false, - pm := true - }; - - /* TS 44.064 Chapter 6.3 */ - type record LlcCtrlFieldU { - BIT3 presence ('111'B), - boolean p_f, - LlcCtrlM m - } with { - variant (p_f) "FIELDLENGTH(1)" - }; - - - /* TS 44.064 Chapter 6.4 */ - type enumerated LlcCtrlS { - LLC_S_RR ('00'B), - LLC_S_ACK ('01'B), - LLC_S_RNR ('10'B), - LLC_S_SACK ('11'B) - } with { variant "FIELDLENGTH(2)" }; - - /* TS 44.064 Chapter 6.4 */ - type enumerated LlcCtrlM { - LLC_M_DM ('0001'B), - LLC_M_DISC ('0100'B), - LLC_M_UA ('0110'B), - LLC_M_SABM ('0111'B), - LLC_M_FRMR ('1000'B), - LLC_M_XID ('1011'B), - LLC_M_NULL ('0000'B) - } with { variant "FIELDLENGTH(4)" }; - - type union LlcCtrlUnion { - LlcCtrlFieldI i, - LlcCtrlFieldS s, - LlcCtrlFieldUI ui, - LlcCtrlFieldU u - } with { variant "TAG(i, presence = '0'B; - s, presence = '10'B; - ui, presence = '110'B; - u, presence = '111'B)" - variant "FIELDORDER(msb)" - }; - - external function enc_LlcCtrlUnion(in LlcCtrlUnion pdu) return octetstring - with { extension "prototype(convert) encode(RAW)" }; - external function dec_LlcCtrlUnion(in octetstring stream) return LlcCtrlUnion - with { extension "prototype(convert) decode(RAW)" }; - - - type uint24_t LlcFcs; - - type record LlcPdu { - LlcAddressField addr, - LlcCtrlUnion ctrl, - octetstring payload//, - //LlcFcs fcs - } with { variant "" }; - - external function enc_LlcPdu(in LlcPdu pdu) return octetstring - with { extension "prototype(convert) encode(RAW)" }; - external function dec_LlcPdu(in octetstring stream) return LlcPdu - with { extension "prototype(convert) decode(RAW)" }; - - - template LlcPdu t_LLC_UI(template boolean c_r, template uint8_t n_u, template octetstring payload, - template LlcSapi sapi := LLC_SAPI_GMM) := { - addr := t_LLC_Addr(c_r, sapi), - ctrl := { - ui := t_LlcCtrlUI(n_u) - }, - payload := payload - }; -} with { encode "RAW"; variant "FIELDORDER(msb)" } diff --git a/gprs_gb/Test.ttcn b/gprs_gb/Test.ttcn index 19cfd26..732b3f5 100644 --- a/gprs_gb/Test.ttcn +++ b/gprs_gb/Test.ttcn @@ -10,9 +10,27 @@ import from NS_Types all; import from NS_Emulation all; import from LLC_Types all; + import from LLC_Templates all; import from RLCMAC_Types all; import from RLCMAC_CSN1_Types all; import from LAPDm_RAW_PT all; + + modulepar { + BssgpConfig mp_gb_cfg := { + nsei := 96, + bvci := 196, + cell_id := { + ra_id := { + lai := { + mcc_mnc := '26242F'H, lac := 13135 + }, + rac := 0 + }, + cell_id := 20960 + }, + sgsn_role := true + }; + } type record MmContext { octetstring imsi optional, @@ -51,7 +69,7 @@ /* connect lower-end of NS emulation to NS_CODEC_PORT (on top of IPl4) */ map(ns_component:NSCP, system:NS_CODEC_PORT); ns_component.start(NSStart()); - bssgp_component.start(BssgpStart()); + bssgp_component.start(BssgpStart(mp_gb_cfg)); lapdm_component := lapdm_CT.create; connect(self:L1, lapdm_component:LAPDM_SP); @@ -138,20 +156,19 @@ const octetstring gmm_auth_req := '081200102198c72477ea104895e8b959acc58b108182'O; - function tx_gmm(boolean c_r, in octetstring gmm_pdu, LlcSapi sapi := LLC_SAPI_GMM) runs on dummy_CT { - var LlcPdu llc; + function tx_gmm(BIT1 c_r, in octetstring gmm_pdu, BIT4 sapi := c_LLC_SAPI_LLGMM) runs on dummy_CT { + var PDU_LLC llc; //log("GMM Tx: ", dec_PDU_L3_SGSN_MS(gmm_pdu)); log(c_r, g_mmctx.n_u, gmm_pdu, sapi); - log(t_LLC_UI(c_r, g_mmctx.n_u, gmm_pdu, sapi)); - llc := valueof(t_LLC_UI(c_r, g_mmctx.n_u, gmm_pdu, sapi)); + llc := valueof(ts_LLC_UI(gmm_pdu, sapi, c_r, g_mmctx.n_u)); log(llc); g_mmctx.n_u := g_mmctx.n_u + 1; - log(ts_BSSGP_DL_UD(g_mmctx.tlli, enc_LlcPdu(llc))); + log(ts_BSSGP_DL_UD(g_mmctx.tlli, enc_PDU_LLC(llc))); - BSSGP.send(ts_BSSGP_DL_UD(g_mmctx.tlli, enc_LlcPdu(llc))); + BSSGP.send(ts_BSSGP_DL_UD(g_mmctx.tlli, enc_PDU_LLC(llc))); } function f_bssgp_establish() runs on dummy_CT { @@ -179,10 +196,10 @@ BSSGP.send(ts_BSSGP_PS_PAGING_PTMSI(bvci, imsi, tmsi)); while (true) { - var PDU_BSSGP pdu; + var BssgpDecoded bd; alt { - [] BSSGP.receive(PDU_BSSGP:?) -> value pdu { - log("BSSGP Rx: ", pdu); + [] BSSGP.receive(tr_BD_L3_MT(?)) -> value bd { + log("BSSGP Rx: ", bd); } [] BSSGP.receive(t_BssgpStsInd(?, ?, BVC_S_UNBLOCKED)) { repeat; } [] BSSGP.receive { repeat; } @@ -322,7 +339,7 @@ var template RlcmacUlBlock blk := t_RLCMAC_UL_DATA_TLLI(0, 0, 0, {t_RLCMAC_LLCBLOCK(payload)}, false, tlli); L1.send(RLCMAC_ph_data_req:{tbf_id := 0, cs := cs, block := blk}); /* ensure that this LLC-PDU arrives from the right TLLI at the (simulated) SGSN */ - BSSGP.receive(tr_BSSGP_UL_UD(tlli, ?, payload)); + BSSGP.receive(tr_BD_BSSGP(tr_BSSGP_UL_UD(tlli, ?, payload))); /* ensure the MS eceives an UL_ACK_NACK */ alt { @@ -346,12 +363,12 @@ f_single_ul_block(CS1); while (true) { - var PDU_BSSGP pdu; + var BssgpDecoded bd; var RLCMAC_ph_data_ind dl_msg; alt { - [] BSSGP.receive(PDU_BSSGP:?) -> value pdu { - log("BSSGP Rx: ", pdu); + [] BSSGP.receive(tr_BD_BSSGP(?)) -> value bd { + log("BSSGP Rx: ", bd); } [] BSSGP.receive(t_BssgpStsInd(?, ?, BVC_S_UNBLOCKED)) { repeat; } [] BSSGP.receive { repeat; } @@ -369,12 +386,12 @@ f_bssgp_establish(); while (true) { - var PDU_BSSGP pdu; + var BssgpDecoded bd; alt { - [] BSSGP.receive(PDU_BSSGP:?) -> value pdu { - log("BSSGP Rx: ", pdu); + [] BSSGP.receive(tr_BD_BSSGP(?)) -> value bd { + log("BSSGP Rx: ", bd); //log("GMM Rx: ", dec_PDU_L3_MS_SGSN(pdu.payload)); - g_mmctx.tlli := oct2int(pdu.pDU_BSSGP_UL_UNITDATA.tLLI); + g_mmctx.tlli := oct2int(bd.bssgp.pDU_BSSGP_UL_UNITDATA.tLLI); tx_gmm(LLC_CR_DL_CMD, gmm_auth_req); } [] BSSGP.receive(t_BssgpStsInd(?, ?, BVC_S_UNBLOCKED)) { repeat; } @@ -386,7 +403,7 @@ function f_llc_dec_and_log(in octetstring inp) { log("LLC Input: ", inp); - var LlcPdu dec := dec_LlcPdu(inp); + var PDU_LLC dec := dec_PDU_LLC(inp); log("LLC Decoded: ", dec); } @@ -405,17 +422,13 @@ testcase TC_selftest_llc() runs on dummy_CT { const octetstring c_gmm_att_pcu := '01c001080103e5e000210a0005f4fb146ddd32f44000c8001d1b53432b37159ef9090070000dd9c6321200e00019b32c642401c00020170580460b'O; const octetstring c_gmm_att_pcu_nofcs := '01c001080103e5e000210a0005f4fb146ddd32f44000c8001d1b53432b37159ef9090070000dd9c6321200e00019b32c642401c000201705'O; - const octetstring c_ctrl_ui := 'c001'O; - - log(dec_LlcCtrlUnion(c_ctrl_ui)); - f_llc_assert(enc_LlcCtrlUnion({ ui := { presence := '110'B, spare := '00'B, n_u := 0, e := false, pm := true } }), c_ctrl_ui); f_llc_dec_and_log(c_gmm_att_pcu); //f_llc_assert(f_LLC_append_fcs(c_gmm_att_pcu_nofcs), c_gmm_att_pcu); - log(valueof(t_LLC_UI(LLC_CR_DL_CMD, g_mmctx.n_u, gmm_auth_req, LLC_SAPI_GMM))); - log(t_LLC_UI(LLC_CR_DL_CMD, g_mmctx.n_u, gmm_auth_req, LLC_SAPI_GMM)); + log(valueof(ts_LLC_UI(gmm_auth_req, c_LLC_SAPI_LLGMM, LLC_CR_DL_CMD, g_mmctx.n_u))); + log(ts_LLC_UI(gmm_auth_req, c_LLC_SAPI_LLGMM, LLC_CR_DL_CMD, g_mmctx.n_u)); } testcase TC_selftest_rlcmac() runs on dummy_CT { diff --git a/gprs_gb/gen_links.sh b/gprs_gb/gen_links.sh index 53b50fa..85a35ab 100755 --- a/gprs_gb/gen_links.sh +++ b/gprs_gb/gen_links.sh @@ -39,9 +39,17 @@ FILES="BSSGP_EncDec.cc BSSGP_Types.ttcn" gen_links $DIR $FILES +DIR=$BASEDIR/titan.ProtocolModules.MobileL3_v13.4.0/src +FILES="MobileL3_CC_Types.ttcn MobileL3_CommonIE_Types.ttcn MobileL3_GMM_SM_Types.ttcn MobileL3_MM_Types.ttcn MobileL3_RRM_Types.ttcn MobileL3_SMS_Types.ttcn MobileL3_SS_Types.ttcn MobileL3_Types.ttcn" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.ProtocolModules.LLC_v7.1.0/src +FILES="LLC_EncDec.cc LLC_Types.ttcn" +gen_links $DIR $FILES DIR=../library FILES="General_Types.ttcn GSM_Types.ttcn GSM_RR_Types.ttcn Osmocom_Types.ttcn RLCMAC_Types.ttcn RLCMAC_CSN1_Types.ttcn RLCMAC_EncDec.cc L1CTL_Types.ttcn L1CTL_PortType.ttcn LAPDm_RAW_PT.ttcn LAPDm_Types.ttcn " FILES+="NS_Emulation.ttcn NS_CodecPort.ttcn NS_CodecPort_CtrlFunct.ttcn NS_CodecPort_CtrlFunctDef.cc " FILES+="BSSGP_Emulation.ttcn Osmocom_Gb_Types.ttcn " +FILES+="LLC_Templates.ttcn L3_Templates.ttcn L3_Common.ttcn " gen_links $DIR $FILES diff --git a/gprs_gb/regen_makefile.sh b/gprs_gb/regen_makefile.sh index 656726b..8b79d73 100755 --- a/gprs_gb/regen_makefile.sh +++ b/gprs_gb/regen_makefile.sh @@ -1,5 +1,5 @@ #!/bin/sh -FILES="*.ttcn BSSGP_EncDec.cc IPL4asp_PT.cc IPL4asp_discovery.cc TCCConversion.cc TCCInterface.cc NS_CodecPort_CtrlFunctDef.cc UD_PT.cc RLCMAC_EncDec.cc" +FILES="*.ttcn BSSGP_EncDec.cc IPL4asp_PT.cc IPL4asp_discovery.cc TCCConversion.cc TCCInterface.cc NS_CodecPort_CtrlFunctDef.cc UD_PT.cc RLCMAC_EncDec.cc LLC_EncDec.cc" ../regen-makefile.sh Test.ttcn $FILES diff --git a/library/BSSGP_Emulation.ttcn b/library/BSSGP_Emulation.ttcn index b41cd25..f981adb 100644 --- a/library/BSSGP_Emulation.ttcn +++ b/library/BSSGP_Emulation.ttcn @@ -1,10 +1,22 @@ module BSSGP_Emulation { +import from General_Types all; +import from Osmocom_Types all; import from NS_Types all; import from NS_Emulation all; import from BSSGP_Types all; import from Osmocom_Gb_Types all; import from IPL4asp_Types all; + +import from MobileL3_GMM_SM_Types all; +import from MobileL3_Types all; + +import from LLC_Types all; +import from LLC_Templates all; + +/*********************************************************************** + * Communication between Client Components and Main Component + ***********************************************************************/ type record BssgpStatusIndication { Nsei nsei, @@ -25,11 +37,15 @@ /* port from our (internal) point of view */ type port BSSGP_SP_PT message { - in PDU_BSSGP; - out PDU_BSSGP, + in PDU_BSSGP, + PDU_L3_MS_SGSN, + PDU_L3_SGSN_MS; + out BssgpDecoded, NsStatusIndication, BssgpStatusIndication, - ASP_Event; + ASP_Event, + PDU_L3_MS_SGSN, + PDU_L3_SGSN_MS; } with { extension "internal" }; /* port from the user point of view */ @@ -37,12 +53,37 @@ in ASP_Event, NsStatusIndication, BssgpStatusIndication, - PDU_BSSGP; - out PDU_BSSGP; + BssgpDecoded, + PDU_L3_MS_SGSN, + PDU_L3_SGSN_MS; + out PDU_BSSGP, + PDU_L3_SGSN_MS, + PDU_L3_MS_SGSN; } with { extension "internal" }; -function BssgpStart(boolean sgsn_role := false) runs on BSSGP_CT { - g_sgsn_role := sgsn_role +signature BSSGP_register_client(hexstring imsi, OCT4 tlli, BssgpCellId cell_id); +signature BSSGP_unregister_client(hexstring imsi); + +type port BSSGP_PROC_PT procedure { + inout BSSGP_register_client, BSSGP_unregister_client; +} with { extension "internal" }; + + +/*********************************************************************** + * Client Component for a single MS/TLLI + ***********************************************************************/ + +type component BSSGP_Client_CT { + port BSSGP_PT BSSGP; + port BSSGP_PROC_PT BSSGP_PROC; +}; + +/*********************************************************************** + * Main Component + ***********************************************************************/ + +function BssgpStart(BssgpConfig cfg) runs on BSSGP_CT { + g_cfg := cfg; f_init(); f_ScanEvents(); } @@ -57,23 +98,53 @@ port NS_PT BSCP; /* NS-User SAP towards the user */ port BSSGP_SP_PT BSSGP_SP; + port BSSGP_PROC_PT BSSGP_PROC; - var boolean g_sgsn_role := true; + var BssgpConfig g_cfg; + var BvcState g_ptp_bvc_state := BVC_S_BLOCKED; timer g_T1 := 15.0; timer g_T2 := 60.0; + + var ClientEntity ClientTable[16]; } -modulepar { - Nsvci mp_nsei := 96; - Nsvci mp_bvci := 196; - BssgpCellId mp_cellid := { ra_id := { lai := { mcc_mnc := '26242F'H, lac := 13135}, rac := 0 }, cell_id := 20960 }; +type record ClientEntity { + OCT4 tlli, + OCT4 tlli_old optional, + hexstring imsi, + BssgpCellId cell_id, + BSSGP_Client_CT comp_ref, + /* LLC entities, one for each SAPI */ + LLC_Entity llc[16] }; -function f_BnsUdReq(template PDU_BSSGP pdu, BssgpBvci bvci := mp_bvci) return NsUnitdataRequest { +type record LLC_Entity { + boolean sgsn_role, + /* N(U) on transmit side for next PDU */ + uint9_t n_u_tx_next, + /* N(U) on receive side, last PDU */ + uint9_t n_u_rx_last optional +}; + +private template LLC_Entity t_LLC_init(boolean sgsn_role := false) := { + sgsn_role := sgsn_role, + n_u_tx_next := 0, + n_u_rx_last := - +} + +type record BssgpConfig { + Nsvci nsei, + Nsvci bvci, + BssgpCellId cell_id, + boolean sgsn_role +}; + +function f_BnsUdReq(template PDU_BSSGP pdu, BssgpBvci bvci) +runs on BSSGP_CT return NsUnitdataRequest { var NsUnitdataRequest udr := { bvci := bvci, - nsei := mp_nsei, + nsei := g_cfg.nsei, /* for some weird reason we get "Dynamic test case error: Text encoder: Encoding an * unbound integer value." when trying to send the reocrd rather than the octetstring */ //sdu := omit, @@ -84,10 +155,11 @@ return udr; } -function f_BnsUdInd(template PDU_BSSGP pdu, template BssgpBvci bvci := mp_bvci) return template NsUnitdataIndication { +function f_BnsUdInd(template PDU_BSSGP pdu, template BssgpBvci bvci) +runs on BSSGP_CT return template NsUnitdataIndication { var template NsUnitdataIndication udi := { bvci := bvci, - nsei := mp_nsei, + nsei := g_cfg.nsei, sdu := *, bssgp := pdu } @@ -97,11 +169,15 @@ private function f_change_state(BvcState new_state) runs on BSSGP_CT { log("BSSGP State Transition: ", g_ptp_bvc_state, " -> ", new_state); g_ptp_bvc_state := new_state; - BSSGP_SP.send(t_BssgpStsInd(mp_nsei, mp_bvci, g_ptp_bvc_state)); + for (var integer i := 0; i < sizeof(ClientTable); i := i+1) { + if (isbound(ClientTable[i].comp_ref)) { + BSSGP_SP.send(t_BssgpStsInd(g_cfg.nsei, g_cfg.bvci, g_ptp_bvc_state)) to ClientTable[i].comp_ref; + } + } } private function f_sendReset() runs on BSSGP_CT { - var PDU_BSSGP pdu := valueof(ts_BVC_RESET(BSSGP_CAUSE_OM_INTERVENTION, mp_bvci, mp_cellid)); + var PDU_BSSGP pdu := valueof(ts_BVC_RESET(BSSGP_CAUSE_OM_INTERVENTION, g_cfg.bvci, g_cfg.cell_id)); log("PDU: ", pdu); log("ENC: ", enc_PDU_BSSGP(pdu)); @@ -112,24 +188,177 @@ } private function f_sendUnblock() runs on BSSGP_CT { - BSCP.send(f_BnsUdReq(t_BVC_UNBLOCK(mp_bvci), 0)); + BSCP.send(f_BnsUdReq(t_BVC_UNBLOCK(g_cfg.bvci), 0)); g_T1.start; } private function f_sendBlock(BssgpCause cause) runs on BSSGP_CT { - BSCP.send(f_BnsUdReq(t_BVC_BLOCK(mp_bvci, cause), 0)); + BSCP.send(f_BnsUdReq(t_BVC_BLOCK(g_cfg.bvci, cause), 0)); g_T1.start; } private function f_sendStatus(BssgpCause cause, PDU_BSSGP pdu) runs on BSSGP_CT { /* FIXME: Make sure correct Signaling or PTP BVCI is used! */ - BSCP.send(f_BnsUdReq(ts_BSSGP_STATUS(mp_bvci, cause, pdu))); + BSCP.send(f_BnsUdReq(ts_BSSGP_STATUS(g_cfg.bvci, cause, pdu), g_cfg.bvci)); +} + +/* attempt to extract the TLLI from a BSSGP PDU */ +function f_bssgp_get_tlli(PDU_BSSGP bssgp) return template OCT4 { + if (ischosen(bssgp.pDU_BSSGP_DL_UNITDATA)) { + return bssgp.pDU_BSSGP_DL_UNITDATA.tLLI_current; + } else if (ischosen(bssgp.pDU_BSSGP_UL_UNITDATA)) { + return bssgp.pDU_BSSGP_UL_UNITDATA.tLLI; + } else if (ischosen(bssgp.pDU_BSSGP_RA_CAPABILITY)) { + return bssgp.pDU_BSSGP_RA_CAPABILITY.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_RA_CAPABILITY_UPDATE)) { + return bssgp.pDU_BSSGP_RA_CAPABILITY_UPDATE.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_RA_CAPABILITY_UPDATE_ACK)) { + return bssgp.pDU_BSSGP_RA_CAPABILITY_UPDATE_ACK.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_RADIO_STATUS)) { + return bssgp.pDU_BSSGP_RADIO_STATUS.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_SUSPEND)) { + return bssgp.pDU_BSSGP_SUSPEND.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_SUSPEND_ACK)) { + return bssgp.pDU_BSSGP_SUSPEND_ACK.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_SUSPEND_NACK)) { + return bssgp.pDU_BSSGP_SUSPEND_NACK.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_RESUME)) { + return bssgp.pDU_BSSGP_RESUME.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_RESUME_ACK)) { + return bssgp.pDU_BSSGP_RESUME_ACK.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_RESUME_NACK)) { + return bssgp.pDU_BSSGP_RESUME_NACK.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_FLUSH_LL)) { + return bssgp.pDU_BSSGP_FLUSH_LL.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_FLUSH_LL_ACK)) { + return bssgp.pDU_BSSGP_FLUSH_LL_ACK.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_LLC_DISCARDED)) { + return bssgp.pDU_BSSGP_LLC_DISCARDED.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_LLC_DISCARDED)) { + return bssgp.pDU_BSSGP_LLC_DISCARDED.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_PAGING_CS) and + isvalue(bssgp.pDU_BSSGP_PAGING_CS.tLLI)) { + return bssgp.pDU_BSSGP_PAGING_CS.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_FLOW_CONTROL_MS)) { + return bssgp.pDU_BSSGP_FLOW_CONTROL_MS.tLLI.tLLI_Value; + } else if (ischosen(bssgp.pDU_BSSGP_FLOW_CONTROL_MS_ACK)) { + return bssgp.pDU_BSSGP_FLOW_CONTROL_MS_ACK.tLLI.tLLI_Value; + } + /* TODO: Handover, PFC, LCS */ + return omit; +} + +/* +private function f_tbl_init() runs on BSSGP_CT { + var integer i; + for (i := 0; i < sizeof(ImsiTable); i := i+1) { + ImsiTable[i] := -; + } + + for (i := 0; i < sizeof(TlliTable); i := i+1) { + TlliTable[i] := -; + } +} +*/ + +private function f_tbl_client_add(hexstring imsi, OCT4 tlli, BssgpCellId cell_id, BSSGP_Client_CT vc_conn) +runs on BSSGP_CT { + var integer i; + for (i := 0; i < sizeof(ClientTable); i := i+1) { + if (not isbound(ClientTable[i].comp_ref)) { + log("Adding Client IMSI=", imsi, ", TLLI=", tlli, ", index=", i); + ClientTable[i] := { + tlli := tlli, + tlli_old := omit, + imsi := imsi, + cell_id := cell_id, + comp_ref := vc_conn, + llc := - + }; + for (var integer j := 0; j < sizeof(ClientTable[i].llc); j := j+1) { + ClientTable[i].llc[j] := valueof(t_LLC_init(g_cfg.sgsn_role)); + } + return; + } + } + setverdict(fail, "Client Table full"); + self.stop; +} + +private function f_tbl_client_del(hexstring imsi, BSSGP_Client_CT vc_conn) runs on BSSGP_CT { + var integer i; + for (i := 0; i < sizeof(ClientTable); i := i+1) { + if (isbound(ClientTable[i].imsi) and ClientTable[i].imsi == imsi) { + if (ClientTable[i].comp_ref != vc_conn) { + setverdict(fail, "Cannot unregister IMSI ", imsi, " registred to ", + ClientTable[i].comp_ref, " from ", vc_conn); + self.stop; + } + log("Removing Client IMSI=", imsi, ", index=", i); + ClientTable[i] := { -, omit, -, -, - }; + return; + } + } + setverdict(fail, "Could not find client for IMSI ", imsi); + self.stop; +} + +private function f_tbl_comp_by_imsi(hexstring imsi) runs on BSSGP_CT return BSSGP_Client_CT { + var integer i; + for (i := 0; i < sizeof(ClientTable); i := i+1) { + if (isbound(ClientTable[i].imsi) and isbound(ClientTable[i].comp_ref) + and ClientTable[i].imsi == imsi) { + return ClientTable[i].comp_ref; + } + } + setverdict(fail, "Couldn't find Component for IMSI ", imsi); + self.stop; +} + +private function f_tbl_comp_by_tlli(OCT4 tlli) runs on BSSGP_CT return BSSGP_Client_CT { + var integer i; + for (i := 0; i < sizeof(ClientTable); i := i+1) { + if (isbound(ClientTable[i].comp_ref) and + (isbound(ClientTable[i].tlli) and (ClientTable[i].tlli == tlli or + isbound(ClientTable[i].tlli_old) and ClientTable[i].tlli_old == tlli) )) { + return ClientTable[i].comp_ref; + } + } + setverdict(fail, "Couldn't find Component for TLLI ", tlli); + self.stop; +} + +private function f_tbl_idx_by_comp(BSSGP_Client_CT comp_ref) runs on BSSGP_CT return integer { + var integer i; + for (i := 0; i < sizeof(ClientTable); i := i+1) { + if (isbound(ClientTable[i].comp_ref) and ClientTable[i].comp_ref == comp_ref) { + return i; + } + } + setverdict(fail, "Couldn't find Client for Component ", comp_ref); + self.stop; +} + +private function f_tbl_tlli_by_comp(BSSGP_Client_CT comp_ref) runs on BSSGP_CT return OCT4 { + var integer i; + for (i := 0; i < sizeof(ClientTable); i := i+1) { + if (isbound(ClientTable[i].tlli) and isbound(ClientTable[i].comp_ref) + and ClientTable[i].comp_ref == comp_ref) { + return ClientTable[i].tlli; + } + } + setverdict(fail, "Couldn't find TLLI for Component ", comp_ref); + self.stop; } altstep as_allstate() runs on BSSGP_CT { + var BSSGP_Client_CT vc_conn; var NsUnitdataIndication udi; var NsStatusIndication nsi; var ASP_Event evt; + var hexstring imsi; + var OCT4 tlli; + var BssgpCellId cell_id; /* Respond to BLOCK for wrong NSVCI */ [] BSCP.receive(f_BnsUdInd(t_BVC_BLOCK(?, ?), 0)) -> value udi { @@ -138,16 +367,16 @@ } /* Respond to RESET with correct BVCI/CellID */ - [] BSCP.receive(f_BnsUdInd(tr_BVC_RESET(?, mp_bvci, mp_cellid), 0)) -> value udi { - log("Rx BVC-RESET for Our BVCI=", mp_bvci); - BSCP.send(f_BnsUdReq(ts_BVC_RESET_ACK(mp_bvci, mp_cellid), 0)); + [] BSCP.receive(f_BnsUdInd(tr_BVC_RESET(?, g_cfg.bvci, g_cfg.cell_id), 0)) -> value udi { + log("Rx BVC-RESET for Our BVCI=", g_cfg.bvci); + BSCP.send(f_BnsUdReq(ts_BVC_RESET_ACK(g_cfg.bvci, g_cfg.cell_id), 0)); f_change_state(BVC_S_UNBLOCKED); } /* Respond to RESET for signalling BVCI 0 */ - [] BSCP.receive(f_BnsUdInd(tr_BVC_RESET(?, 0, mp_cellid), 0)) -> value udi { + [] BSCP.receive(f_BnsUdInd(tr_BVC_RESET(?, 0, g_cfg.cell_id), 0)) -> value udi { log("Rx BVC-RESET for Signaling BVCI=0"); - BSCP.send(f_BnsUdReq(ts_BVC_RESET_ACK(0, mp_cellid), 0)); + BSCP.send(f_BnsUdReq(ts_BVC_RESET_ACK(0, g_cfg.cell_id), 0)); } /* Respond to RESET with wrong NSEI/NSVCI */ @@ -162,95 +391,258 @@ f_sendStatus(BSSGP_CAUSE_PDU_NOT_COMPATIBLE_WITH_PROTOCOL_STATE, udi.bssgp); } /* Forwarding of ASP_Event and NsStatusIndication to user */ - [] BSCP.receive(ASP_Event:?) -> value evt { BSSGP_SP.send(evt); } + [] BSCP.receive(ASP_Event:?) -> value evt { + for (var integer i := 0; i < sizeof(ClientTable); i := i+1) { + if (isbound(ClientTable[i].comp_ref)) { + BSSGP_SP.send(evt) to ClientTable[i].comp_ref; + } + } + } [] BSCP.receive(NsStatusIndication:?) -> value nsi { /* if we just became NS-unblocked, send a BCC-RESET */ if (nsi.old_state != NSE_S_ALIVE_UNBLOCKED and nsi.new_state == NSE_S_ALIVE_UNBLOCKED) { - if (g_sgsn_role == false) { + if (g_cfg.sgsn_role == false) { f_sendReset(); } - /* Idea: We coudl send BVC-UNBLOCK here like some SGSN do */ + /* Idea: We could send BVC-UNBLOCK here like some SGSN do */ } - BSSGP_SP.send(nsi); + for (var integer i := 0; i < sizeof(ClientTable); i := i+1) { + if (isbound(ClientTable[i].comp_ref)) { + BSSGP_SP.send(nsi) to ClientTable[i].comp_ref; + } + } + } + + [] BSSGP_PROC.getcall(BSSGP_register_client:{?,?,?}) -> param(imsi, tlli, cell_id) sender vc_conn { + f_tbl_client_add(imsi, tlli, cell_id, vc_conn); + BSSGP_PROC.reply(BSSGP_register_client:{imsi, tlli, cell_id}); + } + [] BSSGP_PROC.getcall(BSSGP_unregister_client:{?}) -> param(imsi) sender vc_conn { + f_tbl_client_del(imsi, vc_conn); + BSSGP_PROC.reply(BSSGP_unregister_client:{imsi}); + } +} + +altstep as_blocked() runs on BSSGP_CT { + [] g_T1.timeout { + f_sendUnblock(); + } + [] BSCP.receive(f_BnsUdInd(t_BVC_UNBLOCK_ACK(g_cfg.bvci), 0)) { + g_T1.stop; + f_change_state(BVC_S_UNBLOCKED); + } + [not g_cfg.sgsn_role] BSCP.receive(f_BnsUdInd(tr_BVC_RESET_ACK(g_cfg.bvci, omit), 0)) { + f_sendUnblock(); } } -private function f_ScanEvents() runs on BSSGP_CT { +altstep as_unblocked() runs on BSSGP_CT { + var BSSGP_Client_CT vc_conn; var NsUnitdataIndication udi; var PDU_BSSGP bs_pdu; - var default d; + var PDU_L3_MS_SGSN l3_mo; + var PDU_L3_SGSN_MS l3_mt; + /* bogus unblock, just respond with ACK */ + [] BSCP.receive(f_BnsUdInd(t_BVC_UNBLOCK(g_cfg.bvci), 0)) -> value udi { + BSCP.send(f_BnsUdReq(t_BVC_UNBLOCK_ACK(g_cfg.bvci), 0)); + } + /* Respond to BLOCK with BLOCK-ACK + change state */ + [] BSCP.receive(f_BnsUdInd(t_BVC_BLOCK(g_cfg.bvci, ?), 0)) -> value udi { + BSCP.send(f_BnsUdReq(t_BVC_BLOCK_ACK(g_cfg.bvci), 0)); + g_T1.stop; + f_change_state(BVC_S_BLOCKED); + } + [] g_T1.timeout { + f_sendBlock(BSSGP_CAUSE_OM_INTERVENTION); + } + [] BSCP.receive(f_BnsUdInd(t_BVC_BLOCK_ACK(g_cfg.bvci), 0)) -> value udi { + g_T1.stop; + f_change_state(BVC_S_BLOCKED); + } + [] BSCP.receive(f_BnsUdInd(tr_BVC_RESET_ACK(g_cfg.bvci, g_cfg.cell_id), 0)) -> value udi { + g_T2.stop; + f_change_state(BVC_S_UNBLOCKED); + } - log("matching against ", tr_BVC_RESET(?, mp_bvci, mp_cellid)); - - d := activate(as_allstate()); - - while (true) { - if (g_ptp_bvc_state == BVC_S_BLOCKED) { - alt { - [] g_T1.timeout { - f_sendUnblock(); - } - [] BSCP.receive(f_BnsUdInd(t_BVC_UNBLOCK_ACK(mp_bvci), 0)) { - g_T1.stop; - f_change_state(BVC_S_UNBLOCKED); - } - [not g_sgsn_role] BSCP.receive(f_BnsUdInd(tr_BVC_RESET_ACK(mp_bvci, omit), 0)) { - f_sendUnblock(); - } - } - } else if (g_ptp_bvc_state == BVC_S_UNBLOCKED) { - alt { - /* bogus unblock, just respond with ACK */ - [] BSCP.receive(f_BnsUdInd(t_BVC_UNBLOCK(mp_bvci), 0)) -> value udi { - BSCP.send(f_BnsUdReq(t_BVC_UNBLOCK_ACK(mp_bvci), 0)); - } - /* Respond to BLOCK with BLOCK-ACK + change state */ - [] BSCP.receive(f_BnsUdInd(t_BVC_BLOCK(mp_bvci, ?), 0)) -> value udi { - BSCP.send(f_BnsUdReq(t_BVC_BLOCK_ACK(mp_bvci), 0)); - g_T1.stop; - f_change_state(BVC_S_BLOCKED); - } - [] g_T1.timeout { - f_sendBlock(BSSGP_CAUSE_OM_INTERVENTION); - } - [] BSCP.receive(f_BnsUdInd(t_BVC_BLOCK_ACK(mp_bvci), 0)) -> value udi { - g_T1.stop; - f_change_state(BVC_S_BLOCKED); - } - [] BSCP.receive(f_BnsUdInd(tr_BVC_RESET_ACK(mp_bvci, mp_cellid), 0)) -> value udi { - g_T2.stop; - f_change_state(BVC_S_UNBLOCKED); - } - - /* simply acknowledge all Flow Control Messages */ + /* simply acknowledge all Flow Control Messages */ /* - [g_sgsn_role] BSCP.receive(f_BnsUdInd(t_BVC_FC_BVC)) { - BSCP.send(f_BnsUdReq(t_BVC_FC_BVC_ACK)); - } - [g_sgsn_role] BSCP.receive(f_BnsUdInd(t_BVC_FC_MS)) { - BSCP.send(f_BnsUdReq(t_BVC_FC_MS_ACK)); - } + [g_cfg.sgsn_role] BSCP.receive(f_BnsUdInd(t_BVC_FC_BVC), g_cfg.bvci) { + BSCP.send(f_BnsUdReq(t_BVC_FC_BVC_ACK), g_cfg.bvci); + } + [g_cfg.sgsn_role] BSCP.receive(f_BnsUdInd(t_BVC_FC_MS), g_cfg.bvci) { + BSCP.send(f_BnsUdReq(t_BVC_FC_MS_ACK), g_cfg.bvci); + } */ - /* BSSGP-UNITDATA PDUs from network to NS-UNITDATA.ind to user */ - [not g_sgsn_role] BSCP.receive(f_BnsUdInd(tr_BSSGP_DL_UD)) -> value udi { - BSSGP_SP.send(udi.bssgp); - } - [g_sgsn_role] BSCP.receive(f_BnsUdInd(tr_BSSGP_UL_UD)) -> value udi { - BSSGP_SP.send(udi.bssgp); - } - /* pass virtually any PDU from user to NS-UNITDATA PDU on network */ - [] BSSGP_SP.receive(PDU_BSSGP:?) -> value bs_pdu { - BSCP.send(f_BnsUdReq(bs_pdu)); - } + /* FIXME: CS PAGING: dispatch by IMSI */ + /* PS PAGING: dispatch by IMSI */ + [] BSCP.receive(f_BnsUdInd(tr_BSSGP_PS_PAGING(g_cfg.bvci), g_cfg.bvci)) -> value udi { + var hexstring imsi := udi.bssgp.pDU_BSSGP_PAGING_PS.iMSI.digits + vc_conn := f_tbl_comp_by_imsi(imsi); + BSSGP_SP.send(f_dec_bssgp(udi.bssgp)) to vc_conn; + } + + /* Any other BSSGP message: If it has TLLi, route to component; otherwise broadcast */ + [] BSCP.receive(f_BnsUdInd(?, g_cfg.bvci)) -> value udi { + var BssgpDecoded dec := f_dec_bssgp(udi.bssgp); + var template OCT4 tlli := f_bssgp_get_tlli(udi.bssgp); + if (isvalue(tlli)) { + vc_conn := f_tbl_comp_by_tlli(valueof(tlli)); + BSSGP_SP.send(dec) to vc_conn; + } else { + log("No TLLI: Broadcasting ", dec); + /* broadcast this message to all components */ + // TITAN DOESN'T DO THIS, *SIGH*: "BSSGP_SP.send(dec) to all component;" + for (var integer i := 0; i < sizeof(ClientTable); i := i+1) { + if (isbound(ClientTable[i].comp_ref)) { + BSSGP_SP.send(dec) to ClientTable[i].comp_ref; + } + } } } - } /* while */ - //deactivate(d); + /* pass virtually any PDU from user to NS-UNITDATA PDU on network */ + [] BSSGP_SP.receive(PDU_BSSGP:?) -> value bs_pdu sender vc_conn { + BSCP.send(f_BnsUdReq(bs_pdu, g_cfg.bvci)); + } + + [not g_cfg.sgsn_role] BSSGP_SP.receive(PDU_L3_MS_SGSN:?) -> value l3_mo sender vc_conn { + var integer idx := f_tbl_idx_by_comp(vc_conn); + var octetstring l3_enc := enc_PDU_L3_MS_SGSN(l3_mo); + var BIT4 sapi := f_llc_sapi_by_l3_mo(l3_mo); + var integer n_u := f_llc_get_n_u_tx(ClientTable[idx].llc[bit2int(sapi)]); + var octetstring llc_enc := enc_PDU_LLC(valueof(ts_LLC_UI(l3_enc, sapi, '0'B, n_u))); + BSCP.send(f_BnsUdReq(ts_BSSGP_UL_UD(ClientTable[idx].tlli, ClientTable[idx].cell_id, llc_enc), g_cfg.bvci)); + } + + [g_cfg.sgsn_role] BSSGP_SP.receive(PDU_L3_SGSN_MS:?) -> value l3_mt sender vc_conn { + var integer idx := f_tbl_idx_by_comp(vc_conn); + var octetstring l3_enc := enc_PDU_L3_SGSN_MS(l3_mt); + var BIT4 sapi := f_llc_sapi_by_l3_mt(l3_mt); + var integer n_u := f_llc_get_n_u_tx(ClientTable[idx].llc[bit2int(sapi)]); + var octetstring llc_enc := enc_PDU_LLC(valueof(ts_LLC_UI(l3_enc, sapi, '1'B, n_u))); + //BSCP.send(f_BnsUdReq(ts_BSSGP_DL_UD(ClientTable[idx].tlli, ClientTable[idx].cell_id, llc_enc)), g_cfg.bvci); + } } +private function f_llc_get_n_u_tx(inout LLC_Entity llc) return uint9_t { + var uint9_t ret := llc.n_u_tx_next; + llc.n_u_tx_next := llc.n_u_tx_next + 1; + return ret; +} + +private function f_llc_sapi_by_l3_mo(PDU_L3_MS_SGSN l3_mo) return BIT4 { + if (ischosen(l3_mo.msgs.gprs_mm)) { + return c_LLC_SAPI_LLGMM; + } else if (ischosen(l3_mo.msgs.gprs_sm)) { + return c_LLC_SAPI_LLSM; + } else if (ischosen(l3_mo.msgs.sms)) { + return c_LLC_SAPI_LLSMS; + } + setverdict(fail, "No LLC SAPI for ", l3_mo); + self.stop; +} + +private function f_llc_sapi_by_l3_mt(PDU_L3_SGSN_MS l3_mt) return BIT4 { + if (ischosen(l3_mt.msgs.gprs_mm)) { + return c_LLC_SAPI_LLGMM; + } else if (ischosen(l3_mt.msgs.gprs_sm)) { + return c_LLC_SAPI_LLSM; + } else if (ischosen(l3_mt.msgs.sms)) { + return c_LLC_SAPI_LLSMS; + } + setverdict(fail, "No LLC SAPI for ", l3_mt); + self.stop; +} + + + +private function f_ScanEvents() runs on BSSGP_CT { + log("matching against ", tr_BVC_RESET(?, g_cfg.bvci, g_cfg.cell_id)); + + while (true) { + alt { + [g_ptp_bvc_state == BVC_S_BLOCKED] as_blocked(); + [g_ptp_bvc_state == BVC_S_UNBLOCKED] as_unblocked(); + [] as_allstate(); + } + } /* while */ +} + +/* PDU_BSSGP enhanced with LLC and possibly L3 decoded payloads */ +type record BssgpDecoded { + PDU_BSSGP bssgp, + PDU_LLC llc optional, + PDU_L3_MS_SGSN l3_mo optional, + PDU_L3_SGSN_MS l3_mt optional +} + +/* Decode a PDU_BSSGP into a BssgpDecoded (i.e. with LLC/L3 decoded, as applicable) */ +private function f_dec_bssgp(PDU_BSSGP bssgp) runs on BSSGP_CT return BssgpDecoded { + var BssgpDecoded dec := { + bssgp := bssgp, + llc := omit, + l3_mo := omit, + l3_mt := omit + }; + + /* Decode LLC, if it is a PDU that contains LLC */ + if (ischosen(bssgp.pDU_BSSGP_DL_UNITDATA)) { + dec.llc := dec_PDU_LLC(bssgp.pDU_BSSGP_DL_UNITDATA.lLC_PDU.lLC_PDU); + } else if (ischosen(bssgp.pDU_BSSGP_UL_UNITDATA)) { + dec.llc := dec_PDU_LLC(bssgp.pDU_BSSGP_UL_UNITDATA.lLC_PDU.lLC_PDU); + } + + /* Decode L3, if it is a LLC PDU containing L3 */ + if (isvalue(dec.llc) and match(dec.llc, tr_LLC_UI_L3)) { + if (g_cfg.sgsn_role) { + dec.l3_mo := dec_PDU_L3_MS_SGSN(dec.llc.pDU_LLC_UI.information_field_UI); + } else { + dec.l3_mt := dec_PDU_L3_SGSN_MS(dec.llc.pDU_LLC_UI.information_field_UI); + } + } + return dec; +} + +function f_bssgp_client_register(hexstring imsi, OCT4 tlli, BssgpCellId cell_id, BSSGP_PROC_PT PT := BSSGP_PROC) +runs on BSSGP_Client_CT { + PT.call(BSSGP_register_client:{imsi, tlli, cell_id}) { + [] PT.getreply(BSSGP_register_client:{imsi, tlli, cell_id}) {}; + } +} + +function f_bssgp_client_unregister(hexstring imsi, BSSGP_PROC_PT PT := BSSGP_PROC) +runs on BSSGP_Client_CT { + PT.call(BSSGP_unregister_client:{imsi}) { + [] PT.getreply(BSSGP_unregister_client:{imsi}) {}; + } +} + +template BssgpDecoded tr_BD_BSSGP(template PDU_BSSGP bg) := { + bssgp := bg, + llc := *, + l3_mo := *, + l3_mt := * +} + + +template BssgpDecoded tr_BD_L3_MT(template PDU_L3_SGSN_MS mt) := { + bssgp := ?, + llc := ?, + l3_mo := omit, + l3_mt := mt +} + +template BssgpDecoded tr_BD_L3_MO(template PDU_L3_MS_SGSN mo) := { + bssgp := ?, + llc := ?, + l3_mo := mo, + l3_mt := omit +} + + + } diff --git a/library/LLC_Templates.ttcn b/library/LLC_Templates.ttcn new file mode 100644 index 0000000..e387b36 --- /dev/null +++ b/library/LLC_Templates.ttcn @@ -0,0 +1,73 @@ +module LLC_Templates { + +import from LLC_Types all; +import from Osmocom_Types all; +import from General_Types all; + +template Address_field t_LLC_Addr(template BIT4 sapi, template BIT1 cr, template BIT1 pd := '0'B) := { + sAPI := sapi, + spare := '00'B, + cR := cr, + pD := '0'B +} + +template (value) Control_field_UI ts_LLC_CtrlUI(uint9_t n_u, boolean encrypted := false, + boolean protected := false) := { + format := '110'B, + spare := '00'B, + nU := n_u, + e := bool2bit(encrypted), + pM := bool2bit(protected) +} + +template Control_field_UI tr_LLC_CtrlUI(template uint9_t n_u, + template boolean encrypted := ?, + template boolean protected := ?) := { + format := '110'B, + spare := '00'B, + nU := n_u, + e := bool2bit_tmpl(encrypted), + pM := bool2bit_tmpl(protected) +} + +template PDU_LLC ts_LLC_UI(octetstring payload, BIT4 sapi, BIT1 cr, uint9_t n_u, + boolean encrypted := false, boolean protected := false) := { + pDU_LLC_UI := { + address_field := t_LLC_Addr(sapi, cr), + control_field := ts_LLC_CtrlUI(n_u, encrypted, protected), + information_field_UI := payload, + fCS := omit /* causes encoder to generate FCS */ + } +} + +template PDU_LLC tr_LLC_UI(template octetstring payload := ?, template BIT4 sapi := ?, + template BIT1 cr := ?, template uint9_t n_u := ?, + template boolean encrypted := ?, template boolean protected := ?) := { + pDU_LLC_UI := { + address_field := t_LLC_Addr(sapi, cr), + control_field := tr_LLC_CtrlUI(n_u, encrypted, protected), + information_field_UI := payload, + fCS := '000000'O /* provided by decoder if FCS OK */ + } +} + +const BIT4 c_LLC_SAPI_LLGMM := '0001'B; +const BIT4 c_LLC_SAPI_TOM2 := '0010'B; +const BIT4 c_LLC_SAPI_LL3 := '0011'B; +const BIT4 c_LLC_SAPI_LL5 := '0101'B; +const BIT4 c_LLC_SAPI_LLSMS := '0111'B; +const BIT4 c_LLC_SAPI_TOM8 := '1000'B; +const BIT4 c_LLC_SAPI_LL9 := '1001'B; +const BIT4 c_LLC_SAPI_LLSM := '1010'B; +const BIT4 c_LLC_SAPI_LL11 := '1011'B; + +const BIT1 LLC_CR_DL_CMD := '1'B; +const BIT1 LLC_CR_DL_RSP := '0'B; +const BIT1 LLC_CR_UL_CMD := '0'B; +const BIT1 LLC_CR_UL_RSP := '1'B; + +/* LLC UI frame with SAPI for L3 payload */ +template PDU_LLC tr_LLC_UI_L3 := ( tr_LLC_UI(?, c_LLC_SAPI_LLGMM), tr_LLC_UI(?, c_LLC_SAPI_LLSM) ); + + +} diff --git a/sgsn/SGSN_Tests.ttcn b/sgsn/SGSN_Tests.ttcn index d535432..c47403e 100644 --- a/sgsn/SGSN_Tests.ttcn +++ b/sgsn/SGSN_Tests.ttcn @@ -6,33 +6,181 @@ import from NS_Emulation all; import from BSSGP_Types all; import from BSSGP_Emulation all; +import from Osmocom_Gb_Types all; + +import from MobileL3_CommonIE_Types all; +import from MobileL3_GMM_SM_Types all; +import from MobileL3_Types all; +import from L3_Templates all; +import from L3_Common all; + +import from GSUP_Emulation all; +import from GSUP_Types all; +import from IPA_Emulation all; + +modulepar { + /* IP/port on which we run our internal GSUP/HLR emulation */ + charstring mp_hlr_ip := "127.0.0.1"; + integer mp_hlr_port := 4222; +}; + +type record GbInstance { + NS_CT vc_NS, + BSSGP_CT vc_BSSGP, + BssgpConfig cfg +}; type component test_CT { - var NS_CT vc_NS; + var GbInstance g_gb[3]; - var BSSGP_CT vc_BSSGP; - port BSSGP_PT BSSGP; + var GSUP_Emulation_CT vc_GSUP; + var IPA_Emulation_CT vc_GSUP_IPA; + /* only to get events from IPA underneath GSUP */ + port IPA_CTRL_PT GSUP_IPA_EVENT; var boolean g_initialized := false; }; + +type component BSSGP_ConnHdlr extends BSSGP_Client_CT, GSUP_ConnHdlr { + var BSSGP_ConnHdlrPars g_pars; +} + +type record SGSN_ConnHdlrNetworkPars { + boolean expect_ptmsi, + boolean expect_auth, + boolean expect_ciph +}; + +type record BSSGP_ConnHdlrPars { + /* IMEI of the simulated ME */ + hexstring imei, + /* IMEI of the simulated MS */ + hexstring imsi, + /* MSISDN of the simulated MS (probably unused) */ + hexstring msisdn, + /* P-TMSI allocated to the simulated MS */ + OCT4 p_tmsi optional, + /* TLLI of the simulated MS */ + OCT4 tlli, + RoutingAreaIdentificationV ra optional, + BssgpCellId bssgp_cell_id, + AuthVector vec optional, + SGSN_ConnHdlrNetworkPars net +}; + + +private function f_init_gb(inout GbInstance gb) runs on test_CT { + gb.vc_NS := NS_CT.create; + gb.vc_BSSGP := BSSGP_CT.create; + /* connect lower end of BSSGP emulation with NS upper port */ + connect(gb.vc_BSSGP:BSCP, gb.vc_NS:NS_SP); + /* connect lower end of NS emulation to NS codec port (on top of IPL4) */ + map(gb.vc_NS:NSCP, system:NS_CODEC_PORT); + + gb.vc_NS.start(NSStart()); + gb.vc_BSSGP.start(BssgpStart(gb.cfg)); +} + +private function f_init_gsup(charstring id) runs on test_CT { + id := id & "-GSUP"; + var GsupOps ops := { + create_cb := refers(GSUP_Emulation.ExpectedCreateCallback) + }; + + vc_GSUP_IPA := IPA_Emulation_CT.create(id & "-IPA"); + vc_GSUP := GSUP_Emulation_CT.create(id); + + map(vc_GSUP_IPA:IPA_PORT, system:IPA_CODEC_PT); + connect(vc_GSUP:GSUP, vc_GSUP_IPA:IPA_GSUP_PORT); + /* we use this hack to get events like ASP_IPA_EVENT_UP */ + connect(vc_GSUP_IPA:IPA_CTRL_PORT, self:GSUP_IPA_EVENT); + + vc_GSUP.start(GSUP_Emulation.main(ops, id)); + vc_GSUP_IPA.start(IPA_Emulation.main_server(mp_hlr_ip, mp_hlr_port)); + + /* wait for incoming connection to GSUP port before proceeding */ + timer T := 10.0; + T.start; + alt { + [] GSUP_IPA_EVENT.receive(t_ASP_IPA_EVT_UD(ASP_IPA_EVENT_UP)) { } + [] T.timeout { + setverdict(fail, "No connection to GSUP Port"); + self.stop; + } + } +} function f_init() runs on test_CT { if (g_initialized == true) { return; } g_initialized := true; + g_gb[0].cfg := { + nsei := 96, + bvci := 196, + cell_id := { + ra_id := { + lai := { + mcc_mnc := '26242F'H, lac := 13135}, + rac := 0 + }, + cell_id := 20960 + }, + sgsn_role := false + }; - vc_NS := NS_CT.create; - vc_BSSGP := BSSGP_CT.create; - /* connect our BSSGP port to upper end of BSSGP emulation */ - connect(self:BSSGP, vc_BSSGP:BSSGP_SP); - /* connect lower end of BSSGP emulation with NS upper port */ - connect(vc_BSSGP:BSCP, vc_NS:NS_SP); - /* connect lower end of NS emulation to NS codec port (on top of IPL4) */ - map(vc_NS:NSCP, system:NS_CODEC_PORT); + f_init_gb(g_gb[0]); + f_init_gsup("SGSN_Test"); +} - vc_NS.start(NSStart()); - vc_BSSGP.start(BssgpStart(false)); +type function void_fn(charstring id) runs on BSSGP_ConnHdlr; + +/* helper function to create, connect and start a BSSGP_ConnHdlr component */ +function f_start_handler(void_fn fn, charstring id, GbInstance gb, integer imsi_suffix) +runs on test_CT return BSSGP_ConnHdlr { + var BSSGP_ConnHdlr vc_conn; + var SGSN_ConnHdlrNetworkPars net_pars := { + expect_ptmsi := true, + expect_auth := true, + expect_ciph := false + }; + var BSSGP_ConnHdlrPars pars := { + imei := f_gen_imei(imsi_suffix), + imsi := f_gen_imsi(imsi_suffix), + msisdn := f_gen_msisdn(imsi_suffix), + p_tmsi := omit, + tlli := 'FFFFFFFF'O, + ra := omit, + bssgp_cell_id := gb.cfg.cell_id, + vec := omit, + net := net_pars + }; + + vc_conn := BSSGP_ConnHdlr.create(id); + connect(vc_conn:BSSGP, gb.vc_BSSGP:BSSGP_SP); + connect(vc_conn:BSSGP_PROC, gb.vc_BSSGP:BSSGP_PROC); + + connect(vc_conn:GSUP, vc_GSUP:GSUP_CLIENT); + connect(vc_conn:GSUP_PROC, vc_GSUP:GSUP_PROC); + + vc_conn.start(f_handler_init(fn, id, pars)); + return vc_conn; +} + +/* first function called in every ConnHdlr */ +private function f_handler_init(void_fn fn, charstring id, BSSGP_ConnHdlrPars pars) +runs on BSSGP_ConnHdlr { + /* do some common stuff like setting up g_pars */ + g_pars := pars; + + /* register with BSSGP core */ + f_bssgp_client_register(g_pars.imsi, g_pars.tlli, g_pars.bssgp_cell_id); + /* tell GSUP dispatcher to send this IMSI to us */ + f_create_gsup_expect(hex2str(g_pars.imsi)); + + /* call the user-supplied test case function */ + fn.apply(id); + f_bssgp_client_unregister(g_pars.imsi); } /* TODO: @@ -59,8 +207,86 @@ f_sleep(20.0); } +altstep as_mm_identity() runs on BSSGP_ConnHdlr { + var MobileIdentityLV mi; + [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_ID_REQ('001'B))) { + mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); + BSSGP.send(ts_GMM_ID_RESP(mi)); + repeat; + } + [] BSSGP.receive(tr_BD_L3_MT(tr_GMM_ID_REQ('010'B))) { + mi := valueof(ts_MI_IMEI_LV(g_pars.imei)); + BSSGP.send(ts_GMM_ID_RESP(mi)); + repeat; + } +} -//control { } +function f_gmm_auth () runs on BSSGP_ConnHdlr { + var BssgpDecoded bd; + var PDU_L3_MS_SGSN l3_mo; + var PDU_L3_SGSN_MS l3_mt; + var default di := activate(as_mm_identity()); + if (g_pars.net.expect_auth) { + g_pars.vec := f_gen_auth_vec_2g(); + var GSUP_IE auth_tuple := valueof(ts_GSUP_IE_AuthTuple2G(g_pars.vec.rand, + g_pars.vec.sres, + g_pars.vec.kc)); + GSUP.receive(tr_GSUP_SAI_REQ(g_pars.imsi)); + GSUP.send(ts_GSUP_SAI_RES(g_pars.imsi, auth_tuple)); + BSSGP.receive(tr_BD_L3_MT(tr_GMM_AUTH_REQ(g_pars.vec.rand))) -> value bd; + l3_mt := bd.l3_mt; + var BIT4 ac_ref := l3_mt.msgs.gprs_mm.authenticationAndCipheringRequest.acReferenceNumber.valueField; + l3_mo := valueof(ts_GMM_AUTH_RESP_2G(ac_ref, g_pars.vec.sres)); + if (ispresent(l3_mt.msgs.gprs_mm.authenticationAndCipheringRequest.imeisvRequest) and + l3_mt.msgs.gprs_mm.authenticationAndCipheringRequest.imeisvRequest.valueField == '001'B) { + l3_mo.msgs.gprs_mm.authenticationAndCipheringResponse.imeisv := + valueof(ts_MI_IMEISV_TLV(g_pars.imei & '0'H)); + } + BSSGP.send(l3_mo); + } + deactivate(di); +} + +private function f_TC_attach(charstring id) runs on BSSGP_ConnHdlr { + var MobileIdentityLV mi; + var RoutingAreaIdentificationV old_ra := { '2'H, '6'H, '2'H, 'F'H, '4'H, '2'H, '2342'O, '00'O }; + + if (ispresent(g_pars.p_tmsi)) { + mi := valueof(ts_MI_TMSI_LV(g_pars.p_tmsi)); + } else { + mi := valueof(ts_MI_IMSI_LV(g_pars.imsi)); + } + + BSSGP.send(ts_GMM_ATTACH_REQ(mi, old_ra, false, false, omit, omit)); + f_gmm_auth(); + /* Expect MSC to perform LU with HLR */ + GSUP.receive(tr_GSUP_UL_REQ(g_pars.imsi)); + GSUP.send(ts_GSUP_ISD_REQ(g_pars.imsi, g_pars.msisdn)); + GSUP.receive(tr_GSUP_ISD_RES(g_pars.imsi)); + GSUP.send(ts_GSUP_UL_RES(g_pars.imsi)); + + BSSGP.receive(tr_BD_L3_MT(tr_GMM_ATTACH_ACCEPT(?, ?, ?))); + BSSGP.send(ts_GMM_ATTACH_COMPL); +/* + alt { + [] as_mm_identity(); + } +*/ + f_sleep(5.0); +} + +testcase TC_attach() runs on test_CT { + var BSSGP_ConnHdlr vc_conn; + f_init(); + f_sleep(1.0); + vc_conn := f_start_handler(refers(f_TC_attach), testcasename(), g_gb[0], 1); + vc_conn.done; +} + + +control { + execute( TC_wait_ns_up() ); +} diff --git a/sgsn/gen_links.sh b/sgsn/gen_links.sh index 4b76196..bc2727d 100755 --- a/sgsn/gen_links.sh +++ b/sgsn/gen_links.sh @@ -55,13 +55,22 @@ FILES="BSSGP_EncDec.cc BSSGP_Types.ttcn" gen_links $DIR $FILES +DIR=$BASEDIR/titan.ProtocolModules.LLC_v7.1.0/src +FILES="LLC_EncDec.cc LLC_Types.ttcn" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.ProtocolModules.MobileL3_v13.4.0/src +FILES="MobileL3_CC_Types.ttcn MobileL3_CommonIE_Types.ttcn MobileL3_GMM_SM_Types.ttcn MobileL3_MM_Types.ttcn MobileL3_RRM_Types.ttcn MobileL3_SMS_Types.ttcn MobileL3_SS_Types.ttcn MobileL3_Types.ttcn" +gen_links $DIR $FILES + DIR=../library FILES="General_Types.ttcn GSM_Types.ttcn GSM_RR_Types.ttcn Osmocom_Types.ttcn RLCMAC_Types.ttcn RLCMAC_CSN1_Types.ttcn RLCMAC_EncDec.cc " FILES+="NS_Emulation.ttcn NS_CodecPort.ttcn NS_CodecPort_CtrlFunct.ttcn NS_CodecPort_CtrlFunctDef.cc " FILES+="BSSGP_Emulation.ttcn Osmocom_Gb_Types.ttcn " FILES+="Osmocom_CTRL_Types.ttcn Osmocom_CTRL_Functions.ttcn Osmocom_CTRL_Adapter.ttcn " FILES+="Osmocom_VTY_Functions.ttcn " +FILES+="LLC_Templates.ttcn L3_Templates.ttcn L3_Common.ttcn " # IPA_Emulation + dependencies FILES+="IPA_Types.ttcn IPA_Emulation.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn -IPA_CodecPort_CtrlFunctDef.cc Native_Functions.ttcn Native_FunctionDefs.cc GSUP_Types.ttcn MGCP_Types.ttcn RSL_Types.ttcn " +IPA_CodecPort_CtrlFunctDef.cc Native_Functions.ttcn Native_FunctionDefs.cc GSUP_Types.ttcn GSUP_Emulation.ttcn MGCP_Types.ttcn RSL_Types.ttcn " gen_links $DIR $FILES diff --git a/sgsn/regen_makefile.sh b/sgsn/regen_makefile.sh index 6824d94..c055065 100755 --- a/sgsn/regen_makefile.sh +++ b/sgsn/regen_makefile.sh @@ -1,5 +1,5 @@ #!/bin/sh -FILES="*.ttcn BSSGP_EncDec.cc IPL4asp_PT.cc IPL4asp_discovery.cc TCCConversion.cc TCCInterface.cc NS_CodecPort_CtrlFunctDef.cc RLCMAC_EncDec.cc Native_FunctionDefs.cc SDP_EncDec.cc SDP_parse_.tab.c lex.SDP_parse_.c TELNETasp_PT.cc IPA_CodecPort_CtrlFunctDef.cc" +FILES="*.ttcn BSSGP_EncDec.cc LLC_EncDec.cc IPL4asp_PT.cc IPL4asp_discovery.cc TCCConversion.cc TCCInterface.cc NS_CodecPort_CtrlFunctDef.cc RLCMAC_EncDec.cc Native_FunctionDefs.cc SDP_EncDec.cc SDP_parse_.tab.c lex.SDP_parse_.c TELNETasp_PT.cc IPA_CodecPort_CtrlFunctDef.cc" ../regen-makefile.sh SGSN_Tests.ttcn $FILES -- To view, visit https://gerrit.osmocom.org/6536 To unsubscribe, visit https://gerrit.osmocom.org/settings Gerrit-MessageType: merged Gerrit-Change-Id: Id66ddf8dbe1c5cfa96a087235588ba67763b7f05 Gerrit-PatchSet: 4 Gerrit-Project: osmo-ttcn3-hacks Gerrit-Branch: master Gerrit-Owner: Harald Welte <laforge at gnumonks.org> Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org> Gerrit-Reviewer: Jenkins Builder