/* * 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. */ #include #include "GPRSL3Messages.h" //#include "TBF.h" #include "Sgsn.h" #include "Ggsn.h" #include "Utils.h" namespace SGSN { class SgsnError; static bool sEnableMsRaCap = true; // MsRaCap had alot of bugs, so provide a way to disable it. #define CASENAME(x) case x: return #x; const char *L3GmmMsg::name(unsigned mt, bool ornull) { switch ((MessageType)mt) { CASENAME(AttachRequest) CASENAME(AttachAccept) CASENAME(AttachComplete) CASENAME(AttachReject) CASENAME(DetachRequest) CASENAME(DetachAccept) CASENAME(RoutingAreaUpdateRequest) CASENAME(RoutingAreaUpdateAccept) CASENAME(RoutingAreaUpdateComplete) CASENAME(RoutingAreaUpdateReject) CASENAME(ServiceRequest) CASENAME(ServiceAccept) CASENAME(ServiceReject) CASENAME(PTMSIReallocationCommand) CASENAME(PTMSIReallocationComplete) CASENAME(AuthenticationAndCipheringReq) CASENAME(AuthenticationAndCipheringResp) CASENAME(AuthenticationAndCipheringRej) CASENAME(AuthenticationAndCipheringFailure) CASENAME(IdentityRequest) CASENAME(IdentityResponse) CASENAME(GMMStatus) CASENAME(GMMInformation) default: return ornull ? 0 : "unrecognized L3GmmMsg type"; } } void L3GmmMsg::text(std::ostream&os) const { os <= src.size()) { SGSNERROR("invalid message size in "<>4,4); // First nibble is in the high nibble of first byte. for (unsigned i = 1; i < mLen; i++) { // The dang nibbles are backwards. What nimrods. result.appendField(mIdData[i]&0xf,4); // Last byte may only have one nibble: // Note that the even/odd indication includes the single nibble // in the first byte, so we counter-intuitively check isodd here // instead of iseven. if (i < mLen-1 || isodd) { result.appendField((mIdData[i]>>4)&0xf,4); } } } // 24.008 10.5.1.4 Mobile Identity //void GmmMobileIdentityIE::setIMSI(ByteVector &imsi) //{ // int len = imsi.size(); // if (len == 0) { // mPresent = false; // return; // } // mPresent = true; // mTypeOfId = 1; // IMSI //} ByteVector GmmMobileIdentityIE::getImsi() const { assert(isImsi()); // Caller was supposed to check this. ByteVector imsi(8); decodeIM(imsi); return imsi; } const string GmmMobileIdentityIE::getAsBcd() const { ByteVector tmp(12); // 12 is overkill. decodeIM(tmp); return tmp.hexstr(); //unsigned i; //for (i=0; i 8) { LLCWARN( "unexpected Mobile Identity length:"< 8) { LLCWARN("invalid Mobile Identity TMSI length (must be <=8):"< 8) datalen = 8; unsigned char *datap = pp.begin() + rp + 1; mTypeOfId = typeIdByte & 7; int isOdd = typeIdByte & 8; switch (mTypeOfId) { case 0: // No identity break; case 4: // TMSI or P-TMSI if (len != 4) { LLCWARN("unexpected Mobile Identity TMSI length:"<>4; memcpy(&mIdData[1],datap,datalen); mNumDigits = (len-1)*2 - (isOdd?1:0); break; case 5: // TMGI and MBMS session id. memcpy(&mIdData[0],datap,datalen); break; default: LLCWARN("unexpecited Mobile Identity type:" << mTypeOfId); break; } // Assume it is p-tmsi and get it. #endif rp += mLen; } void GmmMobileIdentityIE::text(std::ostream&os) const { if (!mPresent) { os << "not present"; return; } switch (mTypeOfId) { case 1: os << LOGVAR2("IMSI",getAsBcd()); break; case 2: os << LOGVAR2("IMEI",getAsBcd()); break; case 3: os << LOGVAR2("IMEISV",getAsBcd()); break; case 4: os << LOGHEX2("(P)TMSI",mTmsi); break; case 5: os << LOGVAR2("TMGI",ByteVectorTemp((char*)mIdData,mLen).hexstr()); break; case 6: os << LOGVAR2("type6",ByteVectorTemp((char*)mIdData,mLen).hexstr()); break; case 7: os << LOGVAR2("type7",ByteVectorTemp((char*)mIdData,mLen).hexstr()); break; } } // write the length and value, but not the IEI type. void GmmMobileIdentityIE::appendLV(ByteVector &msg) { if (mTypeOfId == 4) { msg.appendByte(5); // Length of IE for TMSI/PTMSI msg.appendByte(0xf4); msg.appendUInt32(mTmsi); } else { msg.appendByte(mLen); msg.append(mIdData,mLen); } } // 3GPP 24.008 10.5.6.5 in octets/sec static const unsigned sPeakThroughputTableSize = 10; static unsigned sPeakThroughputTable[sPeakThroughputTableSize] = { /* 0 */ 0, // 0 means 'subscribed peak throughput' /* 1 */ 1000, /* 2 */ 2000, /* 3 */ 4000, /* 4 */ 8000, /* 5 */ 16000, /* 6 */ 32000, /* 7 */ 64000, /* 8 */ 128000, /* 9 */ 256000, }; void SmQoS::setPeakThroughput(unsigned bytepSec) // In Bytes/sec { for (unsigned code = 1; code < sPeakThroughputTableSize; code++) { if (bytepSec <= sPeakThroughputTable[code]) { setPeakThroughputCode(code); } } setPeakThroughputCode(sPeakThroughputTableSize-1); } // Result is Bytes/sec unsigned SmQoS::getPeakThroughput() { unsigned code = getPeakThroughputCode(); return code <= 9 ? sPeakThroughputTable[code] : 0; } // 3GPP 24.008 10.5.6.5 in octets/hour static const unsigned sMeanThroughputTableSize = 19; static unsigned sMeanThroughputTable[sMeanThroughputTableSize] = { /* 0 */ 0, /* 1 */ 100, /* 2 */ 200, /* 3 */ 500, /* 4 */ 1*1000, /* 5 */ 2*1000, /* 6 */ 5*1000, /* 7 */ 10*1000, /* 8 */ 20*1000, /* 9 */ 50*1000, /* 10 */ 100*1000, /* 11 */ 200*1000, /* 12 */ 500*1000, /* 13 */ 1*1000000, /* 14 */ 2*1000000, /* 15 */ 5*1000000, /* 16 */ 10*1000000, /* 17 */ 20*1000000, /* 18 */ 40*1000000 }; // Result is in Bytes/hour // 0 means best effort. unsigned SmQoS::getMeanThroughput() { unsigned code = getMeanThroughputCode(); return code <= 18 ? sMeanThroughputTable[code] : 0; } // We probably will not use this, just set 'best effort' using setMeanThroughputCode() void SmQoS::setMeanThroughput(unsigned bytepHour) // KBytes/sec { for (unsigned code = 1; code < sMeanThroughputTableSize; code++) { if (bytepHour <= sMeanThroughputTable[code]) { setMeanThroughputCode(code); } } setMeanThroughputCode(sMeanThroughputTableSize-1); } // In kilobits/s. 24.008 table 10.5.165 void SmQoS::setMaxBitRate(unsigned kbitps, bool uplink) { unsigned code; if (kbitps == 0) { code = 0xff; } else if (kbitps <= 64) { code = kbitps; } else if (kbitps <= 568) { code = 0x40 + ((kbitps-64)/8); } else if (kbitps <= 8640) { code = 0x80 + ((kbitps-576)/64); } else { // There is a way to encode this using extended bytes, but we wont need it. // Just use the max value: code = 0xfe; } if (uplink) { setMaxBitRateUplinkCode(code); } else { setMaxBitRateDownlinkCode(code); } } // Return -1 if not defined. int SmQoS::getMaxBitRate(bool uplink) { // The IE is variable sized and may not include these fields. if (size() <= 6) {return -1;} unsigned code = uplink ? getMaxBitRateUplinkCode() : getMaxBitRateDownlinkCode(); if (code == 0xff) { return 0; } switch (code & 0xc0) { case 0: return code; case 0x40: return 64 + 8*(code & 0x3f); default: return 576 + 64*(code & 0x3f); } } // 3GPP 24.008 10.5.6.5 // Set defaults for PS [Packet Switched] services. // For streaming video, set udp to true. // Specify rates in KBytes/sec, using K=1000 not 1024. void SmQoS::defaultPS(unsigned rateDownlink, unsigned rateUplink) { setDelayClass(4); // best effort // Reliability Class: // 3 => unacknowledged GTP and LLC, acknowledged RLC, protected data. // 4 => unacknowledged GTP and LLC, RLC, protected data. // 5 => unacknowledged GTP and LLC, RLC, unprotected data. setReliabilityClass(3); //mPeakThroughput = 6); // 32k/s 1 is lowest peak throughput unsigned peakThroughput = 1000* max(rateUplink,rateDownlink); setPeakThroughput(peakThroughput); setPrecedenceClass(2); // normal setMeanThroughput(0x1f); // best effort // Traffic class: 3 => Interactive, 4=background, 2=streaming, 1=conversational setTrafficClass(3); setDeliveryOrder(2); // unordered setDeliveryOfErrSdu(2); // yes, or could use 1 = nodetect setMaxSduSize(0x99); // 1520 bytes setMaxBitRate(8*rateDownlink,0); setMaxBitRate(8*rateUplink,1); //setMaxBitRateForUplinkCode(0x3f); // 63kbps; anything from 1 to 0x3f is value * 1k //setMaxBitRateForDownlinkCode(0x3f); // 63kbps setResidualBER(1); // 5e-2 setSduErrorRatio(1); // 1e-2 setTransferDelay(0x10); // 200ms setTrafficHandlingPriority(3); // value 1-3. setGuaranteedBitRateUplinkCode(0xff); // 0k, ignored for traffic class interactive. setGuaranteedBitRateDownlinkCode(0xff); // 0k, ignored for traffic class interactive. setSignalingIndication(0); // not optimized // Source statistics: 0=unknown, 1 =speech, but: // "The Source Statistics Descriptor value is ignored if the Traffic Class // is Interactive class or Background Class" setSourceStatisticsDescriptor(0); } const char *AccessTechnologyType2Name(AccessTechnologyType type) { switch (type) { CASENAME(GSM_P) CASENAME(GSM_E) CASENAME(GSM_R) CASENAME(GSM_1800) CASENAME(GSM_1900) CASENAME(GSM_450) CASENAME(GSM_480) CASENAME(GSM_850) CASENAME(GSM_750) CASENAME(GSM_T380) CASENAME(GSM_T410) CASENAME(GSM_UNUSED) CASENAME(GSM_710) CASENAME(GSM_T810) default: return "unknown AccessTechnologyType"; } } const char *AccessCapabilities::CapName(CapType type) const { switch (type) { //CASENAME(eAccessTechnologyType) CASENAME(RFPowerCapability) CASENAME(A5Bits) CASENAME(ESInd) CASENAME(PS) CASENAME(VGCS) CASENAME(VBS) // multislot capabilities: CASENAME(HSCSDMultislotClass) CASENAME(GPRSMultislotClass) CASENAME(GPRSExtendedDynamicAllocationCapability) CASENAME(SMS_VALUE) CASENAME(SM_VALUE) CASENAME(ECSDMultislotClass) CASENAME(EGPRSMultislotClass) CASENAME(EGPRSExtendedDynamicAllocationCapability) CASENAME(DTMGPRSMultiSlotClass) CASENAME(SingleSlotDTM) CASENAME(DTMEGPRSMultiSlotClass) // Additions in release 99: CASENAME(EightPSKPowerCapability) CASENAME(COMPACTInterferenceMeasurementCapability) CASENAME(RevisionLevelIndicator) CASENAME(UMTSFDDRadioAccessTechnologyCapability) CASENAME(UMTS384McpsTDDRadioAccessTechnologyCapability) CASENAME(CDMA2000RadioAccessTechnologyCapability) // Additions in release 4: CASENAME(UMTS128McpsTDDRadioAccessTechnologyCapability) CASENAME(GERANFeaturePackage1) CASENAME(ExtendedDTMGPRSMultiSlotClass) CASENAME(ExtendedDTMEGPRSMultiSlotClass) CASENAME(ModulationBasedMultislotClassSupport) // Additions in release 5: CASENAME(HighMultislotCapability) //eGMSKPowerClass //EightPSKPowerClass default: return "unknown CapName"; } } void AccessCapabilities::parseAccessCapabilities( ByteVector &bv, // bytevector we are parsing size_t &rp, // location in bytevector AccessCapabilities *prev, // Previous capabilities or null. size_t end) // end of capabilities list { mCaps[RFPowerCapability] = bv.readField(rp,3); if (bv.readField(rp,1)) { mCaps[A5Bits] = bv.readField(rp,7); } else if (prev) { mCaps[A5Bits] = prev->mCaps[A5Bits]; } mCaps[ESInd] = bv.readField(rp,1); mCaps[PS] = bv.readField(rp,1); mCaps[VGCS] = bv.readField(rp,1); mCaps[VBS] = bv.readField(rp,1); if (bv.readField(rp,1)) { // multislot capability struct present if (bv.readField(rp,1)) { mCaps[HSCSDMultislotClass] = bv.readField(rp,5); } if (bv.readField(rp,1)) { mCaps[GPRSMultislotClass] = bv.readField(rp,5); mCaps[GPRSExtendedDynamicAllocationCapability] = bv.readField(rp,1); } if (bv.readField(rp,1)) { mCaps[SMS_VALUE] = bv.readField(rp,4); mCaps[SM_VALUE] = bv.readField(rp,4); } // Additions in release 99: Just scan past some of these, dont bother saving. if (rp >= end) return; if (bv.readField(rp,1)) { // ECSD multslot class bv.readField(rp,5); // Toss it. } if (rp >= end) return; if (bv.readField(rp,1)) { // EGPRS multslot class mCaps[EGPRSMultislotClass] = bv.readField(rp,5); mCaps[EGPRSExtendedDynamicAllocationCapability] = bv.readField(rp,1); } if (rp >= end) return; if (bv.readField(rp,1)) { // DTM GPRS Multi Slot Class present mCaps[DTMGPRSMultiSlotClass] = bv.readField(rp,2); mCaps[SingleSlotDTM] = bv.readField(rp,1); if (bv.readField(rp,1)) { // DTM EGPRS Multi Slot Class present mCaps[DTMEGPRSMultiSlotClass] = bv.readField(rp,2); } } } else if (prev) { // No multislot struct means same as previous. for (int i = HSCSDMultislotClass; i <= DTMEGPRSMultiSlotClass; i++) { mCaps[i] = prev->mCaps[i]; } } // Additions in release 99 if (rp >= end) return; if (bv.readField(rp,1)) { mCaps[EightPSKPowerCapability] = bv.readField(rp,2); } if (rp >= end) return; mCaps[COMPACTInterferenceMeasurementCapability] = bv.readField(rp,1); mCaps[RevisionLevelIndicator] = bv.readField(rp,1); mCaps[UMTSFDDRadioAccessTechnologyCapability] = bv.readField(rp,1); mCaps[UMTS384McpsTDDRadioAccessTechnologyCapability] = bv.readField(rp,1); mCaps[CDMA2000RadioAccessTechnologyCapability] = bv.readField(rp,1); // Additions in release 4: if (rp >= end) return; mCaps[UMTS128McpsTDDRadioAccessTechnologyCapability] = bv.readField(rp,1); if (rp >= end) return; mCaps[GERANFeaturePackage1] = bv.readField(rp,1); if (rp >= end) return; if (bv.readField(rp,1)) { mCaps[ExtendedDTMGPRSMultiSlotClass] = bv.readField(rp,2); mCaps[ExtendedDTMEGPRSMultiSlotClass] = bv.readField(rp,2); } if (rp >= end) return; mCaps[ModulationBasedMultislotClassSupport] = bv.readField(rp,1); // Additions in release 5: if (rp >= end) return; if (bv.readField(rp,1)) { mCaps[HighMultislotCapability] = bv.readField(rp,2); } // Rest ignored. } AccessCapabilities::CapType AccessCapabilities::mPrintList[] = { GPRSMultislotClass, GPRSExtendedDynamicAllocationCapability, GERANFeaturePackage1 }; void AccessCapabilities::text2(std::ostream &os,bool verbose) const { if (!sEnableMsRaCap ) {return;} if (!verbose) { // TODO: how to switch to get the full list? // Short list: for (unsigned j = 0; j < sizeof(mPrintList)/sizeof(CapType); j++) { unsigned cap = mPrintList[j]; if (mCaps[cap] != -1) { os < 0) { // Otherwise it is an error on the part of the MS. *mCList[numTechs].mCaps = *mCList[numTechs-1].mCaps; } // There are two more fields: GMSK Power Class and 8PSK Power Class // which are included in the length and so skipped over without any more code here. } else { size_t trp = rp; AccessCapabilities *prev = numTechs ? &mCList[numTechs-1] : 0; mCList[numTechs].parseAccessCapabilities(*this,trp,prev,rp+len); } // Advance rp. rp += len; if (rp + 15 >= sizeBits()) { break; } // Not enough room left for anything useful. if (readField(rp,1) == 0) { break; } // End of list marker. } catch(ByteVectorError) { // oops! break; // End of that. } } } void MsRaCapability::text2(std::ostream &os, bool verbose) const { if (!sEnableMsRaCap ) {return;} for (int numTechs = 0; numTechs < sMsRaCapMaxTypes; numTechs++) { if (! mCList[numTechs].mValid) {continue;} // Dont bother to print the types that Range does not support. AccessTechnologyType atype = mCList[numTechs].mTechType; switch (atype) { case GSM_E: case GSM_850: case GSM_1800: case GSM_1900: os << (verbose ? "\t" : " "); os <<" MsRaCapability[" << AccessTechnologyType2Name(atype) << "]=("; if (mCList[numTechs].mSameAsPrevious) { os <<"same"; } else { mCList[numTechs].text2(os,verbose); } os <<")\n"; default: continue; } } } void MsRaCapability::text(std::ostream &os) const { text2(os,0); } void L3GmmMsgServiceRequest::gmmParseBody(L3GmmFrame &src, size_t &rp) { mServiceType = src.getNibble(rp,0); mCypheringKeySequenceNumber = src.getNibble(rp,1); rp++; mMobileId.parseLV(src,rp); if (rp < src.size()) { unsigned iei = src.readIEI(rp); if (iei == 0x32) { rp++; // skip length mPdpContextStatus.mStatus[0] = src.readByte(rp); mPdpContextStatus.mStatus[1] = src.readByte(rp); } } } void L3GmmMsgServiceAccept::gmmWriteBody(ByteVector &msg) { msg.appendByte(0x32); msg.appendByte(0x02); msg.appendByte(mPdpContextStatus.mStatus[0]); msg.appendByte(mPdpContextStatus.mStatus[1]); } void L3GmmMsgServiceReject::gmmWriteBody(ByteVector &msg) { msg.appendByte(mGmmCause); } void L3GmmMsgRAUpdateRequest::gmmParseBody(L3GmmFrame &src, size_t &rp) { unsigned update_type = src.getNibble(rp,0); mUpdateType = update_type & 7; mFollowOnRequestPending = !!(update_type&8); mCypheringKeySequenceNumber = src.getNibble(rp,1); rp++; mOldRaId.parseElement(src,rp); mMsRadioAccessCapability = src.readLVasBV(rp); gmParseIEs(src,rp,"RAUpdateRequest"); } void L3GmmMsgRAUpdateAccept::gmmWriteBody(ByteVector &msg) { //msg.appendByte(RoutingAreaUpdateAccept); msg.appendField(mUpdateResult,4); // nibbles reversed. msg.appendField(mForceToStandby,4); //msg.appendByte(mPeriodicRAUpdateTimer.getIEValue()); mPeriodicRAUpdateTimer.appendElement(msg); GMMRoutingAreaIdIE mRaId; mRaId.raLoad(); mRaId.appendElement(msg); // End of mandatory IEs. // Add the allocated P-TMSI: if (mAllocatedPTmsi) { msg.appendByte(0x18); // Allocate P-TMSI IEI GmmMobileIdentityIE midtmp; midtmp.setTmsi(mAllocatedPTmsi); midtmp.appendLV(msg); } // Add the mobile identity that it sent to us: //msg.appendByte(0x23); // MS identity IEI. //mMobileId.appendLV(msg); if (mTmsi) { msg.appendByte(0x23); // MS identity IEI. GmmMobileIdentityIE idtmp; idtmp.setTmsi(mTmsi); idtmp.appendLV(msg); } // 10.5.7.1 PDP Context Status // And I quote: "This IE shall be included by the Network". Hmm. // If you set this to zeros the MS relinquishes its PDP contexts. { msg.appendByte(0x32); msg.appendByte(2); // length is 2 bytes msg.appendByte(mPdpContextStatusCurrent.mStatus[0]); msg.appendByte(mPdpContextStatusCurrent.mStatus[1]); } } void L3GmmMsgRAUpdateReject::gmmWriteBody(ByteVector &msg) { //msg.appendByte(RoutingAreaUpdateReject); msg.appendByte(mGmmCause); msg.appendByte(0); // spare half octet and force-to-standby } void L3GmmMsgAttachAccept::gmmWriteBody(ByteVector &msg) { mPeriodicRAUpdateTimer.setSeconds(gConfig.getNum("SGSN.Timer.RAUpdate")); mReadyTimer.setSeconds(gConfig.getNum("SGSN.Timer.Ready")); //msg.appendByte(AttachAccept); // message type msg.appendField(mForceToStandby,4); // high nibble first. msg.appendField(mAttachResult,4); //msg.appendByte(mPeriodicRAUpdateTimer.getIEValue()); mPeriodicRAUpdateTimer.appendElement(msg); // Next byte is SMS and TOM8 message priority. // I am hard coding them to the lowest value, which is 4. msg.appendByte(0x44); GMMRoutingAreaIdIE mRaId; mRaId.raLoad(); mRaId.appendElement(msg); // End of mandatory elements. // Add the allocated P-TMSI: if (mPTmsi) { // TLV Allocated P-TMSI, but only if we send it. GmmMobileIdentityIE idtmp; idtmp.setTmsi(mPTmsi); msg.appendByte(0x18); // Allocated P-TMSI IEI idtmp.appendLV(msg); } // 6-7-2012: Removed this. Per 24.008 9.4.2: Attach Accept Description, // I now believe the second mobile identity is included only to set // TMSI in case of a combined attach, so we should not include // this IE unless using NMO 1 and we support the combined attach. //if (mMobileId.mPresent) { // msg.appendByte(0x23); // MS identity IEI. // mMobileId.appendLV(msg); //} // Note: you can also set timer T3302, T3319, T3323 values. // These are all MM timers in Table 11.3a page 545, and not too interesting. } void L3GmmMsgAttachRequest::gmmParseBody(L3GmmFrame &src, size_t &rp) { mMsNetworkCapability = src.readLVasBV(rp); mAttachType = src.getNibble(rp,0); mCypheringKeySequenceNumber = src.getNibble(rp,1); rp++; // skip to end of nibbles we just read, above. mDrxParameter = src.readUInt16(rp); mMobileId.parseLV(src,rp); mOldRaId.parseElement(src,rp); mMsRadioAccessCapability = src.readLVasBV(rp); gmParseIEs(src,rp,"GmmAttachRequest"); } // 9.5.4.2 Mobile Originated Detach Request. void L3GmmMsgDetachRequest::gmmParseBody(L3GmmFrame &src, size_t &rp) { mDetachType = 0xf & src.readByte(rp); // Low nibble is detach type, high nibble is unused. mMobileId.parseLV(src,rp); while (rp < src.size()) { unsigned iei = src.readIEI(rp); switch (iei) { case 0x18: // P-TMSI mMobileId.parseLV(src,rp); mMobileIdPresent = true; break; case 0x19: // P-TMSI signature. Skip it. src.skipLV(rp,3,3,"P-TMSI signature"); break; default: // Unknown IEI. src.skipLV(rp); break; } } } // 9.5.4.1 Network Originated Detach Request. void L3GmmMsgDetachRequest:: gmmWriteBody(ByteVector &msg) { msg.appendByte((mForceToStandby<<4) | mDetachType); if (mGmmCausePresent) { msg.appendByte(0x25); msg.appendUInt16(mGmmCause); } } void L3GmmMsgDetachRequest::textBody(std::ostream &os) const { // The contents are different in uplink and downlink directions. // We just print anything that has a 'present' flag set. os< src.size()) { // last one will have nextrp == src.size() SGSNERROR("invalid message size in ActivatePdpContextRequest"); return; } switch (iei) { case 0x28: mApName = src.readLVasBV(rp); // 10.5.6.1 continue; case 0x27: mPco = src.readLVasBV(rp); // 10.5.6.3 continue; default: rp = nextrp; continue; } } } // 3GPP 24.007 11.2.3.1.3 Transaction Identifier. // First bit is the TIflag that must be set for a message reply. // And I quote: "A message has a TI flag set to "0" when it belongs to transaction initiated by its sender, // and to "1" otherwise." // Transaction id values > 7 requre an extension byte. void L3SmDlMsg::appendTiPd(ByteVector &msg) { unsigned tiFlag = isSenseCmd() ? 0 : 0x8; // Note: nibbles are reversed, as always for L3 messages. if (mTransactionId < 7) { msg.appendField(tiFlag|mTransactionId,4); // Transaction id. The 0x8 indicates this is a command. msg.appendField(GSM::L3GPRSSessionManagementPD,4); // protocol discriminator. } else { msg.appendField(tiFlag|0x7,4); // Magic value indicates additional transaction id field present. msg.appendField(GSM::L3GPRSSessionManagementPD,4); // protocol discriminator. msg.appendByte(0x80|mTransactionId); // The 0x80 is a required but meaningless extension indicator. } } void L3SmMsgActivatePdpContextAccept::smWriteBody(ByteVector &msg) { //msg.appendByte(L3SmMsg::ActivatePDPContextAccept); msg.appendByte(mLlcSapi); // LV: QoS. msg.appendByte(mQoS.size()); msg.append(mQoS); msg.appendField(0,4); // "spare half octet" msg.appendField(mRadioPriority,4); // TLV: Pdp Address msg.appendByte(0x2b); // IEI msg.appendByte(mPdpAddress.size()); msg.append(mPdpAddress); // TLV: Protocol Configuration Options. msg.appendByte(0x27); // IEI msg.appendByte(mPco.size()); msg.append(mPco); } // 24.008 9.5.14 void L3SmMsgDeactivatePdpContextRequest::smParseBody(L3SmFrame &src, size_t &rp) { //size_t rp = parseSmHeader(src); if (rp >= src.size()) { SGSNERROR("DeactivatePdpContextRequest too short according to spec"); mCause = 0; return; // But we dont care. } mCause = src.readByte(rp); // optional ieis: while (rp < src.size()) { unsigned iei = src.readIEI(rp); if ((iei & 0xf0) == 0x90) { mTearDownIndicator = iei & 0x1; // 10.5.6.10 continue; } if (rp >= src.size()) {break;} // This is an error, but we dont care. int len = src.getByte(rp); if (iei == 0x27) { mPco = src.readLVasBV(rp); // 10.5.6.3 continue; } // Ignoring; optional MBMS protocol configuration options // Ignoring: any IEs not in our spec. rp = rp + len + 1; } } void L3SmMsgDeactivatePdpContextRequest::smWriteBody(ByteVector &msg) { //msg.appendByte(L3SmMsg::DeactivatePDPContextRequest); msg.appendByte(mCause); if (mTearDownIndicator) { msg.appendByte(0x91); } // Ignoring: // optional protcol configuration options // optional MBMS protocol configuration options } void L3SmMsgDeactivatePdpContextRequest::textBody(std::ostream &os) const { os<