/*
* 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
