/* * Copyright 2011, 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. */ #ifndef _GPRSL3MESSAGES_H_ #define _GPRSL3MESSAGES_H_ #include //#include "GPRSInternal.h" #include "Configuration.h" #include "GSMCommon.h" #include "GSML3Message.h" #include "ByteVector.h" #include "Utils.h" #include "ScalarTypes.h" #include "SgsnBase.h" //#include "GPRSL3New.h" using namespace std; #define DEHEXIFY(c) ((c)>=10 ? (c)-10+'a' : (c)+'0') namespace SGSN { struct LlcEntity; // 10.5.5.18 Update Type uses all four values. // 10.5.5.17 Update Result uses the first two values. // RA is for GPRS, and LA is for GSM CS [circuit switched]. enum RAUpdateType { RAUpdated = 0, CombinedRALAUpdated = 1, CombinedRALAWithImsiAttach = 2, PeriodicUpdating = 3 }; #if 0 // An L3Message as known by the GPRS code. // The L3Message is not really applicable here, because we do not // really know enough about this message to set the bodylength etc. yet. //struct GPRSL3Message : ByteVector { // unsigned mSkip; // unsignd mPD; // unsigned mMITI; // // // Parse out the common L3Message header // GPRSL3Message(ByteVector &vec) : ByteVector(vec.begin(),vec,size()) // { // mSkip = getByte(0) >> 4; // mPD = getByte(0) & 0xf; // mMITI = getByte(1); // } //}; #endif // The L3Message is built on L3Frame which assumes that messages are in BitVectors, // but for GPRS messages come from RLC (and LLC and LLE) and they are ByteVectors. class L3GprsMsg : public Text2Str { public: virtual void text(std::ostream &os) const = 0; virtual int MTI() const =0; virtual const char *mtname() const =0; }; // All Gmm and Sm messages need a sense that is either reply or command. // For SM messages, the sense is encoded in the transaction-identifier field. // For both types, the sense is also encoded in one of the LLC headers. // Cant be be too too redundant redundant. class L3GprsDlMsg : public virtual L3GprsMsg { public: enum MsgSense { senseInvalid, senseReply, senseCmd } mSense; L3GprsDlMsg(MsgSense wSense) : mSense(wSense) {} // This dummy constructor is used for uplink messages, when message can be both up and downlink: L3GprsDlMsg() : mSense(senseInvalid) {} // void setSense(MsgSense wSense) { mSense = wSense; } //unused. bool isSenseCmd() { assert(mSense != senseInvalid); return mSense == senseCmd; } virtual void gWrite(ByteVector &msg) = 0; }; // An L3Frame for Gprs. class L3GprsFrame : public ByteVector { public: void dump(std::ostream &os); L3GprsFrame(ByteVector &vec) : ByteVector(vec) {} GSM::L3PD getPD() { return (GSM::L3PD)getNibble(0,0); } // protocol descriminator //unsigned getSkip() { return getNibble(0,1); } // skip indicator virtual unsigned getMsgType() { assert(0); } // TODO: Handle extended transaction id 24.007 virtual unsigned getBodyOffset() { assert(0); } unsigned mIEI; // cache for message parsers. unsigned readIEI(size_t &rp) { mIEI = readByte(rp); return mIEI; } ByteVector readLVasBV(size_t &rp) { int len = readByte(rp); ByteVector result; result.clone(segment(rp,len)); rp += len; return result; } // Skip over an IE we dont care about: void skipLV(size_t &rp) { int len = readByte(rp); rp += len; } // The minlen and maxlen are of the IE itself, excluding to Type (if any) and Length byte. void skipLV(size_t &rp, int minlen, int maxlen, const char *text) { int len = readByte(rp); if (len < minlen || len > maxlen) { LLCWARN("unexpected message length for iei:"< 0 (2-sec interval) or 1 (minutes) or 2 (deci-hours) or 7 deactivated unsigned mValue; // 5 bit range. GprsTimerIE() : mUnits(7), mValue(0) {} // Deactivate. void setSeconds(unsigned numSeconds) { // 6-2013: 0 value now disables. if (numSeconds == 0) { mUnits = 7; mValue = 0; return; } if (numSeconds > 62) { setMinutes((numSeconds+59)/60); return; } mUnits = 0; // 2-second increments. mValue = numSeconds/2; // } void setMinutes(unsigned numMinutes) { if (numMinutes >= 200) { // Deactivate mUnits = 7; mValue = 0; } else if (numMinutes > 186) { // Use maximum value. mUnits = 2; mValue = 31; } else if (numMinutes > 31) { mUnits = 2; // deci-hours mValue = (numMinutes+5)/6; } else { mUnits = 1; // minutes mValue = numMinutes; } } unsigned getSeconds() const { switch (mUnits) { case 0: return mValue*2; case 1: return mValue*60; case 2: return mValue*600; case 7: return 0; // special value means disabled. default: return 0xffffffff; // error. } } unsigned getIEValue() const { return (mUnits << 5) | (mValue & 0x1f); } void appendElement(ByteVector &msg) { msg.appendByte(getIEValue()); } }; // 24.008 10.5.5.15 // See also GSM::L3LocationAreaIdentity struct GMMRoutingAreaIdIE : Text2Str { unsigned char mMCC[3], mMNC[3]; uint16_t mLAC; uint8_t mRAC; void parseElement(ByteVector &pp, size_t &rp); void raLoad(); void appendElement(ByteVector &msg); void text(std::ostream&os) const; GMMRoutingAreaIdIE(); // constructor zeros mMCC and mMNC bool valid() { return mMCC[0] || mMCC[1] || mMCC[2]; } // Has IE been set? }; // 24.008 10.5.1.4 Mobile Identity // We dont really care what the MS/UE tells us except for TMSI, // so treat TMSI specially, and otherwise just save the whole thing. struct GmmMobileIdentityIE : Text2Str { Field_z<3> mTypeOfId; // From the first byte. // Either mTmsi or mIdData+mLen is valid. uint32_t mTmsi; // tmsi or ptmsi unsigned char mIdData[8]; // Original data from the IEI. unsigned mLen; // Length of mIdData = length of IE. bool mPresent; GmmMobileIdentityIE() : mPresent(false) {} bool isImsi() const { return mTypeOfId == 1; } bool isTmsi() const { return mTypeOfId == 4; } // TMSI or P-TMSI // Parse out an IMSI from the data, and return it in the ByteVector, which must have room to hold it, // and return true if it was found, false otherwise. ByteVector getImsi() const; uint32_t getTmsi() const { assert(isTmsi()); return mTmsi; } void decodeIM(ByteVector &result) const; void parseLV(ByteVector &pp, size_t &rp); const string getAsBcd() const; // its not bcd but readable void text(std::ostream&os) const; // write the length and value, but not the IEI type. void appendLV(ByteVector &msg); void setTmsi(uint32_t tmsi) { mPresent = true; mTypeOfId = 4; mTmsi = tmsi; // TMSI or P-TMSI } }; // We keep only the ByteVector of the QoS IE itself, // but the description in 24.008 of the IE numbers octets starting at 3, // and bits 8..1, so we will do the same: #define DEFINE_QOS_FIELD(name,octet,bitr,length) \ unsigned get##name() { return getField2(octet-3,8-bitr,length); } \ void set##name(unsigned val) { setField2(octet-3,8-bitr,val,length); } // 3GPP 24.008 10.5.6.5 Quality of Service // We leave it as a ByteVector, read the values out of it. struct SmQoS : public ByteVector { // We dont fill in every bit, and the unused ones are defined as zero, // so just zero the whole thing before starting. SmQoS(ByteVector &bv) : ByteVector(bv) {} SmQoS(unsigned size) : ByteVector(size) {fill(0);} // Octet 3 (meaning first byte in the QoS ByteVector.) DEFINE_QOS_FIELD(DelayClass,3,6,3) DEFINE_QOS_FIELD(ReliabilityClass,3,3,3) // Octet 4: DEFINE_QOS_FIELD(PeakThroughputCode,4,8,4) DEFINE_QOS_FIELD(PrecedenceClass,4,3,3) // Octet 5: DEFINE_QOS_FIELD(MeanThroughputCode,5,5,5) // Octet 6: DEFINE_QOS_FIELD(TrafficClass,6,8,3) DEFINE_QOS_FIELD(DeliveryOrder,6,5,2) DEFINE_QOS_FIELD(DeliveryOfErrSdu,6,3,3) // Octet 7: DEFINE_QOS_FIELD(MaxSduSize,7,8,8) // Octet 8,9: (actual 0-based bytes number 5 and 6) DEFINE_QOS_FIELD(MaxBitRateUplinkCode,8,8,8) DEFINE_QOS_FIELD(MaxBitRateDownlinkCode,9,8,8) // Octet 10: DEFINE_QOS_FIELD(ResidualBER,10,8,4) DEFINE_QOS_FIELD(SduErrorRatio,10,4,4) // Octet 11: DEFINE_QOS_FIELD(TransferDelay,11,8,6) DEFINE_QOS_FIELD(TrafficHandlingPriority,11,2,2) // Octet 12,13: // "The Guaranteed Bit Rate is ignored if the Traffic Class is // Interactive or Background class or the max bit rate for downlink is 0 kBps // 0xff implies 0kBps, ie, unspecified. DEFINE_QOS_FIELD(GuaranteedBitRateUplinkCode,12,8,8) DEFINE_QOS_FIELD(GuaranteedBitRateDownlinkCode,13,8,8) // Octet 14: DEFINE_QOS_FIELD(SignalingIndication,14,5,1) DEFINE_QOS_FIELD(SourceStatisticsDescriptor,14,4,4) // Optional Octets 15-18 are for extented Max and Guaranteed bit-rates. // Encoding methods using raw methods above: // Maximum and Guaranteed bit rate for uplink and downlink have // normal and extended versions, where the extended versions // add yet another extra octet at the end of the QoS IE. // Return -1 if not specified in the IE, because it was too short, // in which case the caller must fall back on PeakThroughput. void setMaxBitRate(unsigned val, bool uplink); int getMaxBitRate(bool uplink); unsigned getPeakThroughput(); void setPeakThroughput(unsigned bytePSec); // We probably dont ever need mean throughput, because you can set it to 'best effort', // implying that only peak throughput is significant. unsigned getMeanThroughput(); void setMeanThroughput(unsigned bytePHour); void defaultPS(unsigned rateDownlink, unsigned rateUplink); }; // 24.008 10.5.5.12a MS Radio Access Capability // 5-15-2015, David says Range supports: // 850: GSM 850, 900: GSM E, 1800: GSM 1800, 1900: GSM 1900 enum AccessTechnologyType { GSM_P = 0, GSM_E = 1, GSM_R = 2, GSM_1800 = 3, GSM_1900 = 4, GSM_450 = 5, GSM_480 = 6, GSM_850 = 7, GSM_750 = 8, GSM_T380 = 9, GSM_T410 = 10, GSM_UNUSED = 11, GSM_710 = 12, GSM_T810 = 13 }; const char *AccessTechnologyType2Name(AccessTechnologyType type); // 24.008 10.5.5.12a MS Radio Access Capability // 45.002 appendix B explains GPRS Multislot Class. // There is so much junk in here I am taking a new tack. // Keep the capabilities in a single array indexed by these names. // A value of -1 indicates not present. struct AccessCapabilities { Bool_z mValid; AccessTechnologyType mTechType; Bool_z mSameAsPrevious; enum CapType { //AccessTechnologyType, RFPowerCapability, A5Bits, ESInd, PS, VGCS, VBS, // multislot capabilities: // Warning: parsing code assumes multislot caps are in the order below. HSCSDMultislotClass, GPRSMultislotClass, GPRSExtendedDynamicAllocationCapability, SMS_VALUE, SM_VALUE, ECSDMultislotClass, // multislot additions in release 99 EGPRSMultislotClass, EGPRSExtendedDynamicAllocationCapability, DTMGPRSMultiSlotClass, SingleSlotDTM, DTMEGPRSMultiSlotClass, // Additions in release 99: EightPSKPowerCapability, COMPACTInterferenceMeasurementCapability, RevisionLevelIndicator, UMTSFDDRadioAccessTechnologyCapability, UMTS384McpsTDDRadioAccessTechnologyCapability, CDMA2000RadioAccessTechnologyCapability, // Additions in release 4: UMTS128McpsTDDRadioAccessTechnologyCapability, GERANFeaturePackage1, // Finally, something we care about. ExtendedDTMGPRSMultiSlotClass, ExtendedDTMEGPRSMultiSlotClass, ModulationBasedMultislotClassSupport, // Addigions in release 5: HighMultislotCapability, //GMSKPowerClass, //8PSKPowerClass, CapsMax // Here to indicate the length of the required list. }; // These are the capabilities that are actually of some interest to us, // and will be printed in the message: static CapType mPrintList[]; const char *CapName(CapType type) const; short mCaps[CapsMax]; AccessCapabilities() { for (int i = 0; i < CapsMax; i++) { mCaps[i] = -1; } } int getCap(CapType captype) { // Return cap value or -1. if (!mValid) return -1; assert(captype < CapsMax); return mCaps[captype]; } void parseAccessCapabilities(ByteVector &bv,size_t &rp,AccessCapabilities *prev,size_t end); void text2(std::ostream &os,bool verbose) const; void text(std::ostream &os) const; }; // 24.008 10.5.5.12a MS Radio Access Capability // It is a list of structures, each of which is either an AccessCapabilitiesStruct // or an AdditionalAccessTechnologiesStruct struct MsRaCapability : public ByteVector { static const int sMsRaCapMaxTypes = 4; // Keep the first four. AccessCapabilities mCList[sMsRaCapMaxTypes]; // Keep first four. void parseMsRaCapability(); void text2(std::ostream &os,bool verbose) const; void text(std::ostream &os) const; MsRaCapability(const ByteVector &bv) : ByteVector(bv) { if (bv.size()) parseMsRaCapability(); } }; struct PdpContextStatus : public Text2Str { unsigned char mStatus[2]; PdpContextStatus() { mStatus[0] = mStatus[1] = 0; } void text(std::ostream &os) const { os <<"PdpContextStatus="< mCypheringKeySequenceNumber; // Only bottom 3 bits used. // value 7 from MS means no key available, which it should be // if we have not sent an Authentication Request. Field_z<16> mDrxParameter; GmmMobileIdentityIE mMobileId; // AttachRequest: PtmsiOrImsi; RAUpdate: mPTmsi; Bool_z mTmsiStatus; // 10.5.5.4: does ms have a valid TMSI? ByteVector mMsRadioAccessCapability;// Required element in both AttachRequest and RAUpdate. GMMRoutingAreaIdIE mOldRaId; // Required element in both AttachRequest and RAUpdate. Field_z<24> mOldPtmsiSignature; // For an incoming RAUpdate or AttachRequest, this is the mRequestedReadyTimerValue. Field_z<8> mRequestedReadyTimerValue; // Unit is encoded per 10.5.7.3 ByteVector mMsNetworkCapability; // For RA update the PDP context status indicates which PDP contexts // are still active in the GGSN, because you can switch SGSNs // while still having active PDP contexts. PdpContextStatus mPdpContextStatus; GmmMobileIdentityIE mAdditionalMobileId; void gmParseIEs(L3GmmFrame &src, size_t &rp, const char *culprit); void text(std::ostream &os) const { os < mCypheringKeySequenceNumber; // Only bottom 3 bits used. GmmMobileIdentityIE mMobileId; // PTmsi; Field<4> mServiceType; // Only bottom 3 bits are used // For RA update the PDP context status indicates which PDP contexts // are still active in the GGSN, because you can switch SGSNs // while still having active PDP contexts. PdpContextStatus mPdpContextStatus; // MBMS context status -- not implemented yet // Uplink data status -- not implemented yet; int MTI() const {return ServiceRequest;} void gmmParseBody(L3GmmFrame &src, size_t &rp); void textBody(std::ostream &os) const { os < mUpdateType; // 10.5.5.18: // 0 => RA updating, 1 => Combined RA/LA updating // 2 => Combined RA/LA updating with IMSI attach // 3 => Periodic updating. bool mFollowOnRequestPending; int MTI() const {return RoutingAreaUpdateRequest;} void gmmParseBody(L3GmmFrame &src, size_t &rp); void textBody(std::ostream &os) const { //os <<"RAUpdateRequest" < mAttachResult; // 1=> GPRS only attach, 3=>combined GPRS/IMSI attach. Bool_z mForceToStandby; GprsTimerIE mPeriodicRAUpdateTimer; GprsTimerIE mReadyTimer; uint32_t mPTmsi; // Per 24.008 9.4.2: Attach Accept Description, // it sounds like the second mobile identity is included only to set // a TMSI in case of a combined attach, so we should not include // this IE unless using NMO 1 and we support the combined attach. // 6-7-2012: Previously I had been returning the IMSI in this spot, // and that worked ok, but I am removing it as incorrect. GmmMobileIdentityIE mMobileId; int MTI() const {return AttachAccept;} // Constructor prior to 6-7-2012: L3GmmMsgAttachAccept(unsigned wAttachResult, uint32_t wPTmsi, GmmMobileIdentityIE /*wMobileId*/) : L3GmmDlMsg(senseReply), mAttachResult(wAttachResult), mPTmsi(wPTmsi) //mMobileId(wMobileId) { } // New constructor, no mobile id. L3GmmMsgAttachAccept(unsigned wAttachResult, uint32_t wPTmsi): L3GmmDlMsg(senseReply), mAttachResult(wAttachResult), mPTmsi(wPTmsi) { } void gmmWriteBody(ByteVector &msg); void textBody(std::ostream &os) const { //os <<"AttachAccept "; unsigned RAUpdateIE = mPeriodicRAUpdateTimer.getIEValue(); os < mAttachType; // 10.5.5.2 int MTI() const {return AttachRequest;} void gmmParseBody(L3GmmFrame &src, size_t &rp); void textBody(std::ostream &os) const { os < mDetachType, mForceToStandby; // The GmmCause and ForceToStandby are only present in the downlink direction. uint16_t mGmmCause; Bool_z mGmmCausePresent; // The tmsi is only present in the uplink direction. GmmMobileIdentityIE mMobileId; // This is supposed to be a PTMSI only, so why encoded as a MobileIdentity IE? Bool_z mMobileIdPresent; // In the uplink direction there is also a P-TMSI signature that we dont use, so we dont parse. // In fact, the MS is not supposed to include it if we didnt sent it one in an earlier message. // This message is bidirectional. This constructor is for reading one in: L3GmmMsgDetachRequest() {} void gmmParseBody(L3GmmFrame &src, size_t &rp); // This message is bidirectional. This constructor is for making one to send downstream: L3GmmMsgDetachRequest(unsigned type, unsigned cause) : L3GmmDlMsg(senseCmd), mDetachType(type), mForceToStandby(0), mGmmCause(cause), mGmmCausePresent(cause != 0) {} void gmmWriteBody(ByteVector &msg); void textBody(std::ostream &os) const; }; // 3GPP 24.008 9.4.6 Detach Accept struct L3GmmMsgDetachAccept : L3GmmUlMsg, L3GmmDlMsg { int MTI() const {return DetachAccept;} Field_z<4> mForceToStandby; // This message is bidirectional. This constructor is for reading one in: L3GmmMsgDetachAccept() {} void gmmParseBody(L3GmmFrame &/*src*/, size_t &/*rp*/) { // Nothing at all. The presence of this message is the indication. } // This message is bidirectional. This constructor is for making one to send downstream. // Good old C++ requires us to make the constructor arguments unique so we will pass in the useless ForceToStandby. L3GmmMsgDetachAccept(unsigned wForceToStandby) : L3GmmDlMsg(senseReply), mForceToStandby(wForceToStandby) {} void gmmWriteBody(ByteVector &msg); void textBody(std::ostream &os) const; }; struct L3GmmMsgIdentityRequest : L3GmmDlMsg { Field<4> mIdentityType, mForceToStandby; int MTI() const {return IdentityRequest;} L3GmmMsgIdentityRequest() : L3GmmDlMsg(senseCmd), mIdentityType(1), // 1 means IMSI, the only kind we ever ask for. mForceToStandby(0) {} void gmmWriteBody(ByteVector &msg); void textBody(std::ostream &os) const; }; // 24.008 9.4.9 Authenticaion and ciphering request. struct L3GmmMsgAuthentication : L3GmmDlMsg { int MTI() const {return AuthenticationAndCipheringReq;} // We wont use any of the IEs: // Ciphering algorithm - always 0 // IMEISV request - request IMEI in response, nope. // Force to standyby - nope // A&C reference number - just used to match up Authentication Response to this message. ByteVector mRand; // 128 bit random number. // GPRS ciphering key sequence - nope // AUTN - if specified, it is a UMTS type challenge. nope. void gmmWriteBody(ByteVector &msg); L3GmmMsgAuthentication(ByteVector &rand) : L3GmmDlMsg(senseCmd), mRand(rand) { assert(rand.size() == 16); } void textBody(std::ostream &os) const { os <