/* * 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 #if RN_UMTS #include #include #include #include using namespace SIP; #endif //#include "RList.h" #include "LLC.h" //#include "MSInfo.h" #include "GPRSL3Messages.h" #include "Ggsn.h" #include "Sgsn.h" #include "Utils.h" #include "Globals.h" //#include "MAC.h" #include "miniggsn.h" using namespace Utils; #define CASENAME(x) case x: return #x; #define SRB3 3 namespace SGSN { typedef std::list SgsnInfoList_t; static SgsnInfoList_t sSgsnInfoList; typedef std::list GmmInfoList_t; static GmmInfoList_t sGmmInfoList; static Mutex sSgsnListMutex; // One lock sufficient for all lists maintained by SGSN. static void dumpGmmInfo(); #if RN_UMTS static void sendAuthenticationRequest(SgsnInfo *si, string IMSI); #endif //static void killOtherTlli(SgsnInfo *si,uint32_t newTlli); static SgsnInfo *sgsnGetSgsnInfoByHandle(uint32_t mshandle, bool create); static int getNMO(); bool sgsnDebug() { return (gConfig.getBool("SGSN.Debug") || gConfig.getBool("GPRS.Debug")); } bool enableMultislot() { return gConfig.getNum(SQL_MULTISLOTMAXDOWNLINK) > 1 || gConfig.getNum(SQL_MULTISLOTMAXUPLINK) > 1; } const char *GmmCause::name(unsigned mt, bool ornull) { switch (mt) { CASENAME(IMSI_unknown_in_HLR) CASENAME(Illegal_MS) CASENAME(IMEI_not_accepted) CASENAME(Illegal_ME) CASENAME(GPRS_services_not_allowed) CASENAME(GPRS_services_and_non_GPRS_services_not_allowed) CASENAME(MS_identity_cannot_be_derived_by_the_network) CASENAME(Implicitly_detached) CASENAME(PLMN_not_allowed) CASENAME(Location_Area_not_allowed) CASENAME(Roaming_not_allowed_in_this_location_area) CASENAME(GPRS_services_not_allowed_in_this_PLMN) CASENAME(No_Suitable_Cells_In_Location_Area) CASENAME(MSC_temporarily_not_reachable) CASENAME(Network_failure) CASENAME(MAC_failure) CASENAME(Synch_failure) CASENAME(Congestion) CASENAME(GSM_authentication_unacceptable) CASENAME(Not_authorized_for_this_CSG) CASENAME(No_PDP_context_activated) // 0x30 to 0x3f - retry upon entry into a new cell? CASENAME(Semantically_incorrect_message) CASENAME(Invalid_mandatory_information) CASENAME(Message_type_nonexistent_or_not_implemented) CASENAME(Message_type_not_compatible_with_the_protocol_state) CASENAME(Information_element_nonexistent_or_not_implemented) CASENAME(Conditional_IE_error) CASENAME(Message_not_compatible_with_the_protocol_state) CASENAME(Protocol_error_unspecified) default: return ornull ? 0 : "unrecognized GmmCause type"; } } SgsnInfo::SgsnInfo(uint32_t wMsHandle) : //mState(GmmState::GmmNotOurTlli), mGmmp(0), mLlcEngine(0), mMsHandle(wMsHandle), mT3310FinishAttach(15000), // 15 seconds mT3370ImsiRequest(6000) // 6 seconds // mSuspended(0), { //memset(mOldMcc,0,sizeof(mOldMcc)); //memset(mOldMnc,0,sizeof(mOldMnc)); time(&mLastUseTime); #if RN_UMTS == 0 mLlcEngine = new LlcEngine(this); #endif sSgsnInfoList.push_back(this); } SgsnInfo::~SgsnInfo() { if (mLlcEngine) {delete mLlcEngine;} } void SgsnInfo::sirm() { std::ostringstream ss; sgsnInfoDump(this,ss); SGSNLOG("Removing SgsnInfo:"<getGmm(); if (gmm && gmm->getSI() == si) { // You cannot delete this si by itself. Must delete the GmmInfo instead. return false; } si->sirm(); return true; } // This is the generalized printer to identify an SgsnInfo. // The alternate sgsnInfoDump is used only for gmmDump and prints // only that info that is not duplicated in the Gmm. std::ostream& operator<<(std::ostream& os, const SgsnInfo*si) { MSUEAdapter *ms = si->getMS(); if (ms) { os << ms->msid(); } else { #if RN_UMTS os << LOGHEX2("URNTI", si->mMsHandle); #else os << LOGHEX2("TLLI", si->mMsHandle); #endif } if (si->getGmm()) { os << LOGVAR2("imsi",si->getGmm()->mImsi.hexstr()); } return os; } // Reset this connection, for example, because it is doing a GmmDetach or a new GmmAttach. void SgsnInfo::sgsnReset() { freePdpAll(true); if (mLlcEngine) { mLlcEngine->getLlcGmm()->reset(); } } // The operator is allowed to choose the P-TMSI allocation strategy, subject to the constraints // that they should not collide in the same routing area, must not be all 1s, and we dont allow all 0s either. // Note that the MS sends RA [Routing Area] along with TMSI. // The MS creates a local TLLI from the P-TMSI by setting the top two bits, // so the P-TMSI is really limited to 30 bits. // For UMTS, the URNTI consists of a 20-bit (really 16-bit, because it must fit in that) UE id // plus a 12 bit SRNC id. static uint32_t gPTmsiNext = 0; static uint32_t allocatePTmsi() { if (gPTmsiNext == 0) { // Add in the time to the starting TMSI so if the BTS is restarted there is a better chance // of not using the same tmsis over again. time_t now; time(&now); gPTmsiNext = ((now&0xff)<<12) + 1; } if (gPTmsiNext == 0 || gPTmsiNext >= (1<<30)) { gPTmsiNext = 1; } return gPTmsiNext++; //return Tlli::makeLocalTlli(gPTmsiNext++); } MSUEAdapter *SgsnInfo::getMS() const { // The MSInfo struct disappears after a period of time, so look it up. //return GPRS::gL2MAC.macFindMSByTLLI(mMsHandle,0); return SgsnAdapter::findMs(mMsHandle); } GmmInfo::GmmInfo(ByteVector &imsi): mImsi(imsi), mState(GmmState::GmmDeregistered), msi(0) { memset(mPdps,0,sizeof(mPdps)); mPTmsi = allocatePTmsi(); mGprsMultislotClass = -1; // -1 means invalid. mAttachTime = 0; // Must set activityTime to prevent immediate removal from list by another phone simultaneously connection. setActivity(); ScopedLock lock(sSgsnListMutex); sGmmInfoList.push_back(this); } GmmInfo::~GmmInfo() { freePdpAll(true); } // Assumes sSgsnListMutex is locked on entry. static void GmmRemove(GmmInfo *gmm) { std::ostringstream ss; gmmInfoDump(gmm,ss,0); SGSNLOG("Removing gmm:"<getGmm() == gmm || gmm->getSI() == si) { si->sirm(); // yes this is suboptimal, but list is short } } #if 0 for (SgsnInfoList_t::iterator itr = sSgsnInfoList.begin(); itr != sSgsnInfoList.end(); ) { SgsnInfo *si = *itr; if (si->getGmm() == gmm) { itr = sSgsnInfoList.erase(itr); delete si; } else { itr++; } } #endif sGmmInfoList.remove(gmm); delete gmm; } // This is for use by the Command Line Interface void cliGmmDelete(GmmInfo *gmm) { ScopedLock lock(sSgsnListMutex); GmmRemove(gmm); } PdpContext *GmmInfo::getPdp(unsigned nsapi) { //return mSndcp[nsapi] ? mSndcp[nsapi]->mPdp : 0; assert(nsapi < sNumPdps); setActivity(); return mPdps[nsapi]; } // True if the pdpcontext is not in state PDP-INACTIVE bool GmmInfo::isNSapiActive(unsigned nsapi) { assert(nsapi < sNumPdps); return !(mPdps[nsapi] == 0 || mPdps[nsapi]->isPdpInactive()); } // This status is sent back to the MS in messages to indicate what the Network thinks // what PDPContexts are currently in use. PdpContextStatus GmmInfo::getPdpContextStatus() { PdpContextStatus result; for (int i = 0; i <= 7; i++) { if (isNSapiActive(i)) { result.mStatus[0] |= (1<mNSapi >= 0 && pdp->mNSapi < (int)sNumPdps); mPdps[pdp->mNSapi] = pdp; // getSI() should never NULL. The mLlcEngine is null in umts. SgsnInfo *si = getSI(); assert(si); if (si->mLlcEngine) { si->mLlcEngine->allocSndcp(si,pdp->mNSapi,pdp->mLlcSapi); } mg_con_open(mgp,pdp); } // Return TRUE if the pdp was allocated. bool GmmInfo::freePdp(unsigned nsapi) { assert(nsapi < sNumPdps); PdpContext *pdp = mPdps[nsapi]; mPdps[nsapi] = 0; if (pdp) delete pdp; // This disconnects the mgp also. // getSI() should never be NULL. The mLlcEngine is null in umts. #if SNDCP_IN_PDP // sndcp is in the PdpContext and deleted automatically. // Do we want to reset the LLC Sapi? Doubt it because it is shared. #else LlcEngine *llc = getSI() ? getSI()->mLlcEngine : NULL; if (llc) { llc->freeSndcp(nsapi); } #endif return !!pdp; } void SgsnInfo::deactivateRabs(unsigned nsapiMask) { #if RN_UMTS MSUEAdapter *ms = getMS(); if (ms) { ms->msDeactivateRabs(nsapiMask); } else { SGSNERROR("ggsn: DeactivatePdpContextRequest: MS not found "<deactivateRabs(rabMask); } } if (rabMask) addShellRequest("PdpDeactivateAll",this); return rabMask; } void SgsnInfo::sgsnSend2PdpLowSide(int nsapi, ByteVector &packet) { PdpContext *pdp = getPdp(nsapi); assert(pdp); pdp->pdpWriteLowSide(packet); } // The rbid is not used by GPRS, and is just 0. void SgsnInfo::sgsnSend2MsHighSide(ByteVector &pdu,const char *descr, int rbid) { MSUEAdapter *ms = getMS(); #if RN_UMTS // TODO: It would be safer not to call getMS, but just send the dlpdu through // an InterthreadQueue and let the UMTS or GPRS L2 handle that part in its own thread. // In that case we have to add oldTlli to the message also. if (!ms) { SGSNWARN("no corresponding MS for URNTI " << mMsHandle); return; } // For UMTS we pass the rbid which is an intrinsic part of this channel. // TODO: Update UMTS to use DownlinkPdu too. ms->msWriteHighSide(pdu,rbid,descr); #else GmmInfo *gmm = getGmm(); uint32_t tlli, aliasTlli = 0; if (gmm && gmm->isRegistered()) { tlli = gmm->getTlli(); // The TLLI based on the assigned P-TMSI. } else { // We send the message using the TLLI of the SgsnInfo, // which is the one the MS used to talk to us. tlli = mMsHandle; // If we know the P-TMSI that will be used for the local TLLI // for this MS after the attach procedure, notify L2. if (gmm) { aliasTlli = gmm->getTlli(); } if (aliasTlli == tlli) { aliasTlli = 0; } // Be tidy; but dont think this can happen. } if (!ms) { LOG(WARNING) << "no corresponding MS for TLLI " << mMsHandle; return; } GprsSgsnDownlinkPdu *dlpdu = new GprsSgsnDownlinkPdu(pdu,tlli,aliasTlli,descr); //ms->msWriteHighSide(dlpdu); // This is thread safe: // Go ahead and enqueue it even if there is no MS SgsnAdapter::saWriteHighSide(dlpdu); #endif } void SgsnInfo::sgsnWriteHighSideMsg(L3GprsDlMsg &msg) { #if RN_UMTS // bypass llc ByteVector bv(1000); bv.setAppendP(0,0); msg.gWrite(bv); SGSNLOG("Sending "<llcWriteHighSide(sdu,nsapi); #endif } // TLLI 03.03 2.6, Specified in binary: // starts with 11 - local tlli // starts with 10 - foreign tlli // starts with 01111 - random tlli // starts with 01110 - auxiliary tlli. // TLLI may not be all 1s, and if it starts with one of the above, cant be all 0s either. //struct Tlli { // enum Type { Unused, LocalTlli, ForeignTlli, RandomTlli, AuxTlli, UnknownTlli }; // static Type tlli2Type(uint32_t tlli) { // unsigned toptwo = tlli >> (32-2); // It is unsigned, dont have to mask. // if (toptwo == 0x3) return LocalTlli; // if (toptwo == 0x2) return ForeignTlli; // unsigned topfive = tlli >> (32-5); // It is unsigned, dont have to mask. // if (topfive == 0x0f) return RandomTlli; // if (topfive == 0x0e) return AuxTlli; // return UnknownTlli; // } // //static uint32_t tlli2ptmsi(uint32_t tlli) { return tlli & ~sLocalTlliMask; } // // Make a local TLLI // //static uint32_t makeLocalTlli(uint32_t tmsi) { return tmsi | sLocalTlliMask; } //}; // Return Network Mode of Operation 1,2,3 static int getNMO() { return gConfig.getNum("GPRS.NMO"); } void sendAttachAccept(SgsnInfo *si) { si->mT3310FinishAttach.reset(); GmmInfo *gmm = si->getGmm(); assert(gmm); //L3GmmMsgAttachAccept aa(si->attachResult(),gmm->getPTmsi(),si->mAttachMobileId); uint32_t ptmsi = gmm->getPTmsi(); L3GmmMsgAttachAccept aa(si->attachResult(),ptmsi); // We are finished with the attach procedure now. // Note that we are using the si (and TLLI) that the message was sent on. // If the BTS and the MS disagreed on the attach state at the start of this procedure, // we reset the MS registration to match what the MS thinks to make sure we will // use the old TLLI in the si, not the new one based on the PTMSI. si->sgsnWriteHighSideMsg(aa); } static void handleAttachStep(SgsnInfo *si) { GmmInfo *gmm = si->getGmm(); if (!gmm) { // This cannot happen. SGSNERROR("No imsi found for MS during Attach procedure"< Network // RRC SecurityModeCommand // MS <--------------------------------- Network // RRC SecurityModeComplete // MS ---------------------------------> Network // L3 AttachAccept // MS <--------------------------------- Network // (pat) Update: Havind added the authentication for NMO I in here, // so the above procedure is now moved to // If we are in NMO 2, authentication was allegedly already done by // the Mobility Management protocol layer, in which case there is // a Kc sitting in the TMSI table. // We need to pass it a nul-terminated IMSI string. string IMSI = gmm->mImsi.hexstr(); //int len = gmm->mImsi.size(); //char imsi[len+2]; //memcpy(imsi,gmm->mImsi.hexstr().c_str(),len); //imsi[len] = 0; LOG(INFO) << "Looking up Kc for imsi " << IMSI; string Kcs = gTMSITable.getKc(IMSI.c_str()); if (Kcs.length() <= 1) { SGSNERROR("No Kc found for MS in TMSI table during Attach procedure"<setGmmState(GmmState::GmmDeregistered); sendAttachAccept(si); #endif } #if RN_UMTS // Called from UMTS when it receives the SecurityModeComplete or SecurityModeFailure msg. void MSUEAdapter::sgsnHandleSecurityModeComplete(bool success) { SgsnInfo *si = sgsnGetSgsnInfo(); // The si would only be null if the UE sent us a spurious SecurityModeComplete command. if (si == NULL) { SGSNERROR("Received spurious SecurityMode completion command for UE:"<mT3310FinishAttach.active()) { SGSNERROR("Received security response after T3310 expiration for UE:"< '9') ? ((ch & 0x0f) + 9) : (ch & 0x0f); rand.setField(i*4,ch,4); } L3GmmMsgAuthentication amsg(rand); si->sgsnWriteHighSideMsg(amsg); si->mRAND = rand; } #endif static void handleAuthenticationResponse(SgsnInfo *si, L3GmmMsgAuthenticationResponse &armsg) { #if RN_UMTS if (Sgsn::isUmts()) { GmmInfo *gmm = si->getGmm(); if (!gmm) { SGSNERROR("No imsi found for MS during Attach procedure"<mImsi.hexstr(); string RAND = si->mRAND.hexstr(); // verify SRES bool success = false; try { SIPEngine engine(gConfig.getStr("SIP.Proxy.Registration").c_str(),IMSI.c_str()); SGSNLOG("waiting for registration on IMSI: " << IMSI); string SRESstr = armsg.mSRES.hexstr(); success = engine.Register(SIPEngine::SIPRegister, &RAND, IMSI.c_str(), SRESstr.c_str()); } catch(SIPTimeout) { SGSNLOG("SIP authentication timed out. Is the proxy running at " << gConfig.getStr("SIP.Proxy.Registration")); // TODO: Reject return; } if (!success) return; LOG(INFO) << "Looking up Kc for imsi " << IMSI; string Kcs = gTMSITable.getKc(IMSI.c_str()); if (Kcs.length() <= 1) { SGSNERROR("No Kc found for MS in TMSI table during Attach procedure"<mMsHandle,Kcs); } #endif } static void handleIdentityResponse(SgsnInfo *si, L3GmmMsgIdentityResponse &irmsg) { if (! si->mT3310FinishAttach.active()) { // Well that is interesting. We got a spurious identity response. SGSNERROR("unexpected message:"<mGmmp. // Use the imsi as the mobileId in the AttachAccept. //si->mAttachMobileId = irmsg.mMobileId; handleAttachStep(si); //si->mT3310FinishAttach.reset(); //GmmInfo *gmm = findGmmByImsi(passbyreftmp,si); // Always succeeds - creates if necessary. // TODO: Why do we send the mobileid? It seems to Work this way, just wondering, because // the message is delivered to the MS based on the L2 connection as defined by si. //L3GmmMsgAttachAccept aa(si->attachResult(),gmm->getPTmsi(),irmsg.mMobileId); //si->sgsnWriteHighSideMsg(aa); } } void AttachInfo::stashMsgInfo(GMMAttach &msgIEs, bool isAttach) // true: attach request; false: RAUpdate { // Save the MCC and MNC from which the MS drifted in on for reporting. // We only save them the first time we see them, because I am afraid // after that they will revert to our own MCC and MNC. if (! mPrevRaId.valid()) { mPrevRaId = msgIEs.mOldRaId; } //if (mOldMcc[0] == 0 && mOldMcc[1] == 0) { // for (int i = 0; i < 3; i++) { mOldMcc[i] = DEHEXIFY(msgIEs.mOldRaId.mMCC[i]); } //} //if (mOldMnc[0] == 0 && mOldMnc[1] == 0) { // for (int i = 0; i < 3; i++) { mOldMnc[i] = DEHEXIFY(msgIEs.mOldRaId.mMNC[i]); } //} // If a PTMSI was specified in the AttachRequest we need to remember it. if (isAttach && msgIEs.mMobileId.isTmsi()) { mAttachReqPTmsi = msgIEs.mMobileId.getTmsi(); } if (msgIEs.mMsRadioAccessCapability.size()) { mMsRadioAccessCap = msgIEs.mMsRadioAccessCapability; } //mAttachMobileId = msgIEs.mMobileId; } void AttachInfo::copyFrom(AttachInfo &other) { if (! mPrevRaId.valid()) { mPrevRaId = other.mPrevRaId; } if (! mAttachReqPTmsi) { mAttachReqPTmsi = other.mAttachReqPTmsi; } if (! mAttachReqType) { mAttachReqType = other.mAttachReqType; } if (other.mMsRadioAccessCap.size()) { mMsRadioAccessCap = other.mMsRadioAccessCap; } } void sendImplicitlyDetached(SgsnInfo *si) { L3GmmMsgGmmStatus statusMsg(GmmCause::Implicitly_detached); si->sgsnWriteHighSideMsg(statusMsg); // The above didn't do it, so try sending one of these too: // Detach type 1 means re-attach required. //L3GmmMsgDetachRequest dtr(1,GmmCause::Implicitly_detached); // 7-2012: Tried taking out the cause to stop the Multitech modem // sending 'invalid mandatory information'. // The only reason obvious to send that is in 24.008 8.5 is an unexpected IE, // so maybe it is the cause. But it did not help. L3GmmMsgDetachRequest dtr(1,0); si->sgsnWriteHighSideMsg(dtr); } // The ms may send a P-TMSI or IMSI in the mobile id. static void handleAttachRequest(SgsnInfo *si, L3GmmMsgAttachRequest &armsg) { switch ((AttachType) (unsigned) armsg.mAttachType) { case AttachTypeGprsWhileImsiAttached: SGSNLOG("NOTICE attach type "<<(int)armsg.mAttachType <mtAttachInfo.mAttachReqType = AttachTypeGprs; break; case AttachTypeCombined: if (getNMO() != 1) { // The MS should not have done this. LOG(ERR)<<"Combined Attach attempt incompatible with NMO 1 "<mtAttachInfo.mAttachReqType = AttachTypeCombined; break; } //uint32_t newptmsi; // Save info from the message: si->mtAttachInfo.stashMsgInfo(armsg,true); // Re-init the state machine. // If the MS does a re-attach, we may have an existing SgsnInfo from earlier, so we must reset it now: // si->sgsnReset(); // 6-3-2012: changed to just freePdpAll. si->freePdpAll(true); GmmInfo *gmm = si->getGmm(); // 7-1-2012: Working on multitech modem failure to reattach bug. // I tried taking this out to send an extra identity request, // but then the modem did not respond to that identity request, // just like before it did not respond to the second attach request. // Even after deleting all but that single SgsnInfo, and modifying the msid // to print both tllis, and looking at pat.log the message is definitely // sent on the correct TLLi. // But if you tell the modem to detach and then try attach again, // then the modem uses a new TLLI and sends an IMSI, so it thinks // it was attached, but it is sending an attach request anyway, with a PTMSI. // But the first attach used a PTMSI, and it succeeded. // Things to try: send protocol incompabible blah blah. // Try converting to local tlli (0xc...); I tried that before but maybe // the tlli change procedure was wrong back then. // Send a detach, although I think the modem ignores this. if (gmm) { // We already have an IMSI for this MS, where the MS was identified by some TLLI // associated with this SgsnInfo, which means we already did // the IdentityResponse challenge. Just use it. } else { // We need an imsi. If it is not in the message, we will need to ask for it. ByteVector imsi; // There is a slight problem that we only have 6 seconds to register the MS, // which may not be enough time to do the IdentityResponse Challenge. // Therefore we save the IMSI associated with the TLLI that we got from the Identity response // challenge in the SgsnInfo, and when the MS tries again with the same TLLI, // we can skip the IdentityRequest phase. //if (si->mImsi.size()) { // // Already did the identity challange; use the previously queried imsi from this ms. // imsi = si->mImsi; //} else { // If the MS did not send us an IMSI already, ask for one. if (armsg.mMobileId.isImsi()) { // The MS included the IMSI in the attach request imsi = armsg.mMobileId.getImsi(); findGmmByImsi(imsi,si); // Create the gmm and associate with si. } else { // 3GPP 24.008 11.2.2 When T3370 expires we can send another Identity Request. // However we are also going to use it inverted, and send Identity Requests // no closer together than T3370. // If this expires, the MS will try again. if (! si->mT3370ImsiRequest.active() || si->mT3370ImsiRequest.expired()) { // Send off a request for the imsi. L3GmmMsgIdentityRequest irmsg; si->mT3370ImsiRequest.set(); // We only use the timer in this case, so we only set it in this case, instead // of at the top of this function. si->mT3310FinishAttach.set(); si->sgsnWriteHighSideMsg(irmsg); } return; } //} //SgsnInfo *si2 = Sgsn::findAssignedSgsnInfoByImsi(imsi); //newptmsi = si2->mMsHandle; } #if 0 // We dont care if the MS already had a P-TMSI. // If it is doing an attach, go ahead and assign a new one. if (!si->mAllocatedTmsiTlli) { si->mAllocatedTmsiTlli = Sgsn::allocateTlli(); } // We cant set the tlli in the MS until it has received the new tlli, // because we have to use the previous tlli to talk to it. #endif // This was for testing: //L3GmmMsgIdentityRequest irmsg; //si->sgsnWriteHighSideMsg(irmsg); // We are assigning this ptmsi to the MS. handleAttachStep(si); //si->mT3310FinishAttach.reset(); //L3GmmMsgAttachAccept aa(si->attachResult(),gmm->getPTmsi(),armsg.mMobileId); //si->sgsnWriteHighSideMsg(aa); } static void handleAttachComplete(SgsnInfo *si, L3GmmMsgAttachComplete &acmsg) { // The ms is acknowledging receipt of the new tlli. GmmInfo *gmm = si->getGmm(); if (! gmm) { // The attach complete does not match this ms state. // Happens, for example, when you first turn on the bts and the ms // is still trying to complete a previous attach. Ignore it. // The MS will timeout and try to attach again. SGSNLOG("Ignoring spurious Attach Complete" << si); // Dont send a reject because we did not reject anything. return; } //SGSNLOG("attach complete gmm="<<((uint32_t)gmm)); gmm->setGmmState(GmmState::GmmRegisteredNormal); gmm->setAttachTime(); #if RN_UMTS #else // Start using the tlli associated with this imsi/ptmsi when we talk to the ms. si->changeTlli(true); #endif addShellRequest("GprsAttach",gmm); #if 0 // nope, we are going to pass the TLLI down with each message and let GPRS deal with it. //if (! Sgsn::isUmts()) { // // Update the TLLI in all the known MS structures. // // Only the SGSN knows that the MSInfo with these various TLLIs // // are in fact the same MS. But GPRS needs to know because // // the MS will continue to use the old TLLIs, and it will botch // // up if, for example, it is in the middle of a procedure on one TLLI // // and the MS is using another TLLI, which is easy to happen given the // // extremely long lag times in message flight. // // The BSSG spec assumes there only two TLLIs, but I have seen // // the Blackberry use three simultaneously. // SgsnInfo *sip; // uint32_t newTlli = gmm->getTlli(); // RN_FOR_ALL(SgsnInfoList_t,sSgsnInfoList,sip) { // if (sip->getGmm == gmm) { // UEAdapter *ms = sip->getMS(); // // or should we set the ptmsi?? // if (ms) ms->changeTlli(newTlli); // } // } //} #endif } static void handleDetachRequest(SgsnInfo *si) { L3GmmMsgDetachAccept detachAccept(0); GmmInfo *gmm = si->getGmm(); if (!gmm) { // Hmm, but fall through, because it is certainly detached. } else { gmm->setGmmState(GmmState::GmmDeregistered); } si->sgsnWriteHighSideMsg(detachAccept); si->sgsnReset(); if (gmm) addShellRequest("GprsDetach",gmm); } static void sendRAUpdateReject(SgsnInfo *si,unsigned cause) { L3GmmMsgRAUpdateReject raur(cause); si->sgsnWriteHighSideMsg(raur); } // TODO: Need to follow 4.7.13 of 24.008 static void handleServiceRequest(SgsnInfo *si, L3GmmMsgServiceRequest &srmsg) { GmmInfo *gmm = si->getGmm(); // TODO: Should we check the PTmsi and the PDP context status??? if (!gmm) { L3GmmMsgServiceReject sr(GmmCause::Implicitly_detached); si->sgsnWriteHighSideMsg(sr); return; } else { gmm->setActivity(); L3GmmMsgServiceAccept sa(si->getPdpContextStatus()); si->sgsnWriteHighSideMsg(sa); } } // 24.008 4.7.5, and I quote: // "The routing area updating procedure is always initiated by the MS. // It is only invoked in state GMM-REGISTERED." // The MS may send an mMobileId containing a P-TMSI, and it sends TmsiStatus // telling if it has a valid TMSI. static void handleRAUpdateRequest(SgsnInfo *si, L3GmmMsgRAUpdateRequest &raumsg) { bool sendTmsi = 0; RAUpdateType updatetype = (RAUpdateType) (unsigned)raumsg.mUpdateType; switch (updatetype) { case RAUpdated: case PeriodicUpdating: updatetype = RAUpdated; if (getNMO() == 1) { updatetype = CombinedRALAUpdated; } break; case CombinedRALAUpdated: case CombinedRALAWithImsiAttach: // As of 4-29-2012, we dont even save the imsi. if (getNMO() != 1) { // TODO: Should we send a reject, or an accept with a different updatetype? // I think the type should have matched the NMO broadcast in the beacon, // so we should reject. // Warning: This reject is saved in the MS semi-permanently, // and it will not try again. // DEBUG: Try just accepting the LAUpdate unconditionally... //sendRAUpdateReject(si,GmmCause::Location_Area_not_allowed); //return; LOG(ERR)<<"Routing Area combined Location Area Update request incompatible with NMO 1 "<mtAttachInfo.stashMsgInfo(raumsg,false); GmmInfo *gmm = si->getGmm(); if (! gmm) { // The MS has not registered with us yet, so reject the RAUpdate. // Doesnt seem like this should be always needed, because we want to accept anyone. // But this seems to work, and it didnt work when I didnt do this. // This makes the MS come back with an AttachRequest message. // I have seen the Blackberry trying RAUpdate with the same foreign TLLI about 10 times, // and getting rejects before coming back with the AttachRequest, but it eventually did. // TODO: Maybe use a different cause for foreign TLLI. // And I quote, from 24.008 Annex G.6: // "Cause value = 9 MS identity cannot be derived by the network // "This cause is sent to the MS when the network cannot derive the MS's identity from // "the P-TMSI in case of inter-SGSN routing area update. // "Cause value = 10 Implicitly detached // "This cause is sent to the MS either if the network has implicitly detached the MS, // "e.g. some while after the Mobile reachable timer has expired, // "or if the GMM context data related to the subscription dose not exist in the // "SGSN e.g. because of a SGSN restart. // Also, see 4.7.1.5.4 which gives the specific behavior the MS to each of these causes. // Specifically, cause 10 is the correct one to make the MS reregister and get new contexts. // I did not try just assigning a new TMSI in the RAUpdateAccept message. // Also have not tried just echoing back the TLLI that the MS used in L2 as // the allocated-TMSI in this response, which might work, // but is not procedurally correct if the TMSI came from a different routing area. if (raumsg.mPdpContextStatus.anyDefined()) { // If the MS thinks it has PDP contexts, we need to explicitly release them. // TODO: We do that in the raaccept message... // Let MS establish a session, then turn BTS off and on; the MS continues to // use the old IP addresses with its new pdp contexts. // Not exactly sure why, maybe my bug somewhere. // 4-30: I tried putting this back in but it did not work - // The blackberry continued to send RAUpdateRequest that indicated it // did not tear them down. // 24.008 4.7.5.1.3 Indicates all you have to do is send an RaUpdateAccept // with the pdpstatus zeroed out. //sendSmStatus(si,SmCause::Unknown_PDP_address_or_PDP_type); //sendPdpDeactivateAll(si, SmCause::Unknown_PDP_address_or_PDP_type); } // 4.7.5.1.4 says that 'cause 9' shall make the MS delete its P-TMSI, // enter state GMM-DEREGISTERED, and subsequently automatically initiate GPRS attach. // However, it didnt work for me on the blackberry after a test that ended in TBF failure. // Had to turn off the MS and turn it back on, then ok. // Maybe it had entered state LIMITED.SERVICE, // which prevents attaches, or maybe NO.CELL.AVAILABLE. // It was trying to register with the mobile-id set to no value. // Cause 10 looks like it might be better: MS releases PDP contexts, // enters GMM-DEREGISTERED.NORMAL, and forces a new attach. sendRAUpdateReject(si,GmmCause::Implicitly_detached); return; } else { gmm->setActivity(); /** wrong if (si->getState() != GmmState::GmmDeregistered) { //sendRAUpdateReject(si,GmmCause::MessageNotCompatibleWithProtocolState); // This is the cause that the opensgsn sends: sendRAUpdateReject(si,GmmCause::MS_identity_cannot_be_derived_by_the_network); return; } ***/ // The RAUpdate result is yes only if the MS has attached to us. // Note that this message gets back to the originating MS guaranteed at layer2, // regardless of whatever tmsi/mobile-id we put in this message. // TODO: Do we need to set the allocated P-TMSI or not? Not sure. // The blackberry did not work without it, but it may have been sql open-registration was wrong. // DONT DO THIS: //if (gConfig.defines("SGSN.RAUpdateIncludeTmsi") && gConfig.getNum("SGSN.RAUpdateIncludeTmsi")) { // tmsi = si->mTlli; //} //if (updatetype == CombinedRALAUpdated) { // DEBUG: try this // Send an authentication request to make the MS happy about this. //sendAuthenticationRequest(si); //} // We are not integrated with the OpenBTS stack yet, // so if we need a tmsi just make one up. uint32_t ptmsi = gmm->getPTmsi(); L3GmmMsgRAUpdateAccept raa(updatetype, si->getPdpContextStatus(),ptmsi, sendTmsi ? ptmsi : 0); si->sgsnWriteHighSideMsg(raa); } } static void handleRAUpdateComplete(SgsnInfo *si, L3GmmMsgRAUpdateComplete &racmsg) { // Do not need to do anything. } // This message may arrive on a DCCH channel via the GSM RR stack, rather than a GPRS message, // and as such, could be running in a separate thread. // We queue the message for processing. // The suspension may be user initiated or by the MS doing some RR messages, // most often, Location Area Update. The spec says we are supposed to freeze // the LLC state and continue after resume. But in the permanent case any incoming packets // will be hopelessly stale after resumption, so we just toss them. Note that web sites chatter // incessantly with keepalives even when they look quiescent to the user, and we dont want // all that crap to back up in the downlink queue. // In the temporary case, which is only a second or two, we will attempt to preserve the packets // to prevent a temporary loss of service. I have observed that the MS first stops responding // to the BSS for about a second before sending the RACH to initiate the RR procedure, // so there is no warning at all. However, we MUST cancel the TBFs. If we dont, and after // finishing the RR procedure the MS gets back to GPRS before the previous TBFs timeout, // it assumes they are new TBFs, which creates havoc, because the acknacks do not correspond // to the previous TBF. This generates the "STUCK" condition, up to a 10 second loss of service, // and I even saw the Blackberry detach and reattach to recover. // In either case the MS signals resumption by sending us anything on the uplink. // WARNING: This runs in a different thread. bool Sgsn::handleGprsSuspensionRequest(uint32_t wTlli, const ByteVector &wraid) // The Routing Area id. { SGSNLOG("Received GPRS SuspensionRequest for"<getMS(); if (ms == NULL) { // This is a serious internal error. SGSNERROR("L3 message "<mMsHandle)); return; } switch (mt) { case L3GmmMsg::AttachRequest: { L3GmmMsgAttachRequest armsg; armsg.gmmParse(frame); SGSNLOG("Received "<sgsnSend2PdpLowSide(rbid, payload); //} // The handle is the URNTI and the rbid specfies the rab. // In gprs, the handle is the TLLI and all the rab info is encoded into the // payload with LLC headers so rbid is not used, which was a pretty dopey design. void MSUEAdapter::sgsnWriteLowSide(ByteVector &payload, uint32_t handle, unsigned rbid) { SgsnInfo *si = sgsnGetSgsnInfoByHandle(handle,true); // Create if necessary. #if RN_UMTS // No Pdcp, so just send it off. si->sgsnSend2PdpLowSide(rbid, payload); #else si->mLlcEngine->llcWriteLowSide(payload,si); #endif } #if RN_UMTS void MSUEAdapter::sgsnHandleL3Msg(uint32_t handle, ByteVector &msgFrame) { SgsnInfo *si = sgsnGetSgsnInfoByHandle(handle,true); // Create if necessary. handleL3Msg(si,msgFrame); } #endif void handleL3Msg(SgsnInfo *si, ByteVector &bv) { unsigned pd = 0; try { L3GprsFrame frame(bv); if (frame.size() == 0) { // David saw this happen. //SGSNWARN("completely empty L3 uplink message "<mMsHandle == handle) {result=si; continue;} #if RN_UMTS #else #if NEW_TLLI_ASSIGN_PROCEDURE if (si->mAltTlli == handle) {result=si;continue;} #endif #endif // Kill off old ones, except ones that are the primary one for a gmm. GmmInfo *gmm = si->getGmm(); if (gmm==NULL || gmm->getSI() != si) { if (now - si->mLastUseTime > idletime) { si->sirm(); } } } if (result) { time(&result->mLastUseTime); return result; } if (!create) { return NULL; } // Make a new one. SgsnInfo *sinew = new SgsnInfo(handle); return sinew; } // Now we create the SgsnInfo for the assigned ptmsi as soon as the ptmsi is created, // even if the MS has not used it yet. //GmmInfo *SgsnInfo::findGmm() //{ // if (mGmmp) { return mGmmp; } // Hooked up previously. // return NULL; // Old comment: // For GPRS, the MS contacts with some random tlli, then we create a GmmInfo and a PTMSI, // and send the PTMSI to the MS, but the GmmInfo is not yet hooked to any SgsnInfos. // The MS will then call us again using a TLLI derived from the PTMSI, // and we hook up that SgsnInfo to the GmmInfo right here. // if (! Sgsn::isUmts()) { // uint32_t tlli = mMsHandle; // // Only a local TLLI can be converted to a P-TMSI to look up the Gmm context. // if (Tlli::tlli2Type(tlli) == Tlli::LocalTlli) { // uint32_t ptmsi = Tlli::tlli2ptmsi(tlli); // GmmInfo *gmm; // RN_FOR_ALL(GmmInfoList_t,sGmmInfoList,gmm) { // if (gmm->mPTmsi == ptmsi) { // SGSNLOG("Hooking up"<setGmm(gmm); // gmm->msi = this; // return gmm; // } // } // } // } else { // // In UMTS the Gmm context is indexed by URNTI. // // If this doesnt work right, we will need to look up the Gmm context // // from the ptmsi in the L3 messages. // } // return NULL; //} // Works, but not currently used: void MSUEAdapter::sgsnFreePdpAll(uint32_t mshandle) { SgsnInfo *si = sgsnGetSgsnInfoByHandle(mshandle,false); if (si) si->freePdpAll(true); } // Forces it to exist if it did not already. static SgsnInfo *sgsnGetSgsnInfoByHandle(uint32_t mshandle, bool create) { // We cant cache this thing for GPRS because it changes // during the TLLI assignment procedure. // We could cache it for UMTS, but that assumes the lifetime of the SgsnInfo // is greater than the UE, both of which are controlled by user parameters, // so to be safe, we are just going to look it up every time. // TODO: go back to caching it in UMTS only. //if (! mSgsnInfo) { //uint32_t mshandle = msGetHandle(); //mSgsnInfo = findSgsnInfoByHandle(mshandle,create); //} //return mSgsnInfo; return findSgsnInfoByHandle(mshandle,create); } #if RN_UMTS SgsnInfo *MSUEAdapter::sgsnGetSgsnInfo() { uint32_t mshandle = msGetHandle(); return findSgsnInfoByHandle(mshandle,false); } #else void MSUEAdapter::sgsnSendKeepAlive() { // TODO } #endif #if RN_UMTS // not applicable #else static void parseCaps(GmmInfo *gmm) { if (/*gmm->mGprsMultislotClass == -1 &&*/ gmm->mgAttachInfo.mMsRadioAccessCap.size()) { MsRaCapability caps(gmm->mgAttachInfo.mMsRadioAccessCap); gmm->mGprsMultislotClass = caps.mCList[0].getCap(AccessCapabilities::GPRSMultislotClass); gmm->mGprsGeranFeaturePackI = caps.mCList[0].getCap(AccessCapabilities::GERANFeaturePackage1); } } int MSUEAdapter::sgsnGetMultislotClass(uint32_t mshandle) { SgsnInfo *si = sgsnGetSgsnInfoByHandle(mshandle,false); if (!si) { return -1; } GmmInfo *gmm = si->getGmm(); // Must be non-null or we would not be here. if (!gmm) { return -1; } // But dont crash if I'm mistaken. parseCaps(gmm); return gmm->mGprsMultislotClass; } bool MSUEAdapter::sgsnGetGeranFeaturePackI(uint32_t mshandle) { SgsnInfo *si = sgsnGetSgsnInfoByHandle(mshandle,false); if (!si) { return -1; } GmmInfo *gmm = si->getGmm(); // Must be non-null or we would not be here. if (!gmm) { return -1; } // But dont crash if I'm mistaken. parseCaps(gmm); return gmm->mGprsGeranFeaturePackI; } #endif // If the MS is registered return the IMSI, otherwise return an empty string. string MSUEAdapter::sgsnFindImsiByHandle(uint32_t handle) { ScopedLock lock(sSgsnListMutex); // Will be locked again recursively in findSgsnInfoByHandle. if (SgsnInfo *si = findSgsnInfoByHandle(handle,false)) { GmmInfo *gmm = si->getGmm(); if (gmm) return gmm->mImsi.hexstr(); } return string(""); } GmmState::state MSUEAdapter::sgsnGetRegistrationState(uint32_t mshandle) { SgsnInfo *si = sgsnGetSgsnInfoByHandle(mshandle,false); if (!si) { return GmmState::GmmDeregistered; } GmmInfo *gmm = si->getGmm(); // Must be non-null or we would not be here. if (!gmm) { return GmmState::GmmDeregistered; } return gmm->getGmmState(); } #if RN_UMTS void MSUEAdapter::sgsnHandleRabSetupResponse(unsigned rabId, bool success) { SgsnInfo *si = sgsnGetSgsnInfo(); if (si == NULL) { // Dont think this can happen, but be safe. SGSNERROR("Received spurious RabSetupResponse for UE:"<getPdp(rabId); if (pdp==NULL) return; // FIXME: Not sure what to do here if (pdp->mUmtsStatePending) { pdp->update(pdp->mPendingPdpr); pdp->mUmtsStatePending = false; } sendPdpContextAccept(si,pdp); } else { // We do NOT want to send a RAB teardown message - we got here because // the RAB setup did not work in the first place. Just free it. si->freePdp(rabId); } } #endif const char *GmmState::GmmState2Name(GmmState::state state) { switch (state) { CASENAME(GmmDeregistered) CASENAME(GmmRegistrationPending) CASENAME(GmmRegisteredNormal) CASENAME(GmmRegisteredSuspsended) } return ""; } // The alternate sgsnInfoPrint is used only for gmmDump and prints void sgsnInfoDump(SgsnInfo *si,std::ostream&os) { //if (si == gmm->getSI()) {continue;} // Already printed the main one. uint32_t handle = si->mMsHandle; os << "SgsnInfo"<mtAttachInfo; if (ati->mPrevRaId.valid()) { os << " prev:"; ati->mPrevRaId.text(os); } if (!si->getGmm()) { os << " no gmm"; } os << endl; } void gmmInfoDump(GmmInfo *gmm,std::ostream&os,int options) { os << " GMM Context:"; os << LOGVAR2("imsi",gmm->mImsi.hexstr()); os << LOGHEX2("ptmsi",gmm->mPTmsi); os << LOGHEX2("tlli",gmm->getTlli()); os << LOGVAR2("state",GmmState::GmmState2Name(gmm->getGmmState())); time_t now; time(&now); os << LOGVAR2("age",(gmm->mAttachTime ? now - gmm->mAttachTime : 0)); os << LOGVAR2("idle",now - gmm->mActivityTime); SgsnInfo *si = gmm->getSI(); if (!(options & printNoMsId)) { if (si) { // Can this be null? No, but dont crash. // The mPrevRaId is generally invalid in the SgsnInfo for the GMM, // because it is the one we assigned, and the routing info is in the SgsnInfo // the MS initially called in on. //os << LOGVAR2("oldMCC",si->mOldMcc); //os << LOGVAR2("oldMNC",si->mOldMnc); // The GPRS ms struct will disappear shortly after the MS stops communicating with us. MSUEAdapter *ms = si->getMS(); if (ms) { os << ms->msid(); } else { os << " MS=not_active"; } } } os << " IPs="; int pdpcnt = 0; for (unsigned nsapi = 0; nsapi < GmmInfo::sNumPdps; nsapi++) { if (gmm->isNSapiActive(nsapi)) { // FIXME: Darn it, we need to lock the pdp contexts for this too. // Go ahead and do it anyway, because collision is very low probability. PdpContext *pdp = gmm->getPdp(nsapi); mg_con_t *mgp; // Temp variable reduces probability of race; the mgp itself is immortal. if (pdp && (mgp=pdp->mgp)) { char buf[30]; if (pdpcnt) {os <<",";} os << ip_ntoa(mgp->mg_ip,buf); } pdpcnt++; } } if (pdpcnt == 0) { os <<"none"; } os << endl; if (options & printDebug) { // Print out all the SgsnInfos associated with this GmmInfo. RN_FOR_ALL(SgsnInfoList_t,sSgsnInfoList,si) { if (si->getGmm() != gmm) {continue;} os <<"\t"; // this sgsn is associated with the GmmInfo just above it. sgsnInfoDump(si,os); } } // Now the caps: if ((options & printCaps) && gmm->mgAttachInfo.mMsRadioAccessCap.size()) { MsRaCapability caps(gmm->mgAttachInfo.mMsRadioAccessCap); caps.text2(os,true); } //os << endl; // This is extra. There is one at the end of the caps. } void gmmDump(std::ostream &os) { // The sSgsnListMutex exists for this moment: to allow this cli list routine // to run from a foreign thread. ScopedLock lock(sSgsnListMutex); int debug = sgsnDebug(); GmmInfo *gmm; RN_FOR_ALL(GmmInfoList_t,sGmmInfoList,gmm) { gmmInfoDump(gmm,os,debug ? printDebug : 0); os << endl; // This is extra. There is one at the end of the caps. } // Finally, print out SgsnInfo that are not yet associated with a GmmInfo. if (debug) { SgsnInfo *si; RN_FOR_ALL(SgsnInfoList_t,sSgsnInfoList,si) { if (! si->getGmm()) { sgsnInfoDump(si,os); } } } } void dumpGmmInfo() { if (sgsnDebug()) { std::ostringstream ss; gmmDump(ss); SGSNLOG(ss.str()); } } void SgsnInfo::setGmm(GmmInfo *gmm) { // Copy pertinent info from the Routing Update or Attach Request message into the GmmInfo. gmm->mgAttachInfo.copyFrom(mtAttachInfo); this->mGmmp = gmm; } #if NEW_TLLI_ASSIGN_PROCEDURE static void killOtherTlli(SgsnInfo *si,uint32_t newTlli) { SgsnInfo *othersi = findSgsnInfoByHandle(newTlli,false); if (othersi && othersi != si) { if (othersi->getGmm()) { // This 'impossible' situation can happen if an MS that // is already attached tries to attach again. // Easy to reproduce by pulling the power on an MS. (Turning off may not be enough.) // For example: // MS -> attachrequest TLLI=80000001; creates new SgsnInfo 80000001 // MS <- attachaccept // MS -> attachcomplete TLLI=c0000001; SgsnInfo 80000001 changed to c0000001 // MS gets confused (turn off or pull battery) // MS -> attachrequest TLLI=80000001; creates new SgsnInfo 80000001 // MS <- attachaccept TLLI=80000001; <--- PROBLEM 1! See below. // MS -> attachcomplete TLLI=c0000001; <--- PROBLEM 2: Both SgsnInfo exist. // PROBLEM 1: We have already issued the TLLI change command so L2 // is now using the new TLLI. We will use the old TLLI (80000001) // in the attachaccept, which will cause a 'change tlli' procedure in L2 // to switch back to TLLI 80000001 temporarily. // PROBLEM 2: Solved by deleting the original registered SgsnInfo (c0000001 above) // and then caller will change the TLLI of the unregistred one (80000001 above.) SGSNWARN("Probable repeat attach request: TLLI change procedure"<sirm(); } } } #endif // Switch to the new tlli. If now, do it now, otherwise, just allocate the new SgsnInfo, #if NEW_TLLI_ASSIGN_PROCEDURE // just set altTlli to the future TLLI we will be using after the attach procedure, #endif // which we do ahead of time so we can inform GPRS L2 that the new and old TLLIs are the same MS. // Return the new si. In the new tlli procedure, it is the same as the old. // ------------------ // Note the following sequence, easy to reproduce by pulling the power on an MS: // MS -> attachrequest TLLI=80000001; creates new SgsnInfo 80000001 // MS <- attachaccept // MS -> attachcomplete TLLI=c0000001; SgsnInfo 80000001 changed to c0000001 // MS gets confused (turn off or pull battery) // MS -> attachrequest TLLI=80000001; creates new SgsnInfo 80000001 // MS <- attachaccept TLLI=80000001; <--- PROBLEM 1! See below. // MS -> attachcomplete TLLI=c0000001; <--- PROBLEM 2: Both SgsnInfo exist. // PROBLEM 1: We have already issued the TLLI change command so L2 // is now using the new TLLI. Easy fix is we use the old TLLI (80000001) // in the attachaccept, which will cause a 'change tlli' procedure in L2 // to switch back to TLLI 80000001 temporarily until we receive attachcomplete. // PROBLEM 2: The SgsnInfo for the new tlli will already exist. // ???? Solved by deleting the original registered SgsnInfo (c0000001 above) // ???? and then caller will change the TLLI of the unregistred one (80000001 above.) SgsnInfo * SgsnInfo::changeTlli(bool now) { GmmInfo *gmm = getGmm(); uint32_t newTlli = gmm->getTlli(); #if NEW_TLLI_ASSIGN_PROCEDURE SgsnInfo *si = this; if (si->mMsHandle != newTlli) { killOtherTlli(si,newTlli); if (now) { si->mAltTlli = si->mMsHandle; si->mMsHandle = newTlli; } else { si->mAltTlli = newTlli; } } return si; #else SgsnInfo *othersi = findSgsnInfoByHandle(newTlli,true); // We will use the new tlli for downlink l3 messages, eg, pdp context messages, // unless they use some other SI specifically, like AttachAccept // must be sent on the SI that the AttachRequest arrived on. othersi->setGmm(gmm); if (now) { gmm->msi = othersi; } return othersi; #endif } // If si, forces the GmmInfo for this imsi to exist and associates it with that si. // If si == NULL, return NULL if gmm not found - used for CLI. GmmInfo *findGmmByImsi(ByteVector &imsi, SgsnInfo *si) { ScopedLock lock(sSgsnListMutex); GmmInfo *gmm, *result = NULL; // 24.008 11.2.2: Implicit Detach timer default is 4 min greater // than T3323, which can be provided in AttachAccept, otherwise // defaults to T3312, which defaults to 54 minutes. int attachlimit = gConfig.getNum("SGSN.Timer.ImplicitDetach"); // expiration time in seconds. time_t now; time(&now); RN_FOR_ALL(GmmInfoList_t,sGmmInfoList,gmm) { if (now - gmm->mActivityTime > attachlimit) { GmmRemove(gmm); continue; } if (gmm->mImsi == imsi) { result = gmm; } } if (result) { if (si) si->setGmm(result); return result; } if (!si) { return NULL; } // Not found. Make a new one in state Registration Pending. gmm = new GmmInfo(imsi); gmm->setGmmState(GmmState::GmmRegistrationPending); si->setGmm(gmm); gmm->msi = si; #if RN_UMTS // For UMTS, the si is indexed by URNTI, which is invariant, so hook up and we are finished. #else // We hook up the GMM context to the SgsnInfo corresponding to the assigned P-TMSI, // even if that SgsnInfo does not exist yet, // rather than the SgsnInfo corresponding to the current TLLI, which could be anything. // The MS will use the SgsnInfo for the P-TMSI to talk to us after a successful attach. si->changeTlli(false); //#if NEW_TLLI_ASSIGN_PROCEDURE // // 3GPP 04.64 7.2.1.1 and 8.3: Perform the TLLI reassignment procedure. // // Change the TLLI in the SgsnInfo the MS is currently using. // // The important point is that the LLC state does not change. // uint32_t newTlli = gmm->getTlli(); // killOtherTlli(si,newTlli); // // Do the TLLI reassignment. // if (si->mMsHandle != newTlli) { // Any other case is extremely unlikely. // // We must continue using the existing MsHandle until we // // receive the AttachComplete message from the MS, but mark // // that the new tlli is an alias for this TLLI. // si->mAltTlli = newTlli; // } //#else // SgsnInfo *newsi = findSgsnInfoByTlli(gmm->getTlli(),true); // newsi->setGmm(gmm); // // NO, not until attachComplete: gmm->msi = newsi; // Use this one instead. //#endif #endif return gmm; } void RabStatus::text(std::ostream &os) const { os <<"RabStatus(mStatus="; switch (mStatus) { case RabIdle: os << "idle"; break; case RabFailure: os << "failure"; break; case RabPending: os << "pending"; break; case RabAllocated: os << "allocated"; break; case RabDeactPending: os << "deactPending"; break; } os<getGmm(); // Must be non-null or we would not be here. if (!gmm) { os << " GMM state unknown\n"; return; } gmmInfoDump(gmm,os,options); } }; // namespace