diff options
author | Neels Hofmeyr <neels@hofmeyr.de> | 2019-05-07 01:20:17 +0200 |
---|---|---|
committer | Neels Hofmeyr <neels@hofmeyr.de> | 2019-05-09 00:55:11 +0200 |
commit | 0ac6315212a35522495ea216f14f94a6d4f9fbb3 (patch) | |
tree | 9891b55103dd801453793000b46517b09d8309e4 | |
parent | 2ca1ab492a8c5e1aa508df0779c7b7ab34129fe0 (diff) |
msc: add inter-BSC and inter-MSC Handover tests
Change-Id: I7d76c982ad4e198534fa488609c41e8892b268ab
-rw-r--r-- | library/BSSMAP_Templates.ttcn | 163 | ||||
-rw-r--r-- | library/GSUP_Types.ttcn | 245 | ||||
-rw-r--r-- | library/L3_Templates.ttcn | 53 | ||||
-rw-r--r-- | library/RAN_Emulation.ttcnpp | 129 | ||||
-rw-r--r-- | msc/BSC_ConnectionHandler.ttcn | 21 | ||||
-rw-r--r-- | msc/MSC_Tests.ttcn | 496 |
6 files changed, 1015 insertions, 92 deletions
diff --git a/library/BSSMAP_Templates.ttcn b/library/BSSMAP_Templates.ttcn index 4df39d42..b7230cd3 100644 --- a/library/BSSMAP_Templates.ttcn +++ b/library/BSSMAP_Templates.ttcn @@ -307,7 +307,7 @@ modifies tr_BSSAP_BSSMAP := { } } -template BSSMAP_IE_CellIdentifierList ts_BSSMAP_IE_CidList(BSSMAP_FIELD_CellIdentificationList cid_list) := { +template BSSMAP_IE_CellIdentifierList ts_BSSMAP_IE_CidList(template BSSMAP_FIELD_CellIdentificationList cid_list) := { elementIdentifier := '1A'O, lengthIndicator := 0, /* overwritten */ cellIdentifierDiscriminator := '0000'B, /* overwritten */ @@ -315,31 +315,6 @@ template BSSMAP_IE_CellIdentifierList ts_BSSMAP_IE_CidList(BSSMAP_FIELD_CellIden cellIdentificationList := cid_list } -template PDU_BSSAP ts_BSSMAP_HandoReq(BssmapCause cause, BSSMAP_FIELD_CellIdentificationList cid_list) -modifies ts_BSSAP_BSSMAP := { - pdu := { - bssmap := { - handoverRequired := { - messageType := '11'O, - cause := ts_BSSMAP_IE_Cause(cause), - responseRequest := omit, - cellIdentifierList := ts_BSSMAP_IE_CidList(cid_list), - circuitPoolList := omit, - currentChannelType1 := omit, - speechVersion := omit, - queueingIndicator := omit, - oldToNewBSSInfo := omit, - sourceToTargetRNCTransparentInfo := omit, - sourceToTargetRNCTransparentInfoCDMA := omit, - gERANClassmark := omit, - talkerPriority := omit, - speechCodec := omit, - cSG_Identifier := omit - } - } - } -} - const OCT1 ChRate_ANY := '00'O; const OCT1 ChRate_TCHF := '08'O; const OCT1 ChRate_TCHH := '09'O; @@ -703,6 +678,33 @@ template PDU_BSSAP tr_BSSMAP_ClearRequest modifies tr_BSSAP_BSSMAP := { } } +template PDU_BSSAP ts_BSSMAP_HandoverRequired(BssmapCause cause, + template BSSMAP_FIELD_CellIdentificationList cid_list) +modifies ts_BSSAP_BSSMAP := { + pdu := { + bssmap := { + handoverRequired := { + messageType := '11'O, + cause := ts_BSSMAP_IE_Cause(cause), + responseRequest := omit, + cellIdentifierList := ts_BSSMAP_IE_CidList(cid_list), + circuitPoolList := omit, + currentChannelType1 := omit, + speechVersion := omit, + queueingIndicator := omit, + oldToNewBSSInfo := omit, + sourceToTargetRNCTransparentInfo := omit, + sourceToTargetRNCTransparentInfoCDMA := omit, + gERANClassmark := omit, + talkerPriority := omit, + speechCodec := omit, + cSG_Identifier := omit + } + } + } +} + + template PDU_BSSAP tr_BSSMAP_HandoverRequired modifies tr_BSSAP_BSSMAP := { pdu := { bssmap := { @@ -713,6 +715,38 @@ template PDU_BSSAP tr_BSSMAP_HandoverRequired modifies tr_BSSAP_BSSMAP := { } } +template PDU_BSSAP tr_BSSMAP_HandoverRequiredReject modifies tr_BSSAP_BSSMAP := { + pdu := { + bssmap := { + handoverRequiredReject := { + messageType := '1A'O + } + } + } +} + +template PDU_BSSAP tr_BSSMAP_HandoverCommand +modifies tr_BSSAP_BSSMAP := { + pdu := { + bssmap := { + handoverCommand := { + messageType := '13'O + } + } + } +} + +template PDU_BSSAP tr_BSSMAP_HandoverSucceeded +modifies tr_BSSAP_BSSMAP := { + pdu := { + bssmap := { + handoverSucceeded := { + messageType := '15'O + } + } + } +} + template (value) PDU_BSSAP ts_BSSMAP_HandoverCommand(octetstring layer3info) modifies ts_BSSAP_BSSMAP := { pdu := { @@ -751,6 +785,16 @@ template PDU_BSSAP tr_BSSMAP_HandoverFailure modifies tr_BSSAP_BSSMAP := { } } +template PDU_BSSAP tr_BSSMAP_HandoverRequest modifies tr_BSSAP_BSSMAP := { + pdu := { + bssmap := { + handoverRequest := { + messageType := '10'O + } + } + } +} + template PDU_BSSAP ts_BSSMAP_HandoverRequest( template BSSMAP_IE_CircuitIdentityCode cic := omit, template BSSMAP_IE_AoIP_TransportLayerAddress aoip_tla := omit, @@ -826,6 +870,41 @@ modifies tr_BSSAP_BSSMAP := { } } +template PDU_BSSAP ts_BSSMAP_HandoverRequestAcknowledge( + template octetstring layer3info, + template LIN1 layer3infoLength, + template BSSMAP_IE_AoIP_TransportLayerAddress aoIPTransportLayer := omit, + template BSSMAP_IE_SpeechCodec speechCodec := omit, + template BSSMAP_IE_ChosenChannel chosenChannel := omit, + template BSSMAP_IE_ChosenEncryptionAlgorithm chosenEncryptionAlgorithm := omit) +modifies ts_BSSAP_BSSMAP := { + pdu := { + bssmap := { + handoverRequestAck := { + messageType := '12'O, + layer3Information := { + elementIdentifier := '17'O, + lengthIndicator := layer3infoLength, + layer3info := layer3info + }, + chosenChannel := chosenChannel, + chosenEncryptionAlgorithm := chosenEncryptionAlgorithm, + circuitPool := omit, + speechVersion := omit, + circuitIdentityCode := omit, + lSAIdentifier := omit, + newBSSToOldBSSInfo := omit, + interSystemInformation := omit, + talkerPriority := omit, + aoIPTransportLayer := aoIPTransportLayer, + codecList := omit, + speechCodec := speechCodec, + lCLS_bSS_Status := omit + } + } + } +} + template PDU_BSSAP tr_BSSMAP_HandoverDetect modifies tr_BSSAP_BSSMAP := { pdu := { @@ -838,6 +917,18 @@ modifies tr_BSSAP_BSSMAP := { } } +template PDU_BSSAP ts_BSSMAP_HandoverDetect +modifies ts_BSSAP_BSSMAP := { + pdu := { + bssmap := { + handoverDetect := { + messageType := '1B'O, + talkerPriority := omit + } + } + } +} + template PDU_BSSAP tr_BSSMAP_HandoverComplete modifies tr_BSSAP_BSSMAP := { pdu := { @@ -856,6 +947,24 @@ modifies tr_BSSAP_BSSMAP := { } } +template PDU_BSSAP ts_BSSMAP_HandoverComplete +modifies ts_BSSAP_BSSMAP := { + pdu := { + bssmap := { + handoverComplete := { + messageType := '14'O, + rR_Cause := omit, + talkerPriority := omit, + speechCodec := omit, + codecList := omit, + chosenEncryptionAlgorithm := omit, + chosenChannel := omit, + lCLS_BSS_Status := omit + } + } + } +} + template PDU_BSSAP tr_BSSMAP_HandoverPerformed modifies tr_BSSAP_BSSMAP := { pdu := { @@ -975,7 +1084,7 @@ modifies ts_BSSAP_BSSMAP := { messageType := '52'O, iMSI := ts_BSSMAP_Imsi(imsi_digits), tMSI := f_tmsi_or_omit(tmsi), - cellIdentifierList := ts_BSSMAP_IE_CidList(valueof(cid_list)), + cellIdentifierList := ts_BSSMAP_IE_CidList(cid_list), channelNeeded := chneed, eMLPP_Priority := omit, pagingInformation := omit /* only VGCS/VBS flag */ diff --git a/library/GSUP_Types.ttcn b/library/GSUP_Types.ttcn index c024d371..73f45626 100644 --- a/library/GSUP_Types.ttcn +++ b/library/GSUP_Types.ttcn @@ -55,7 +55,16 @@ type enumerated GSUP_IEI { OSMO_GSUP_SM_ALERT_RSN_IE ('46'O), OSMO_GSUP_IMEI_IE ('50'O), - OSMO_GSUP_IMEI_RESULT_IE ('51'O) + OSMO_GSUP_IMEI_RESULT_IE ('51'O), + + OSMO_GSUP_MESSAGE_CLASS_IE ('0a'O), + + OSMO_GSUP_SOURCE_NAME_IE ('60'O), + OSMO_GSUP_DESTINATION_NAME_IE ('61'O), + OSMO_GSUP_AN_APDU_IE ('62'O), + OSMO_GSUP_CAUSE_RR_IE ('63'O), + OSMO_GSUP_CAUSE_BSSAP_IE ('64'O), + OSMO_GSUP_CAUSE_SM_IE ('65'O) } with { variant "FIELDLENGTH(8)" }; type enumerated GSUP_MessageType { @@ -103,7 +112,27 @@ type enumerated GSUP_MessageType { OSMO_GSUP_MSGT_CHECK_IMEI_REQUEST ('00110000'B), OSMO_GSUP_MSGT_CHECK_IMEI_ERROR ('00110001'B), - OSMO_GSUP_MSGT_CHECK_IMEI_RESULT ('00110010'B) + OSMO_GSUP_MSGT_CHECK_IMEI_RESULT ('00110010'B), + + OSMO_GSUP_MSGT_E_PREPARE_HANDOVER_REQUEST ('00110100'B), + OSMO_GSUP_MSGT_E_PREPARE_HANDOVER_ERROR ('00110101'B), + OSMO_GSUP_MSGT_E_PREPARE_HANDOVER_RESULT ('00110110'B), + + OSMO_GSUP_MSGT_E_PREPARE_SUBSEQUENT_HANDOVER_REQUEST ('00111000'B), + OSMO_GSUP_MSGT_E_PREPARE_SUBSEQUENT_HANDOVER_ERROR ('00111001'B), + OSMO_GSUP_MSGT_E_PREPARE_SUBSEQUENT_HANDOVER_RESULT ('00111010'B), + + OSMO_GSUP_MSGT_E_SEND_END_SIGNAL_REQUEST ('00111100'B), + OSMO_GSUP_MSGT_E_SEND_END_SIGNAL_ERROR ('00111101'B), + OSMO_GSUP_MSGT_E_SEND_END_SIGNAL_RESULT ('00111110'B), + + OSMO_GSUP_MSGT_E_PROCESS_ACCESS_SIGNALLING_REQUEST ('01000000'B), + OSMO_GSUP_MSGT_E_FORWARD_ACCESS_SIGNALLING_REQUEST ('01000100'B), + + OSMO_GSUP_MSGT_E_CLOSE ('01000111'B), + OSMO_GSUP_MSGT_E_ABORT ('01001011'B), + + OSMO_GSUP_MSGT_E_ROUTING_ERROR ('01001110'B) } with { variant "FIELDLENGTH(8)" }; type enumerated GSUP_CancelType { @@ -128,6 +157,14 @@ type enumerated GSUP_SessionState { OSMO_GSUP_SESSION_STATE_END (3) } with { variant "FIELDLENGTH(8)" }; +type enumerated GSUP_Message_Class { + OSMO_GSUP_MESSAGE_CLASS_UNSET (0), + OSMO_GSUP_MESSAGE_CLASS_SUBSCRIBER_MANAGEMENT (1), + OSMO_GSUP_MESSAGE_CLASS_SMS (2), + OSMO_GSUP_MESSAGE_CLASS_USSD (3), + OSMO_GSUP_MESSAGE_CLASS_INTER_MSC (4) +} with { variant "FIELDLENGTH(8)" }; + type record GSUP_MSISDN { uint8_t len, hexstring digits optional @@ -138,6 +175,16 @@ type record GSUP_IMEI { hexstring digits optional } with { variant (len) "LENGTHTO(digits)" }; +type enumerated GSUP_AN_PROTO { + OSMO_GSUP_AN_PROTO_48006 (1), + OSMO_GSUP_AN_PROTO_25413 (2) +} with { variant "FIELDLENGTH(8)" }; + +type record GSUP_AN_APDU { + GSUP_AN_PROTO proto, + octetstring pdu +}; + type record GSUP_IE { GSUP_IEI tag, uint8_t len, @@ -175,6 +222,13 @@ type record GSUP_IE { sm_alert_rsn, tag = OSMO_GSUP_SM_ALERT_RSN_IE; imei, tag = OSMO_GSUP_IMEI_IE; imei_result, tag = OSMO_GSUP_IMEI_RESULT_IE; + message_class, tag = OSMO_GSUP_MESSAGE_CLASS_IE; + source_name, tag = OSMO_GSUP_SOURCE_NAME_IE; + destination_name, tag = OSMO_GSUP_DESTINATION_NAME_IE; + an_apdu, tag = OSMO_GSUP_AN_APDU_IE; + cause_rr, tag = OSMO_GSUP_CAUSE_RR_IE; + cause_bssap, tag = OSMO_GSUP_CAUSE_BSSAP_IE; + cause_sm, tag = OSMO_GSUP_CAUSE_SM_IE; )" }; @@ -219,7 +273,18 @@ type union GSUP_IeValue { GSUP_SM_ALERT_RSN_Type sm_alert_rsn, GSUP_IMEI imei, - GSUP_IMEIResult imei_result + GSUP_IMEIResult imei_result, + + GSUP_Message_Class message_class, + + octetstring source_name, + octetstring destination_name, + + GSUP_AN_APDU an_apdu, + + OCT1 cause_rr, + OCT1 cause_bssap, + OCT1 cause_sm }; type record GSUP_PDU { @@ -930,6 +995,70 @@ template GSUP_IE tr_GSUP_IE_SSInfo(template octetstring ss) := { } } +template GSUP_IE tr_GSUP_IE_Message_Class(template GSUP_Message_Class val) := { + tag := OSMO_GSUP_MESSAGE_CLASS_IE, + len := ?, + val := { + message_class := val + } +} + +template (value) GSUP_IE ts_GSUP_IE_Message_Class(GSUP_Message_Class val) := { + tag := OSMO_GSUP_MESSAGE_CLASS_IE, + len := 0, /* overwritten */ + val := { + message_class := val + } +} + +template GSUP_IE tr_GSUP_IE_Source_Name(template octetstring name) := { + tag := OSMO_GSUP_SOURCE_NAME_IE, + len := ?, + val := { + source_name := name + } +} + +template (value) GSUP_IE ts_GSUP_IE_Source_Name(octetstring name) := { + tag := OSMO_GSUP_SOURCE_NAME_IE, + len := 0, /* overwritten */ + val := { + source_name := name + } +} + +template GSUP_IE tr_GSUP_IE_Destination_Name(template octetstring name) := { + tag := OSMO_GSUP_DESTINATION_NAME_IE, + len := ?, + val := { + destination_name := name + } +} + +template (value) GSUP_IE ts_GSUP_IE_Destination_Name(octetstring name) := { + tag := OSMO_GSUP_DESTINATION_NAME_IE, + len := 0, /* overwritten */ + val := { + destination_name := name + } +} + +template GSUP_IE tr_GSUP_IE_AN_APDU(template GSUP_AN_APDU an_apdu) := { + tag := OSMO_GSUP_AN_APDU_IE, + len := ?, + val := { + an_apdu := an_apdu + } +} + +template (value) GSUP_IE ts_GSUP_IE_AN_APDU(GSUP_AN_APDU an_apdu) := { + tag := OSMO_GSUP_AN_APDU_IE, + len := 0, /* overwritten */ + val := { + an_apdu := an_apdu + } +} + private function f_gen_ts_ss_ies( hexstring imsi, OCT4 sid, @@ -962,14 +1091,20 @@ private function f_gen_tr_ss_ies( tr_GSUP_IE_SessionId(sid), tr_GSUP_IE_SessionState(state) }; + var integer last_idx := 3; /* Optional SS payload */ if (istemplatekind(ss, "*")) { ies[3] := *; + last_idx := last_idx + 1; } else if (not istemplatekind(ss, "omit")) { ies[3] := tr_GSUP_IE_SSInfo(ss); + last_idx := last_idx + 1; } + ies[last_idx] := tr_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_USSD); + last_idx := last_idx + 1; + return ies; } @@ -1036,7 +1171,8 @@ template GSUP_PDU tr_GSUP_PROC_SS_ERR( tr_GSUP_IE_IMSI(imsi), tr_GSUP_IE_Cause(cause), tr_GSUP_IE_SessionId(sid), - tr_GSUP_IE_SessionState(state) + tr_GSUP_IE_SessionState(state), + tr_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_SMS) } ); @@ -1069,7 +1205,8 @@ template GSUP_PDU tr_GSUP_MO_FORWARD_SM_REQ( tr_GSUP_IE_SM_RP_MR(sm_rp_mr), tr_GSUP_IE_SM_RP_DA(sm_rp_da), tr_GSUP_IE_SM_RP_OA(sm_rp_oa), - tr_GSUP_IE_SM_RP_UI(sm_rp_ui) + tr_GSUP_IE_SM_RP_UI(sm_rp_ui), + tr_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_SMS) } ); @@ -1090,7 +1227,8 @@ template GSUP_PDU tr_GSUP_MO_FORWARD_SM_RES( OSMO_GSUP_MSGT_MO_FORWARD_SM_RESULT, { tr_GSUP_IE_IMSI(imsi), - tr_GSUP_IE_SM_RP_MR(sm_rp_mr) + tr_GSUP_IE_SM_RP_MR(sm_rp_mr), + tr_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_SMS) } ); @@ -1115,7 +1253,8 @@ template GSUP_PDU tr_GSUP_MO_FORWARD_SM_ERR( { tr_GSUP_IE_IMSI(imsi), tr_GSUP_IE_SM_RP_MR(sm_rp_mr), - tr_GSUP_IE_SM_RP_CAUSE(sm_rp_cause) + tr_GSUP_IE_SM_RP_CAUSE(sm_rp_cause), + tr_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_SMS) } ); @@ -1162,7 +1301,8 @@ template GSUP_PDU tr_GSUP_MT_FORWARD_SM_REQ( tr_GSUP_IE_SM_RP_DA(sm_rp_da), tr_GSUP_IE_SM_RP_OA(sm_rp_oa), tr_GSUP_IE_SM_RP_UI(sm_rp_ui), - tr_GSUP_IE_SM_RP_MMS(sm_rp_mms) + tr_GSUP_IE_SM_RP_MMS(sm_rp_mms), + tr_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_SMS) } ); @@ -1183,7 +1323,8 @@ template GSUP_PDU tr_GSUP_MT_FORWARD_SM_RES( OSMO_GSUP_MSGT_MT_FORWARD_SM_RESULT, { tr_GSUP_IE_IMSI(imsi), - tr_GSUP_IE_SM_RP_MR(sm_rp_mr) + tr_GSUP_IE_SM_RP_MR(sm_rp_mr), + tr_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_SMS) } ); @@ -1208,7 +1349,8 @@ template GSUP_PDU tr_GSUP_MT_FORWARD_SM_ERR( { tr_GSUP_IE_IMSI(imsi), tr_GSUP_IE_SM_RP_MR(sm_rp_mr), - tr_GSUP_IE_SM_RP_CAUSE(sm_rp_cause) + tr_GSUP_IE_SM_RP_CAUSE(sm_rp_cause), + tr_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_SMS) } ); @@ -1233,7 +1375,8 @@ template GSUP_PDU tr_GSUP_MO_READY_FOR_SM_REQ( { tr_GSUP_IE_IMSI(imsi), tr_GSUP_IE_SM_RP_MR(sm_rp_mr), - tr_GSUP_IE_SM_ALERT_RSN(sm_alert_rsn) + tr_GSUP_IE_SM_ALERT_RSN(sm_alert_rsn), + tr_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_SMS) } ); @@ -1254,7 +1397,8 @@ template GSUP_PDU tr_GSUP_MO_READY_FOR_SM_RES( OSMO_GSUP_MSGT_READY_FOR_SM_RESULT, { tr_GSUP_IE_IMSI(imsi), - tr_GSUP_IE_SM_RP_MR(sm_rp_mr) + tr_GSUP_IE_SM_RP_MR(sm_rp_mr), + tr_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_SMS) } ); @@ -1279,7 +1423,8 @@ template GSUP_PDU tr_GSUP_MO_READY_FOR_SM_ERR( { tr_GSUP_IE_IMSI(imsi), tr_GSUP_IE_SM_RP_MR(sm_rp_mr), - tr_GSUP_IE_SM_RP_CAUSE(sm_rp_cause) + tr_GSUP_IE_SM_RP_CAUSE(sm_rp_cause), + tr_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_SMS) } ); @@ -1293,5 +1438,79 @@ function f_gsup_find_ie(GSUP_PDU msg, GSUP_IEI iei, out GSUP_IeValue ret) return return false; } +template GSUP_AN_APDU t_GSUP_AN_APDU( + template GSUP_AN_PROTO an_proto := ?, + template octetstring pdu := ? +) := { + proto := an_proto, + pdu := pdu +}; + +template GSUP_PDU tr_GSUP_E_AN_APDU( + template GSUP_MessageType msgt, + template hexstring imsi := ?, + template octetstring source_name := ?, + template octetstring destination_name := ?, + template GSUP_AN_APDU an_apdu := ? +) := tr_GSUP( + msgt, + { + tr_GSUP_IE_IMSI(imsi), + tr_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_INTER_MSC), + tr_GSUP_IE_Source_Name(source_name), + tr_GSUP_IE_Destination_Name(destination_name), + tr_GSUP_IE_AN_APDU(an_apdu) + } +); + +template GSUP_PDU tr_GSUP_E_NO_PDU( + template GSUP_MessageType msgt, + template hexstring imsi := ?, + template octetstring source_name := ?, + template octetstring destination_name := ? +) := tr_GSUP( + msgt, + { + tr_GSUP_IE_IMSI(imsi), + tr_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_INTER_MSC), + tr_GSUP_IE_Source_Name(source_name), + tr_GSUP_IE_Destination_Name(destination_name) + } +); + +template (value) GSUP_PDU ts_GSUP_E_AN_APDU( + GSUP_MessageType msgt, + hexstring imsi, + octetstring source_name, + octetstring destination_name, + GSUP_AN_APDU an_apdu +) := ts_GSUP( + msgt, + { + valueof(ts_GSUP_IE_IMSI(imsi)), + valueof(ts_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_INTER_MSC)), + valueof(ts_GSUP_IE_Source_Name(source_name)), + valueof(ts_GSUP_IE_Destination_Name(destination_name)), + valueof(ts_GSUP_IE_AN_APDU(an_apdu)) + } +); + +template (value) GSUP_PDU ts_GSUP_E_PrepareHandoverResult( + hexstring imsi, + hexstring msisdn, + octetstring source_name, + octetstring destination_name, + GSUP_AN_APDU an_apdu +) := ts_GSUP( + OSMO_GSUP_MSGT_E_PREPARE_HANDOVER_RESULT, + { + valueof(ts_GSUP_IE_IMSI(imsi)), + valueof(ts_GSUP_IE_MSISDN(msisdn)), + valueof(ts_GSUP_IE_Message_Class(OSMO_GSUP_MESSAGE_CLASS_INTER_MSC)), + valueof(ts_GSUP_IE_Source_Name(source_name)), + valueof(ts_GSUP_IE_Destination_Name(destination_name)), + valueof(ts_GSUP_IE_AN_APDU(an_apdu)) + } +); } with { encode "RAW"; variant "FIELDORDER(msb)" } diff --git a/library/L3_Templates.ttcn b/library/L3_Templates.ttcn index 4d71f270..cf6d64ce 100644 --- a/library/L3_Templates.ttcn +++ b/library/L3_Templates.ttcn @@ -582,6 +582,59 @@ template (value) PDU_ML3_NW_MS ts_RR_HandoverCommand := { } } +template PDU_ML3_NW_MS tr_RR_HandoverCommand := { + discriminator := '0110'B, + tiOrSkip := { + skipIndicator := '0000'B + }, + msgs := { + rrm := { + handoverCommand := { + messageType := '00101011'B, + cellDescription := ?, + channelDescription2 := ?, + handoverReference := ?, + powerCommandAndAccesstype := ?, + synchronizationIndication := *, + frequencyShortListAfterTime := *, + frequencyListAfterTime := *, + cellChannelDescription := *, + multislotAllocation := *, + modeOfChannelSet1 := *, + modeOfChannelSet2 := *, + modeOfChannelSet3 := *, + modeOfChannelSet4 := *, + modeOfChannelSet5 := *, + modeOfChannelSet6 := *, + modeOfChannelSet7 := *, + modeOfChannelSet8 := *, + descrOf2ndCh_at := *, + modeOf2ndChannel := *, + frequencyChannelSequence_at := *, + mobileAllocation_at := *, + startingTime := *, + timeDifference := *, + timingAdvance := *, + frequencyShortListBeforeTime := *, + frequencyListBeforeTime := *, + descrOf1stCh_bt := *, + descrOf2ndCh_bt := *, + frequencyChannelSequence_bt := *, + mobileAllocation_bt := *, + cipherModeSetting := *, + vGCS_TargetModeIndication := *, + multiRateConfiguration := *, + dynamicARFCN_Mapping := *, + vGCS_Ciphering_Parameters := *, + dedicatedServiceInformation := *, + pLMNIndex := *, + extendedTSCSet_afterTime := *, + extendedTSCSet_beforeTime := * + } + } + } +} + function ts_CM3_TLV(template (omit) OCTN cm3) return template MobileStationClassmark3_TLV { if (not isvalue(cm3)) { return omit; diff --git a/library/RAN_Emulation.ttcnpp b/library/RAN_Emulation.ttcnpp index d6d74e26..843cc9e1 100644 --- a/library/RAN_Emulation.ttcnpp +++ b/library/RAN_Emulation.ttcnpp @@ -133,6 +133,7 @@ type port RAN_Conn_PT message { RAN_Conn_Prim; } with { extension "internal" }; +type uint2_t N_Sd_Array[4]; /* represents a single BSSAP connection over SCCP */ type record ConnectionData { @@ -146,7 +147,7 @@ type record ConnectionData { /* CIC that has been used for voice of this channel (BSC side) */ integer cic optional, /* array of N(SD) values for MO DTAP messages, indexed by discriminator */ - uint2_t n_sd[4] + N_Sd_Array n_sd } type record ImsiMapping { @@ -671,28 +672,51 @@ private function f_L3_is_rr(template octetstring l3) return boolean { return false; } +function f_next_n_sd(inout N_Sd_Array n_sd, in integer n_sd_idx) return uint2_t { + var uint2_t seq_nr; + if (n_sd_idx == 0) { + seq_nr := n_sd[0]; + n_sd[0] := (n_sd[0] + 1) mod 4; + } else if (n_sd_idx >= 1 and n_sd_idx <= 3) { + seq_nr := n_sd[n_sd_idx]; + n_sd[n_sd_idx] := (n_sd[n_sd_idx] + 1) mod 2; + } else { + /* no sequence number to patch */ + seq_nr := 0; + } + return seq_nr; +} + /* patch N(SD) into enc_l3, according to 24.007 11.2.3.2 */ -function f_ML3_patch_seq(inout ConnectionData cd, in PDU_ML3_MS_NW dtap, inout octetstring enc_l3) { +function f_ML3_patch_seq_nr(in uint2_t seq_nr, inout octetstring enc_l3) { + log("patching N(SD)=", seq_nr, " into dtap ", enc_l3); + enc_l3[1] := (enc_l3[1] and4b '3f'O) or4b bit2oct(int2bit(seq_nr, 8) << 6); + log("patched enc_l3: ", enc_l3); +} + +function f_ML3_n_sd_idx(in PDU_ML3_MS_NW dtap) return integer { var uint2_t seq_nr; if (ischosen(dtap.msgs.cc) or ischosen(dtap.msgs.mm) or ischosen(dtap.msgs.ss)) { - seq_nr := cd.n_sd[0]; - cd.n_sd[0] := (cd.n_sd[0] + 1) mod 4; + return 0; } else if (ischosen(dtap.msgs.gcc)) { - seq_nr := cd.n_sd[1]; - cd.n_sd[1] := (cd.n_sd[1] + 1) mod 2; + return 1; } else if (ischosen(dtap.msgs.bcc)) { - seq_nr := cd.n_sd[2]; - cd.n_sd[2] := (cd.n_sd[2] + 1) mod 2; + return 2; } else if (ischosen(dtap.msgs.loc)) { - seq_nr := cd.n_sd[3]; - cd.n_sd[3] := (cd.n_sd[3] + 1) mod 2; - } else { - /* no sequence number to patch */ + return 3; + } + /* no sequence number to patch */ + return -1; +} + +/* patch N(SD) into enc_l3, according to 24.007 11.2.3.2 */ +function f_ML3_patch_seq(inout ConnectionData cd, in PDU_ML3_MS_NW dtap, inout octetstring enc_l3) { + var integer n_sd_idx := f_ML3_n_sd_idx(dtap); + if (n_sd_idx < 0) { return; } - log("patching N(SD)=", seq_nr, " into dtap ", enc_l3); - enc_l3[1] := (enc_l3[1] and4b '3f'O) or4b bit2oct(int2bit(seq_nr, 8) << 6); - log("patched enc_l3: ", enc_l3); + var uint2_t seq_nr := f_next_n_sd(cd.n_sd, n_sd_idx); + f_ML3_patch_seq_nr(seq_nr, enc_l3); } private altstep as_reset_ack() runs on RAN_Emulation_CT { @@ -732,6 +756,8 @@ private altstep as_main_bssap() runs on RAN_Emulation_CT { var BSSAP_Conn_Req creq; var PDU_BSSAP bssap; var RAN_ConnHdlr vc_conn; + var integer targetPointCode; + var N_Sd_Array last_n_sd; /* SCCP -> Client: UNIT-DATA (connectionless SCCP) from a BSC */ [] BSSAP.receive(BSSAP_N_UNITDATA_ind:?) -> value ud_ind { @@ -823,7 +849,18 @@ private altstep as_main_bssap() runs on RAN_Emulation_CT { ConnectionTable[idx].n_sd[0] := 1; log("patch: N(SD) for ConnIdx ", idx, " set to 1"); } + } + + [] PROC.getcall(RAN_last_n_sd:{?,-}) -> param(vc_conn) { + var integer idx := f_idx_by_comp(vc_conn); + last_n_sd := ConnectionTable[idx].n_sd; + PROC.reply(RAN_last_n_sd:{vc_conn, last_n_sd}) to vc_conn; + } + [] PROC.getcall(RAN_continue_after_n_sd:{?,?}) -> param(last_n_sd, vc_conn) { + var integer idx := f_idx_by_comp(vc_conn); + ConnectionTable[idx].n_sd := last_n_sd; + PROC.reply(RAN_continue_after_n_sd:{last_n_sd, vc_conn}) to vc_conn; } #else [false] CLIENT.receive {} @@ -1045,6 +1082,7 @@ function main(RanOps ops, charstring id) runs on RAN_Emulation_CT { var octetstring l3_info; var hexstring imsi; var OCT4 tmsi; + var integer targetPointCode; alt { [g_ran_ops.protocol == RAN_PROTOCOL_BSSAP] as_main_bssap(); @@ -1075,6 +1113,11 @@ function main(RanOps ops, charstring id) runs on RAN_Emulation_CT { PROC.reply(RAN_register:{l3_info, vc_hdlr}) to vc_hdlr; } + [] PROC.getcall(RAN_register_handoverRequest:{?,?}) -> param(targetPointCode, vc_hdlr) { + f_create_expect(omit, vc_hdlr, targetPointCode); + PROC.reply(RAN_register_handoverRequest:{targetPointCode, vc_hdlr}) to vc_hdlr; + } + [] PROC.getcall(RAN_register_imsi:{?,?,?}) -> param(imsi, tmsi, vc_hdlr) { f_create_imsi(imsi, tmsi, vc_hdlr); PROC.reply(RAN_register_imsi:{imsi, tmsi, vc_hdlr}) to vc_hdlr; @@ -1101,18 +1144,26 @@ private function f_mgcp_ep_extract_cic(charstring inp) return integer { type record ExpectData { /* L3 payload based on which we can match it */ octetstring l3_payload optional, + integer handoverRequestPointCode optional, /* component reference for this connection */ RAN_ConnHdlr vc_conn } /* procedure based port to register for incoming connections */ signature RAN_register(in octetstring l3, in RAN_ConnHdlr hdlr); +signature RAN_register_handoverRequest(in integer targetPointCode, in RAN_ConnHdlr hdlr); /* procedure based port to register for incoming IMSI/TMSI */ signature RAN_register_imsi(in hexstring imsi, in OCT4 tmsi, in RAN_ConnHdlr hdlr); +/* If DTAP happens across other channels (e.g. GSUP), provide manual advancing of the n_sd sequence number */ +signature RAN_last_n_sd(in RAN_ConnHdlr hdlr, out N_Sd_Array last_n_sd); + +/* Update conn's n_sd sequence nr after the connection was taken over from elsewhere */ +signature RAN_continue_after_n_sd(N_Sd_Array last_n_sd, in RAN_ConnHdlr hdlr); + type port RAN_PROC_PT procedure { - inout RAN_register, RAN_register_imsi; + inout RAN_register, RAN_register_imsi, RAN_register_handoverRequest, RAN_last_n_sd, RAN_continue_after_n_sd; } with { extension "internal" }; #ifdef RAN_EMULATION_BSSAP @@ -1121,16 +1172,35 @@ function ExpectedCreateCallback(BSSAP_N_CONNECT_ind conn_ind, charstring id) runs on RAN_Emulation_CT return RAN_ConnHdlr { var RAN_ConnHdlr ret := null; var octetstring l3_info; + var boolean handoverRequest := false; + var integer handoverRequestPointCode; var integer i; - if (not ischosen(conn_ind.userData.pdu.bssmap.completeLayer3Information)) { - setverdict(fail, "N-CONNECT.ind with L3 != COMPLETE L3"); + if (ischosen(conn_ind.userData.pdu.bssmap.completeLayer3Information)) { + l3_info := conn_ind.userData.pdu.bssmap.completeLayer3Information.layer3Information.layer3info; + log("ExpectedCreateCallback completeLayer3Information"); + } else if (ischosen(conn_ind.userData.pdu.bssmap.handoverRequest)) { + handoverRequest := true; + handoverRequestPointCode := bit2int(conn_ind.calledAddress.signPointCode); + log("ExpectedCreateCallback handoverRequest ", handoverRequestPointCode); + } else { + setverdict(fail, "N-CONNECT.ind with L3 != COMPLETE L3 nor a Handover Request"); mtc.stop; return ret; } - l3_info := conn_ind.userData.pdu.bssmap.completeLayer3Information.layer3Information.layer3info; for (i := 0; i < sizeof(ExpectTable); i:= i+1) { + if (handoverRequest) { + log("ExpectTable[", i, "].handoverRequestPointCode = ", ExpectTable[i].handoverRequestPointCode, + " ==? ", handoverRequestPointCode); + if (ExpectTable[i].handoverRequestPointCode == handoverRequestPointCode) { + ret := ExpectTable[i].vc_conn; + log("Found Expect[", i, "] for handoverRequest handled at ", ret); + return ret; + } else { + continue; + } + } if (not ispresent(ExpectTable[i].l3_payload)) { continue; } @@ -1185,14 +1255,26 @@ runs on RAN_Emulation_CT return RAN_ConnHdlr { } #endif -private function f_create_expect(octetstring l3, RAN_ConnHdlr hdlr) +private function f_create_expect(template octetstring l3, RAN_ConnHdlr hdlr, + template integer handoverRequestPointCode := omit) runs on RAN_Emulation_CT { var integer i; + log("f_create_expect(l3 := ", l3, ", handoverRequest := ", handoverRequestPointCode); for (i := 0; i < sizeof(ExpectTable); i := i+1) { - if (not ispresent(ExpectTable[i].l3_payload)) { - ExpectTable[i].l3_payload := l3; + if (not ispresent(ExpectTable[i].l3_payload) + and not ispresent(ExpectTable[i].handoverRequestPointCode)) { + if (ispresent(l3)) { + ExpectTable[i].l3_payload := valueof(l3); + } + if (ispresent(handoverRequestPointCode)) { + ExpectTable[i].handoverRequestPointCode := valueof(handoverRequestPointCode); + } ExpectTable[i].vc_conn := hdlr; - log("Created Expect[", i, "] for ", l3, " to be handled at ", hdlr); + if (ispresent(handoverRequestPointCode)) { + log("Created Expect[", i, "] for handoverRequest to be handled at ", hdlr); + } else { + log("Created Expect[", i, "] for ", l3, " to be handled at ", hdlr); + } return; } } @@ -1218,6 +1300,7 @@ private function f_expect_table_init() runs on RAN_Emulation_CT { for (var integer i := 0; i < sizeof(ExpectTable); i := i+1) { ExpectTable[i].l3_payload := omit; + ExpectTable[i].handoverRequestPointCode := omit; } } diff --git a/msc/BSC_ConnectionHandler.ttcn b/msc/BSC_ConnectionHandler.ttcn index 44d3c6da..1cec69c3 100644 --- a/msc/BSC_ConnectionHandler.ttcn +++ b/msc/BSC_ConnectionHandler.ttcn @@ -1133,6 +1133,27 @@ function f_expect_clear(float t := 5.0) runs on BSC_ConnHdlr { } } +function f_create_bssmap_exp_handoverRequest(integer targetPointCode) runs on BSC_ConnHdlr { + BSSAP_PROC.call(RAN_register_handoverRequest:{targetPointCode, self}) { + [] BSSAP_PROC.getreply(RAN_register_handoverRequest:{?, ?}) {}; + } +} + +function f_bssmap_last_n_sd() runs on BSC_ConnHdlr return N_Sd_Array { + var N_Sd_Array last_n_sd; + BSSAP_PROC.call(RAN_last_n_sd:{self, -}) { + [] BSSAP_PROC.getreply(RAN_last_n_sd:{self, ?}) -> param(last_n_sd) { + return last_n_sd; + }; + } +} + +function f_bssmap_continue_after_n_sd(N_Sd_Array last_n_sd) runs on BSC_ConnHdlr { + BSSAP_PROC.call(RAN_continue_after_n_sd:{last_n_sd, self}) { + [] BSSAP_PROC.getreply(RAN_continue_after_n_sd:{last_n_sd, self}); + } +} + type record SmsParametersTp { OCT1 msg_ref, TP_DA da, diff --git a/msc/MSC_Tests.ttcn b/msc/MSC_Tests.ttcn index 709a73cb..3a6711b0 100644 --- a/msc/MSC_Tests.ttcn +++ b/msc/MSC_Tests.ttcn @@ -468,31 +468,6 @@ modifies ts_BSSAP_BSSMAP := { } } -template PDU_BSSAP ts_BSSMAP_HandoReq(BssmapCause cause, BSSMAP_IE_CellIdentifierList cid_list) -modifies ts_BSSAP_BSSMAP := { - pdu := { - bssmap := { - handoverRequired := { - messageType := '11'O, - cause := ts_BSSMAP_IE_Cause(cause), - responseRequest := omit, - cellIdentifierList := cid_list, - circuitPoolList := omit, - currentChannelType1 := omit, - speechVersion := omit, - queueingIndicator := omit, - oldToNewBSSInfo := omit, - sourceToTargetRNCTransparentInfo := omit, - sourceToTargetRNCTransparentInfoCDMA := omit, - gERANClassmark := omit, - talkerPriority := omit, - speechCodec := omit, - cSG_Identifier := omit - } - } - } -} - type function void_fn(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr; /* FIXME: move into BSC_ConnectionHandler? */ @@ -536,14 +511,14 @@ runs on MTC_CT return BSC_ConnHdlrPars { return pars; } -function f_start_handler_with_pars(void_fn fn, BSC_ConnHdlrPars pars) runs on MTC_CT return BSC_ConnHdlr { +function f_start_handler_with_pars(void_fn fn, BSC_ConnHdlrPars pars, integer bssap_idx := 0) runs on MTC_CT return BSC_ConnHdlr { var BSC_ConnHdlr vc_conn; - var charstring id := testcasename(); + var charstring id := testcasename() & int2str(bssap_idx); vc_conn := BSC_ConnHdlr.create(id); /* BSSMAP part / A interface */ - connect(vc_conn:BSSAP, g_bssap[pars.ran_idx].vc_RAN:CLIENT); - connect(vc_conn:BSSAP_PROC, g_bssap[pars.ran_idx].vc_RAN:PROC); + connect(vc_conn:BSSAP, g_bssap[pars.ran_idx + bssap_idx].vc_RAN:CLIENT); + connect(vc_conn:BSSAP_PROC, g_bssap[pars.ran_idx + bssap_idx].vc_RAN:PROC); /* MNCC part */ connect(vc_conn:MNCC, vc_MNCC:MNCC_CLIENT); connect(vc_conn:MNCC_PROC, vc_MNCC:MNCC_PROC); @@ -4776,6 +4751,464 @@ testcase TC_sgsap_vlr_failure() runs on MTC_CT { * */ +private function f_tc_ho_inter_bsc_unknown_cell(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + var CallParameters cpars := valueof(t_CallParams('12345'H, 0)); + cpars.bss_rtp_port := 1110; + cpars.mgcp_connection_id_bss := '22222'H; + cpars.mgcp_connection_id_mss := '33333'H; + cpars.mgcp_ep := "rtpbridge/1@mgw"; + cpars.mo_call := true; + + f_perform_lu(); + f_mo_call_establish(cpars); + + f_sleep(1.0); + + var myBSSMAP_Cause cause_val := GSM0808_CAUSE_BETTER_CELL; + var BssmapCause cause := enum2int(cause_val); + + var template BSSMAP_FIELD_CellIdentificationList cil; + cil := { cIl_LAI := { ts_BSSMAP_CI_LAI('023'H, '42'H, 999) } }; + + BSSAP.send(ts_BSSMAP_HandoverRequired(cause, cil)); + BSSAP.receive(tr_BSSMAP_HandoverRequiredReject); + + f_call_hangup(cpars, true); +} +testcase TC_ho_inter_bsc_unknown_cell() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(); + + vc_conn := f_start_handler(refers(f_tc_ho_inter_bsc_unknown_cell), 53); + vc_conn.done; +} + +private altstep as_mgcp_ack_all_mdcx(CallParameters cpars) runs on BSC_ConnHdlr { + var MgcpCommand mgcp_cmd; + [] MGCP.receive(tr_MDCX) -> value mgcp_cmd { + var SDP_Message sdp := valueof(ts_SDP(cpars.mgw_rtp_ip_mss, cpars.mgw_rtp_ip_mss, + hex2str(cpars.mgcp_call_id), "42", + cpars.mgw_rtp_port_mss, + { int2str(cpars.rtp_payload_type) }, + { valueof(ts_SDP_rtpmap(cpars.rtp_payload_type, + cpars.rtp_sdp_format)), + valueof(ts_SDP_ptime(20)) })); + MGCP.send(ts_MDCX_ACK(mgcp_cmd.line.trans_id, cpars.mgcp_connection_id_mss, sdp)); + repeat; + } +} + +private function f_tc_ho_inter_bsc0(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + var CallParameters cpars := valueof(t_CallParams('12345'H, 0)); + cpars.bss_rtp_port := 1110; + cpars.mgcp_connection_id_bss := '22222'H; + cpars.mgcp_connection_id_mss := '33333'H; + cpars.mgcp_ep := "rtpbridge/1@mgw"; + cpars.mo_call := true; + + f_init_handler(pars); + + f_vty_transceive(MSCVTY, "configure terminal"); + f_vty_transceive(MSCVTY, "msc"); + f_vty_transceive(MSCVTY, "neighbor a cgi 262 42 23 42 ran-pc 0.24.1"); + f_vty_transceive(MSCVTY, "neighbor a lac 5 ran-pc 0.24.2"); + f_vty_transceive(MSCVTY, "exit"); + f_vty_transceive(MSCVTY, "exit"); + + f_perform_lu(); + f_mo_call_establish(cpars); + + f_sleep(1.0); + + var default ack_mdcx := activate(as_mgcp_ack_all_mdcx(cpars)); + + var myBSSMAP_Cause cause_val := GSM0808_CAUSE_BETTER_CELL; + var BssmapCause cause := enum2int(cause_val); + + var template BSSMAP_FIELD_CellIdentificationList cil; + cil := { cIl_LAI := { ts_BSSMAP_CI_LAI('023'H, '42'H, 5) } }; + + /* old BSS sends Handover Required */ + BSSAP.send(ts_BSSMAP_HandoverRequired(cause, cil)); + + /* Now the action goes on in f_tc_ho_inter_bsc1() */ + + /* MSC forwards the RR Handover Command to old BSS */ + var PDU_BSSAP ho_command; + BSSAP.receive(tr_BSSMAP_HandoverCommand) -> value ho_command; + + log("GOT HandoverCommand", ho_command); + + BSSAP.receive(tr_BSSMAP_HandoverSucceeded); + + /* f_tc_ho_inter_bsc1() completes Handover, then expecting a Clear here. */ + f_expect_clear(); + + log("FIRST inter-BSC Handover done"); + + + /* ------------------------ */ + + /* Ok, that went well, now the other BSC is handovering back here -- + * from now on this here is the new BSS. */ + f_create_bssmap_exp_handoverRequest(193); + + var PDU_BSSAP ho_request; + BSSAP.receive(tr_BSSMAP_HandoverRequest) -> value ho_request; + + /* new BSS composes a RR Handover Command */ + var PDU_ML3_NW_MS rr_ho_cmd := valueof(ts_RR_HandoverCommand); + var octetstring rr_ho_cmd_enc := enc_PDU_ML3_NW_MS(rr_ho_cmd); + var BSSMAP_IE_AoIP_TransportLayerAddress tla := valueof(ts_BSSMAP_IE_AoIP_TLA4('01020304'O, 2342)); + BSSAP.send(ts_BSSMAP_HandoverRequestAcknowledge(rr_ho_cmd_enc, lengthof(rr_ho_cmd_enc), + tla, ts_BSSMAP_IE_SpeechCodec({ts_CodecFR}))); + + /* Now f_tc_ho_inter_bsc1() expects HandoverCommand */ + + f_sleep(0.5); + + /* Notify that the MS is now over here */ + + BSSAP.send(ts_BSSMAP_HandoverDetect); + f_sleep(0.1); + BSSAP.send(ts_BSSMAP_HandoverComplete); + + f_sleep(3.0); + + deactivate(ack_mdcx); + + var default ccrel := activate(as_optional_cc_rel(cpars, true)); + + /* blatant cheating */ + var N_Sd_Array last_n_sd := f_bssmap_last_n_sd(); + last_n_sd[0] := 3; + f_bssmap_continue_after_n_sd(last_n_sd); + + f_call_hangup(cpars, true); + f_sleep(1.0); + deactivate(ccrel); + + setverdict(pass); +} +private function f_tc_ho_inter_bsc1(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + f_init_handler(pars); + f_create_bssmap_exp_handoverRequest(194); + + var PDU_BSSAP ho_request; + BSSAP.receive(tr_BSSMAP_HandoverRequest) -> value ho_request; + + /* new BSS composes a RR Handover Command */ + var PDU_ML3_NW_MS rr_ho_cmd := valueof(ts_RR_HandoverCommand); + var octetstring rr_ho_cmd_enc := enc_PDU_ML3_NW_MS(rr_ho_cmd); + var BSSMAP_IE_AoIP_TransportLayerAddress tla := valueof(ts_BSSMAP_IE_AoIP_TLA4('01020304'O, 2342)); + BSSAP.send(ts_BSSMAP_HandoverRequestAcknowledge(rr_ho_cmd_enc, lengthof(rr_ho_cmd_enc), + tla, ts_BSSMAP_IE_SpeechCodec({ts_CodecFR}))); + + /* Now f_tc_ho_inter_bsc0() expects HandoverCommand */ + + f_sleep(0.5); + + /* Notify that the MS is now over here */ + + BSSAP.send(ts_BSSMAP_HandoverDetect); + f_sleep(0.1); + BSSAP.send(ts_BSSMAP_HandoverComplete); + + f_sleep(3.0); + + /* Now I'd like to f_call_hangup() but we don't know any cpars here. So + * ... handover back to the first BSC :P */ + + var myBSSMAP_Cause cause_val := GSM0808_CAUSE_BETTER_CELL; + var BssmapCause cause := enum2int(cause_val); + + var template BSSMAP_FIELD_CellIdentificationList cil; + cil := { cIl_LAI := { ts_BSSMAP_CI_LAI('262'H, '42'H, 23) } }; + + /* old BSS sends Handover Required */ + BSSAP.send(ts_BSSMAP_HandoverRequired(cause, cil)); + + /* Now the action goes on in f_tc_ho_inter_bsc0() */ + + /* MSC forwards the RR Handover Command to old BSS */ + var PDU_BSSAP ho_command; + BSSAP.receive(tr_BSSMAP_HandoverCommand) -> value ho_command; + + log("GOT HandoverCommand", ho_command); + + BSSAP.receive(tr_BSSMAP_HandoverSucceeded); + + /* f_tc_ho_inter_bsc1() completes Handover, then expecting a Clear here. */ + f_expect_clear(); + setverdict(pass); +} +testcase TC_ho_inter_bsc() runs on MTC_CT { + var BSC_ConnHdlr vc_conn0; + var BSC_ConnHdlr vc_conn1; + f_init(2); + + var BSC_ConnHdlrPars pars0 := f_init_pars(53); + var BSC_ConnHdlrPars pars1 := f_init_pars(53); + + vc_conn0 := f_start_handler_with_pars(refers(f_tc_ho_inter_bsc0), pars0, 0); + vc_conn1 := f_start_handler_with_pars(refers(f_tc_ho_inter_bsc1), pars1, 1); + vc_conn0.done; + vc_conn1.done; +} + +function f_ML3_patch_seq_nr_MS_NW(in uint2_t seq_nr, inout octetstring enc_l3) { + log("MS_NW patching N(SD)=", seq_nr, " into dtap ", enc_l3); + enc_l3[2] := (enc_l3[2] and4b '3f'O) or4b bit2oct(int2bit(seq_nr, 8) << 6); + log("MS_NW patched enc_l3: ", enc_l3); +} + +private function f_tc_ho_inter_msc_out(charstring id, BSC_ConnHdlrPars pars) runs on BSC_ConnHdlr { + var CallParameters cpars := valueof(t_CallParams('12345'H, 0)); + cpars.bss_rtp_port := 1110; + cpars.mgcp_connection_id_bss := '22222'H; + cpars.mgcp_connection_id_mss := '33333'H; + cpars.mgcp_ep := "rtpbridge/1@mgw"; + cpars.mo_call := true; + var hexstring ho_number := f_gen_msisdn(99999); + + f_init_handler(pars); + + f_create_mncc_expect(hex2str(ho_number)); + + f_vty_transceive(MSCVTY, "configure terminal"); + f_vty_transceive(MSCVTY, "msc"); + f_vty_transceive(MSCVTY, "neighbor a cgi 017 017 1 1 msc-ipa-name msc-017-017-1"); + f_vty_transceive(MSCVTY, "exit"); + f_vty_transceive(MSCVTY, "exit"); + + f_perform_lu(); + f_mo_call_establish(cpars); + + f_sleep(1.0); + + var default ack_mdcx := activate(as_mgcp_ack_all_mdcx(cpars)); + + var myBSSMAP_Cause cause_val := GSM0808_CAUSE_BETTER_CELL; + var BssmapCause cause := enum2int(cause_val); + + var template BSSMAP_FIELD_CellIdentificationList cil; + cil := { cIl_LAI := { ts_BSSMAP_CI_LAI('017'H, '017'H, 1) } }; + + /* old BSS sends Handover Required */ + BSSAP.send(ts_BSSMAP_HandoverRequired(cause, cil)); + + /* The target cell 017-017 LAC 1 is configured to be a remote MSC of name "msc-017-017-1". + * This MSC tries to reach the other MSC via GSUP. */ + + var octetstring remote_msc_name := '6D73632D3031372D3031372D3100'O; /* "msc-017-017-1\0" as octetstring */ + var GSUP_PDU prep_ho_req; + GSUP.receive(tr_GSUP_E_AN_APDU(OSMO_GSUP_MSGT_E_PREPARE_HANDOVER_REQUEST, + pars.imsi, destination_name := remote_msc_name)) -> value prep_ho_req; + + var GSUP_IeValue source_name_ie; + f_gsup_find_ie(prep_ho_req, OSMO_GSUP_SOURCE_NAME_IE, source_name_ie); + var octetstring local_msc_name := source_name_ie.source_name; + + /* Remote MSC has figured out its BSC and signals success */ + var PDU_ML3_NW_MS rr_ho_cmd := valueof(ts_RR_HandoverCommand); + var octetstring rr_ho_cmd_enc := enc_PDU_ML3_NW_MS(rr_ho_cmd); + var PDU_BSSAP ho_req_ack := valueof(ts_BSSMAP_HandoverRequestAcknowledge(rr_ho_cmd_enc, lengthof(rr_ho_cmd_enc), + aoIPTransportLayer := omit, + speechCodec := ts_BSSMAP_IE_SpeechCodec({ts_CodecFR}))); + GSUP.send(ts_GSUP_E_PrepareHandoverResult( + pars.imsi, + ho_number, + remote_msc_name, local_msc_name, + valueof(t_GSUP_AN_APDU(OSMO_GSUP_AN_PROTO_48006, enc_PDU_BSSAP(ho_req_ack))))); + + /* MSC forwards the RR Handover Command to old BSS */ + BSSAP.receive(tr_BSSMAP_HandoverCommand); + + /* The MS shows up at remote new BSS */ + + GSUP.send(ts_GSUP_E_AN_APDU(OSMO_GSUP_MSGT_E_PROCESS_ACCESS_SIGNALLING_REQUEST, + pars.imsi, remote_msc_name, local_msc_name, + valueof(t_GSUP_AN_APDU(OSMO_GSUP_AN_PROTO_48006, + enc_PDU_BSSAP(valueof(ts_BSSMAP_HandoverDetect)))))); + BSSAP.receive(tr_BSSMAP_HandoverSucceeded); + f_sleep(0.1); + + /* Save the MS sequence counters for use on the other connection */ + var N_Sd_Array last_n_sd := f_bssmap_last_n_sd(); + + GSUP.send(ts_GSUP_E_AN_APDU(OSMO_GSUP_MSGT_E_SEND_END_SIGNAL_REQUEST, + pars.imsi, remote_msc_name, local_msc_name, + valueof(t_GSUP_AN_APDU(OSMO_GSUP_AN_PROTO_48006, + enc_PDU_BSSAP(valueof(ts_BSSMAP_HandoverComplete)))))); + + /* The local BSS conn clears, all communication goes via remote MSC now */ + f_expect_clear(); + + /**********************************/ + /* Play through some signalling across the inter-MSC link. + * This is a copy of f_tc_lu_and_mo_ussd_single_request() translated into GSUP AN-APDUs. */ + + if (false) { + var template OCTN facility_req := f_USSD_FACILITY_IE_INVOKE( + invoke_id := 5, /* Phone may not start from 0 or 1 */ + op_code := SS_OP_CODE_PROCESS_USS_REQ, + ussd_string := "*#100#" + ); + + var template OCTN facility_rsp := f_USSD_FACILITY_IE_RETURN_RESULT( + invoke_id := 5, /* InvokeID shall be the same for both REQ and RSP */ + op_code := SS_OP_CODE_PROCESS_USS_REQ, + ussd_string := "Your extension is " & hex2str(g_pars.msisdn) & "\r" + ) + + /* Compose a new SS/REGISTER message with request */ + var template (value) PDU_ML3_MS_NW ussd_req := ts_ML3_MO_SS_REGISTER( + tid := 1, /* We just need a single transaction */ + ti_flag := c_TIF_ORIG, /* Sent from the side that originates the TI */ + facility := valueof(facility_req) + ); + var PDU_ML3_MS_NW ussd_req_v := valueof(ussd_req); + + /* Compose SS/RELEASE_COMPLETE template with expected response */ + var template PDU_ML3_NW_MS ussd_rsp := tr_ML3_MT_SS_RELEASE_COMPLETE( + tid := 1, /* Response should arrive within the same transaction */ + ti_flag := c_TIF_REPL, /* Sent to the side that originates the TI */ + facility := valueof(facility_rsp) + ); + + /* Compose expected MSC -> HLR message */ + var template GSUP_PDU gsup_req := tr_GSUP_PROC_SS_REQ( + imsi := g_pars.imsi, + state := OSMO_GSUP_SESSION_STATE_BEGIN, + ss := valueof(facility_req) + ); + + /* To be used for sending response with correct session ID */ + var GSUP_PDU gsup_req_complete; + + /* Request own number */ + /* From remote MSC instead of BSSAP directly */ + /* Patch the correct N_SD value into the message. */ + var octetstring l3_enc := enc_PDU_ML3_MS_NW(ussd_req_v); + var RAN_Emulation.ConnectionData cd; + f_ML3_patch_seq_nr_MS_NW(f_next_n_sd(last_n_sd, f_ML3_n_sd_idx(ussd_req_v)), l3_enc); + GSUP.send(ts_GSUP_E_AN_APDU(OSMO_GSUP_MSGT_E_PROCESS_ACCESS_SIGNALLING_REQUEST, + pars.imsi, remote_msc_name, local_msc_name, + valueof(t_GSUP_AN_APDU(OSMO_GSUP_AN_PROTO_48006, + enc_PDU_BSSAP(valueof(ts_BSSAP_DTAP(l3_enc))) + )) + )); + + /* Expect GSUP message containing the SS payload */ + gsup_req_complete := f_expect_gsup_msg(gsup_req); + + /* Compose the response from HLR using received session ID */ + var template GSUP_PDU gsup_rsp := ts_GSUP_PROC_SS_REQ( + imsi := g_pars.imsi, + sid := gsup_req_complete.ies[1].val.session_id, + state := OSMO_GSUP_SESSION_STATE_END, + ss := valueof(facility_rsp) + ); + + /* Finally, HLR terminates the session */ + GSUP.send(gsup_rsp); + + /* The USSD response goes out to remote MSC, on GSUP E instead of BSSAP */ + var GSUP_PDU gsup_ussd_rsp; + GSUP.receive(tr_GSUP_E_AN_APDU(OSMO_GSUP_MSGT_E_FORWARD_ACCESS_SIGNALLING_REQUEST, + pars.imsi, destination_name := remote_msc_name)) -> value gsup_ussd_rsp; + + var GSUP_IeValue an_apdu; + if (not f_gsup_find_ie(gsup_ussd_rsp, OSMO_GSUP_AN_APDU_IE, an_apdu)) { + setverdict(fail, "No AN-APDU in received GSUP message. Expected USSD response in DTAP, got", gsup_ussd_rsp); + mtc.stop; + } + var PDU_BSSAP bssap_dtap_mt := dec_PDU_BSSAP(an_apdu.an_apdu.pdu); + var PDU_ML3_NW_MS dtap_mt := dec_PDU_ML3_NW_MS(bssap_dtap_mt.pdu.dtap); + log("Expecting", ussd_rsp); + log("Got", dtap_mt); + if (not match(dtap_mt, ussd_rsp)) { + setverdict(fail, "Unexpected GSUP message. Expected USSD response in DTAP, got", gsup_ussd_rsp); + mtc.stop; + } + } + /**********************************/ + + + /* inter-MSC handover back to the first MSC */ + f_create_bssmap_exp_handoverRequest(193); + cil := { cIl_CGI := { ts_BSSMAP_CI_CGI('262'H, '42'H, 23, 42) } }; + + /* old BSS sends Handover Required, via inter-MSC E link: like + * BSSAP.send(ts_BSSMAP_HandoverRequired(cause, cil)); + * but via GSUP */ + GSUP.send(ts_GSUP_E_AN_APDU(OSMO_GSUP_MSGT_E_PREPARE_SUBSEQUENT_HANDOVER_REQUEST, + pars.imsi, remote_msc_name, local_msc_name, + valueof(t_GSUP_AN_APDU(OSMO_GSUP_AN_PROTO_48006, + enc_PDU_BSSAP(valueof(ts_BSSMAP_HandoverRequired(cause, cil))) + )) + )); + + /* MSC asks local BSS to prepare Handover to it */ + BSSAP.receive(tr_BSSMAP_HandoverRequest); + + /* Make sure the new BSSAP conn continues with the correct N_SD sequence numbers */ + f_bssmap_continue_after_n_sd(last_n_sd); + + /* new BSS composes a RR Handover Command */ + rr_ho_cmd := valueof(ts_RR_HandoverCommand); + rr_ho_cmd_enc := enc_PDU_ML3_NW_MS(rr_ho_cmd); + var BSSMAP_IE_AoIP_TransportLayerAddress tla := valueof(ts_BSSMAP_IE_AoIP_TLA4('01020304'O, 2342)); + BSSAP.send(ts_BSSMAP_HandoverRequestAcknowledge(rr_ho_cmd_enc, lengthof(rr_ho_cmd_enc), + tla, ts_BSSMAP_IE_SpeechCodec({ts_CodecFR}))); + + /* HandoverCommand goes out via remote MSC-I */ + var GSUP_PDU prep_subsq_ho_res; + GSUP.receive(tr_GSUP_E_AN_APDU(OSMO_GSUP_MSGT_E_PREPARE_SUBSEQUENT_HANDOVER_RESULT, + pars.imsi, destination_name := remote_msc_name)) -> value prep_subsq_ho_res; + + /* MS shows up at the local BSS */ + BSSAP.send(ts_BSSMAP_HandoverDetect); + f_sleep(0.1); + BSSAP.send(ts_BSSMAP_HandoverComplete); + + /* Handover Succeeded message */ + GSUP.receive(tr_GSUP_E_AN_APDU(OSMO_GSUP_MSGT_E_FORWARD_ACCESS_SIGNALLING_REQUEST, + pars.imsi, destination_name := remote_msc_name)); + + /* MS has handovered to here, Clear Command goes out via remote MSC-I -- in form of a GSUP Close. */ + GSUP.receive(tr_GSUP_E_NO_PDU(OSMO_GSUP_MSGT_E_CLOSE, + pars.imsi, destination_name := remote_msc_name)); + + /* Handover ends successfully. Call goes on for a little longer and then we hang up. */ + + f_sleep(1.0); + deactivate(ack_mdcx); + + /* FIXME: the inter-MSC call has put a number of MNCC messages in the queue, which above code should expect and + * clear out. The f_call_hangup() expects an MNCC_REL_IND, so, for the time being, just clear the MNCC messages + * before starting the call hangup. Instead of this, the individual messages should be tested for above. */ + MNCC.clear; + + var default ccrel := activate(as_optional_cc_rel(cpars, true)); + f_call_hangup(cpars, true); + f_sleep(1.0); + deactivate(ccrel); + + setverdict(pass); +} +testcase TC_ho_inter_msc_out() runs on MTC_CT { + var BSC_ConnHdlr vc_conn; + f_init(1); + + var BSC_ConnHdlrPars pars := f_init_pars(54); + + vc_conn := f_start_handler_with_pars(refers(f_tc_ho_inter_msc_out), pars, 0); + vc_conn.done; +} + + control { execute( TC_cr_before_reset() ); execute( TC_lu_imsi_noauth_tmsi() ); @@ -4870,6 +5303,11 @@ control { execute( TC_sgsap_lu_and_mt_call() ); execute( TC_sgsap_vlr_failure() ); + execute( TC_ho_inter_bsc_unknown_cell() ); + execute( TC_ho_inter_bsc() ); + + execute( TC_ho_inter_msc_out() ); + /* Run this last: at the time of writing this test crashes the MSC */ execute( TC_lu_imsi_auth_tmsi_encr_3_1_log_msc_debug() ); execute( TC_gsup_mt_multi_part_sms() ); |