Change in osmo-ttcn3-hacks[master]: msc: add inter-BSC and inter-MSC Handover tests

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/.

Neels Hofmeyr gerrit-no-reply at lists.osmocom.org
Wed May 8 23:00:54 UTC 2019


Neels Hofmeyr has submitted this change and it was merged. ( https://gerrit.osmocom.org/13617 )

Change subject: msc: add inter-BSC and inter-MSC Handover tests
......................................................................

msc: add inter-BSC and inter-MSC Handover tests

Change-Id: I7d76c982ad4e198534fa488609c41e8892b268ab
---
M library/BSSMAP_Templates.ttcn
M library/GSUP_Types.ttcn
M library/L3_Templates.ttcn
M library/RAN_Emulation.ttcnpp
M msc/BSC_ConnectionHandler.ttcn
M msc/MSC_Tests.ttcn
6 files changed, 1,016 insertions(+), 93 deletions(-)

Approvals:
  Jenkins Builder: Verified
  Neels Hofmeyr: Looks good to me, approved



diff --git a/library/BSSMAP_Templates.ttcn b/library/BSSMAP_Templates.ttcn
index 4df39d4..b7230cd 100644
--- a/library/BSSMAP_Templates.ttcn
+++ b/library/BSSMAP_Templates.ttcn
@@ -307,7 +307,7 @@
 	}
 }
 
-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 @@
 	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 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_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_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 @@
 	}
 }
 
+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 @@
 	}
 }
 
+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 @@
 	}
 }
 
+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 @@
 				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 c024d37..73f4562 100644
--- a/library/GSUP_Types.ttcn
+++ b/library/GSUP_Types.ttcn
@@ -55,7 +55,16 @@
 	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 @@
 
 	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 @@
 	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 @@
 	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 @@
 				 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 @@
 	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_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 @@
 		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 @@
 		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 @@
 		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 @@
 	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 @@
 	{
 		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 @@
 		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 @@
 	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 @@
 	{
 		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 @@
 	{
 		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 @@
 	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 @@
 	{
 		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 @@
 		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 4d71f27..cf6d64c 100644
--- a/library/L3_Templates.ttcn
+++ b/library/L3_Templates.ttcn
@@ -582,6 +582,59 @@
 	}
 }
 
+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 d6d74e2..843cc9e 100644
--- a/library/RAN_Emulation.ttcnpp
+++ b/library/RAN_Emulation.ttcnpp
@@ -133,6 +133,7 @@
 		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 @@
 	/* 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,30 +672,53 @@
 	return false;
 }
 
-/* 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_next_n_sd(inout N_Sd_Array n_sd, in integer n_sd_idx) return uint2_t {
 	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;
-	} else if (ischosen(dtap.msgs.gcc)) {
-		seq_nr := cd.n_sd[1];
-		cd.n_sd[1] := (cd.n_sd[1] + 1) mod 2;
-	} else if (ischosen(dtap.msgs.bcc)) {
-		seq_nr := cd.n_sd[2];
-		cd.n_sd[2] := (cd.n_sd[2] + 1) mod 2;
-	} else if (ischosen(dtap.msgs.loc)) {
-		seq_nr := cd.n_sd[3];
-		cd.n_sd[3] := (cd.n_sd[3] + 1) mod 2;
+	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 */
-		return;
+		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_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)) {
+		return 0;
+	} else if (ischosen(dtap.msgs.gcc)) {
+		return 1;
+	} else if (ischosen(dtap.msgs.bcc)) {
+		return 2;
+	} else if (ischosen(dtap.msgs.loc)) {
+		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;
+	}
+	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 {
 #ifdef RAN_EMULATION_BSSAP
 	var BSSAP_N_UNITDATA_ind ud_ind;
@@ -732,6 +756,8 @@
 		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 @@
 				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 @@
 		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 @@
 			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 @@
 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 @@
 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 @@
 }
 #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 @@
 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 44d3c6d..1cec69c 100644
--- a/msc/BSC_ConnectionHandler.ttcn
+++ b/msc/BSC_ConnectionHandler.ttcn
@@ -1133,6 +1133,27 @@
 	}
 }
 
+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 709a73c..3a6711b 100644
--- a/msc/MSC_Tests.ttcn
+++ b/msc/MSC_Tests.ttcn
@@ -468,31 +468,6 @@
 	}
 }
 
-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 @@
 	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 @@
    *
  */
 
+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 at 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 at 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 at 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 @@
 	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() );

-- 
To view, visit https://gerrit.osmocom.org/13617
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: I7d76c982ad4e198534fa488609c41e8892b268ab
Gerrit-Change-Number: 13617
Gerrit-PatchSet: 6
Gerrit-Owner: Neels Hofmeyr <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder (1000002)
Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr at sysmocom.de>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20190508/86898133/attachment.htm>


More information about the gerrit-log mailing list