mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-22 23:32:00 +00:00
1690 lines
58 KiB
C++
1690 lines
58 KiB
C++
/*
|
|
* 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 <list>
|
|
#if RN_UMTS
|
|
#include <SIPInterface.h>
|
|
#include <SIPUtility.h>
|
|
#include <SIPMessage.h>
|
|
#include <SIPEngine.h>
|
|
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<SgsnInfo*> SgsnInfoList_t;
|
|
static SgsnInfoList_t sSgsnInfoList;
|
|
typedef std::list<GmmInfo*> 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:"<<ss);
|
|
sSgsnInfoList.remove(this);
|
|
delete this;
|
|
}
|
|
|
|
// This is for use by the Command Line Interface
|
|
// Return true on success.
|
|
bool cliSgsnInfoDelete(SgsnInfo *si)
|
|
{
|
|
ScopedLock lock(sSgsnListMutex);
|
|
GmmInfo *gmm = si->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:"<<ss);
|
|
SgsnInfo *si;
|
|
RN_FOR_ALL(SgsnInfoList_t,sSgsnInfoList,si) {
|
|
// The second test here should be redundant.
|
|
if (si->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<<i); }
|
|
if (isNSapiActive(i+8)) { result.mStatus[1] |= (1<<i); }
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void GmmInfo::connectPdp(PdpContext *pdp, mg_con_t *mgp)
|
|
{
|
|
// Order may be important here.
|
|
// We dont want to hook the mgp up until the stack is all connected and prepared
|
|
// to receive packets, because they could come blasting in any time,
|
|
// even before any outgoing packets are sent.
|
|
assert(pdp->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 "<<this);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Return a mask of RABs that were freed.
|
|
unsigned GmmInfo::freePdpAll(bool freeRabsToo)
|
|
{
|
|
unsigned rabMask = 0;
|
|
for (unsigned nsapi = 0; nsapi < sNumPdps; nsapi++) {
|
|
if (freePdp(nsapi)) { rabMask |= 1<<nsapi; }
|
|
}
|
|
if (freeRabsToo && rabMask) {
|
|
// It would be a serious internal error for getSI() to fail, but check anyway.
|
|
if (getSI()) { getSI()->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 "<<msg.str() <<this);
|
|
sgsnSend2MsHighSide(bv,msg.mtname(),SRB3); // TODO: Is SRB3 correct?
|
|
#else
|
|
LlcDlFrame lframe(1000);
|
|
lframe.setAppendP(0,0);
|
|
msg.gWrite(lframe);
|
|
SGSNLOG("Sending "<<msg.str() <<this<<" frame(first20)="<<lframe.head(MIN(20,lframe.size())));
|
|
mLlcEngine->getLlcGmm()->lleWriteHighSide(lframe,msg.isSenseCmd(),msg.mtname());
|
|
#endif
|
|
}
|
|
|
|
// Incoming packets on a PdpContext come here.
|
|
void SgsnInfo::sgsnWriteHighSide(ByteVector &sdu,int nsapi)
|
|
{
|
|
#if RN_UMTS
|
|
// The PDCP is a complete no-op.
|
|
sgsnSend2MsHighSide(sdu,"userdata",nsapi);
|
|
#else
|
|
mLlcEngine->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"<<si);
|
|
return;
|
|
}
|
|
#if RN_UMTS
|
|
// Must do the Security Proecedure first, message flow like this:
|
|
// L3 AttachRequest
|
|
// MS ---------------------------------> 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"<<si);
|
|
// need to do authentication, send authentication request
|
|
//sendAuthenticationRequest(si);
|
|
}
|
|
sendAuthenticationRequest(si,IMSI);
|
|
#else
|
|
// We must use the TLLI that the MS used, not the PTMSI.
|
|
// To do that, reset the registered status.
|
|
gmm->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:"<<msid());
|
|
return;
|
|
}
|
|
if (! si->mT3310FinishAttach.active()) {
|
|
SGSNERROR("Received security response after T3310 expiration for UE:"<<si);
|
|
return;
|
|
}
|
|
if (success) {
|
|
sendAttachAccept(si); // happiness
|
|
} else {
|
|
SGSNERROR("Integrity Protection failed for UE:"<<si);
|
|
// Oops! We could send an attach reject, but why bother?
|
|
// The UE already knows it failed, no recovery is possible,
|
|
// and it will timeout shortly anyway.
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if RN_UMTS
|
|
static void sendAuthenticationRequest(SgsnInfo *si, string IMSI)
|
|
{
|
|
SIPEngine engine(gConfig.getStr("SIP.Proxy.Registration").c_str(),IMSI.c_str());
|
|
string RAND;
|
|
//bool success =
|
|
engine.Register(SIPEngine::SIPRegister, &RAND);
|
|
// Stick new UE into TMSI table if its not already there
|
|
if (!gTMSITable.TMSI(IMSI.c_str())) gTMSITable.assign(IMSI.c_str());
|
|
|
|
ByteVector rand(RAND.size()/2); // Leave it random.
|
|
for (unsigned i = 0; i < RAND.size(); i++) {
|
|
char ch = (RAND.c_str())[i];
|
|
ch = (ch > '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"<<si);
|
|
return;
|
|
}
|
|
|
|
string IMSI = gmm->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"<<si);
|
|
// need to do authentication, send authentication request
|
|
//sendAuthenticationRequest(si);
|
|
}
|
|
|
|
SgsnAdapter::startIntegrityProtection(si->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:"<<irmsg.str());
|
|
return;
|
|
} else {
|
|
// The MS sent an attach request. Try to send the response using the new IMSI.
|
|
if (! irmsg.mMobileId.isImsi()) {
|
|
SGSNERROR("Identity Response message does not include imsi:"<<irmsg.str());
|
|
return;
|
|
}
|
|
ByteVector passbyreftmp = irmsg.mMobileId.getImsi(); // c++ foo bar
|
|
findGmmByImsi(passbyreftmp,si); // Always succeeds - creates if necessary, sets si->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 <<si);
|
|
// Fall through
|
|
case AttachTypeGprs:
|
|
si->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 "<<si;
|
|
} else {
|
|
SGSNLOG("NOTICE attach type "<<(int)armsg.mAttachType <<si);
|
|
}
|
|
si->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 "<<si;
|
|
}
|
|
updatetype = CombinedRALAUpdated;
|
|
if (! raumsg.mTmsiStatus) {
|
|
// We must assign a tmsi, so make one up.
|
|
// Just use the tlli, but lop off some bits so we can tell what it is.
|
|
sendTmsi = true;
|
|
}
|
|
break;
|
|
}
|
|
si->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"<<LOGHEX2("tlli",wTlli));
|
|
return false; // Not handled yet.
|
|
// TODO:
|
|
// if sgsn not enabled, return false.
|
|
// save the channel?
|
|
// Send the resumption ie in the RR channel release afterward.
|
|
}
|
|
|
|
// WARNING: This runs in a different thread.
|
|
void Sgsn::notifyGsmActivity(const char *imsi)
|
|
{
|
|
}
|
|
|
|
static void handleL3GmmMsg(SgsnInfo *si,ByteVector &frame1)
|
|
{
|
|
L3GmmFrame frame(frame1);
|
|
// Standard L3 header is 2 bytes:
|
|
unsigned mt = frame.getMsgType(); // message type
|
|
//SGSNLOG("CRACKING GMM MSG TYPE "<<mt);
|
|
MSUEAdapter *ms = si->getMS();
|
|
if (ms == NULL) {
|
|
// This is a serious internal error.
|
|
SGSNERROR("L3 message "<<L3GmmMsg::name(mt)
|
|
<<" for non-existent MS Info struct" <<LOGHEX2("tlli",si->mMsHandle));
|
|
return;
|
|
}
|
|
switch (mt) {
|
|
case L3GmmMsg::AttachRequest: {
|
|
L3GmmMsgAttachRequest armsg;
|
|
armsg.gmmParse(frame);
|
|
SGSNLOG("Received "<<armsg.str()<<si);
|
|
handleAttachRequest(si,armsg);
|
|
dumpGmmInfo();
|
|
break;
|
|
}
|
|
case L3GmmMsg::AttachComplete: {
|
|
L3GmmMsgAttachComplete acmsg;
|
|
//acmsg.gmmParse(frame); // not needed, nothing in it.
|
|
SGSNLOG("Received "<<acmsg.str()<<si);
|
|
handleAttachComplete(si,acmsg);
|
|
dumpGmmInfo();
|
|
break;
|
|
}
|
|
case L3GmmMsg::IdentityResponse: {
|
|
L3GmmMsgIdentityResponse irmsg;
|
|
irmsg.gmmParse(frame);
|
|
SGSNLOG("Received "<<irmsg.str()<<si);
|
|
handleIdentityResponse(si,irmsg);
|
|
break;
|
|
}
|
|
case L3GmmMsg::DetachRequest: {
|
|
SGSNLOG("Received DetachRequest");
|
|
handleDetachRequest(si);
|
|
break;
|
|
}
|
|
case L3GmmMsg::DetachAccept:
|
|
SGSNLOG("Received DetachAccept");
|
|
//TODO...
|
|
break;
|
|
case L3GmmMsg::RoutingAreaUpdateRequest: {
|
|
L3GmmMsgRAUpdateRequest raumsg;
|
|
raumsg.gmmParse(frame);
|
|
SGSNLOG("Received "<<raumsg.str()<<si);
|
|
handleRAUpdateRequest(si,raumsg);
|
|
break;
|
|
}
|
|
case L3GmmMsg::RoutingAreaUpdateComplete: {
|
|
L3GmmMsgRAUpdateComplete racmsg;
|
|
//racmsg.gmmParse(frame); not needed
|
|
SGSNLOG("Received RAUpdateComplete "<<si);
|
|
handleRAUpdateComplete(si,racmsg);
|
|
break;
|
|
}
|
|
case L3GmmMsg::GMMStatus: {
|
|
L3GmmMsgGmmStatus stmsg;
|
|
stmsg.gmmParse(frame);
|
|
SGSNLOG("Received GMMStatus: "<<stmsg.mCause<<"=" <<GmmCause::name(stmsg.mCause)<<si);
|
|
break;
|
|
}
|
|
case L3GmmMsg::AuthenticationAndCipheringResp: {
|
|
L3GmmMsgAuthenticationResponse armsg;
|
|
armsg.gmmParse(frame);
|
|
SGSNLOG("Received AuthenticationAndCipheringResp message "<<si);
|
|
handleAuthenticationResponse(si,armsg);
|
|
break;
|
|
}
|
|
case L3GmmMsg::ServiceRequest: {
|
|
L3GmmMsgServiceRequest srmsg;
|
|
srmsg.gmmParse(frame);
|
|
SGSNLOG("Received ServiceRequest message" << si);
|
|
handleServiceRequest(si,srmsg);
|
|
break;
|
|
}
|
|
|
|
// Downlink direction messages:
|
|
//RoutingAreaUpdateAccept = 0x09,
|
|
//AttachAccept = 0x02,
|
|
//AttachReject = 0x04,
|
|
//RoutingAreaUpdateReject = 0x0b,
|
|
|
|
// Other: TODO?
|
|
//ServiceAccept = 0x0d,
|
|
//ServiceReject = 0x0e,
|
|
//PTMSIReallocationCommand = 0x10,
|
|
//PTMSIReallocationComplete = 0x11,
|
|
//AuthenticationAndCipheringRej = 0x14,
|
|
//AuthenticationAndCipheringFailure = 0x1c,
|
|
//GMMInformation = 0x21,
|
|
default:
|
|
//SGSNWARN("Ignoring GPRS GMM message type "<<mt <<L3GmmMsg::name(mt));
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// This is the old UMTS-centric entry point
|
|
//void Sgsn::sgsnWriteLowSide(ByteVector &payload,SgsnInfo *si, unsigned rbid)
|
|
//{
|
|
// // No Pdcp, so just send it off.
|
|
// si->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 "<<si);
|
|
return;
|
|
}
|
|
pd = frame.getNibble(0,0); // protocol descriminator
|
|
switch ((GSM::L3PD) pd) {
|
|
case GSM::L3GPRSMobilityManagementPD: { // Couldnt we shorten this?
|
|
handleL3GmmMsg(si,frame);
|
|
break;
|
|
}
|
|
case GSM::L3GPRSSessionManagementPD: { // Couldnt we shorten this?
|
|
Ggsn::handleL3SmMsg(si,frame);
|
|
break;
|
|
}
|
|
// TODO: Send GSM messages somewhere
|
|
default:
|
|
SGSNERROR("unsupported L3 Message PD:"<<pd);
|
|
}
|
|
} catch(SgsnError) {
|
|
return; // Handled already
|
|
} catch(ByteVectorError) { // oops!
|
|
SGSNERROR("internal error assembling SGSN message, pd="<<pd); // not much to go on.
|
|
}
|
|
}
|
|
|
|
// Forces the SgsnInfo to exist.
|
|
// For GPRS the handle is a TLLI.
|
|
// From GSM03.03 sec 2.6 Structure of TLLI; and reproduced at class MSInfo comments.
|
|
// The top bits of the TLLI encode where it came from.
|
|
// A local TLLI has top 2 bits 11, and low 30 bits are the P-TMSI.
|
|
// For UMTS, the handle is the invariant URNTI.
|
|
SgsnInfo *findSgsnInfoByHandle(uint32_t handle, bool create)
|
|
{
|
|
// Update: the lock is needed because the suspension request is sent by the GSM RR stack
|
|
// running in a separate thread.
|
|
ScopedLock lock(sSgsnListMutex); // I dont think this is necessary, but be safe.
|
|
|
|
SgsnInfo *si, *result = NULL;
|
|
// We can delete unused SgsnInfo as soon as the attach procedure is over,
|
|
// which is 15s, but let them hang around a bit longer so the user can see them.
|
|
int idletime = gConfig.getNum("SGSN.Timer.MS.Idle");
|
|
time_t now; time(&now);
|
|
RN_FOR_ALL(SgsnInfoList_t,sSgsnInfoList,si) {
|
|
if (si->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"<<LOGHEX2("tlli",tlli)<<" to"<<LOGHEX2("ptmsi",ptmsi));
|
|
// this->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:"<<msid());
|
|
return;
|
|
}
|
|
if (success) {
|
|
PdpContext *pdp = si->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"<<LOGHEX(handle)
|
|
<<" T3370:active="<<si->mT3370ImsiRequest.active()
|
|
<<" remaining=" << si->mT3370ImsiRequest.remaining();
|
|
MSUEAdapter *ms = si->getMS();
|
|
if (ms) { os << ms->msid(); }
|
|
else { os << " MS=not_active"; }
|
|
AttachInfo *ati = &si->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"<<LOGVAR(newTlli)
|
|
<<" for SgsnInfo:"<<si
|
|
<<" found existing registered SgsnInfo:"<<othersi);
|
|
// I dont think any recovery is possible; sgsn is screwed up.
|
|
} else {
|
|
// We dont know or care where this old SgsnInfo came from.
|
|
// Destroy it with prejudice and use si, which is the
|
|
// SgsnInfo the MS is using to talk with us right now.
|
|
SGSNWARN("TLLI change procedure"<<LOGVAR(newTlli)
|
|
<<" for SgsnInfo:"<<si
|
|
<<" overwriting existing unregistered SgsnInfo:"<<othersi);
|
|
othersi->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<<LOGVAR2("mFailCode",SmCause::name(mFailCode));
|
|
os<<LOGVAR(mRateDownlink)<<LOGVAR(mRateUplink);
|
|
os<<")";
|
|
}
|
|
|
|
void MSUEAdapter::sgsnPrint(uint32_t mshandle, int options, std::ostream &os)
|
|
{
|
|
ScopedLock lock(sSgsnListMutex); // Probably not needed.
|
|
SgsnInfo *si = sgsnGetSgsnInfoByHandle(mshandle,false);
|
|
if (!si) { os << " GMM state unknown\n"; return; }
|
|
GmmInfo *gmm = si->getGmm(); // Must be non-null or we would not be here.
|
|
if (!gmm) { os << " GMM state unknown\n"; return; }
|
|
gmmInfoDump(gmm,os,options);
|
|
}
|
|
|
|
}; // namespace
|