OpenBTS-UMTS/UMTS/URRCMessages.cpp

2060 lines
94 KiB
C++
Raw Normal View History

2014-10-16 14:42:05 -07:00
/*
* OpenBTS provides an open source alternative to legacy telco protocols and
* traditionally complex, proprietary hardware systems.
*
* Copyright 2014 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero General
* Public License version 3. See the COPYING and NOTICE files in the main
* directory for licensing information.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*/
#include "UMTSTransfer.h"
#include "URRCMessages.h"
#include "MACEngine.h"
#include "AsnHelper.h"
#include "Logger.h"
#include "SgsnExport.h"
#include "URRC.h"
#include "UMTSLogicalChannel.h"
//#include "asn_system.h" included from AsnHelper.h
namespace ASN {
//#include "BIT_STRING.h"
#include "UL-CCCH-Message.h"
#include "DL-CCCH-Message.h"
#include "UL-DCCH-Message.h"
#include "DL-DCCH-Message.h"
#include "InitialUE-Identity.h"
#define PAT_SAMSUNG_TEST 1 // Try to get the samsung galaxy to accept this message.
#include "asn_SEQUENCE_OF.h"
//#include "RRCConnectionRequest.h"
//#include "RRCConnectionSetup.h"
};
#define CASENAME(x) case x: return #x;
#define CASEASNCOMMENT(foo) case ASN::foo: return #foo;
using namespace SGSN;
namespace UMTS {
const std::string descrRrcConnectionSetup("RRC_Connection_Setup_Message");
const std::string descrRrcConnectionRelease("RRC_Connection_Release_Message");
const std::string descrRadioBearerSetup("RRC Radio Bearer Setup Message");
const std::string descrRadioBearerRelease("RRC Radio Bearer Release Message");
const std::string descrCellUpdateConfirm("RRC Cell Update Confirm Message");
const std::string descrSecurityModeCommand("RRC Security Mode Command");
typedef unsigned char uchar;
static const char *asnUlDcchMsg2Name(ASN::UL_DCCH_MessageType_PR mtype)
{
switch (mtype) {
CASEASNCOMMENT(UL_DCCH_MessageType_PR_activeSetUpdateComplete)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_activeSetUpdateFailure)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_cellChangeOrderFromUTRANFailure)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_counterCheckResponse)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_handoverToUTRANComplete)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_initialDirectTransfer)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_handoverFromUTRANFailure)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_measurementControlFailure)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_measurementReport)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_physicalChannelReconfigurationComplete)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_physicalChannelReconfigurationFailure)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_radioBearerReconfigurationComplete)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_radioBearerReconfigurationFailure)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_radioBearerReleaseComplete)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_radioBearerReleaseFailure)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_radioBearerSetupComplete)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_radioBearerSetupFailure)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_rrcConnectionReleaseComplete)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_rrcConnectionSetupComplete)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_rrcStatus)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_securityModeComplete)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_securityModeFailure)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_signallingConnectionReleaseIndication)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_transportChannelReconfigurationComplete)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_transportChannelReconfigurationFailure)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_transportFormatCombinationControlFailure)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_ueCapabilityInformation)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_uplinkDirectTransfer)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_utranMobilityInformationConfirm)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_utranMobilityInformationFailure)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_mbmsModificationRequest)
CASEASNCOMMENT(UL_DCCH_MessageType_PR_spare1)
default: return "UL_DCCH_unrecognized_message_type";
}
}
static const char *asnUlCcchMsg2Name(ASN::UL_CCCH_MessageType_PR type)
{
switch (type) {
CASEASNCOMMENT(UL_CCCH_MessageType_PR_cellUpdate)
CASEASNCOMMENT(UL_CCCH_MessageType_PR_rrcConnectionRequest)
CASEASNCOMMENT(UL_CCCH_MessageType_PR_uraUpdate)
default:
return "UL_CCCH_unrecognized_message_type";
}
}
static string getRBFailureCause(ASN::FailureCauseWithProtErr_t *failureCause)
{
unsigned failcode = failureCause->present;
string msg;
switch (failcode) {
case ASN::FailureCauseWithProtErr_PR_configurationUnsupported:
msg = "configurationUnsupported"; break;
case ASN::FailureCauseWithProtErr_PR_physicalChannelFailure:
msg = "physicalChannelASN::Failure"; break;
case ASN::FailureCauseWithProtErr_PR_incompatibleSimultaneousReconfiguration:
msg = "incompatibleSimultaneousReconfiguration"; break;
case ASN::FailureCauseWithProtErr_PR_compressedModeRuntimeError:
msg = "compressedModeRuntimeError"; break;
case ASN::FailureCauseWithProtErr_PR_protocolError:
msg = "protocolError";
// The protocolError choice has additional info, but since our spec has only one
// possible value for that info, it is of little interest so ignore it.
break;
case ASN::FailureCauseWithProtErr_PR_cellUpdateOccurred:
msg = "cellUpdateOccurred"; break;
case ASN::FailureCauseWithProtErr_PR_invalidConfiguration:
msg = "invalidConfiguration"; break;
case ASN::FailureCauseWithProtErr_PR_configurationIncomplete:
msg = "configurationIncomplete"; break;
case ASN::FailureCauseWithProtErr_PR_unsupportedMeasurement:
msg = "unsupportedMeasurement"; break;
case ASN::FailureCauseWithProtErr_PR_mbmsSessionAlreadyReceivedCorrectly:
msg ="mbmsSessionAlreadyReceivedCorrectly"; break;
case ASN::FailureCauseWithProtErr_PR_lowerPriorityMBMSService:
msg = "lowerPriorityMBMSService"; break;
default:
msg = "unknown type"; break;
}
return msg;
}
ASN::RRC_StateIndicator UEState2Asn(UEState state)
{
switch (state) {
case stIdleMode:
LOG(ERR) << "UE is unexpectedly in idle mode";
// Assume CELL_FACH and fall through.
return ASN::RRC_StateIndicator_cell_FACH;
case stCELL_FACH: return ASN::RRC_StateIndicator_cell_FACH;
case stCELL_DCH: return ASN::RRC_StateIndicator_cell_DCH;
case stCELL_PCH: return ASN::RRC_StateIndicator_cell_PCH;
case stURA_PCH: return ASN::RRC_StateIndicator_ura_PCH;
default: assert(0);
}
}
// Run the Integrity Protection Algorithm and ASN encode the message.
// Return result in &result.
static bool encodeDcchMsg(UEInfo *uep, RbId rbid, ASN::DL_DCCH_Message_t *msg, ByteVector &result, string descr)
{
if (uep->integrity.isStarted()) {
// Step 1: Set the integrity check info to the values for encoding specified in 10.3.3.16.
ASN::IntegrityCheckInfo *ici = RN_CALLOC(ASN::IntegrityCheckInfo);
msg->integrityCheckInfo = ici;
ici->messageAuthenticationCode = allocAsnBIT_STRING(32);
// The RBID goes in the 5 LSB of the 32-bit string that will
// later hold the MAC-I [Message Authentication Code].
AsnBitString2BVTemp(ici->messageAuthenticationCode).setField(32-5,rbid-0*1,5);
ici->rrc_MessageSequenceNumber = 0; // 10.3.3.16 of 25.331;
// Step 2: Encode the message
if (!uperEncodeToBV(&ASN::asn_DEF_DL_DCCH_Message,msg,result,descr)) {return false;}
// After encoding it is safe to advance the COUNT-I, ie, this message advances
// the RRC SN [sequence number.]
//uep->integrity.advanceDlRrcSn(rbid);
// Step 3: Run the message through the Integrity Protection Algorithm.
uint32_t maci = uep->integrity.runF9(rbid,1,result);
// After encoding it is safe to advance the COUNT-I, ie, this message advances
// the RRC SN [sequence number.]
ici->rrc_MessageSequenceNumber = uep->integrity.getDlRrcSn(rbid); // 10.3.3.16 of 25.331
uep->integrity.advanceDlRrcSn(rbid);
// Step 4: Set the Message Authentication Code in the message.
AsnBitString2BVTemp(ici->messageAuthenticationCode).setField(0,maci,32);
}
// Encode the message (again), completely oblivious to cpu cycles consumed.
if (!uperEncodeToBV(&ASN::asn_DEF_DL_DCCH_Message,msg,result,descr)) {return false;}
std::string comment = format("DL_DCCH %s message size=%d",descr.c_str(),result.size());
asnLogMsg(rbid, &ASN::asn_DEF_DL_DCCH_Message, msg,comment.c_str(),uep);
//if (gConfig.getNum("UMTS.Debug.Messages")) {
// asn_fprint(stdout,&ASN::asn_DEF_DL_DCCH_Message, msg);
// std::string readable = asn2string(&ASN::asn_DEF_DL_DCCH_Message, msg);
// fprintf(stdout,"Encoded message size=%d bytes\n%s",result.size());
// fflush(stdout);
//}
return true;
}
// Return TRUE on success.
static bool encodeCcchMsg(ASN::DL_CCCH_Message_t *msg, ByteVector &result,
string descr, // Description for the log
UEInfo *uep, // UE for the log, or NULL if none.
uint32_t urnti // URNTI for the log, unneeded if uep is non-null.
)
{
// We can just return: uperEncodeToBV printed a message on failure.
bool stat = uperEncodeToBV(&ASN::asn_DEF_DL_CCCH_Message,msg,result,descr);
if (stat) {
string comment = format("DL_CCCH %s message size=%d",descr.c_str(),result.size());
asnLogMsg(0, &ASN::asn_DEF_DL_CCCH_Message, msg,comment.c_str(),uep,urnti);
}
//if (gConfig.getNum("UMTS.Debug.Messages")) {
// asn_fprint(stdout,&ASN::asn_DEF_DL_CCCH_Message, msg);
// fprintf(stdout,"Encoded message size=%d bytes\n",result.size());
// fflush(stdout);
//}
return stat;
}
// Same as RB_InformationSetup but without PDCP info.
// The list we put these things in may be either SRB_InformationSetupList or SRB_InformationSetupList2,
// which are 100% identical, but nevertheless the result list type must be void**
// 25.331 10.3.4.24
static void toAsnSRB_InformationSetupList(RrcMasterChConfig *masterConfig, void*srblist)
{
// Set up SRBs.
for (unsigned rbid = SRB1; rbid <= SRB3; rbid++) {
RBInfo *rb = masterConfig->getRB(rbid);
assert(rb->valid());
ASN::SRB_InformationSetup *srbie = RN_CALLOC(ASN::SRB_InformationSetup);
srbie->rb_Identity = RN_CALLOC(long);
*srbie->rb_Identity = rbid;
srbie->rlc_InfoChoice.present = ASN::RLC_InfoChoice_PR_rlc_Info;
rb->toAsnRLC_Info(&srbie->rlc_InfoChoice.choice.rlc_Info);
// This assumes uplink and downlink are configured with identical TrCh.
// They dont have to be.
TrChId tcid = rb->mTrChAssigned;
defaultRbMappingInfoToAsn(masterConfig->getUlTrChInfo(tcid),
masterConfig->getDlTrChInfo(tcid),rbid,&srbie->rb_MappingInfo);
ASN_SEQUENCE_ADD(srblist,srbie);
}
}
// This is almost the same as above, but allows you to change
// only the RBMappinginfo, not other RLC programming.
// We can use it to switch the SRBs between CELL_DCH and CELL_FACH mode,
// because it allows us to specify a new TrCh, which implies a new TFS,
// which allows the new rlcs to resize to match the new TFS.
static ASN::RB_InformationAffectedList *toAsnRB_InformationAffectedList(
RrcMasterChConfig *masterConfig)
{
ASN::RB_InformationAffectedList *result = RN_CALLOC(ASN::RB_InformationAffectedList);
// Set up SRBs.
for (unsigned rbid = SRB1; rbid <= SRB3; rbid++) {
RBInfo *rb = masterConfig->getRB(rbid);
assert(rb->valid());
ASN::RB_InformationAffected *srbie = RN_CALLOC(ASN::RB_InformationAffected);
srbie->rb_Identity = rbid;
// This assumes uplink and downlink are configured with identical TrCh.
// They dont have to be.
TrChId tcid = rb->mTrChAssigned;
defaultRbMappingInfoToAsn(masterConfig->getUlTrChInfo(tcid),
masterConfig->getDlTrChInfo(tcid),rbid,&srbie->rb_MappingInfo);
ASN_SEQUENCE_ADD(&result->list,srbie);
}
return result;
}
static void toAsnRAB_Identity(int rbid,ASN::RAB_Identity_t *rabp)
{
// 10.3.1.14: RAB Identity "This information uniquely identifies a RAB within a CN domain."
// TODO: There are different kinds for GSM-MAP or ANSI-41, but I dont think it matters
// because we dont use it and the phone shouldnt care, although it may need to be unique,
// which we can insure simply by using the rbid.
rabp->present = ASN::RAB_Identity_PR_gsm_MAP_RAB_Identity;
setAsnBIT_STRING(&rabp->choice.gsm_MAP_RAB_Identity,(uint8_t*)calloc(1,1),8);
AsnBitString2BVTemp(&rabp->choice.gsm_MAP_RAB_Identity).setField(0,rbid,8);
}
// 3GPP 25.331 10.3.4.8
// ASN RAB_Info_t rab_Info;
void toAsnRAB_Info(RBInfo *rb,ASN::RAB_Info_t *rabInfoIE)
{
// RAB_Identity_t rab_Identity;
toAsnRAB_Identity(rb->mRbId,&rabInfoIE->rab_Identity);
// CN_DomainIdentity_t cn_DomainIdentity; // cs or ps?
bool csdomain = rb->isCsDomain();
rabInfoIE->cn_DomainIdentity = toAsnEnumerated(
csdomain ? ASN::CN_DomainIdentity_cs_domain : ASN::CN_DomainIdentity_ps_domain);
// NAS_Synchronisation_Indicator_t *nas_Synchronisation_Indicator /* OPTIONAL */;
// Re_EstablishmentTimer_t re_EstablishmentTimer;
// TODO: Which should we use here? Choice is T314 or T315
// 25.331 13.1 implies T314 is for CS and T315 is for PS, so why are they asking us?
// Also says see 8.3.1.13 and 8.3.1.14
rabInfoIE->re_EstablishmentTimer = toAsnEnumerated(
csdomain ? ASN::Re_EstablishmentTimer_useT314 : ASN::Re_EstablishmentTimer_useT315);
// Finished with RAB_Info_t
}
// 3GPP 10.3.4.20
// ASN RB_InformationSetup
static ASN::RB_InformationSetup *toAsnRB_InformationSetup(RrcMasterChConfig *masterConfig, RBInfo *rb)
{
assert(rb->valid());
int rbid = rb->mRbId;
ASN::RB_InformationSetup *rbie = RN_CALLOC(ASN::RB_InformationSetup);
// RB_Identity_t rb_Identity;
rbie->rb_Identity = rbid;
// struct PDCP_Info *pdcp_Info /* OPTIONAL */;
// We dont use PDCP. So why is it here at all? Maybe it is mandatory for non-signalling RBs?
rbie->pdcp_Info = RN_CALLOC(ASN::PDCP_Info);
rbie->pdcp_Info->losslessSRNS_RelocSupport = RN_CALLOC(ASN::LosslessSRNS_RelocSupport);
rbie->pdcp_Info->losslessSRNS_RelocSupport->present = ASN::LosslessSRNS_RelocSupport_PR_notSupported;
//rbie->pdcpInfo->mLosslessSRNS_RelocSupport->choice = ASN::NULL;
rbie->pdcp_Info->pdcp_PDU_Header = toAsnEnumerated(ASN::PDCP_PDU_Header_absent);
// RLC_InfoChoice_t rlc_InfoChoice;
rbie->rlc_InfoChoice.present = ASN::RLC_InfoChoice_PR_rlc_Info;
rb->toAsnRLC_Info(&rbie->rlc_InfoChoice.choice.rlc_Info);
// RB_MappingInfo_t rb_MappingInfo;
TrChId tcid = rb->mTrChAssigned; // The TrCh carrying this rb.
defaultRbMappingInfoToAsn(masterConfig->getUlTrChInfo(tcid),
masterConfig->getDlTrChInfo(tcid),rbid,&rbie->rb_MappingInfo);
return rbie;
}
// 3GPP 25.331 10.3.4.10
// ASN struct RAB_InformationSetup
// For a CS connection we use three consecutive rbids.
static ASN::RAB_InformationSetup *toAsnRAB_InformationSetup(
RrcMasterChConfig *masterConfig, int rbid,
int *pNumRBperRAB) // Return the number of RBs in this RAB. 1 for PS and 3 for CS.
{
ASN::RAB_InformationSetup *result = RN_CALLOC(ASN::RAB_InformationSetup);
// RAB_Info_t rab_Info;
RBInfo *rb = masterConfig->getRB(rbid);
toAsnRAB_Info(rb,&result->rab_Info);
*pNumRBperRAB = rb->isCsDomain() ? 3 : 1;
// RB_InformationSetupList_t rb_InformationSetupList;
// A_SEQUENCE_OF(struct RB_InformationSetup) list
for (int i = 0; i < *pNumRBperRAB; i++, rbid++) {
rb = masterConfig->getRB(rbid);
// 3GPP 10.3.4.20
ASN::RB_InformationSetup *rbInfoIE = toAsnRB_InformationSetup(masterConfig, rb);
ASN_SEQUENCE_ADD(&result->rb_InformationSetupList.list,rbInfoIE);
}
return result;
}
// 3GPP 25.331 10.3.4.10 RAB Information for Setup.
static void toAsnRAB_InformationSetupList(RrcMasterChConfig *masterConfig, ASN::RAB_InformationSetupList *rbSetupListIE)
{
// For PS each RB is a RAB using one rb-id. There can be multiple RABs,
// although I think we will only setup one at a time because the UE can
// only ask for one at a time.
// For voice, the first 3 RBs constitute a RAB with 3 'sub-rab-flows' for the AMR codec.
// We dont allow mixing cs and ps connections on the same UE.
for (unsigned rbid = 5; rbid < masterConfig->mNumRB; ) {
RBInfo *rb = masterConfig->getRB(rbid);
if (!rb || ! rb->valid()) { continue; }
int numRBperRAB;
ASN::RAB_InformationSetup *rabSetupIE =
toAsnRAB_InformationSetup(masterConfig, rbid, &numRBperRAB);
ASN_SEQUENCE_ADD(&rbSetupListIE->list,rabSetupIE);
rbid += numRBperRAB; // Advance by the number of rbids used by this RAB.
}
}
static ASN::UL_CommonTransChInfo *toAsnUL_CommonTransChInfo(RrcMasterChConfig *masterConfig)
{
ASN::UL_CommonTransChInfo *result = RN_CALLOC(ASN::UL_CommonTransChInfo);
// struct TFC_Subset *tfc_Subset /* OPTIONAL */;
// struct TFCS *prach_TFCS /* OPTIONAL */;
// struct UL_CommonTransChInfo__modeSpecificInfo {...} modeSpecificInfo;
// The whole modeSpecificInfo is optional, even though it is not marked as such in ASN.
typedef ASN::UL_CommonTransChInfo::UL_CommonTransChInfo__modeSpecificInfo msi;
result->modeSpecificInfo = RN_CALLOC(msi);
result->modeSpecificInfo->present = ASN::UL_CommonTransChInfo__modeSpecificInfo_PR_fdd;
masterConfig->getUlTfcs()->toAsnTfcs(&result->modeSpecificInfo->choice.fdd.ul_TFCS,TrChUlDCHType);
return result;
}
static ASN::DL_CommonTransChInfo *toAsnDL_CommonTransChInfo(RrcMasterChConfig *masterConfig)
{
// struct DL_CommonTransChInfo
ASN::DL_CommonTransChInfo *result = RN_CALLOC(ASN::DL_CommonTransChInfo);
// struct TFCS *sccpch_TFCS /* OPTIONAL */;
// struct DL_CommonTransChInfo__modeSpecificInfo
// DL_CommonTransChInfo__modeSpecificInfo_PR present;
result->modeSpecificInfo.present = ASN::DL_CommonTransChInfo__modeSpecificInfo_PR_fdd;
// union DL_CommonTransChInfo__modeSpecificInfo_u {...} choice;
// struct DL_CommonTransChInfo__modeSpecificInfo__fdd {...} fdd;
// struct DL_CommonTransChInfo__modeSpecificInfo__fdd__dl_Parameters {...} *dl_Parameters;
typedef ASN::DL_CommonTransChInfo::
DL_CommonTransChInfo__modeSpecificInfo::
DL_CommonTransChInfo__modeSpecificInfo_u::
DL_CommonTransChInfo__modeSpecificInfo__fdd::
DL_CommonTransChInfo__modeSpecificInfo__fdd__dl_Parameters longthing;
result->modeSpecificInfo.choice.fdd.dl_Parameters = RN_CALLOC(longthing);
// This is where you can say same as uplink: (but we dont)
// DL_CommonTransChInfo__modeSpecificInfo__fdd__dl_Parameters_PR present;
result->modeSpecificInfo.choice.fdd.dl_Parameters->present =
ASN::DL_CommonTransChInfo__modeSpecificInfo__fdd__dl_Parameters_PR_dl_DCH_TFCS;
// union DL_CommonTransChInfo__modeSpecificInfo__fdd__dl_Parameters_u {...} choice;
// TFCS_t dl_DCH_TFCS;
masterConfig->getDlTfcs()->toAsnTfcs(
&result->modeSpecificInfo.choice.fdd.dl_Parameters->choice.dl_DCH_TFCS,TrChDlDCHType);
return result;
}
ASN::UL_AddReconfTransChInfoList *toAsnUL_AddReconfTransChInfoList(RrcMasterChConfig *masterConfig)
{
ASN::UL_AddReconfTransChInfoList *result = RN_CALLOC(ASN::UL_AddReconfTransChInfoList);
unsigned numtc = masterConfig->getUlNumTrCh();
// This assumes transport channels always start at 0.
for (TrChId tcid = 0; tcid < numtc; tcid++) {
//TrChInfo *tc = masterConfig->getUlTrChInfo(tcid);
// typedef struct UL_AddReconfTransChInformation
ASN::UL_AddReconfTransChInformation *addTcIE =
RN_CALLOC(ASN::UL_AddReconfTransChInformation);
// UL_TrCH_Type_t ul_TransportChannelType; // dch or usch.
asn_long2INTEGER(&addTcIE->ul_TransportChannelType,ASN::UL_TrCH_Type_dch);
// TransportChannelIdentity_t transportChannelIdentity;
addTcIE->transportChannelIdentity = tcid+1; // ASN TrCh starts at 1
// TransportFormatSet_t transportFormatSet;
masterConfig->getUlTfs()->toAsnTfs(&addTcIE->transportFormatSet);
ASN_SEQUENCE_ADD(&result->list,addTcIE);
}
return result;
}
// Create a fake uplink TrCh, needed just to align an ASN struct.
static void toAsnFakeUL_AddReconfTransChInfoList(ASN::UL_AddReconfTransChInfoList *ulchlist)
{
// The easiest way to do this is to manufacturer a TFS and use that:
UlTrChInfo ulfoo;
ulfoo.setTrCh(TrChRACHType,1,true);
ulfoo.getTfs()
->setSemiStatic(10,RrcDefs::Convolutional,ASN::CodingRate_half,256,16)
// ->setCommonCh() This did not work - failed to ASN encode.
->setDedicatedCh()
->addTF(0+4,0);
ASN::UL_AddReconfTransChInformation *dummyUlDCH = RN_CALLOC(ASN::UL_AddReconfTransChInformation);
asn_long2INTEGER(&dummyUlDCH->ul_TransportChannelType,ASN::UL_TrCH_Type_dch);
dummyUlDCH->transportChannelIdentity = 31;
// This does not work; asn_CHOICE does not except PR_NOTHING, anywhere!
//dummyUlDCH->transportFormatSet.present = ASN::TransportFormatSet_PR_NOTHING;
ulfoo.getTfs()->toAsnTfs(&dummyUlDCH->transportFormatSet);
ASN_SEQUENCE_ADD(&ulchlist->list,dummyUlDCH);
}
static ASN::DL_AddReconfTransChInfoList *toAsnDL_AddReconfTransChInfoList(RrcMasterChConfig *masterConfig)
{
ASN::DL_AddReconfTransChInfoList *result = RN_CALLOC(ASN::DL_AddReconfTransChInfoList);
unsigned numtc = masterConfig->getDlNumTrCh();
// This assumes transport channels always start at 0.
for (TrChId tcid = 0; tcid < numtc; tcid++) {
//TrChInfo *tc = getDlTrChInfo(tcid);
// v typedef struct DL_AddReconfTransChInformation
ASN::DL_AddReconfTransChInformation *addTcIE =
RN_CALLOC(ASN::DL_AddReconfTransChInformation);
// . DL_TrCH_Type_t dl_TransportChannelType;
asn_long2INTEGER(&addTcIE->dl_TransportChannelType,ASN::DL_TrCH_Type_dch);
// TransportChannelIdentity_t dl_transportChannelIdentity;
addTcIE->dl_transportChannelIdentity = tcid+1;
// vv struct DL_AddReconfTransChInformation__tfs_SignallingMode {...} tfs_SignallingMode;
// .. DL_AddReconfTransChInformation__tfs_SignallingMode_PR present;
addTcIE->tfs_SignallingMode.present = ASN::DL_AddReconfTransChInformation__tfs_SignallingMode_PR_explicit_config;
// vvv union DL_AddReconfTransChInformation__tfs_SignallingMode_u {...} choice;
// ... TransportFormatSet_t explicit_config;
// vvvv typedef struct TransportFormatSet
masterConfig->getDlTfs()->toAsnTfs(&addTcIE->tfs_SignallingMode.choice.explicit_config);
// ^^^^ struct TransportFormatSet
// ^^^ union DL_AddReconfTransChInformation__tfs_SignallingMode_u {...} choice;
// .. struct QualityTarget *dch_QualityTarget /* OPTIONAL */;
// .. struct TM_SignallingInfo *dummy /* OPTIONAL */;
// ^^ struct DL_AddReconfTransChInformation__tfs_SignallingMode {...} tfs_SignallingMode;
ASN_SEQUENCE_ADD(&result->list,addTcIE);
// ^ struct DL_AddReconfTransChInfoList
}
return result;
}
void toAsnDL_AddReconfTransChInfoListSameAsUl(
ASN::DL_AddReconfTransChInfoList *dlchlist,
int dltcid, // Downlink 1-based TrCh-id to configure
int ultcid) // Uplink 1-based TrCh-id to copy to downlink trch.
{
ASN::DL_AddReconfTransChInformation *dummyDlDCH =
RN_CALLOC(ASN::DL_AddReconfTransChInformation);
asn_long2INTEGER(&dummyDlDCH->dl_TransportChannelType,ASN::DL_TrCH_Type_dch);
dummyDlDCH->dl_transportChannelIdentity = dltcid;
// Since this is just a dummy structure, use the simplest type,which is sameAsUlTrCh.
dummyDlDCH->tfs_SignallingMode.present = ASN::DL_AddReconfTransChInformation__tfs_SignallingMode_PR_sameAsULTrCH;
asn_long2INTEGER(&dummyDlDCH->tfs_SignallingMode.choice.sameAsULTrCH.ul_TransportChannelType,
ASN::UL_TrCH_Type_dch);
dummyDlDCH->tfs_SignallingMode.choice.sameAsULTrCH.ul_TransportChannelIdentity = ultcid;
ASN_SEQUENCE_ADD(&dlchlist->list,dummyDlDCH);
}
ByteVector* sendDirectTransfer(UEInfo* uep, ByteVector &dlpdu, const char *descr, bool psDomain)
{
ASN::DL_DCCH_Message_t msg;
memset(&msg,0,sizeof(msg));
msg.message.present = ASN::DL_DCCH_MessageType_PR_downlinkDirectTransfer;
ASN::DownlinkDirectTransfer_t *ddt = &msg.message.choice.downlinkDirectTransfer;
ddt->present = ASN::DownlinkDirectTransfer_PR_r3;
ASN::DownlinkDirectTransfer_r3_IEs *ies = &ddt->choice.r3.downlinkDirectTransfer_r3;
ies->rrc_TransactionIdentifier = uep->newTransactionId();
ies->cn_DomainIdentity = psDomain ? toAsnEnumerated(ASN::CN_DomainIdentity_ps_domain) : toAsnEnumerated(ASN::CN_DomainIdentity_cs_domain);
ies->nas_Message.buf = dlpdu.begin();
ies->nas_Message.size = dlpdu.size();
ByteVector *result = new ByteVector(dlpdu.size()+100);
RN_MEMLOG(ByteVector,result);
if (!encodeDcchMsg(uep,SRB3,&msg,*result,descr)) {return NULL;}
return result;
}
static void toAsnURNTI(ASN::U_RNTI_t *urnti,unsigned srncid,unsigned srnti)
{
setAsnBIT_STRING(&urnti->srnc_Identity,(uint8_t*)calloc(1,2),12);
AsnBitString2BVTemp(urnti->srnc_Identity).setField(0,srncid,12);
setAsnBIT_STRING(&urnti->s_RNTI,(uint8_t*)calloc(1,3),20);
AsnBitString2BVTemp(urnti->s_RNTI).setField(0,srnti,20);
}
static ASN::C_RNTI_t *toAsnCRNTI(unsigned crnti)
{
// C_RNTI_t *new_c_RNTI /* OPTIONAL */;
ASN::C_RNTI_t *result = RN_CALLOC(ASN::C_RNTI_t);
// new_c_RNTI is a BIT_STRING_t
setAsnBIT_STRING(result,(uint8_t*)calloc(1,2),16);
AsnBitString2BVTemp(result).setField(0,crnti,16);
return result;
}
// NOTE: The RRC Connection Setup messages are defined as using CCCH and SRB0 which is TM
// uplink and UM downlink. That makes sense because uplink messages are small and the downlink
// message is huge and may need to be segmented.
// Note that for CCCH downlink messages the MAC does not include a UE-id, in fact,
// it doesn't have one yet because the RRC Connection Setup message is what is going
// to supply the URNTI and CRNTI; rather the UE-id is included in the message.
// Apparently the UE monitors the entire CCCH all the time in order to gather all the
// segments of all the messages and must look at them all to see if they are for it.
// Note that this message is still small: the rlc-info for TM&UM is pretty small,
// and the TrCh info comes from SIB5/6.
// Release 3 version of this message. The samsung and other phones did not seem to like this,
// so 11-16-2012 tried switching to release 4 version.
void sendRrcConnectionSetup(UEInfo *uep, ASN::InitialUE_Identity *ueInitialId)
{
ASN::DL_CCCH_Message msg;
memset(&msg,0,sizeof(msg));
msg.message.present = ASN::DL_CCCH_MessageType_PR_rrcConnectionSetup;
ASN::RRCConnectionSetup *csp = &msg.message.choice.rrcConnectionSetup;
unsigned transactionId = uep->newTransactionId();
ByteVector result(1000);
bool version4 = false; // Use version4 of this message.
bool version5 = false; // Use version5 of this message with a default config.
if (version4 | version5) {
csp->present = ASN::RRCConnectionSetup_PR_later_than_r3; // Guessing we can use any of the variants.
// WARNING: Now there are temporarily two pointers to the memory in UE_Identity.
csp->choice.later_than_r3.initialUE_Identity = *ueInitialId;
csp->choice.later_than_r3.rrc_TransactionIdentifier = transactionId;
//ASN::RRCConnectionSetup::RRCConnectionSetup_u::RRCConnectionSetup__later_than_r3 *iep =
//&csp->choice.later_than_r3;
//iep->initialUE_Identity = *ueInitialId;
//iep->rrc_TransactionIdentifier = transactionId;
//typedef ASN::RRCConnectionSetup::RRCConnectionSetup_u::RRCConnectionSetup__later_than_r3 later_than_r3_t;
//ASN::RRCConnectionSetup::RRCConnectionSetup_u::RRCConnectionSetup__later_than_r3::RRCConnectionSetup__later_than_r3__criticalExtensions *extp = &iep->criticalExtensions;
if (version5) {
// version 5 of the message. A little verbose here...
csp->choice.later_than_r3.criticalExtensions.present = ASN::RRCConnectionSetup__later_than_r3__criticalExtensions_PR_criticalExtensions;
csp->choice.later_than_r3.criticalExtensions.choice.criticalExtensions.present = ASN::RRCConnectionSetup__later_than_r3__criticalExtensions__criticalExtensions_PR_r5;
ASN::RRCConnectionSetup_r5_IEs_t *ie5 = &csp->choice.later_than_r3.criticalExtensions.choice.criticalExtensions.choice.r5.rrcConnectionSetup_r5;
// ActivationTime_t *activationTime /* OPTIONAL */;
// U_RNTI_t new_U_RNTI;
toAsnURNTI(&ie5->new_U_RNTI,uep->getSrncId(),uep->getSRNTI());
// C_RNTI_t *new_c_RNTI /* OPTIONAL */;
ie5->new_c_RNTI = toAsnCRNTI(uep->mCRNTI);
// RRC_StateIndicator_t rrc_StateIndicator;
asn_long2INTEGER(&ie5->rrc_StateIndicator,ASN::RRC_StateIndicator_cell_FACH);
// UTRAN_DRX_CycleLengthCoefficient_t utran_DRX_CycleLengthCoeff;
ie5->utran_DRX_CycleLengthCoeff = 3; // Must be in range 3..9
ie5->specificationMode.present = ASN::RRCConnectionSetup_r5_IEs__specificationMode_PR_preconfiguration;
ie5->specificationMode.choice.preconfiguration.preConfigMode.present =
ASN::RRCConnectionSetup_r5_IEs__specificationMode__preconfiguration__preConfigMode_PR_defaultConfig;
asn_long2INTEGER(&ie5->specificationMode.choice.preconfiguration.preConfigMode.choice.defaultConfig.defaultConfigMode,ASN::DefaultConfigMode_fdd);
ie5->specificationMode.choice.preconfiguration.preConfigMode.choice.defaultConfig.defaultConfigIdentity = 0;
} else {
// version 4 of the message.
csp->choice.later_than_r3.criticalExtensions.present = ASN::RRCConnectionSetup__later_than_r3__criticalExtensions_PR_r4;
ASN::RRCConnectionSetup_r4_IEs_t *ie4 = &csp->choice.later_than_r3.criticalExtensions.choice.r4.rrcConnectionSetup_r4;
// ActivationTime_t *activationTime /* OPTIONAL */;
// U_RNTI_t new_U_RNTI;
toAsnURNTI(&ie4->new_U_RNTI,uep->getSrncId(),uep->getSRNTI());
// C_RNTI_t *new_c_RNTI /* OPTIONAL */;
ie4->new_c_RNTI = toAsnCRNTI(uep->mCRNTI);
// RRC_StateIndicator_t rrc_StateIndicator;
// 11-17-2012: Tried cell_pch state instead, to see if Samsung likes that better.
// Nope. Still sends protocol error.
asn_long2INTEGER(&ie4->rrc_StateIndicator,ASN::RRC_StateIndicator_cell_FACH);
// UTRAN_DRX_CycleLengthCoefficient_t utran_DRX_CycleLengthCoeff;
ie4->utran_DRX_CycleLengthCoeff = 3; // Must be in range 3..9
// struct CapabilityUpdateRequirement_r4 *capabilityUpdateRequirement /* OPTIONAL */;
// SRB_InformationSetupList2_t srb_InformationSetupList;
toAsnSRB_InformationSetupList(gRrcDcchConfig, &ie4->srb_InformationSetupList.list);
if (0) {
// As a debug measure to try to get the Samsung to work, try putting in the TrCh items.
// But this is just not right, the DL_CommonTransChInfo IEs dont even have an option of setting up a FACH TrCh config.
// struct UL_CommonTransChInfo_r4 *ul_CommonTransChInfo /* OPTIONAL */;
ie4->ul_CommonTransChInfo = RN_CALLOC(ASN::UL_CommonTransChInfo_r4);
typedef ASN::UL_CommonTransChInfo_r4::UL_CommonTransChInfo_r4__modeSpecificInfo ulmode;
ie4->ul_CommonTransChInfo->modeSpecificInfo = RN_CALLOC(ulmode);
ie4->ul_CommonTransChInfo->modeSpecificInfo->present = ASN::UL_CommonTransChInfo_r4__modeSpecificInfo_PR_fdd;
gRrcDcchConfig->getUlTfcs()->toAsnTfcs(&ie4->ul_CommonTransChInfo->modeSpecificInfo->choice.fdd.ul_TFCS,TrChUlDCHType);
// struct UL_AddReconfTransChInfoList *ul_AddReconfTransChInfoList /* OPTIONAL */;
ie4->ul_AddReconfTransChInfoList = toAsnUL_AddReconfTransChInfoList(gRrcDcchConfig);
// struct DL_CommonTransChInfo_r4 *dl_CommonTransChInfo /* OPTIONAL */;
ie4->dl_CommonTransChInfo = RN_CALLOC(ASN::DL_CommonTransChInfo_r4);
typedef ASN::DL_CommonTransChInfo_r4::DL_CommonTransChInfo_r4__modeSpecificInfo dlmode;
ie4->dl_CommonTransChInfo->modeSpecificInfo = RN_CALLOC(dlmode);
ie4->dl_CommonTransChInfo->modeSpecificInfo->present = ASN::DL_CommonTransChInfo_r4__modeSpecificInfo_PR_fdd;
typedef ASN::DL_CommonTransChInfo_r4::
DL_CommonTransChInfo_r4__modeSpecificInfo::
DL_CommonTransChInfo_r4__modeSpecificInfo_u::
DL_CommonTransChInfo_r4__modeSpecificInfo__fdd::
DL_CommonTransChInfo_r4__modeSpecificInfo__fdd__dl_Parameters dl_ridiculous;
ie4->dl_CommonTransChInfo->modeSpecificInfo->choice.fdd.dl_Parameters = RN_CALLOC(dl_ridiculous);
ie4->dl_CommonTransChInfo->modeSpecificInfo->choice.fdd.dl_Parameters->present =
ASN::DL_CommonTransChInfo_r4__modeSpecificInfo__fdd__dl_Parameters_PR_sameAsUL;
// struct DL_AddReconfTransChInfoList_r4 *dl_AddReconfTransChInfoList /* OPTIONAL */;
//gRrcCcchConfig->getDlTfs()->toAsnTfs(&fachPchInfo->transportFormatSet);
//gRrcCcchConfig->getUlTfs()->toAsnTfs((prach_SI->rach_TransportFormatSet = RN_CALLOC(ASN::TransportFormatSet)));
}
// struct FrequencyInfo *frequencyInfo /* OPTIONAL */;
// MaxAllowedUL_TX_Power_t *maxAllowedUL_TX_Power /* OPTIONAL */;
// struct UL_ChannelRequirement_r4 *ul_ChannelRequirement /* OPTIONAL */;
// struct DL_CommonInformation_r4 *dl_CommonInformation /* OPTIONAL */;
// struct DL_InformationPerRL_List_r4 *dl_InformationPerRL_List /* OPTIONAL */;
} // version 4
if (!encodeCcchMsg(&msg,result,descrRrcConnectionSetup,uep,0)) {return;}
// Zero out the initialUE_Identity that we copied above so that there
// is only one copy of it and we dont try to free it twice.
memset(&csp->choice.later_than_r3.initialUE_Identity,0,sizeof(ASN::InitialUE_Identity_t));
} else {
// Version 3 of this message.
// struct RRCConnectionSetup_r3_IEs
csp->present = ASN::RRCConnectionSetup_PR_r3; // Guessing we can use any of the variants.
ASN::RRCConnectionSetup_r3_IEs_t *iep = &csp->choice.r3.rrcConnectionSetup_r3;
// InitialUE_Identity_t initialUE_Identity;
// WARNING: Now there are temporarily two pointers to the memory in UE_Identity.
iep->initialUE_Identity = *ueInitialId;
// RRC_TransactionIdentifier_t rrc_TransactionIdentifier;
iep->rrc_TransactionIdentifier = transactionId;
// ActivationTime_t *activationTime /* OPTIONAL */;
// U_RNTI_t new_U_RNTI;
// U-RNTI is mandatory.
// They took apart the U_RNTI into its constituent parts, which was kinda dumb:
// the parts are 12 bit SRNC id and 20 bit S-RNTI.
toAsnURNTI(&iep->new_U_RNTI,uep->getSrncId(),uep->getSRNTI());
//setAsnBIT_STRING(&iep->new_U_RNTI.srnc_Identity,(uint8_t*)calloc(1,2),12);
//AsnBitString2BVTemp(iep->new_U_RNTI.srnc_Identity).setField(0,uep->getSrncId(),12);
//setAsnBIT_STRING(&iep->new_U_RNTI.s_RNTI,(uint8_t*)calloc(1,3),20);
//AsnBitString2BVTemp(iep->new_U_RNTI.s_RNTI).setField(0,uep->getSRNTI(),20);
// srnti = 0x12345;
//ByteVector tst(3);
//tst.setField(0,srnti,20);
//printf("SRNTI=0x%x bv=%s\n",srnti,tst.hexstr().c_str());
// C_RNTI_t *new_c_RNTI /* OPTIONAL */;
// C-RNTI
iep->new_c_RNTI = toAsnCRNTI(uep->mCRNTI);
//iep->new_c_RNTI = RN_CALLOC(ASN::C_RNTI_t);
// new_c_RNTI is a BIT_STRING_t
//setAsnBIT_STRING(iep->new_c_RNTI,(uint8_t*)calloc(1,2),16);
//AsnBitString2BVTemp(iep->new_c_RNTI).setField(0,uep->mCRNTI,16);
// RRC_StateIndicator_t rrc_StateIndicator;
asn_long2INTEGER(&iep->rrc_StateIndicator,ASN::RRC_StateIndicator_cell_FACH);
// UTRAN_DRX_CycleLengthCoefficient_t utran_DRX_CycleLengthCoeff;
// 10.3.3.49: DRC mode. "Refers to 'k' in the formula 25.304 Discontinous Reception".
iep->utran_DRX_CycleLengthCoeff = 3; // Must be in range 3..9
// skip optional CapabilityUpdateRequirement
// struct CapabilityUpdateRequirement *capabilityUpdateRequirement /* OPTIONAL */;
iep->capabilityUpdateRequirement = RN_CALLOC(ASN::CapabilityUpdateRequirement);
iep->capabilityUpdateRequirement->ue_RadioCapabilityFDDUpdateRequirement = true;
iep->capabilityUpdateRequirement->ue_RadioCapabilityTDDUpdateRequirement = false;
// SRB_InformationSetupList2_t srb_InformationSetupList;
toAsnSRB_InformationSetupList(gRrcDcchConfig, &iep->srb_InformationSetupList.list);
// struct UL_CommonTransChInfo *ul_CommonTransChInfo /* OPTIONAL */;
// struct DL_CommonInformation *dl_CommonInformation /* OPTIONAL */;
// UL_AddReconfTransChInfoList_t ul_AddReconfTransChInfoList;
// DL_AddReconfTransChInfoList_t dl_AddReconfTransChInfoList;
// All the TrCh info is optional, and not used in our case because we are
// defining RACH/FACH rather than DCH, BUT...
// For ul_ and dl_AddReconfTransChInfoList we are required to put in something anyway,
// and 8.1.3.4 recommends a single zero-sized TF.
// You can not use the "NOTHING" option - the ASN compiler just uses
// that to mark an uninitialized value and fails.
toAsnFakeUL_AddReconfTransChInfoList(&iep->ul_AddReconfTransChInfoList);
toAsnDL_AddReconfTransChInfoListSameAsUl(&iep->dl_AddReconfTransChInfoList,31,31);
// These IEs are all skipped, needed only for DCH:
// struct UL_ChannelRequirement *ul_ChannelRequirement /* OPTIONAL */;
// struct DL_InformationPerRL_List *dl_InformationPerRL_List /* OPTIONAL */;
//PhCh *phch = new PhCh(DPDCHType,256,254,256,9999,NULL);
//iep->ul_ChannelRequirement = phch->toAsnUL_ChannelRequirement();
//iep->dl_CommonInformation = phch->toAsnDL_CommonInformation();
//iep->dl_InformationPerRL_List = phch->toAsnDL_InformationPerRL_List();
/*ASN::DL_InformationPerRL_List *result2 = RN_CALLOC(ASN::DL_InformationPerRL_List);
ASN::DL_InformationPerRL *one = RN_CALLOC(ASN::DL_InformationPerRL);
one->modeSpecificInfo.present = ASN::DL_InformationPerRL__modeSpecificInfo_PR_fdd;
int primarySC = gConfig.getNum("UMTS.Downlink.ScramblingCode");
one->modeSpecificInfo.choice.fdd.primaryCPICH_Info.primaryScramblingCode = primarySC;
//one->dl_DPCH_InfoPerRL = toAsnDL_DPCH_InfoPerRL();
ASN_SEQUENCE_ADD(&result2->list,one);
iep->dl_InformationPerRL_List = result2;*/
// struct FrequencyInfo *frequencyInfo /* OPTIONAL */;
// TODO: Do we need this?
// MaxAllowedUL_TX_Power_t *maxAllowedUL_TX_Power /* OPTIONAL */;
if (!encodeCcchMsg(&msg,result,descrRrcConnectionSetup,uep,0)) {return;}
// Zero out the initialUE_Identity that we copied above so that there
// is only one copy of it and we dont try to free it twice.
memset(&iep->initialUE_Identity,0,sizeof(ASN::InitialUE_Identity_t));
}
LOG(INFO) << "gNodeB: " << gNodeB.clock().get() << ", SCCPCH: " << result;
// TODO: This crashes
// ASN_STRUCT_FREE_CONTENTS_ONLY(ASN::asn_DEF_DL_CCCH_Message,&msg);
// Configure the UE to be ready for incoming on the new SRBs...
uep->ueConnectRlc(gRrcDcchConfig,stCELL_FACH);
// Set a UeTransaction: see ueWriteLowSide for its use.
// there is no chance that other unrelated messages can happen simultaneously.
// But we will print an error if the received transaction does not match.
// The Rab Mask parameter is not relevant for this transaction type.
UeTransaction(uep,UeTransaction::ttRrcConnectionSetup, 0, transactionId,stCELL_FACH);
gMacSwitch.writeHighSideCcch(result,descrRrcConnectionSetup);
}
// Sent when an unrecognized UE tries to talk to us.
// Tell it to release the connection and start over.
static void sendRrcConnectionReleaseCcch(int32_t urnti)
{
ASN::DL_CCCH_Message_t msg;
memset(&msg,0,sizeof(msg));
msg.message.present = ASN::DL_CCCH_MessageType_PR_rrcConnectionRelease;
ASN::RRCConnectionRelease_CCCH *m1 = &msg.message.choice.rrcConnectionRelease;
m1->present = ASN::RRCConnectionRelease_CCCH_PR_r3;
ASN::RRCConnectionRelease_CCCH_r3_IEs *m2 = &m1->choice.r3.rrcConnectionRelease_CCCH_r3;
unsigned srncid = urnti >> 20 & 0xfff;
unsigned srnti = urnti & 0xfffff;
toAsnURNTI(&m2->u_RNTI, srncid,srnti);
ASN::RRCConnectionRelease_r3_IEs_t *m3 = &m2->rrcConnectionRelease;
m3->rrc_TransactionIdentifier = 0; // bogus, because we are not expecting a reply.
m3->releaseCause = toAsnEnumerated(ASN::ReleaseCause_unspecified); // TODO: What cause should we use?
ByteVector result(1000);
if (!encodeCcchMsg(&msg,result,descrRrcConnectionRelease,NULL,urnti)) {return;}
gMacSwitch.writeHighSideCcch(result,descrRrcConnectionRelease);
}
// This puts the phone in idle mode.
void sendRrcConnectionRelease(UEInfo *uep) //, ASN::InitialUE_Identity *ueInitialId
{
// Create the RB Setup Message.
ASN::DL_DCCH_Message_t msg;
memset(&msg,0,sizeof(msg));
msg.message.present = ASN::DL_DCCH_MessageType_PR_rrcConnectionRelease;
msg.message.choice.rrcConnectionRelease.present = ASN::RRCConnectionRelease_PR_r3;
ASN::RRCConnectionRelease_r3_IEs_t *ies =
&msg.message.choice.rrcConnectionRelease.choice.r3.rrcConnectionRelease_r3;
// Note: The RRC spec has a U-RNTI in the message, but it is not in the ASN.
// I do not see why it would be needed. If the message goes on DCCH the
// U-RNTI will be in the MAC header.
// RRC_TransactionIdentifier_t rrc_TransactionIdentifier;
unsigned transactionId = uep->newTransactionId();
ies->rrc_TransactionIdentifier = transactionId;
// N_308_t *n_308 /* OPTIONAL */;
// N308 is the number of times UE sends response RrcConnectionReleaseComplete.
// It is mandatory when this message is sent on DCCH.
ies->n_308 = RN_CALLOC(ASN::N_308_t); // It is a long.
*ies->n_308 = 1; // must be 1..8
// ReleaseCause_t releaseCause;
// Causes we might use are: normalEvent, userInactivity, pre_emptiveRelease.
ies->releaseCause = toAsnEnumerated(ASN::ReleaseCause_normalEvent);
// struct Rplmn_Information *rplmn_information /* OPTIONAL */;
ByteVector result(1000);
if (!encodeDcchMsg(uep,SRB2,&msg,result,descrRrcConnectionRelease)) {return;}
// Prepare to receive the reply to this message:
UeTransaction(uep,UeTransaction::ttRrcConnectionRelease,0,transactionId,stIdleMode);
uep->ueWriteHighSide(SRB2, result, descrRrcConnectionRelease);
}
// 3GPP 25.331 10.2.33
// This is the main message to create DCH channels.
// It is invoked by the SGSN to create a RAB for an internet connection.
// It will also be invoked by GMM L3 to create CS connections.
// In both cases a state machine in the caller must wait for the phones
// response before proceeding.
//
// We are sending a setup for a DCH and moving the UE to CELL_DCH state.
// The masterConfig indicates the DCH L2 setup, for example, how many TrCh.
// Return non-zero on error.
bool sendRadioBearerSetup(UEInfo *uep, RrcMasterChConfig *masterConfig, PhCh *phch, bool srbstoo)
{
// Create the RB Setup Message.
ASN::DL_DCCH_Message_t msg;
memset(&msg,0,sizeof(msg));
msg.message.present = ASN::DL_DCCH_MessageType_PR_radioBearerSetup;
ASN::RadioBearerSetup_t &rbstop = msg.message.choice.radioBearerSetup;
// There are various versions of this message. Lets use the oldest version:
rbstop.present = ASN::RadioBearerSetup_PR_r3;
ASN::RadioBearerSetup_r3_IEs_t &rbs = rbstop.choice.r3.radioBearerSetup_r3;
// =============== UE Information Elements ==============
// Comments are directly from the ASN::RadioBearerSetup_r3_IEs_t
// RRC_TransactionIdentifier_t rrc_TransactionIdentifier;
unsigned transactionId = uep->newTransactionId();
rbs.rrc_TransactionIdentifier = transactionId;
// struct IntegrityProtectionModeInfo *integrityProtectionModeInfo /* OPTIONAL */;
// struct CipheringModeInfo *cipheringModeInfo /* OPTIONAL */;
// ActivationTime_t *activationTime /* OPTIONAL */;
// struct U_RNTI *new_U_RNTI /* OPTIONAL */;
// C_RNTI_t *new_C_RNTI /* OPTIONAL */;
// RRC_StateIndicator_t rrc_StateIndicator;
asn_long2INTEGER(&rbs.rrc_StateIndicator, ASN::RRC_StateIndicator_cell_DCH);
// UTRAN_DRX_CycleLengthCoefficient_t *utran_DRX_CycleLengthCoeff /* OPTIONAL */;
// URA_Identity_t *ura_Identity /* OPTIONAL */;
// struct CN_InformationInfo *cn_InformationInfo /* OPTIONAL */;
// =============== RB Information Elements ==============
// Set up new SRBs on the new DCH.
// The new SRBs will be multiplexed using the paradigm of the defaultRBMappingInfo
// struct SRB_InformationSetupList *srb_InformationSetupList /* OPTIONAL */;
if (srbstoo) {
// 3GPP 25.331 10.3.4.24
rbs.srb_InformationSetupList = RN_CALLOC(ASN::SRB_InformationSetupList);
toAsnSRB_InformationSetupList(masterConfig,&rbs.srb_InformationSetupList->list);
}
// The RAB is where the new RBID for the data channel needs to go.
// struct RAB_InformationSetupList *rab_InformationSetupList /* OPTIONAL */;
// The UE is allowed to request a PS channel on any of RBs 5..15, so check them all.
bool haveDataCh = false; // Does the config define any data channels?
RBInfo *rb; unsigned rbid;
for (rbid = 5; rbid < masterConfig->mNumRB; rbid++) {
if (!(rb = masterConfig->getRB(rbid)) || !rb->valid()) { continue; }
haveDataCh = true;
break;
}
if (haveDataCh) {
// 3GPP 25.331 10.3.4.10 RAB Information for Setup.
// TODO: AMR rate defaults to "t7" - what is that?
rbs.rab_InformationSetupList = RN_CALLOC(ASN::RAB_InformationSetupList);
// A_SEQUENCE_OF(struct RAB_InformationSetup) list;
toAsnRAB_InformationSetupList(masterConfig, rbs.rab_InformationSetupList);
}
// struct RB_InformationAffectedList *rb_InformationAffectedList /* OPTIONAL */;
// struct DL_CounterSynchronisationInfo *dl_CounterSynchronisationInfo /* OPTIONAL */;
// =============== Uplink Transport Channels ==============
// struct UL_CommonTransChInfo *ul_CommonTransChInfo /* OPTIONAL */;
rbs.ul_CommonTransChInfo = toAsnUL_CommonTransChInfo(masterConfig);
// struct UL_DeletedTransChInfoList *ul_deletedTransChInfoList /* OPTIONAL */;
// struct UL_AddReconfTransChInfoList *ul_AddReconfTransChInfoList /* OPTIONAL */;
rbs.ul_AddReconfTransChInfoList = toAsnUL_AddReconfTransChInfoList(masterConfig);
// =============== Downlink Transport Channels ==============
// struct RadioBearerSetup_r3_IEs__dummy { } *dummy;
// struct DL_CommonTransChInfo *dl_CommonTransChInfo /* OPTIONAL */;
rbs.dl_CommonTransChInfo = toAsnDL_CommonTransChInfo(masterConfig);
// struct DL_DeletedTransChInfoList *dl_DeletedTransChInfoList /* OPTIONAL */;
// struct DL_AddReconfTransChInfoList *dl_AddReconfTransChInfoList /* OPTIONAL */;
rbs.dl_AddReconfTransChInfoList = toAsnDL_AddReconfTransChInfoList(masterConfig);
// =============== PhyCH Information Elements ==============
// struct FrequencyInfo *frequencyInfo /* OPTIONAL */;
// TODO: Do we need to add frequency info? For FDD is it is just the ARFCN,
// and I assume we are single ARFCN for now.
// =============== Uplink Radio Resources ==============
// MaxAllowedUL_TX_Power_t *maxAllowedUL_TX_Power /* OPTIONAL */;
// TODO: Do we need to specify UL power?
// This is a container for 10.3.6.88 "Uplink DPCH Info" in RRC spec.
// struct UL_ChannelRequirement *ul_ChannelRequirement /* OPTIONAL */;
// This is for DPCH only.
rbs.ul_ChannelRequirement = phch->toAsnUL_ChannelRequirement();
// struct RadioBearerSetup_r3_IEs__modeSpecificPhysChInfo { ... } modeSpecificPhysChInfo;
rbs.modeSpecificPhysChInfo.present = ASN::RadioBearerSetup_r3_IEs__modeSpecificPhysChInfo_PR_fdd;
// But the contents are optional.
// =============== Downlink Radio Resources ==============
// struct DL_CommonInformation *dl_CommonInformation /* OPTIONAL */;
// And I quote 8.2.1:
// If after state transition the UE enters CELL_DCH state from CELL_FACH or from CELL_PCH state:
// 1> if the IE "Default DPCH Offset Value" is not included:
// 2> the UE behaviour is not specified.
// The DPCH offset is inside this element:
// (with the totally unpredictable value of "0" which apparently the UE could never have guessed on its own.)
rbs.dl_CommonInformation = phch->toAsnDL_CommonInformation();
// struct DL_InformationPerRL_List *dl_InformationPerRL_List /* OPTIONAL */;
rbs.dl_InformationPerRL_List = phch->toAsnDL_InformationPerRL_List();
// note: encodeDcchMsg dumps the message to the log file.
//asn_fprint(stdout,&ASN::asn_DEF_DL_DCCH_Message, &msg); // Dump it all.
//fflush(stdout);
ByteVector result(1000);
if (!encodeDcchMsg(uep,SRB2,&msg,result,descrRadioBearerSetup)) {return 1;}
LOG(INFO) << "gNodeB: " << gNodeB.clock().get() << ", RadioBearerSetup: " << result;
// Prepare to receive the reply to this message:
UeTransaction(uep,UeTransaction::ttRadioBearerSetup, 1<<rbid, transactionId,stCELL_DCH);
// This message is being written into a queue, but the MAC could find
// it there and send it immediately. The caller is responsible
// for making sure the UE is prepared to receive replies before calling us.
uep->ueWriteHighSide(SRB2, result, descrRadioBearerSetup);
return 0; // ok
}
// 3GPPP 25.331 8.2.2
// pat 12-27-2012: This message is implemented incorrectly. I was hoping if you told the UE
// to go to CELL_FACH it would use the info from SIB5 or SIB6, but nope, you have to download
// a completely new config that is basically going to be a copy of SIB5.
// I'm not sure we have to do that at all, because when the last radio bearer is released
// we can send an RrcConnectionRelease to drop the UE back into idle mode.
// The UE keeps its SGSN level attach status regardless of mode; idle mode is fine.
// Update: Some phones (Samsung) activate/deactivate the PDPContexts rapidly, and we dont want
// to drop them all the way back to idle mode, because the transition from idle->fach takes a long
// time because we have to go through the authorization and security steps, while fach->dch is just a single message.
// Long term, we dont want to leave the UE in CELL_FACH mode after the radio-bearer-release anyway,
// it should be in CELL_PCH so we can page it, or if we dont need to page it, in idle mode.
// Harvind (3-17-13) I think this message is fine. The UE seems to like it during the PDP Deactivation Process
void sendRadioBearerRelease(UEInfo *uep,
unsigned rabMask, // Mask of RABs/RBs to release.
bool finished) // If set, all RABs released so move UE back to CELL_FACH mode.
{
#if 1
if (finished) {
sendRrcConnectionRelease(uep);
return;
}
//return; // Do nothing. We will just leave the existing DCH setup alone; it does not matter
#endif
// how many RBs are sharing the DCH.
UEState nextState = finished ? stCELL_FACH : stCELL_DCH;
ASN::DL_DCCH_Message_t msg;
memset(&msg,0,sizeof(msg));
msg.message.present = ASN::DL_DCCH_MessageType_PR_radioBearerRelease;
ASN::RadioBearerRelease_t &rbrtop = msg.message.choice.radioBearerRelease;
// There are various versions of this message. Lets use the oldest version:
rbrtop.present = ASN::RadioBearerRelease_PR_r3;
ASN::RadioBearerRelease_r3_IEs_t &rbr = rbrtop.choice.r3.radioBearerRelease_r3;
// =============== UE Information Elements ==============
//RRC_TransactionIdentifier_t rrc_TransactionIdentifier;
unsigned transactionId = uep->newTransactionId();
rbr.rrc_TransactionIdentifier = transactionId;
//struct IntegrityProtectionModeInfo *integrityProtectionModeInfo /* OPTIONAL */;
//struct CipheringModeInfo *cipheringModeInfo /* OPTIONAL */;
//ActivationTime_t *activationTime /* OPTIONAL */;
//struct U_RNTI *new_U_RNTI /* OPTIONAL */;
//C_RNTI_t *new_C_RNTI /* OPTIONAL */;
//RRC_StateIndicator_t rrc_StateIndicator;
rbr.rrc_StateIndicator = toAsnEnumerated(UEState2Asn(nextState));
//UTRAN_DRX_CycleLengthCoefficient_t *utran_DRX_CycleLengthCoeff /* OPTIONAL */;
// =============== CN Information Elements ==============
//struct CN_InformationInfo *cn_InformationInfo /* OPTIONAL */;
//CN_DomainIdentity_t *signallingConnectionRelIndication /* OPTIONAL */;
// =============== UTRAN Mobility Information Elements ==============
//URA_Identity_t *ura_Identity /* OPTIONAL */;
// =============== RB Information Elements ==============
// 8.6.4.5 says RB Information to Reconfigure can modify SRBs.
#if 0 // If you include this code you get an ASN encoder violation from encodeDcchMsg
// Furthermore, the examples I have seen dont use this, they release the RBs,
// and the UE must infer the RAB from the RB.
//struct RAB_InformationReconfigList *rab_InformationReconfigList /* OPTIONAL */;
for (RbId rbid = 5; rbid < gsMaxRB; rbid++) {
if (rabMask & (1<<rbid)) {
// 25.331 10.3.4.11 RAB Information to Reconfigure.
RBInfo *rb;
if (!(rb = uep->ueGetConfig()->getRB(rbid)) || !rb->valid()) { continue; }
ASN::RAB_InformationReconfig *rabreconfig = RN_CALLOC(ASN::RAB_InformationReconfig);
toAsnRAB_Identity(rbid,&rabreconfig->rab_Identity);
bool csdomain = rb->isCsDomain();
rabreconfig->cn_DomainIdentity = toAsnEnumerated(
csdomain ? ASN::CN_DomainIdentity_cs_domain : ASN::CN_DomainIdentity_ps_domain);
// NAS_Synchronisation_Indicator_t nas_Synchronisation_Indicator;
// From the documentation:
// A container for non-access stratum information to be transferred transparently through UTRAN.
// Note 1: The nas_Synchronisation_Indicator is only relevant for CS domains.
// On the first valid RAB, allocate the list.
if (rbr.rab_InformationReconfigList == NULL) {
rbr.rab_InformationReconfigList = RN_CALLOC(ASN::RAB_InformationReconfigList);
}
ASN_SEQUENCE_ADD(&rbr.rab_InformationReconfigList->list,rabreconfig);
}
}
#endif
//RB_InformationReleaseList_t rb_InformationReleaseList;
for (RbId rbid = 5; rbid < gsMaxRB; rbid++) {
if (rabMask & (1<<rbid)) {
ASN::RB_Identity_t *val = RN_CALLOC(ASN::RB_Identity_t); // Its just a long.
*val = rbid;
ASN_SEQUENCE_ADD(&rbr.rb_InformationReleaseList.list,val);
}
}
// The RB Information to Reconfigure IE is only in ASN version 6 and higher.
// The difference between that and RB Information to be Affected is that
// the former lets you change other RLC programming, while
// RB Information to be Affected lets you change only the RB Mapping Info,
// but that is all you need to switch the SRBs between CELL_DCH or CELL_FACH state.
//struct RB_InformationAffectedList *rb_InformationAffectedList /* OPTIONAL */;
// 12-22-2012: This message is not working, so tried removing this, but did not help:
if (finished) {
// Reconfigure the SRBs to whatever they need to be in CELL_FACH state.
rbr.rb_InformationAffectedList = toAsnRB_InformationAffectedList(gRrcDcchConfig);
}
//struct DL_CounterSynchronisationInfo *dl_CounterSynchronisationInfo /* OPTIONAL */;
// =============== TrCh Information Elements ==============
//struct UL_CommonTransChInfo *ul_CommonTransChInfo /* OPTIONAL */;
//struct UL_DeletedTransChInfoList *ul_deletedTransChInfoList /* OPTIONAL */;
//struct UL_AddReconfTransChInfoList *ul_AddReconfTransChInfoList /* OPTIONAL */;
//struct RadioBearerRelease_r3_IEs__dummy { } *dummy;
//struct DL_CommonTransChInfo *dl_CommonTransChInfo /* OPTIONAL */;
//struct DL_DeletedTransChInfoList *dl_DeletedTransChInfoList /* OPTIONAL */;
//struct DL_AddReconfTransChInfo2List *dl_AddReconfTransChInfoList /* OPTIONAL */;
// pat 1-24-2013: Now making the assumption that we use different TrCh for CELL_FACH and CELL_DCH mode,
// so when we are 'finished' with DCH we can just delete the TrCh used for CELL_DCH.
// In this case we still need to send an updated TFCS.
// If we are not 'finished' with the DCH, we dont touch it, because we use logical channel multiplexing
// to distinguish the various RBs on a single TrCh.
if (finished) {
// pat 1-24-2013:
// Apparently we are supposed to delete the TrCh being used by DCH. I still dont know if UE stores
// the two separate TFS for use in CELL_FACH and CELL_DCH modes; this critical piece of info is not in 25.331.
// To be safe, I would like to use different TrChs numbers for RACH/FACH and DCH, but
// unfortunately there are several places in our code that assume that all TrChId in use
// are numbered starting at 1 (TrCh ids are 1-based), so we cannot yet allocate a
// different TrCh for DCH than RACH/FACH, and I was hesitant to change all those places
// because I cannot test because no radio.
// So instead, just delete TrCh 1 and hope the UE reloads TrCh 1 for FACH from SIB5.
// EXCEPT: The SIB5 can (and currently does) have the wrong FACH TrCh numbers in it, and the UE apparently does not even care!
// The only thing I can imagine is that the UE is pretty much ignoring the TrCh ids in FACH state, implying that
// the spec is totally screwed up here and nobody knows how it is really supposed to work.
{
rbr.ul_deletedTransChInfoList = RN_CALLOC(ASN::UL_DeletedTransChInfoList_t);
ASN::UL_TransportChannelIdentity_t *ult = RN_CALLOC(ASN::UL_TransportChannelIdentity_t);
ult->ul_TransportChannelType = toAsnEnumerated(ASN::UL_TrCH_Type_dch);
// TODO: We are simply assuming that there is only one TrCh here.
//ult->ul_TransportChannelIdentity = uep->mUeDchConfig.getUlTrChInfo(0)->mTransportChannelIdentity;
ult->ul_TransportChannelIdentity = 1;
ASN_SEQUENCE_ADD(&rbr.ul_deletedTransChInfoList->list,ult);
}
{
rbr.dl_DeletedTransChInfoList = RN_CALLOC(ASN::DL_DeletedTransChInfoList_t);
ASN::DL_TransportChannelIdentity_t *dlt = RN_CALLOC(ASN::DL_TransportChannelIdentity_t);
dlt->dl_TransportChannelType = toAsnEnumerated(ASN::DL_TrCH_Type_dch);
// TODO: We are simply assuming that there is only the TrCh here.
//dlt->dl_TransportChannelIdentity = uep->mUeDchConfig.getDlTrChInfo(0)->mTransportChannelIdentity;
dlt->dl_TransportChannelIdentity = 1;
ASN_SEQUENCE_ADD(&rbr.dl_DeletedTransChInfoList->list,dlt);
}
// The only example RadioBearerRelease message example I have is Nokia's "Call Setup PS" but that
// leaves the UE in CELL_DCH mode afterward, not CELL_FACH. They download a new TFCS but not a TFS.
// I dont think we should do this unless we are leaving the UE in CELL_DCH state too.
RrcMasterChConfig *newConfig = gRrcDcchConfig;
rbr.ul_CommonTransChInfo = toAsnUL_CommonTransChInfo(newConfig);
// The Add/Reconfig TrChInfoList is only for DCHs, not FACH/RACHes
// Therefore it must pick up the FACH TFS from SIB5, and It is totally nonsensical to me why
// we must download a TFCS here instead of the UE using the one from SIB5.
//rbr.ul_AddReconfTransChInfoList = toAsnUL_AddReconfTransChInfoList(newConfig);
rbr.dl_CommonTransChInfo = toAsnDL_CommonTransChInfo(newConfig);
//rbr.dl_AddReconfTransChInfo2List = toAsnDL_AddReconfTransChInfo2List(newConfig);
}
// =============== Physical Information Elements ============== We dont need these.
//struct FrequencyInfo *frequencyInfo /* OPTIONAL */;
//MaxAllowedUL_TX_Power_t *maxAllowedUL_TX_Power /* OPTIONAL */;
//struct UL_ChannelRequirement *ul_ChannelRequirement /* OPTIONAL */;
//struct RadioBearerRelease_r3_IEs__modeSpecificPhysChInfo { } modeSpecificPhysChInfo;
// Must set present, but the contents are optional.
rbr.modeSpecificPhysChInfo.present = ASN::RadioBearerRelease_r3_IEs__modeSpecificPhysChInfo_PR_fdd;
//struct DL_CommonInformation *dl_CommonInformation /* OPTIONAL */;
//struct DL_InformationPerRL_List *dl_InformationPerRL_List /* OPTIONAL */;
ByteVector result(1000);
if (!encodeDcchMsg(uep,SRB2,&msg,result,descrRadioBearerRelease)) {return;}
if (finished) {
// Configure the UE to be ready for incoming on the new SRBs...
uep->ueConnectRlc(gRrcDcchConfig,nextState);
}
// Prepare to receive the reply to this message:
UeTransaction(uep,UeTransaction::ttRadioBearerRelease, rabMask, transactionId,
nextState);
uep->ueWriteHighSide(SRB2, result, descrRadioBearerRelease);
}
static unsigned commonCellUpdateConfirm(UEInfo *uep, ASN::CellUpdateConfirm_r3_IEs_t *ies)
{
// Apparently even CCCH messages get a transaction id, which will be used if the UE
// replies to indicate an error in this message.
unsigned transactionId = uep->newTransactionId();
ies->rrc_TransactionIdentifier = transactionId;
// Huge message but almost everything is optional. Here are the mandatory parts:
ies->rrc_StateIndicator = toAsnEnumerated(UEState2Asn(uep->ueGetState()));
ies->rlc_Re_establishIndicatorRb2_3or4 = 0;
ies->rlc_Re_establishIndicatorRb5orAbove = 0;
ies->modeSpecificTransChInfo.present = ASN::CellUpdateConfirm_r3_IEs__modeSpecificTransChInfo_PR_fdd;
ies->modeSpecificPhysChInfo.present = ASN::CellUpdateConfirm_r3_IEs__modeSpecificPhysChInfo_PR_fdd;
// We can define a new URNTI.
// TODO: Do we need to assign a new URNTI if this message is on CCCH?
return transactionId;
}
// The CellUpdateConfirm message may be sent out on either DCCH or CCCH.
// This version is for DCCH.
// TODO: It would be wise to implement the RLC re-establish indicators.
static void sendCellUpdateConfirmDcch(UEInfo *uep)
{
ASN::DL_DCCH_Message_t msg;
memset(&msg,0,sizeof(msg));
// struct IntegrityCheckInfo *integrityCheckInfo /* OPTIONAL */;
msg.message.present = ASN::DL_DCCH_MessageType_PR_cellUpdateConfirm;
msg.message.choice.cellUpdateConfirm.present = ASN::CellUpdateConfirm_PR_r3;
ASN::CellUpdateConfirm_r3_IEs_t *ies =
&msg.message.choice.cellUpdateConfirm.choice.r3.cellUpdateConfirm_r3;
unsigned transactionId = commonCellUpdateConfirm(uep,ies);
ByteVector result(1000);
if (!encodeDcchMsg(uep,SRB2,&msg,result,descrCellUpdateConfirm)) {return;}
UeTransaction(uep,UeTransaction::ttCellUpdateConfirm, 0, transactionId);
uep->ueWriteHighSide(SRB2, result, descrCellUpdateConfirm);
}
static void sendCellUpdateConfirmCcch(UEInfo *uep)
{
ASN::DL_CCCH_Message_t msg;
memset(&msg,0,sizeof(msg));
msg.message.present = ASN::DL_CCCH_MessageType_PR_cellUpdateConfirm;
msg.message.choice.cellUpdateConfirm.present = ASN::CellUpdateConfirm_CCCH_PR_r3;
// U_RNTI_t u_RNTI;
toAsnURNTI(&msg.message.choice.cellUpdateConfirm.choice.r3.u_RNTI,uep->getSrncId(),uep->getSRNTI());
// CellUpdateConfirm_r3_IEs_t cellUpdateConfirm_r3;
ASN::CellUpdateConfirm_r3_IEs_t *ies =
&msg.message.choice.cellUpdateConfirm.choice.r3.cellUpdateConfirm_r3;
unsigned transactionId = commonCellUpdateConfirm(uep,ies);
ByteVector result(1000);
if (!encodeCcchMsg(&msg,result,descrCellUpdateConfirm,uep,0)) {return;}
UeTransaction(uep,UeTransaction::ttCellUpdateConfirm, 0, transactionId);
gMacSwitch.writeHighSideCcch(result,descrCellUpdateConfirm);
}
void sendCellUpdateConfirm(UEInfo *uep)
{
switch (uep->ueGetState()) {
case stCELL_DCH:
sendCellUpdateConfirmDcch(uep);
return;
default:
sendCellUpdateConfirmCcch(uep);
return;
}
}
// 33.102 5.1.2 Specifies two types of Security procedure: section 6.3 describes the main
// authentication method using Ki; section 6.5 describes a local authentication mechanism
// using an integrity key. You must do one of the two procedures at each connection setup,
// including the one for an L3 Service Request.
void sendSecurityModeCommand(UEInfo *uep)
{
ASN::DL_DCCH_Message_t msg;
memset(&msg,0,sizeof(msg));
msg.message.present = ASN::DL_DCCH_MessageType_PR_securityModeCommand;
msg.message.choice.securityModeCommand.present = ASN::SecurityModeCommand_PR_r3;
//ASN::SecurityModeCommand_t::SecurityModeCommand_u::SecurityMode_command__r3::Security *ies =
ASN::SecurityModeCommand_r3_IEs *ies =
&msg.message.choice.securityModeCommand.choice.r3.securityModeCommand_r3;
unsigned transactionId = uep->newTransactionId();
ies->rrc_TransactionIdentifier = transactionId;
// SecurityCapability_t securityCapability;
if (uep->radioCapability) {
// just copy this from UE Info to make UE happy.
// 8.1.12.3 implies that if these capabilities don't exactly match those of the UE's, the UE will release
ies->securityCapability = uep->radioCapability->securityCapability;
}
else {
ies->securityCapability.cipheringAlgorithmCap = allocAsnBIT_STRING(16);
// We are not going to do ciphering, but I am going to set one of the
// ciphering algorithm bits in case it is needed to make the UE happy.
AsnBitString2BVTemp(ies->securityCapability.cipheringAlgorithmCap).setField(
ASN::SecurityCapability__cipheringAlgorithmCap_uea0,1,1);
AsnBitString2BVTemp(ies->securityCapability.cipheringAlgorithmCap).setField(
ASN::SecurityCapability__cipheringAlgorithmCap_uea1,1,1);
ies->securityCapability.integrityProtectionAlgorithmCap = allocAsnBIT_STRING(16);
// Set the bit for algorithm UIA1, which is the kasumi-based one we support.
AsnBitString2BVTemp(ies->securityCapability.integrityProtectionAlgorithmCap).setField(
ASN::SecurityCapability__integrityProtectionAlgorithmCap_uia1,1,1); // FIXME: 10.3.3.7 of 25.331 doesn't follow this
}
// Not doing ciphering
// struct CipheringModeInfo *cipheringModeInfo /* OPTIONAL */;
/*ASN::CipheringModeInfo *cmi = RN_CALLOC(ASN::CipheringModeInfo);
ies->cipheringModeInfo = cmi;
cmi->cipheringModeCommand.present = ASN::CipheringModeCommand_PR_startRestart;
cmi->cipheringModeCommand.choice.startRestart = toAsnEnumerated(ASN::CipheringAlgorithm_uea0);
*/
// Since we are setting up Integrity Protection (and not just ciphering)
// we need to include this optional IE.
// struct IntegrityProtectionModeInfo *integrityProtectionModeInfo /* OPTIONAL */;
ASN::IntegrityProtectionModeInfo *ipmi = RN_CALLOC(ASN::IntegrityProtectionModeInfo);
ies->integrityProtectionModeInfo = ipmi;
ipmi->integrityProtectionModeCommand.present = ASN::IntegrityProtectionModeCommand_PR_startIntegrityProtection;
ipmi->integrityProtectionModeCommand.choice.startIntegrityProtection.integrityProtInitNumber = allocAsnBIT_STRING(32);
AsnBitString2BVTemp(ipmi->integrityProtectionModeCommand.choice.startIntegrityProtection.integrityProtInitNumber)
.setField(0, uep->integrity.getFresh(),32);
// This is optional - is it necessary?
ipmi->integrityProtectionAlgorithm = RN_CALLOC(ASN::IntegrityProtectionAlgorithm_t);
*ipmi->integrityProtectionAlgorithm = toAsnEnumerated(ASN::IntegrityProtectionAlgorithm_uia1);
ies->cn_DomainIdentity = toAsnEnumerated(ASN::CN_DomainIdentity_ps_domain);
// struct InterRAT_UE_SecurityCapList *ue_SystemSpecificSecurityCap /* OPTIONAL */;
/*ies->ue_SystemSpecificSecurityCap = RN_CALLOC(ASN::InterRAT_UE_SecurityCapList);
ASN::InterRAT_UE_SecurityCapability* secCap = RN_CALLOC(ASN::InterRAT_UE_SecurityCapability);
secCap->present = ASN::InterRAT_UE_SecurityCapability_PR_gsm;
secCap->choice.gsm.gsmSecurityCapability = allocAsnBIT_STRING(7);
AsnBitString2BVTemp(secCap->choice.gsm.gsmSecurityCapability).setField(ASN::GsmSecurityCapability_a5_1,1,1);
ASN_SEQUENCE_ADD(&ies->ue_SystemSpecificSecurityCap->list,secCap);
*/
// 25.331 8.5.10 and 8.6.3.5
// The security mode command itself is the first one that is protected.
// The start of the protection is indicated to the UE by the inclusion of the IntegrityCheck IE in the message.
// 13.4.10: Integrity Protection is turned off when entering/leaving idle mode.
uep->integrity.integrityStart();
ByteVector result(1000);
if (!encodeDcchMsg(uep,SRB2,&msg,result,descrSecurityModeCommand)) {return;}
UeTransaction(uep,UeTransaction::ttSecurityModeCommand, 0, transactionId);
uep->ueWriteHighSide(SRB2, result, descrSecurityModeCommand);
}
static void handleSecurityModeComplete(UEInfo*uep, ASN::SecurityModeComplete_t *secmsg)
{
// I'm not sure what to do with any of the optional contents of this message.
// struct IntegrityProtActivationInfo *ul_IntegProtActivationInfo /* OPTIONAL */;
// struct RB_ActivationTimeInfoList *rb_UL_CiphActivationTimeInfo /* OPTIONAL */;
// If the UE is not confused, the security procedure was initiated
// by the SGSN during an AttachRequest, so notify the SGSN that we are ready to roll.
uep->sgsnHandleSecurityModeComplete(true);
}
// Just print an error message based on the error code.
// We have lost communication with this UE and should do something.
// TODO: what?
static void handleSecurityModeFailure(UEInfo*uep, ASN::SecurityModeFailure_t *secfailmsg)
{
const char *why = "";
char whybuf[100];
switch (secfailmsg->failureCause.present) {
case ASN::FailureCauseWithProtErr_PR_configurationUnsupported: why = "configurationUnsupported"; break;
case ASN::FailureCauseWithProtErr_PR_physicalChannelFailure: why = "physicalChannelFailure";break;
case ASN::FailureCauseWithProtErr_PR_incompatibleSimultaneousReconfiguration: why="incompatibleSimultaneousReconfiguration"; break;
case ASN::FailureCauseWithProtErr_PR_compressedModeRuntimeError: why="compressedModeRuntimeError"; break;
case ASN::FailureCauseWithProtErr_PR_protocolError: {
why="protocolError";
// There is more interesting information provided with this cause:
ASN::ProtocolErrorInformation *pei = &secfailmsg->failureCause.choice.protocolError;
if (pei->diagnosticsType.present == ASN::ProtocolErrorInformation__diagnosticsType_PR_type1) {
long cause = asnEnum2long(pei->diagnosticsType.choice.type1.protocolErrorCause);
switch (cause) {
case ASN::ProtocolErrorCause_asn1_ViolationOrEncodingError:
why = "ProtocolErrorCause_asn1_ViolationOrEncodingError";
break;
case ASN::ProtocolErrorCause_messageTypeNonexistent:
why = "ProtocolErrorCause_messageTypeNonexistent";
break;
case ASN::ProtocolErrorCause_messageNotCompatibleWithReceiverState:
why = "ProtocolErrorCause_messageNotCompatibleWithReceiverState";
break;
case ASN::ProtocolErrorCause_ie_ValueNotComprehended:
why = "ProtocolErrorCause_ie_ValueNotComprehended";
break;
case ASN::ProtocolErrorCause_informationElementMissing:
why = "ProtocolErrorCause_informationElementMissing";
break;
case ASN::ProtocolErrorCause_messageExtensionNotComprehended:
why = "ProtocolErrorCause_messageExtensionNotComprehended";
break;
default:
sprintf(whybuf,"protocol error cause %ld",cause);
why = whybuf;
break;
}
}
break;
}
case ASN::FailureCauseWithProtErr_PR_cellUpdateOccurred: why="cellUpdateOccurred";break;
case ASN::FailureCauseWithProtErr_PR_invalidConfiguration: why="invalidConfiguration";break;
case ASN::FailureCauseWithProtErr_PR_configurationIncomplete: why="configurationIncomplete";break;
case ASN::FailureCauseWithProtErr_PR_unsupportedMeasurement: why="unsupportedMeasurement";break;
case ASN::FailureCauseWithProtErr_PR_mbmsSessionAlreadyReceivedCorrectly: why="mbmsSessionAlreadyRecievedCorrectly";break;
case ASN::FailureCauseWithProtErr_PR_lowerPriorityMBMSService: why="lowerPriorityMBMSService";break;
default: break;
}
LOG(ERR)<<format("Security Mode Failure cause=%d %s",secfailmsg->failureCause.present,why)<<uep;
uep->sgsnHandleSecurityModeComplete(false);
}
static void handleCellUpdate(ASN::CellUpdate_t *msg)
{
// This message sends a whole bunch of possible errors. Lets print them out.
bool srbErr = msg->am_RLC_ErrorIndicationRb2_3or4;
bool rbErr = msg->am_RLC_ErrorIndicationRb5orAbove;
bool radioErr = 0;
bool otherErr = 0;
long cuCause = 0;
long failCause = 0;
bool protoErrValid = 0; long protoErr;
// I think the transaction id in this message, which is hidden down in
// the failureCause, is the transaction id of the transaction that failed.
unsigned failedTransactionId = 0;
asn_INTEGER2long(&msg->cellUpdateCause,&cuCause);
switch (cuCause) {
// These are the two failure causes; all the other causes are normal.
case ASN::CellUpdateCause_radiolinkFailure: radioErr = true; break;
case ASN::CellUpdateCause_rlc_unrecoverableError: otherErr = true; break;
}
if (msg->failureCause) {
failedTransactionId = msg->failureCause->rrc_TransactionIdentifier;
failCause = msg->failureCause->failureCause.present;
if (failCause == ASN::FailureCauseWithProtErr_PR_protocolError) {
protoErrValid = true;
// Geesh. Just assume type1 and get it.
asn_INTEGER2long(&msg->failureCause->failureCause.choice.protocolError.
diagnosticsType.choice.type1.protocolErrorCause,&protoErr);
}
}
// They transmit the URNTI in its constituent SRNCid and S-RNTI parts.
uint32_t srnc = AsnBitString2BVTemp(msg->u_RNTI.srnc_Identity).getField(0,12);
uint32_t srnti = AsnBitString2BVTemp(msg->u_RNTI.s_RNTI).getField(0,20);
uint32_t urnti = (srnc<<20) | srnti;
const char *comment = "UL_CCCH_MessageType_PR_cellUpdate";
UEInfo *uep = gRrc.findUeByUrnti(urnti);
if (!uep) { comment = "UL_CCCH_MessageType_PR_cellUpdate (new UE)"; }
asnLogMsg(0, &ASN::asn_DEF_CellUpdate, msg,comment,uep,urnti);
bool anyErr = (srbErr || rbErr || otherErr || radioErr || failCause || protoErrValid);
if (anyErr) {
LOG(ERR) << "Received cell update message with failure info:"
<<LOGVAR(cuCause) <<LOGVAR(srbErr) <<LOGVAR(rbErr)
<<LOGVAR(otherErr) <<LOGVAR(radioErr) <<LOGVAR(failCause)
<<LOGVAR(protoErrValid) <<LOGVAR(protoErr)
<<LOGVAR(failedTransactionId)
<<uep;
} else if (uep == NULL) {
LOG(ERR) << format("Received cell update message with unrecognized URNTI=0x%x",
urnti);
}
if (uep) {
//msg->startList.
//if (rbscmsg->start_Value) {
// uint32_t startVal = AsnBitString2BV(rbscmsg->start_Value).getField(0,20);
// uep->integrity.updateStartValue(startVal);
//}
// Dont need this printf. The START value is not supposed to change. It was a bug that was changing it.
// printf("actual START value is: %u\n", uep->integrity.getStart());
sendCellUpdateConfirm(uep);
}
else {
// This is the first we have heard from this UE. This is illegal.
sendRrcConnectionReleaseCcch(urnti);
}
}
void handleRrcConnectionRequest(ASN::RRCConnectionRequest_t *msg)
{
//RrcUeId ueid(&msg->initialUE_Identity);
// establishmentCause; Do we care?
// We dont care about protocolError.
// We dont yet care about MeasuredResultsOnRACH.
// TODO: Do we need to worry about multiple requests from the same UE?
// If we get an identical request keep the same URNTI.
// (Not doing so was one of the bugs in GPRS.)
// There is an argument that if it is a duplicate request, just issue another URNTI,
// and whichever URNTI the UE decides to use will be fine with us, but I think
// that confuses the UE sometimes.
AsnUeId aid(msg->initialUE_Identity);
const char *comment = "UL_CCCH_MessageType_PR_rrcConnectionRequest";
UEInfo *uep = gRrc.findUeByAsnId(&aid);
if (uep == NULL) {
uep = new UEInfo(&aid);
comment = "UL_CCCH_MessageType_PR_rrcConnectionRequest (new UE)";
}
asnLogMsg(0, &ASN::asn_DEF_RRCConnectionRequest, msg,comment,uep);
// (pat 12-18-2012) If we get this it means the UE dropped into idle mode possibly
// without telling us, so fix that:
// FIXME: What about CELL_PCH or CELL_URA
uep->ueSetState(stIdleMode);
// We need to do stop integrity protection before sending the L3 Authentication message,
// which gets wrapped in an RRC direct transfer, which must not be integrity protected.
uep->integrity.integrityStop(); // Redudant with code in ueSetState, but make sure.
// Send the RRC Connection Setup Message,
// using the exact initialUE_Identity the UE provided, whatever it is.
sendRrcConnectionSetup(uep,&msg->initialUE_Identity);
}
#if 0 // Probably works, but not used, so take out to shut up compiler.
static bool fromAsnCnDomainIdentity(const char *inform, ASN::CN_DomainIdentity_t &asnthing)
{
bool psDomain;
long cnDomainId;
if (asn_INTEGER2long(&asnthing,&cnDomainId)) {
LOG(ERR) <<inform <<":"<<"invalid CN Domain Identity";
}
switch (cnDomainId) {
case ASN::CN_DomainIdentity_cs_domain: psDomain = false; break;
case ASN::CN_DomainIdentity_ps_domain: psDomain = true; break;
default: LOG(ERR) <<inform <<":"<<"invalid CN domain value";
}
return psDomain;
}
#endif
// This message is passed transparently from the MAC using RLC-TM.
// The MAC should pop off the headers and pass the rest.
// The only possible messages are:
// RRC Connection Request
// Cell Update
// URA Update
void rrcRecvCcchMessage(BitVector &tb,unsigned asc)
{
ASN::UL_CCCH_Message *msg1 = (ASN::UL_CCCH_Message*)uperDecodeFromBitV(&ASN::asn_DEF_UL_CCCH_Message, tb);
if (!msg1) {
LOG(ALERT) << "undecodable CCCH message received";
return;
}
//if (gConfig.getNum("UMTS.Debug.Messages")) {
// asn_fprint(stdout,&ASN::asn_DEF_UL_CCCH_Message, msg1);
// fflush(stdout);
//}
switch (msg1->message.present) {
case ASN::UL_CCCH_MessageType_PR_cellUpdate:
LOG(INFO) << "CELL_UPDATE message received";
//asn_fprint(stdout,&ASN::asn_DEF_CellUpdate, &msg1->message.choice.cellUpdate);
handleCellUpdate(&msg1->message.choice.cellUpdate);
return;
case ASN::UL_CCCH_MessageType_PR_rrcConnectionRequest:
LOG(INFO) << "RRC Connection Request received";
handleRrcConnectionRequest(&msg1->message.choice.rrcConnectionRequest);
return;
case ASN::UL_CCCH_MessageType_PR_uraUpdate:
// We didnt crack out the URNTI for the message so just leave it null.
asnLogMsg(0, &ASN::asn_DEF_UL_CCCH_Message, msg1,asnUlCcchMsg2Name(msg1->message.present),NULL);
LOG(INFO) << "URA_UPDATE message ignored";
return;
default:
asnLogMsg(0, &ASN::asn_DEF_UL_CCCH_Message, msg1,asnUlCcchMsg2Name(msg1->message.present),NULL);
LOG(ERR) << "CCCH message ignored, unknown type="<<msg1->message.present;
return;
}
ASN_STRUCT_FREE(ASN::asn_DEF_UL_CCCH_Message, msg1);
}
void UEInfo::ueRecvL3Msg(ByteVector &msgframe, UEInfo *uep)
{
unsigned pd = msgframe.getNibble(0,0); // protocol descriminator
LOG(INFO) << "Received L3 message of with protocol discriminator " << pd;
switch ((GSM::L3PD) pd) {
case GSM::L3GPRSMobilityManagementPD: // Couldnt we shorten this?
case GSM::L3GPRSSessionManagementPD: // Couldnt we shorten this?
//LOG(INFO) << "Sending L3 message of descr " << pd << "up to SGSN";
sgsnHandleL3Msg(uep->mURNTI,msgframe);
//LOG(INFO) << "Sent to SGSN";
break;
// TODO: Send GSM messages somewhere
case GSM::L3CallControlPD:
case GSM::L3MobilityManagementPD:
case GSM::L3RadioResourcePD:
// In GSM these go on the logical channel, which is polled by DCCHDispatcher,
// then calls DCCHDispatchMessage, which then calls some sub-processor
// which may generate message traffic on a LogicalChannel class.
// The best way to interface to the existing code is probably to put
// these on the LogicalChannel and let the DCCH service loop find them.
// It wants to find an ESTABLISH primitive as the first thing,
// and then it times out if nothing happens soon?
// Or we could try calling direct: DCCHDispatchMessage(??,??);
// FIXME: Ignore these until L3 GSM code is integrated
LOG(ERR) << "L3 GSM control message ignored";
//uep->mGsmL3->l3writeHighSide(msgframe);
return;
case GSM::L3SMSPD:
// In GSM these apparently arrive on the DTCHLogicalChannel?
// Not sure how SMS works. Looks like an MM message CMServiceRequest
// arrives to DCCHDispatchMM() which then calls CMServiceResponder()
// which calls MOSMSController() which seems to do a bunch of message
// traffic on a DCCHLogicalChannel, and SMS messages go there.
LOG(ERR) <<"L3 SMS Message ignored";
return;
default:
LOG(ERR)<< "unsupported L3 Message PD:"<<pd;
}
}
void UEInfo::ueRecvStatusMsg(ASN::UL_DCCH_Message *msg1)
{
ASN::RRCStatus_t *statusmsg = &msg1->message.choice.rrcStatus;
if (statusmsg->protocolErrorInformation.diagnosticsType.present !=
ASN::ProtocolErrorMoreInformation__diagnosticsType_PR_type1) {
LOG(WARNING) <<"Received RRCStatus that is not type1 " <<this;
return; // Cant do anything more; type1 is all we handle.
}
// ASN gets a little verbose here...
ASN::ProtocolErrorMoreInformation::
ProtocolErrorMoreInformation__diagnosticsType::
ProtocolErrorMoreInformation__diagnosticsType_u::
ProtocolErrorMoreInformation__diagnosticsType__type1 *info =
&statusmsg->protocolErrorInformation.diagnosticsType.choice.type1;
char const *errtype;
#define STATUS_INFO_TYPE(what) \
ASN::ProtocolErrorMoreInformation__diagnosticsType__type1_PR_##what: \
errtype = #what;
// This is a overkill since rmsg is in the same location in the union every time.
ASN::IdentificationOfReceivedMessage_t *rmsg = 0;
switch (info->present) {
case STATUS_INFO_TYPE(asn1_ViolationOrEncodingError)
break;
case STATUS_INFO_TYPE(messageTypeNonexistent)
break;
case STATUS_INFO_TYPE(messageNotCompatibleWithReceiverState)
rmsg = &info->choice.messageNotCompatibleWithReceiverState;
break;
case STATUS_INFO_TYPE(ie_ValueNotComprehended)
rmsg = &info->choice.ie_ValueNotComprehended;
break;
case STATUS_INFO_TYPE(conditionalInformationElementError)
rmsg = &info->choice.conditionalInformationElementError;
break;
case STATUS_INFO_TYPE(messageExtensionNotComprehended)
rmsg = &info->choice.messageExtensionNotComprehended;
break;
default: errtype = "unrecognized";
}
// Finally, lets print something.
// I'm not going to bother decoding all this info into strings,
// so the message wont make sense unless you have access to ASN and our code.
char rbuf[200]; rbuf[0] = 0;
if (rmsg) {
long rmsgtype;
asn_INTEGER2long(&rmsg->receivedMessageType,&rmsgtype);
unsigned transId = rmsg->rrc_TransactionIdentifier;
UeTransaction *tr = this->getTransactionRaw(transId,"RrcStatus");
sprintf(rbuf," ReceivedMessageType=%ld transId=%d %s",
rmsgtype,transId,tr?tr->str().c_str():"");
}
LOG(WARNING) <<"Received RRCStatus errnum="<<(int)info->present
<<" errtype="<<errtype <<this <<rbuf;
}
// This is called by the high side of the RLC engine for SRB1,2,3,4.
void UEInfo::ueRecvDcchMessage(ByteVector &bv,unsigned rbNum)
{
ueRegisterActivity(); // Not sure if all these messages count as activity.
UEInfo *uep = this;
//bool verbose = gConfig.getNum("UMTS.Debug.Messages"); // redundant with asnLogMsg.
//if (verbose) { fprintf(stdout,"Received DCCH message for %s rb=%d\n",uep->ueid().c_str(),rbNum); }
LOG(INFO) << "DCCH ByteVector: " << bv;
ASN::UL_DCCH_Message *msg1 = (ASN::UL_DCCH_Message*)uperDecodeFromByteV(&ASN::asn_DEF_UL_DCCH_Message, bv);
if (!msg1) return;
//if (verbose) { asn_fprint(stdout,&ASN::asn_DEF_UL_DCCH_Message, msg1); fflush(stdout); }
asnLogMsg(rbNum, &ASN::asn_DEF_UL_DCCH_Message, msg1, asnUlDcchMsg2Name(msg1->message.present),uep);
//const char *inform; // informative string for error messages.
unsigned transId; // Transaction Id.
// The DCCH messages should arrive on rbnum 3, but who cares.
switch (msg1->message.present) {
case ASN::UL_DCCH_MessageType_PR_rrcConnectionSetupComplete: {
ASN::RRCConnectionSetupComplete *rcscmsg =
&msg1->message.choice.rrcConnectionSetupComplete;
if (rcscmsg->ue_RadioAccessCapability) {
uep->radioCapability = RN_CALLOC(ASN::UE_RadioAccessCapability);
memcpy(uep->radioCapability,rcscmsg->ue_RadioAccessCapability,sizeof(struct ASN::UE_RadioAccessCapability));
}
// This message should arrive on SRB1 to indicate initial RRC connection is set up.
// If you get this far, you have succeeded in establishing an RRC connection.
LOG(NOTICE) << "Received RRC Connection Setup Complete! Congratulations!";
// The fact of the message arriving is the major piece of information.
// Message also contains START info and UE Radio Access Capability Info.
// The UE has already switched to CELL_FACH state in order to send this
// message on DCCH.
// Now inform ourselves; we already connected DCCH appropriately
// before sending the ConnectionSetup msg.
// This transaction was handled specially in ueWriteLowSide because
// we had to process it before it can go through the RLC.
//transId = rcscmsg->rrc_TransactionIdentifier;
//uep->ueRecvRrcConnectionSetupResponse(transId,true,"RrcConnectionSetupComplete");
break;
}
case ASN::UL_DCCH_MessageType_PR_rrcConnectionReleaseComplete: {
ASN::RRCConnectionReleaseComplete *rcrcmsg =
&msg1->message.choice.rrcConnectionReleaseComplete;
transId = rcrcmsg->rrc_TransactionIdentifier;
uep->ueRecvRrcConnectionReleaseResponse(transId,true,"RrcConnectionReleaseComplete");
break;
}
case ASN::UL_DCCH_MessageType_PR_radioBearerSetupComplete: {
// Currently we only send RadioBearerSetup for PS connections, so it must be for an SGSN PDP context.
ASN::RadioBearerSetupComplete *rbscmsg =
&msg1->message.choice.radioBearerSetupComplete;
// I am ignoring the transaction identifier.
// I am ignoring the "START" values.
///*if (rbscmsg->start_Value) {
// uint32_t startVal = AsnBitString2BV(rbscmsg->start_Value).getField(0,20);
// uep->integrity.updateStartValue(startVal);
//}*/
// We need the rbid of the PDP context, which in the RRCP spec is in
// "RB with PDPCP Information", but this does not appear in our ASN description,
// unless it is buried inside the extensions which are a bitstring,
// and no decoder provided.
// So I guess the presense of this message and the transactionId
// is the only indication we get.
transId = rbscmsg->rrc_TransactionIdentifier;
uep->ueRecvRadioBearerSetupResponse(transId,true,"RadioBearerSetupComplete");
break;
}
case ASN::UL_DCCH_MessageType_PR_radioBearerSetupFailure: {
ASN::RadioBearerSetupFailure *rbsfmsg =
&msg1->message.choice.radioBearerSetupFailure;
transId = rbsfmsg->rrc_TransactionIdentifier;
// Print out the failure cause; Just print the ASN enum value directly.
// It is FailureCauseWithProtErr_t
unsigned failcode = rbsfmsg->failureCause.present;
string msg = getRBFailureCause(&rbsfmsg->failureCause);
LOG(ERR) << "Radio Bearer Setup Failure code="<<failcode <<" ("<<msg<<")"
<<" rb="<<rbNum
<<" ue:"<<uep;
uep->ueRecvRadioBearerSetupResponse(transId,false,"RadioBearerSetupFailure");
break;
}
// TODO: Not sure we need these at all:
case ASN::UL_DCCH_MessageType_PR_radioBearerReleaseComplete: {
ASN::RadioBearerReleaseComplete *rbrcmsg =
&msg1->message.choice.radioBearerReleaseComplete;
transId = rbrcmsg->rrc_TransactionIdentifier;
uep->ueRecvRadioBearerReleaseResponse(transId,true,"RadioBearerReleaseComplete");
break;
}
case ASN::UL_DCCH_MessageType_PR_radioBearerReleaseFailure: {
ASN::RadioBearerReleaseFailure *rbrfmsg =
&msg1->message.choice.radioBearerReleaseFailure;
transId = rbrfmsg->rrc_TransactionIdentifier;
// Print out the failure cause; Just print the ASN enum value directly.
// It is FailureCauseWIthProtErr_t
unsigned failcode = rbrfmsg->failureCause.present;
LOG(ERR) << "Radio Bearer Setup Failure code="<<failcode
<<" rb="<<rbNum
<<" ue:"<<uep;
uep->ueRecvRadioBearerReleaseResponse(transId,false,"RadioBearerReleaseFailure");
break;
}
case ASN::UL_DCCH_MessageType_PR_uplinkDirectTransfer: {
// This message should arrive on SRB3
// This is a subsequent uplink message to L3.
//inform = "Uplink Direct Transfer Message";
ASN::UplinkDirectTransfer_t *udtmsg = &msg1->message.choice.uplinkDirectTransfer;
// Do we care about this ps-cs domain flag at this point?
// We can direct the message based on the message protocol descriptor.
// bool psDomain = fromAsnCnDomainIdentity(&udtmsg.cn_DomainIdentity);
ByteVector l3UdtMsgBytes(udtmsg->nas_Message.buf,udtmsg->nas_Message.size);
uep->ueRecvL3Msg(l3UdtMsgBytes,uep);
break;
}
case ASN::UL_DCCH_MessageType_PR_initialDirectTransfer: {
//inform = "Initial Direct Transfer Message";
// This message should arrive on SRB3
// This is the first uplink message to L3.
// On the UE side, it causes SRB1-SRB3 (and optionally SRB4) to be set up
// before it complete. On the network side, it is like the uplink direct transfer
// but includes setup information for routing on the CN [Core Network] side.
// This msg contains IntraDomainNasNodeSelector which has another UE-id in it,
// but it doesnt include anything really interesting, for example, if it is an IMSI,
// they only send a few bits of it, not the whole thing,
// we already know what UE we are talking to, so dont bother to even look at it.
ASN::InitialDirectTransfer_t *idtmsg = &msg1->message.choice.initialDirectTransfer;
// Get CS or PS?
// bool psDomain = fromAsnCnDomainIdentity(&idtmsg.cn_DomainIdentity);
// We can direct the message based on the message protocol descriptor.
// What we want is the NAS message, which is an ASN::OCTET_STRING;
// convert it to an allocated ByteVector; allocate it just for safety's sake,
// but TODO: Use a ByteVectorTemp because we shouldnt need to allocate it,
// we can just use it right out of the OCTET_STRING struct.
ByteVector l3IdtMsgBytes(idtmsg->nas_Message.buf,idtmsg->nas_Message.size);
uep->ueRecvL3Msg(l3IdtMsgBytes,uep);
break;
}
case ASN::UL_DCCH_MessageType_PR_rrcStatus: {
// This sends an error indication about some received message.
uep->ueRecvStatusMsg(msg1);
break;
}
case ASN::UL_DCCH_MessageType_PR_signallingConnectionReleaseIndication: {
// The UE sends this to tell us it wants to drop the connection.
// FIXME: Or does it send this to tell us it already dropped the connection? In which case
// we dont need to send the RrcConnectionRelease message.
// Harvind (3-17-13). This message is the industry standard for terminating a DCH connection to save battery life
// SCRI->RRC Conn. Release
// When UE wants to reconnect... RRC Conn. Setup->Service Request procedure
UeTransaction *lastTrans = getLastTransaction();
if (lastTrans->mTransactionType != ttComplete && lastTrans->elapsed()<1000) {
// There is some current transaction happening.
// Lets wait a while to see what happens.
return;
}
switch (uep->ueGetState()) {
case stIdleMode:
// This is not possible.
LOG(ERR) << "Received SignallingConnectionReleaseIndication message"
<<" for ue inIdleMode"<<uep;
break;
case stCELL_DCH:
// The SGSN will call back to rrc to send a RadioBearerRelease message,
// which should cause the UE to send RadioBearerReleaseComplete/Failure,
// which will kick the UE down to CELL_FACH state.
// Harvind (3-17-13) See above. UE barfs when we send RadioBearerRelease.
uep->ueSetState(stCELL_DCH);
if (lastTrans->mTransactionType == ttRadioBearerRelease &&
lastTrans->elapsed()<1000) {
// Hmm. Its not working. Not sure what to do. Try harder.
sendRrcConnectionRelease(uep);
} else {
// Harvind (3-17-13) See above. Keep PDP context intact for smoother operation.
//printf("Freeing all PDP contexts.");
//uep->sgsnFreePdpAll(mURNTI);
sendRrcConnectionRelease(uep);
}
break;
default:
sendRrcConnectionRelease(uep);
break;
}
break;
}
case ASN::UL_DCCH_MessageType_PR_securityModeComplete: {
ASN::SecurityModeComplete_t *secmsg = &msg1->message.choice.securityModeComplete;
transId = secmsg->rrc_TransactionIdentifier;
handleSecurityModeComplete(uep,secmsg);
UeTransaction *tr = getTransaction(transId,ttSecurityModeCommand,"SecurityModeComplete");
// The security mode was started when we sent the command, not when we receive the response,
// so there is nothing special to do here. If we changed the Kc by re-running the Layer3
// Authentication procedure while a connection was running, then we would have to apply the
// new keys at this time, but we dont ever do that.
if (tr) tr->transClose(); // Done with this one.
break;
}
case ASN::UL_DCCH_MessageType_PR_securityModeFailure: {
ASN::SecurityModeFailure_t *secfailmsg = &msg1->message.choice.securityModeFailure;
transId = secfailmsg->rrc_TransactionIdentifier;
handleSecurityModeFailure(uep,secfailmsg);
UeTransaction *tr = getTransaction(transId,ttSecurityModeCommand,"SecurityModeFailure");
if (tr) tr->transClose(); // Done with this one.
break;
}
default:
LOG(ERR) << "DCCH message ignored, unhandled type="<<msg1->message.present
<<" "<<asnUlDcchMsg2Name(msg1->message.present);
}
}
bool AsnUeId::eql(AsnUeId &other)
{
// All fields are inited, so just compare everything.
if (idType != other.idType) return false;
if (mImsi != other.mImsi || mImei != other.mImei || mTmsiDS41 != other.mTmsiDS41) return false;
if (mMcc != other.mMcc || mMnc != other.mMnc) return false;
if (mTmsi != other.mTmsi || mPtmsi != other.mPtmsi || mEsn != other.mEsn) return false;
if (mLac != other.mLac || mRac != other.mRac) return false;
return true;
}
void AsnUeId::asnParse(ASN::InitialUE_Identity &uid)
{
switch (uid.present) {
case ASN::InitialUE_Identity_PR_imsi:
// A_SEQUENCE_OF(Digit_t) list
mImsi = AsnSeqOfDigit2BV(&uid.choice.imsi.list);
break;
case ASN::InitialUE_Identity_PR_tmsi_and_LAI:
// TMSI_GSM_MAP_t (aka BIT_STRING_t) tmsi
// LAI_t lai;
// PLMN_Identity_t plmn_Identity
// MCC_t (A_SEQUENCE_OF(Digit_t) mcc;
// MNC_t (A_SEQUENCE_OF(Digit_t) mnc;
// BIT_STRING_t lac;
mTmsi = AsnBitString2BVTemp(uid.choice.tmsi_and_LAI.tmsi).getField(0,32);
mLac = AsnBitString2BVTemp(uid.choice.tmsi_and_LAI.lai.lac).getField(0,16);
{ ByteVector bmcc = AsnSeqOfDigit2BV(&uid.choice.tmsi_and_LAI.lai.plmn_Identity.mcc);
mMcc = bmcc.getField(0,bmcc.sizeBits());
}
{ ByteVector bmnc = AsnSeqOfDigit2BV(&uid.choice.tmsi_and_LAI.lai.plmn_Identity.mnc);
mMnc = bmnc.getField(0,bmnc.sizeBits());
}
//mMcc = AsnSeqOfDigit2BV(&uid.choice.tmsi_and_LAI.lai.plmn_Identity.mcc);
//mMnc = AsnSeqOfDigit2BV(&uid.choice.tmsi_and_LAI.lai.plmn_Identity.mnc);
break;
case ASN::InitialUE_Identity_PR_p_TMSI_and_RAI:
// P_TMSI_GSM_MAP_t (aka BIT_STRING_t) p_TMSI;
// RAI_t rai;
// LAI_t lai;
// PLMN_Identity_t (see above) plmn_Identity;
// BIT_STRING_t lac;
// RoutingAreaCode_t (aka BIT_STRING_t) rac;
mPtmsi = AsnBitString2BVTemp(uid.choice.p_TMSI_and_RAI.p_TMSI).getField(0,32);
mLac = AsnBitString2BVTemp(uid.choice.p_TMSI_and_RAI.rai.lai.lac).getField(0,16);
mRac = AsnBitString2BVTemp(uid.choice.p_TMSI_and_RAI.rai.rac).getField(0,8);
{ ByteVector bmcc = AsnSeqOfDigit2BV(&uid.choice.p_TMSI_and_RAI.rai.lai.plmn_Identity.mcc);
mMcc = bmcc.getField(0,bmcc.sizeBits());
}
{ ByteVector bmnc = AsnSeqOfDigit2BV(&uid.choice.p_TMSI_and_RAI.rai.lai.plmn_Identity.mnc);
mMnc = bmnc.getField(0,bmnc.sizeBits());
}
//mMcc = AsnSeqOfDigit2BV(&uid.choice.p_TMSI_and_RAI.rai.lai.plmn_Identity.mcc);
//mMnc = AsnSeqOfDigit2BV(&uid.choice.p_TMSI_and_RAI.rai.lai.plmn_Identity.mnc);
break;
case ASN::InitialUE_Identity_PR_imei:
// A_SEQUENCE_OF(Digit_t) list
mImei =AsnSeqOfDigit2BV(&uid.choice.imei.list);
break;
case ASN::InitialUE_Identity_PR_esn_DS_41:
// BIT_STRING_t // 32 bits
mEsn = AsnBitString2BVTemp(uid.choice.esn_DS_41).getField(0,32);
break;
case ASN::InitialUE_Identity_PR_imsi_DS_41:
// OCTET_STRING_t
mImsi = AsnOctetString2BV(uid.choice.imsi_DS_41);
break;
case ASN::InitialUE_Identity_PR_imsi_and_ESN_DS_41:
// IMSI_DS_41_t (aka OCTET_STRING_t) imsi_DS_41; // 5-7 bytes
// ESN_DS_41_t (aka BIT_STRING_t) esn_DS_41; // 32 bits.
mImsi = AsnOctetString2BV(uid.choice.imsi_and_ESN_DS_41.imsi_DS_41);
mEsn = AsnBitString2BVTemp(uid.choice.imsi_and_ESN_DS_41.esn_DS_41).getField(0,32);
break;
case ASN::InitialUE_Identity_PR_tmsi_DS_41:
// OCTET_STRING_t
mTmsiDS41 = AsnOctetString2BV(uid.choice.tmsi_DS_41);
break;
default:
LOG(ERR) << "Unexpected UE id type:"<<(int)uid.present;
break;
}
}
#if 0 // Works, but unused.
// Get the MCC as a ByteVector. Assumes str is correct.
static ByteVector stringOfDigitsToByteVector(const string &str)
{
ByteVector result(str.length());
for (unsigned i=0; i < str.length(); i++) {
result.setByte(i,(int8_t) str[i] - '0');
}
return result;
}
#endif
// Does the routing/location area in this asn id match our BTS?
bool AsnUeId::RaiMatches()
{
// MCC and MNC from the PLMN_identity must exist and match.
//if (mMcc.size() == 0 || mMnc.size() == 0) { return false; }
const string mccstr = gConfig.getStr("UMTS.Identity.MCC");
const string mncstr = gConfig.getStr("UMTS.Identity.MNC");
unsigned mcc = atoi(mccstr.c_str());
unsigned mnc = atoi(mncstr.c_str());
//ByteVector mcc = stringOfDigitsToByteVector(mccstr);
//ByteVector mnc = stringOfDigitsToByteVector(mncstr);
if (mMcc != mcc && mMnc != mnc) {
LOG(INFO) << "RAI of UE"<<LOGVAR(mcc)<<LOGVAR(mnc) <<" does not match"<<LOGVAR(mMcc)<<LOGVAR(mMnc);
return false;
}
// LAC and RAC must match.
unsigned lac = gConfig.getNum("UMTS.Identity.LAC");
unsigned rac = gConfig.getNum("GPRS.RAC");
if (lac != mLac || rac != mRac) {
LOG(INFO) << "RAI of UE" <<LOGVAR(lac)<<LOGVAR(rac)<<" does not match"<<LOGVAR(mLac)<<LOGVAR(mRac);
return false;
}
LOG(INFO) << "RAI of UE matches";
return true;
}
}; // namespace UMTS