mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-23 07:42:01 +00:00
310 lines
13 KiB
C++
310 lines
13 KiB
C++
/*
|
|
* Copyright 2013, 2014 Range Networks, Inc.
|
|
*
|
|
* This software is distributed under multiple licenses;
|
|
* see the COPYING file in the main directory for licensing
|
|
* information for this specific distribution.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*/
|
|
|
|
#ifndef _L3MOBILITYMANAGEMENT_H_
|
|
#define _L3MOBILITYMANAGEMENT_H_ 1
|
|
|
|
#include <GSMCommon.h>
|
|
//#include <memory> // for auto_ptr, shared_ptr
|
|
#include "L3StateMachine.h"
|
|
#include "TMSITable.h"
|
|
#include <SIPBase.h>
|
|
#include <GSML3MMElements.h>
|
|
|
|
|
|
namespace SIP { class DialogMessage; };
|
|
|
|
namespace Control {
|
|
using namespace GSM;
|
|
|
|
void NewPagingResponseHandler(const L3PagingResponse* resp, MMContext* mmchan);
|
|
void NewCMServiceResponder(const L3CMServiceRequest* cmsrq, MMContext* mmchan);
|
|
|
|
|
|
|
|
// (pat) LISTEN UP! Before you touch this class hiearchy:
|
|
// There is a major fauxpax in C++ that all virtual classes bypass the class hierarchy and are finalized only in the most derived class.
|
|
// I dont see any implementation reason for this, so it is just speced this way arbitrarily.
|
|
// Therefore if a virtual method in a virtual base class is over-ridden in an intermediate class in a hierarchy involving multiple paths
|
|
// to a final derived class, there must also be a unique method defined in the final derived class.
|
|
// This is true if any class in the path is virtual, so the the virtual class basically contaminates the hierarchy
|
|
// and makes virtual methods nearly worthless in that hierarchy. It is a pretty big oops.
|
|
// The consequence for us in our attempt to create state machines using protected methods to implement the states is that
|
|
// it creates a problem with shared data. The shared data wants to be in a virtual class common to all the classes implementing state machines.
|
|
// However, that same virtual class cannot be in the path of the virtual methods implementing the states.
|
|
// Therefore the shared data must be in a separate class so that the main path to virtual methods is not contaminated by 'virtual' anywhere.
|
|
// The alternatives are either to put the data in a separately allocated class and use pointers, or put virtual accessor methods in
|
|
// the LUBase class and the data itself in the most derived class.
|
|
// Another problem to beware is if the final derived class defines a pure virtual method, that seems to over-ride the definition of the same method
|
|
// in the intermediate classes, which may be a bug.
|
|
|
|
class L3IdentifyMachine : public MachineBase
|
|
{
|
|
const GSM::L3MobileIdentity mMobileID; // We make a copy because the original disappears.
|
|
bool *mResultPtr;
|
|
|
|
protected:
|
|
enum States {
|
|
stateStart,
|
|
};
|
|
MachineStatus machineRunState(int state, const GSM::L3Message *l3msg, const SIP::DialogMessage*sipmsg);
|
|
|
|
public:
|
|
// On success the resultant imsi is placed in wTran->subscriber.
|
|
L3IdentifyMachine(TranEntry *wTran,
|
|
const GSM::L3MobileIdentity &wMobileID,
|
|
bool *wResultPtr) // Returns true on success, false on failure.
|
|
: MachineBase(wTran), mMobileID(wMobileID), mResultPtr(wResultPtr)
|
|
{}
|
|
|
|
const char *debugName() const { return "L3IdentifyMachine"; }
|
|
};
|
|
|
|
enum RegistrationStatus {
|
|
RegistrationUninitialized,
|
|
// We distinguish the "Error" case from "Fail" case, the latter meaning rejection by Registrar.
|
|
RegistrationError, // Something went wrong: network failure, invalid SIP message, etc.
|
|
RegistrationChallenge,
|
|
RegistrationSuccess,
|
|
RegistrationFail, // Rejected by Registrar, in which case the mSipCode tells why.
|
|
};
|
|
|
|
struct RegistrationResult {
|
|
RegistrationStatus mRegistrationStatus; // Over-all result from Registrar.
|
|
MMRejectCause mRejectCause; // Only if mRegistrationStatus == RegistrationFail.
|
|
unsigned mSipCode; // Registration result SIP code, used only for error messages.
|
|
string mRand; // The authentication challenge rand.
|
|
RegistrationResult() : mRegistrationStatus(RegistrationUninitialized), mRejectCause(L3RejectCause::Zero) , mSipCode(0)
|
|
{}
|
|
void regSetSuccess() { mRegistrationStatus = RegistrationSuccess; }
|
|
void regSetError() { mRegistrationStatus = RegistrationError; }
|
|
void regSetFail(unsigned sipCode, MMRejectCause cause) { mRegistrationStatus = RegistrationFail; mSipCode = sipCode; mRejectCause = cause; }
|
|
void regSetChallenge(string wRand) { mRegistrationStatus = RegistrationChallenge; mRand = wRand; }
|
|
bool isValid() { return mRegistrationStatus != RegistrationUninitialized; }
|
|
bool isSuccess() { return mRegistrationStatus == RegistrationSuccess; }
|
|
string text();
|
|
};
|
|
|
|
// Follow on Proceed flag in LocationUpdateAccept, dont release RR connection until T3255, value not specified in spec.
|
|
|
|
// Making the LUStart be base classes did not work because
|
|
// we need a final override for each of the states in each virtual base class.
|
|
// The MMSharedData must not contain MachineBase as a base class because virtual methods
|
|
// and virtual classes do not mix well.
|
|
|
|
// MS sends IMSI
|
|
// A. authorize
|
|
// if authorization failure:
|
|
// TMSITable set AUTH_STATUS = 0. Do we want to delete the record?
|
|
// exit
|
|
// if authorization success:
|
|
// if existing record:
|
|
// TMSITable update lai, classmark, AUTH_STATUS
|
|
// send assignedTmsi to handset.
|
|
// else:
|
|
// assignedTmsi = allocate new TMSI.
|
|
// TMSITable update everything.
|
|
// send newTmsi to handset.
|
|
// MS sends TMSI
|
|
// oldTmsi = tmsi.
|
|
// TMSITable get IMSI for oldTmsi.
|
|
// if no record, get IMSI.
|
|
// authorize
|
|
// if authorization success
|
|
// TMSITable update accessed, and everything else in case it changed.
|
|
// auth failure:
|
|
// We do not delete the record because it may belong to someone else.
|
|
// get IMSI
|
|
// if IMSI matches record, exit.
|
|
// goto A.
|
|
//
|
|
|
|
// The LUR may have been using a TMSI or IMSI.
|
|
// If we get a TMSI we try authentication, but if we fail, it may be a TMSI collision,
|
|
// so at that point we have to query for IMSI and retry authorization.
|
|
enum TmsiStatus {
|
|
tmsiNone, // We dont have a TMSI.
|
|
tmsiProvisional, // MS sent a TMSI that was found in the TMSI table but has not been authenticated yet.
|
|
tmsiAuthenticated, // MS sent a TMSI that was found in the TMSI table (started as tmsiProvisional) and authenticated ok.
|
|
tmsiNotAssigned, // MS sent an IMSI that was found in the TMSI table.
|
|
tmsiFailed, // TMSI failed authentication.
|
|
tmsiNew, // We allocated a new TMSI for this MS.
|
|
};
|
|
|
|
// This class holds the common data for all LocationUpdating sub-states.
|
|
// We save up everything until we have authenticated the MS and then put it in the TMSI table only if MS authenticates ok.
|
|
// Previously it was included as virtual by every Procedure, but now it lives in the TransactionEntry.
|
|
class MMSharedData {
|
|
uint32_t mAssignedTmsi; // a TMSI that has been assigned to the MS either now or in the past.
|
|
TmsiStatus mTmsiStatus;
|
|
public:
|
|
GSM::L3MobileIdentity mLUMobileId; // Copy saved from original request.
|
|
GSM::L3LocationAreaIdentity mLULAI; // Copy saved from original request.
|
|
LocationUpdateType mLUType;
|
|
uint32_t mOldTmsi; // The tmsi sent in LUR, which is irrelevant if the LAI is not ours, but saved only for reporting purposes.
|
|
//string mRAND;
|
|
TmsiTableStore store; // In-memory storage for stuff in the TMSI_TABLE.
|
|
GSM::MobileIDType mQueryType; // What mobileId did we last request: IMSIType or IMEIType?
|
|
RegistrationResult mRegistrationResult;
|
|
// If we received or queried for IMSI (as opposed to registration by TMSI) we set mFullQuery so that
|
|
// in this case we will also optionally query for IMEI.
|
|
Bool_z mFullQuery;
|
|
// The second attempt occurs if registration by TMSI fails, so we have to register by IMSI.
|
|
// This has nothing to do with keeping track of the 2 register messages for each over-all registration attempt.
|
|
Bool_z mSecondAttempt;
|
|
string mPrevRegisterAttemptImsi;
|
|
Bool_z mExpectingTmsiReallocationComplete;
|
|
#if CACHE_AUTH
|
|
Bool_z mUsingCachedAuthentication;
|
|
#endif
|
|
|
|
// Only prints the subset that is interesting for debugging.
|
|
string text() {
|
|
return format("AssignedTmsi=0x%x status=%d RegistrationResult=%s",mAssignedTmsi,mTmsiStatus,
|
|
mRegistrationResult.text().c_str());
|
|
//return format("AssignedTmsi=0x%x status=%d RegistrationResult=%s rand=%s",mAssignedTmsi,mTmsiStatus,
|
|
// mRegistrationResult.text().c_str(),mRAND.c_str());
|
|
}
|
|
|
|
// You must set the tmsistatus of any tmsi you assign, so we wrap that in a method.
|
|
void setTmsi(uint32_t tmsi,TmsiStatus status) { mAssignedTmsi = tmsi; mTmsiStatus = status; }
|
|
// But you can update the tmsi status without changing the tmsi.
|
|
void setTmsiStatus(TmsiStatus status) { mTmsiStatus = status; }
|
|
TmsiStatus getTmsiStatus() { return mTmsiStatus; }
|
|
|
|
//bool haveTmsi() { return mTmsiStatus != tmsiNone && mTmsiStatus ; }
|
|
// Is this a brand new registration?
|
|
// FIXME THIS needs to check that authorization has changed
|
|
// Need two variables: one for new authorization, one for welcome message.
|
|
// bool isFirstTime() { return mTmsiStatus == tmsiNone || mTmsiStatus == tmsiNew; }
|
|
bool isImsiAttach() { return this->mLUType == LUTImsiAttach; }
|
|
// Is this the initial attach on this BTS? Any attach type except periodic updating.
|
|
bool isInitialAttach() { return this->mLUType == LUTImsiAttach || this->mLUType == LUTNormalLocationUpdating; }
|
|
bool needsTmsiAssignment() { return mTmsiStatus == tmsiNew || mTmsiStatus == tmsiNotAssigned; }
|
|
uint32_t getTmsi() { return mAssignedTmsi; }
|
|
|
|
MMSharedData() :
|
|
mAssignedTmsi(0), mTmsiStatus(tmsiNone), mOldTmsi(0), mQueryType(GSM::NoIDType)
|
|
{}
|
|
};
|
|
|
|
class LUBase: public MachineBase {
|
|
protected:
|
|
MMSharedData* ludata() const;
|
|
public:
|
|
LUBase(TranEntry *tran) : MachineBase(tran) {}
|
|
//L3RejectCause getRejectCause();
|
|
bool openRegistration() const;
|
|
bool failOpen() const;
|
|
// Return a persistent IMSI string that will not go away
|
|
string getImsi() const;
|
|
const char * getImsiCh() const;
|
|
uint32_t getTmsi() const { return ludata()->getTmsi(); }
|
|
const string getImsiName() const;
|
|
FullMobileId &subscriber() const;
|
|
};
|
|
|
|
// Initial identification phase of LU - Location Updating.
|
|
class LUStart : public LUBase /*, public virtual LUSharedData*/ {
|
|
MachineStatus sendQuery(GSM::MobileIDType);
|
|
public:
|
|
enum State { // There is no integral start state because the state machine start state receives the LUR message.
|
|
stateSecondAttempt,
|
|
stateHaveImsi,
|
|
stateHaveIds,
|
|
stateRegister1Response,
|
|
};
|
|
MachineStatus stateRecvLocationUpdatingRequest(const GSM::L3LocationUpdatingRequest*);
|
|
MachineStatus stateRecvIdentityResponse(const GSM::L3IdentityResponse *);
|
|
//MachineStatus stateExpiredT3260();
|
|
public:
|
|
MachineStatus machineRunState(int state, const GSM::L3Message* l3msg=0, const SIP::DialogMessage *sipmsg=0);
|
|
public:
|
|
LUStart(TranEntry *wTran) : LUBase(wTran) {}
|
|
friend class L3ProcedureLocationUpdate;
|
|
const char *debugName() const { return "LUStart"; }
|
|
} /*mLUStart*/;
|
|
|
|
class LUAuthentication: public LUBase /*, public virtual LUSharedData*/ {
|
|
enum States {
|
|
stateStart,
|
|
stateRegister2Response
|
|
};
|
|
|
|
MachineStatus machineRunState(int state, const GSM::L3Message* l3msg=0, const SIP::DialogMessage *sipmsg=0);
|
|
public:
|
|
LUAuthentication(TranEntry *wTran) : LUBase(wTran) {}
|
|
const char *debugName() const { return "LUAuthentication"; }
|
|
} /*mLUAuthentication*/;
|
|
|
|
class LUFinish: public LUBase /*, public virtual LUSharedData*/ {
|
|
enum States {
|
|
stateStart,
|
|
stateRegister2Response,
|
|
stateLUAcceptTimeout,
|
|
};
|
|
MachineStatus stateExpiredT3270();
|
|
MachineStatus machineRunState(int state, const GSM::L3Message* l3msg, const SIP::DialogMessage *sipmsg=0);
|
|
MachineStatus statePostAccept();
|
|
MachineStatus stateQueryClassmark();
|
|
MachineStatus stateSendLUResponse();
|
|
//MachineStatus stateLUAcceptTimeout();
|
|
public:
|
|
LUFinish(TranEntry *wTran) : LUBase(wTran) {}
|
|
const char *debugName() const { return "LUFinish"; }
|
|
} /*mLUFinish*/;
|
|
|
|
class LUNetworkFailure: public LUBase /*, public virtual LUSharedData*/ {
|
|
enum States {
|
|
stateStart
|
|
};
|
|
MachineStatus machineRunState(int state, const GSM::L3Message* l3msg, const SIP::DialogMessage *sipmsg=0);
|
|
public:
|
|
const char *debugName() const { return "LUNetworkFailure"; }
|
|
LUNetworkFailure(TranEntry *wTran) : LUBase(wTran) {}
|
|
} /*mLUNetworkFailure*/;
|
|
|
|
void LURInit(const GSM::L3Message *l3msg, MMContext *dcch);
|
|
|
|
|
|
class L3RegisterMachine : public LUBase //MachineBase
|
|
{
|
|
// I started using the engine in the transaction, but we have to create
|
|
// a new callid for each transaction, and the easiest way was to create a new SIPEngine.
|
|
// Update: Now just changing the call_id of the SIPEngine
|
|
string mSRES;
|
|
RegistrationResult *mRResult;
|
|
|
|
protected:
|
|
enum States { // Only state 0 is used, so dont bother with an enum.
|
|
stateStart,
|
|
};
|
|
MachineStatus machineRunState(int state, const GSM::L3Message *l3msg, const SIP::DialogMessage*sipmsg);
|
|
|
|
public:
|
|
L3RegisterMachine(TranEntry *wTran,
|
|
SIP::DialogType wMethod,
|
|
string &wSRES, // may be NULL for the initial registration query to elicit a
|
|
RegistrationResult *wResult // Result returned here: true (1), false(0), timeout (-1).
|
|
);
|
|
const char *debugName() const { return "L3RegisterMachine"; }
|
|
SIP::SipMessage *makeRegisterMsg1();
|
|
};
|
|
|
|
extern void imsiDetach(L3MobileIdentity mobid, L3LogicalChannel *chan);
|
|
|
|
}; // namespace Control
|
|
#endif
|