/**@file @brief GSM Radio Resorce messages, from GSM 04.08 9.1. */ /* * Copyright 2008, 2009, 2010 Free Software Foundation, Inc. * Copyright 2011, 2012, 2014 Range Networks, Inc. * * This software is distributed under multiple licenses; * see the COPYING file in the main directory for licensing * information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #define LOG_GROUP LogGroup::GSM // Can set Log.Level.GSM for debugging #include #include #include "GSML3RRMessages.h" #include "GSMConfig.h" // For gBTS #include using namespace std; using namespace GSM; void L3Message::writeBody(L3Frame&,size_t&) const { LOG(ERR) << "not implemented for " << MTI(); assert(0); } void L3Message::parseBody(const L3Frame&, size_t&) { LOG(ERR) << "not implemented for " << MTI(); assert(0); } const char *L3RRMessage::name(MessageType mt) { switch (mt) { case L3RRMessage::SystemInformationType1: return "System Information Type 1"; case L3RRMessage::SystemInformationType2: return "System Information Type 2"; case L3RRMessage::SystemInformationType2bis: return "System Information Type 2bis"; case L3RRMessage::SystemInformationType2ter: return "System Information Type 2ter"; case L3RRMessage::SystemInformationType3: return "System Information Type 3"; case L3RRMessage::SystemInformationType4: return "System Information Type 4"; case L3RRMessage::SystemInformationType5: return "System Information Type 5"; case L3RRMessage::SystemInformationType5bis: return "System Information Type 5bis"; case L3RRMessage::SystemInformationType5ter: return "System Information Type 5ter"; case L3RRMessage::SystemInformationType6: return "System Information Type 6"; case L3RRMessage::SystemInformationType7: return "System Information Type 7"; case L3RRMessage::SystemInformationType8: return "System Information Type 8"; case L3RRMessage::SystemInformationType9: return "System Information Type 9"; case L3RRMessage::SystemInformationType13: return "System Information Type 13"; case L3RRMessage::SystemInformationType16: return "System Information Type 16"; case L3RRMessage::SystemInformationType17: return "System Information Type 17"; case L3RRMessage::PagingResponse: return "Paging Response"; case L3RRMessage::PagingRequestType1: return "Paging Request Type 1"; case L3RRMessage::MeasurementReport: return "Measurement Report"; case L3RRMessage::AssignmentComplete: return "Assignment Complete"; case L3RRMessage::ImmediateAssignment: return "Immediate Assignment"; case L3RRMessage::ImmediateAssignmentReject: return "Immediate Assignment Reject"; case L3RRMessage::AssignmentCommand: return "Assignment Command"; case L3RRMessage::AssignmentFailure: return "Assignment Failure"; case L3RRMessage::ChannelRelease: return "Channel Release"; case L3RRMessage::ChannelModeModify: return "Channel Mode Modify"; case L3RRMessage::ChannelModeModifyAcknowledge: return "Channel Mode Modify Acknowledge"; case L3RRMessage::GPRSSuspensionRequest: return "GPRS Suspension Request"; case L3RRMessage::ClassmarkEnquiry: return "Classmark Enquiry"; case L3RRMessage::ClassmarkChange: return "Classmark Change"; case L3RRMessage::RRStatus: return "RR Status"; case L3RRMessage::ApplicationInformation: return "Application Information"; case L3RRMessage::HandoverCommand: return "Handover Command"; case L3RRMessage::HandoverComplete: return "Handover Complete"; case L3RRMessage::HandoverFailure: return "Handover Failure"; case L3RRMessage::CipheringModeCommand: return "Ciphering Mode Command"; case L3RRMessage::CipheringModeComplete: return "Ciphering Mode Complete"; case L3RRMessage::PhysicalInformation: return "Physical Information"; default: return "?"; } } ostream& GSM::operator<<(ostream& os, L3RRMessage::MessageType val) { const char *result = L3RRMessage::name(val); if (result[0] == '?') { os << hex << "0x" << (int)val << dec; } else { os << result; } return os; } void L3RRMessage::text(ostream& os) const { os << "RR " << (MessageType) MTI() << " "; } L3RRMessage* GSM::L3RRFactory(L3RRMessage::MessageType MTI) { switch (MTI) { case L3RRMessage::ChannelRelease: return new L3ChannelRelease(); case L3RRMessage::AssignmentComplete: return new L3AssignmentComplete(); case L3RRMessage::AssignmentFailure: return new L3AssignmentFailure(); case L3RRMessage::RRStatus: return new L3RRStatus(); case L3RRMessage::PagingResponse: return new L3PagingResponse(); case L3RRMessage::ChannelModeModifyAcknowledge: return new L3ChannelModeModifyAcknowledge(); case L3RRMessage::ClassmarkChange: return new L3ClassmarkChange(); case L3RRMessage::ClassmarkEnquiry: return new L3ClassmarkEnquiry(); case L3RRMessage::MeasurementReport: return new L3MeasurementReport(); case L3RRMessage::ApplicationInformation: return new L3ApplicationInformation(); case L3RRMessage::HandoverComplete: return new L3HandoverComplete(); case L3RRMessage::HandoverFailure: return new L3HandoverFailure(); case L3RRMessage::CipheringModeComplete: return new L3CipheringModeComplete(); case L3RRMessage::HandoverCommand: return new L3HandoverCommand(); // Partial support just to get along with some phones. case L3RRMessage::GPRSSuspensionRequest: return new L3GPRSSuspensionRequest(); default: LOG(WARNING) << "no L3 RR factory support for " << MTI; return NULL; } } L3RRMessage* GSM::parseL3RR(const L3Frame& source) { L3RRMessage::MessageType MTI = (L3RRMessage::MessageType)source.MTI(); LOG(DEBUG) << "parseL3RR MTI="<parse(source); return retVal; } /** This is a local function to map the GSM::ChannelType enum to one of the codes from GMS 04.08 10.5.2.8. */ unsigned channelNeededCode(ChannelType wType) { switch (wType) { case AnyDCCHType: return 0; case SDCCHType: return 1; case TCHFType: return 2; case AnyTCHType: return 3; default: assert(0); } } size_t L3PagingRequestType1::l2BodyLength() const { int sz = mMobileIDs.size(); assert(sz<=2); size_t sum=1; sum += mMobileIDs[0].lengthLV(); if (sz>1) sum += mMobileIDs[1].lengthTLV(); return sum; } void L3PagingRequestType1::writeBody(L3Frame& dest, size_t &wp) const { // See GSM 04.08 9.1.22. // Page Mode Page Mode M V 1/2 10.5.2.26 // Channels Needed M V 1/2 // Mobile Identity 1 M LV 2-9 10.5.1.4 // 0x17 Mobile Identity 2 O TLV 3-10 10.5.1.4 int sz = mMobileIDs.size(); assert(sz<=2); // Remember to reverse orders of 1/2-octet fields. // Because GSM transmits LSB-first within each byte. // (pat) No, it is because the fields are written MSB first here, // and then later byte-reversed in the encoder before being sent to radio LSB first. dest.writeField(wp,channelNeededCode(mChannelsNeeded[1]),2); dest.writeField(wp,channelNeededCode(mChannelsNeeded[0]),2); // "normal paging", GSM 04.08 Table 10.5.63 dest.writeField(wp,0x0,4); // the actual mobile IDs mMobileIDs[0].writeLV(dest,wp); if (sz>1) mMobileIDs[1].writeTLV(0x17,dest,wp); } void L3PagingRequestType1::text(ostream& os) const { L3RRMessage::text(os); os << " mobileIDs=("; for (unsigned i=0; i Optional selection parameters dest.writeL(wp); // SI4 Rest Octets_O -> Optional Power offset if (GPRS::GPRSConfig::IsEnabled()) { dest.writeH(wp); // SI4 Rest Octets_O -> GPRS Indicator (present) dest.writeField(wp,mRA_COLOUR,3); dest.write0(wp); // SI13 message is sent on BCCH Norm schedule. } else { dest.writeL(wp); // SI4 Rest Octets_O -> GPRS Indicator (absent) } dest.writeL(wp); // Indicates 'Break Indicator' branch of message. dest.writeL(wp); // Break Indicator == L means no extra info sent in SI Type 7 and 8. #endif } size_t L3SIType4RestOctets::lengthBits() const { #if GPRS_PAT|GPRS_TESTSI4 return 1 + 1 + (GPRS::GPRSConfig::IsEnabled() ? 5 : 1) + 1 + 1; #else return 0; #endif } void L3SIType4RestOctets::writeText(std::ostream& os) const { #if GPRS_PAT|GPRS_TESTSI4 if (GPRS::GPRSConfig::IsEnabled()) { os << "GPRS enabled; RA_COLOUR=(" << mRA_COLOUR << ")"; } #endif } L3SystemInformationType4::L3SystemInformationType4() :L3RRMessageRO(), mHaveCBCH(false) { // Dont advertise CBCH unless and until we have a valid channel description for it. if (isCBSEnabled() && gBTS.mCBCHDescription.initialized()) { mHaveCBCH = true; mCBCHChannelDescription = gBTS.mCBCHDescription; } } size_t L3SystemInformationType4::l2BodyLength() const { size_t len = mLAI.lengthV(); len += mCellSelectionParameters.lengthV(); len += mRACHControlParameters.lengthV(); if (mHaveCBCH) len += mCBCHChannelDescription.lengthTV(); return len; } size_t L3SystemInformationType4::restOctetsLength() const { return mType4RestOctets.lengthV(); } void L3SystemInformationType4::writeBody(L3Frame& dest, size_t &wp) const { /* System Information Type 4, GSM 04.08 9.1.36 - Location Area Identification 10.5.1.3 M V 5 - Cell Selection Parameters 10.5.2.4 M V 2 - RACH Control Parameters 10.5.2.29 M V 3 */ size_t wpstart = wp; mLAI.writeV(dest,wp); mCellSelectionParameters.writeV(dest,wp); mRACHControlParameters.writeV(dest,wp); if (mHaveCBCH) { mCBCHChannelDescription.writeTV(0x64,dest,wp); } mType4RestOctets.writeV(dest,wp); while (wp & 7) { dest.writeL(wp); } // Zero to byte boundary. assert(wp-wpstart == fullBodyLength() * 8); } void L3SystemInformationType4::text(ostream& os) const { L3RRMessage::text(os); os << "LAI=(" << mLAI << ")"; os << " cellSelectionParameters=(" << mCellSelectionParameters << ")"; os << " RACHControlParameters=(" << mRACHControlParameters << ")"; if (mHaveCBCH) { os << "CBCHChannelDescription=(" << mCBCHChannelDescription << ")"; } mType4RestOctets.writeText(os); } void L3SystemInformationType5::writeBody(L3Frame& dest, size_t &wp) const { /* System Information Type 5, GSM 04.08 9.1.37 - BCCH Frequency List 10.5.2.22 M V 16 */ mBCCHFrequencyList.writeV(dest,wp); wp -= 111; int p = gConfig.getFloat("GSM.Cipher.RandomNeighbor") * (float)0xFFFFFF; for (unsigned i = 1; i <= 111; i++) { int b = ((random() & 0xFFFFFF) < p) | dest.peekField(wp, 1); dest.writeField(wp, b, 1); } } void L3SystemInformationType5::text(ostream& os) const { L3RRMessage::text(os); os << "BCCHFrequencyList=(" << mBCCHFrequencyList << ")"; } void L3SystemInformationType6::writeBody(L3Frame& dest, size_t &wp) const { /* System Information Type 6, GSM 04.08 9.1.40 - Cell Identity 10.5.1.11 M V 2 - Location Area Identification 10.5.1.3 M V 5 - Cell Options (SACCH) 10.5.2.3 M V 1 - NCC Permitted 10.5.2.27 M V 1 */ mCI.writeV(dest,wp); mLAI.writeV(dest,wp); mCellOptions.writeV(dest,wp); mNCCPermitted.writeV(dest,wp); } void L3SystemInformationType6::text(ostream& os) const { L3RRMessage::text(os); os << "CI=" << mCI; os << " LAI=(" << mLAI << ")"; os << " cellOptions=(" << mCellOptions << ")"; os << " NCCPermitted=(" << mNCCPermitted << ")"; } void L3ImmediateAssignment::writeStartTime(L3Frame& dest, size_t &wp) const { // The names T1, T2, T3 are defined in GSM 4.08 table 10.5.2.39 (same as 10.5.79) unsigned T1 = (mStartTimeFrame/1326)%32; unsigned T3 = mStartTimeFrame%51; unsigned T2 = mStartTimeFrame%26; // Recompute original startframe: // Note that T3-T2 may be negative: //int recomputed = 51 * (((int)T3-(int)T2) % 26) + T3 + 51 * 26 * T1; //unsigned startframemod = (startframe % 42432); // Note: The fields are written here Most-Significant-Bit first in each byte, // then the bytes are reversed in the encoder before being sent to the radio. // 7 6 5 4 3 2 1 0 // [ T1[4:0] ][ T3[5:3] ] Octet 1 // [ T3[2:0] ][ T2[4:0] ] Octet 2 dest.writeField(wp,T1,5); dest.writeField(wp,T3,6); // Yes T3 comes before T2. dest.writeField(wp,T2,5); } void L3ImmediateAssignment::writeBody( L3Frame &dest, size_t &wp ) const { size_t wpstart = wp; /* - Page Mode 10.5.2.26 M V 1/2 - Dedicated mode or TBF 10.5.2.25b M V 1/2 - Channel Description 10.5.2.5 C V 3 - Request Reference 10.5.2.30 M V 3 - Timing Advance 10.5.2.40 M V 1 (ignoring optional elements) */ // reverse order of 1/2-octet fields // (pat) Because we are writing MSB first here. mDedicatedModeOrTBF.writeV(dest, wp); mPageMode.writeV(dest, wp); mChannelDescription.writeV(dest, wp); // From L3ChannelDescription mRequestReference.writeV(dest, wp); mTimingAdvance.writeV(dest, wp); // No mobile allocation in non-hopping systems. // A zero-length LV. Just write L=0. (pat) LV, etc. defined in GSM04.07 sec 11.2.1.1 dest.writeField(wp,0,8); if (mStartTimePresent) { dest.writeField(wp,0x7c,8); // The type of the TLV. writeStartTime(dest,wp); } mIARestOctets.writeBits(dest,wp); assert(wp-wpstart == fullBodyLength() * 8); } void L3ImmediateAssignment::text(ostream& os) const { L3RRMessage::text(os); os << "PageMode=("<=src.size()+2*8 && 0x01 == src.peekField(rp,8)) { rp+=8; // Skip over the IEI type mServiceSupport = src.readField(rp,8); } } void L3GPRSSuspensionRequest::text(ostream& os) const { L3RRMessage::text(os); os <