OpenBTS-UMTS/SGSNGGSN/Sgsn.h

350 lines
16 KiB
C++

/*
* OpenBTS provides an open source alternative to legacy telco protocols and
* traditionally complex, proprietary hardware systems.
*
* Copyright 2011, 2014 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero General
* Public License version 3. See the COPYING and NOTICE files in the main
* directory for licensing information.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*/
#ifndef _SGSN_H_
#define _SGSN_H_
#include "GPRSL3Messages.h"
#include "SgsnExport.h"
#include "GSMCommon.h" // For Z100Timer
#ifndef MIN
#define MIN(a,b) ((a)<=(b)?(a):(b))
#endif
#ifndef RN_FOR_ALL
#define RN_FOR_ALL(type,list,var) \
for (type::iterator itr = (list).begin(); \
itr == (list).end() ? 0 : ((var=*itr++),1);)
#define RN_FOR_ALL_WITH_ITR(type,list,var,itr) \
type::iterator itr; \
for (type::iterator itr1 = (list).begin(),itr=itr1; \
itr1 == (list).end() ? 0 : ((itr=itr1),(var=*itr1++),1);)
#endif
#define NEW_TLLI_ASSIGN_PROCEDURE 0 // NO, dont use this.
namespace SGSN {
extern bool enableMultislot();
extern void sendImplicitlyDetached(SgsnInfo *si);
// 10.5.5.2 AttachType can take all 3 values.
// 10.5.5.1 Attach Result can only be GPRS or Combined, not value 2.
enum AttachType {
AttachTypeGprs = 1, AttachTypeGprsWhileImsiAttached = 2, AttachTypeCombined = 3
};
class SgsnInfo;
const static uint32_t sLocalTlliMask = 0xc0000000;
struct AttachInfo {
AttachType mAttachReqType;
uint32_t mAttachReqPTmsi; // Saved if specified in the attach request.
// MsRadioAcces caps saved from the AttachRequest to go in GmmInfo when it is created.
ByteVector mMsRadioAccessCap;
GMMRoutingAreaIdIE mPrevRaId;
void stashMsgInfo(GMMAttach &msgIEs, bool isAttach);
void copyFrom(AttachInfo &other);
AttachInfo() :
// wont be used until initialized, but init to invalid value for copyFrom.
mAttachReqType((AttachType)0),
mAttachReqPTmsi(0)
{}
};
// This is the state for a single MS identified by IMSI.
// No IMSI, no GmmInfo; this struct is not allocated until the MS coughs up an IMSI.
// TODO: We may, however, eventually keep a list of known TMSI and/or IMSI in
// an sql data-base somewhere so the phone can subsequently attach without
// having to send us an IMSI, however, this will still be per-IMSI.
//
class GmmInfo
{
public:
ByteVector mImsi; // The IMSI.
uint32_t mPTmsi; // The unique P-TMSI we create for this Gmm context.
uint32_t getPTmsi() const { return mPTmsi; }
// For GPRS, teturn the TLLI that we assign to the MS.
// (pat) I think this TLLI may not necessarily equal the TLLI in getSI()->mMsHandle between
// the time we learn the IMSI (and create the GmmInfo) and when we perform the changeTLLI procedure.
uint32_t getTlli() const { return mPTmsi | sLocalTlliMask; }
static const unsigned sNumPdps = 16;
time_t mAttachTime;
time_t mActivityTime;
int mGprsMultislotClass; // -1 means invalid.
Bool_z mGprsGeranFeaturePackI;
AttachInfo mgAttachInfo; // Copied from SgsnInfo.
void setActivity() { time(&mActivityTime); }
void setAttachTime() { time(&mAttachTime); mActivityTime = mAttachTime; }
private:
// mState starts in GmmDeregistered.
GmmState::state mState;
#if RN_UMTS
// When we send a security mode command we save the state here so we know what to do
// when we receive the security mode complete message from the UE.
// We could use the attach status to differentiate these two states, however, it is possible
// that the SGSN and UE disagree about the attach state, so we use a special state for this case.
public:
enum SecurityState {
SecurityStateNone = 0, // nothin happening
SecurityStateAttach = 1, // Starting integrity protection during an attach procedure.
SecurityStateServiceRequest = 2, // Starting integrity protection during a service request procedure.
SecurityStateStarted = 3, // Received Security Mode Complete message.
};
SecurityState mSecState;
#endif
// PDPContext are allocated on request, via ActivatePdpContext message.
// In GPRS, each PdpContext here corresponds to a Sndcp in the mSndcp[].
private:
PdpContext *mPdps[sNumPdps]; // Only 5..15 are used, but we allocate the whole array and index into
// it directly with the RB [Radio Bearer] index, aka NSAPI.
public:
// This points to the SgsnInfo we will use for downlink messages after registration.
// It is not used for the responses during the attach process.
// If msi does not already exist, it is allocated at the same time as GmmInfo,
// however, in that case where it did not pre-exist, it means we have not yet
// heard from the MS using the PTMSI we are going to assign to it, which means
// that there may be no corresponding MSInfo structure yet for this SgsnInfo,
// which case is handled by the TLLI change procecure in L2.
SgsnInfo *msi;
SgsnInfo *getSI() { return msi; }
bool isRegistered() {
return mState == GmmState::GmmRegisteredNormal ||
mState == GmmState::GmmRegisteredSuspsended;
}
void setGmmState(GmmState::state newstate) { mState = newstate; }
GmmState::state getGmmState() { return mState; }
bool isNSapiActive(unsigned nsapi); // True if the pdpcontext is not in state PDP-INACTIVE
// 3GPP 24.008 10.5.7.1: PDP Context Status.
// It is a bit-map of the allocated PdpContext for this ms.
// The result bytes for this IE are not in network order, so return it as two bytes.
PdpContextStatus getPdpContextStatus();
void connectPdp(PdpContext *pdp, mg_con_t *mgp);
bool freePdp(unsigned nsapi); // Free both the PdpContext and the Sndcp.
unsigned freePdpAll(bool freeRabsToo);
PdpContext *getPdp(unsigned nsapi);
void sendSecurityModeComplete();
GmmInfo(ByteVector &imsi);
~GmmInfo();
};
// The data path through SGSN is different for GPRS and UMTS.
// For GPRS it includes LLC, LLE, and SNDCP.
// For UMTS it includes PDCP, which is a no-op, so it is nearly empty.
// Uplink Data Path:
// GPRS and UMTS have significantly different uplink paradigms in that:
// GPRS sends all packets to a single entry point in LLC, whence packets are
// directed based on information in the packet.
// But there is a wrinkle in that during the attach process there are multiple active TLLIs
// (the old one the ms uses to contact us, and the new one we are assigning) and they
// each have their own LLC state machine, but both must map to the same MS state info.
// TODO: The above is incorrect - there should be only one set of LLC.
// UMTS segregates data channels into RBs, so there is one entry point at the low end of the
// sgsn for each tuple of UE-identifier + RB-id, where the UE-identifier uniquely
// corresponds to an SgsnInfo, and RB-id is 3 is for L3 messages and 5..15 are for user data.
// The GPRS MS sends stuff to:
// sgsnWriteLowSide(ByteVector &payload,SgsnInfo *si);
// The UMTS UE sends stuff to:
// sgsnWriteLowSide(ByteVector &payload,RB????);
// At the high end of the uplink path, the PdpContext sends packets to:
// void SgsnInfo::sgsnSend2PdpLowSide(int nsapi, ByteVector &packet)
// which calls:
// PdpContext *pdp->pdpWriteLowSide();
// Downlink Data Path:
// The pdp context sends stuff to:
// SgsnInfo *si->sgsnWriteHighSide(ByteVector &pdu,int mNSapi);
// At the low end of the downlink path, stuff goes to:
// void sgsnSend2MsHighSide(ByteVector &pdu,const char *descr, int rbid);
// which calls:
// SgsnInfo *si->getMS()->msDownlinkQueue.write(GPRS::DownlinkQPdu *dlpdu);
// There is one SgsnInfo for each TLLI.
// This does not map one-to-one to MSInfo structs because MSInfo change TLLI when registered.
// During the attach process an MS may call in with several different TLLIs
// and/or PTMSIs, which will create a bunch of MSInfo structs in GPRS and corresponding
// SgsnInfos here. We dont know what MS these really are until we get an IMSI,
// or todo: see a known TMSI/PTSMI+RAI pair.
// Upon completion of a successful attach, sgsn assigns (or looks up) a new PTMSI
// for the MS, which it will use to talk back to us with yet another new TLLI based on
// the PTMSI that it sent us.
// The Session Management State is in the GmmInfo and is per-MS, not per-TLLI.
// There is a problem that during the attach procedure, uplink and downlink
// messages may be occuring simultaneously with different TLLIs,
// even though they are just one MS and we want to keep gprs straight about
// that so it does not try to start multiple TBFs for the same MS.
// This is how we resolve all that:
// The per-tlli info is in two structs: MSInfo in gprs and SgsnInfo here.
// The MSInfo has a tlli, an oldTlli, and an altTlli, see documentation at the class.
// In the sgsn, there is one SgsnInfo for each tlli.
// (It could have been organized like MSInfo, but is not just for historical reasons.)
// In the sgsn, before we have identified the MS we keep all the information
// in the SgsnInfo, which includes everything we need to remember from the
// RAUpdate or AttachRequest messages.
// After a successful AttachComplete, we will subsequently use only
// one TLLI, and therefore one SgsnInfo, for downlink messages,
// although we still accept uplink messages using the old TLLI,
// and the MS may continue to send messages using
// those old TLLIs well into the PdpContext activation procedures.
// So this process results in one SgsnInfo that is associated with the TLLI that
// finally successfully attached, one SgsnInfo for the TLLI the MS used to initiate
// the attach, and we also may end up with a bunch of ephemeral SgsnInfo
// associated with TLLIs that the MS tried but did not finish attaching.
// Note that the ephemeral SgsnInfo must send send an L3 attach message
// (to assign the local TLLI) using the immensely over-bloated LLC machinery
// to get the MS attached to a semi-permanent SgsnInfo.
// I was really tempted just to hard-code the single LLC message that is required,
// but I didn't; we allocate a whole new LLC and send the L3 message through it.
// I think after the attach process we could just get rid of the SgsnInfo, and let them
// be created anew if the MS wants to talk to us again.
// ==========
// The LLC state machine is DEFINED as being per-TLLI.
// From 3GPP 04.64 [LLC] 4.5.1: "A logical link connection is identified by a DLCI
// consisting of two identifiers: a SAPI and a TLLI."
// However, during the Attach process we change the TLLI using the procedures
// defined in 3GPP 04.64 8.3.
// The GmmInfo holds the Session Management info, and is associated with one
// and only one MS identified by IMSI.
// There are two major types of SgsnInfo:
class SgsnInfo
{
friend class MSUEAdapter;
// The LlcEngine is used only by GPRS.
// UMTS uses PDCP, but it is a complete no-op, so is not represented here at all.
// The LLC sits between GPRS and SGSN and could have been combined with GPRS instead
// of being in the SGSN at all. Here, it is encapsulated entirely within this object.
// I left the LLC component in the SGSN for several reasons:
// in case we implement handover, the LLC state must be passed too;
// and just because that is the way things are partitioned in commercial systems.
private:
GmmInfo *mGmmp;
public:
GmmInfo *getGmm() const { return mGmmp; }
void setGmm(GmmInfo *gmm);
LlcEngine *mLlcEngine;
time_t mLastUseTime;
//GmmMobileIdentityIE mAttachMobileId;
// For the local SGSN, this is the P-TMSI/TLLI that we [are attempting to]
// assign to this MS. In the old separated SGSN system, the SGSN would remember both old
// and new [being assigned] TLLI, and would try to change the TLLI in the BTS simultaneously,
// keeping everything in sync, but we dont do that any more. We let the GPRS directory create
// a separate MSInfo structure for each TLLI, each of which maps to an independent SgsnInfo struct
// for that TLLI, because each needs a unique LLC state machine.
//
// In L3 messages, we send P-TMSI to ms and it is supposed to convert that to
// a "TLLI" of some sort when talking back to us by twiddling the top bits
// depending on the Routing Area. In the same Routing Area, it sets the top 2 bits.
// Which is silly for us, so we just set the top 2 bits to start with and use
// the same 32-bit number for TLLI and P-TMSI.
uint32_t mMsHandle; // The single TLLI or URNTI associated with this SgsnInfo.
// If it is an assigned SgsnInfo, this is equal to the P-TMSI we allocated for the MS.
// I tried to implement the TLLI Assign Procedure by adding an AltTlli to this
// SgsnInfo struct, but it does not work well, because after an attach, if
// the MS tries to attach again (happens if it is powercycled or confused)
// then we really need the two SgsnInfo separate again so we can communicate
// with the MS distinctly with the old TLLI for the attach accept message.
// Also it was easy to end up with two SgsnInfo with the same TLLI in that case,
// so its easier to just keep them distinct.
// So an SgsnInfo is synonymous with a single TLLI.
#if NEW_TLLI_ASSIGN_PROCEDURE
uint32_t mAltTlli; // For GPRS, this is the alternate TLLI before a TLLI assignment
// procedure that occurs at AttachComplete. This SgsnInfo must
// respond to both new and alt tllis, but only the tlli
// in mMsHandle is used for downlink communication.
#endif
SgsnInfo *changeTlli(bool now);
//uint32_t getTmsi() { return mMsHandle; } // NOT RIGHT!
//uint32_t getPTmsi() { return mMsHandle; } // NOT RIGHT!
ByteVector mRAND;
// The information in the L3 AttachRequest is rightly part of the GmmInfo context,
// but when we receive the message, that does not exist yet, so we save the
// AttachRequest info in the SgsnInfo here.
// This is the type of attach the MS requested. It is saved from the AttachRequest so it can
// be used to send the correct attach accept message after the MS identity request handshake(s).
AttachInfo mtAttachInfo;
AttachType attachResult() {
if (mtAttachInfo.mAttachReqType == AttachTypeGprsWhileImsiAttached) {
return AttachTypeCombined;
}
return mtAttachInfo.mAttachReqType;
}
void deactivateRabs(unsigned nsapiMask);
// Pass throughs to GmmInfo. All must be protected from mGmmp == NULL.
bool isRegistered() const { return mGmmp && mGmmp->isRegistered(); }
PdpContextStatus getPdpContextStatus() { return mGmmp ? mGmmp->getPdpContextStatus() : PdpContextStatus(); }
bool freePdp(unsigned nsapi) { return mGmmp ? mGmmp->freePdp(nsapi) : false; }
unsigned freePdpAll(bool freeRabsToo) { return mGmmp ? mGmmp->freePdpAll(freeRabsToo) : 0; }
PdpContext *getPdp(unsigned nsapi) { return mGmmp ? mGmmp->getPdp(nsapi) : 0; }
// After packet has been processed by LLC or PDCP, this sends it to the correct PdpContext.
void sgsnSend2PdpLowSide(int nsapi, ByteVector &packet);
void sgsnSend2MsHighSide(ByteVector &pdu,const char *descr, int rbid);
void sgsnWriteHighSide(ByteVector &sdu,int nsapi);
// 24.008 11.2.2. T3310 is in the MS and is 15s. We are using it here similarly to place a limit
// on the Attach Request process, so when we receive an Identity Response we dont send
// an attach long afterward.
GSM::Z100Timer mT3310FinishAttach;
// T3370 is the ImsiRequest repeat at 6s, but the total time is only 15s, so its hardly worth bothering,
// so we dont; the MS will RACH again if necessary. But here is the timer anyway.
GSM::Z100Timer mT3370ImsiRequest;
SgsnInfo(uint32_t wTlli); // May be a URNTI instead of TLLI.
~SgsnInfo();
void sirm(); // Remove si from list and delete it.
MSUEAdapter *getMS() const;
void sgsnReset();
// Downlink L3 Messages come here.
void sgsnWriteHighSideMsg(L3GprsDlMsg &msg);
//GMMAttach *atp; // All the info from the attach or ra-update.
//GPRS::MSInfo *mMs; // The MSInfo associated with this SgsnInfo.
friend std::ostream& operator<<(std::ostream& os, const SgsnInfo*si);
};
std::ostream& operator<<(std::ostream& os, const SgsnInfo*si);
void handleL3Msg(SgsnInfo *si, ByteVector &payload);
void gmmDump(std::ostream&os);
void sgsnInfoDump(SgsnInfo *si,std::ostream&os);
void gmmInfoDump(GmmInfo *si,std::ostream&os,int options);
SgsnInfo *findSgsnInfoByHandle(uint32_t handle,bool create);
GmmInfo *findGmmByImsi(ByteVector&imsi,SgsnInfo *si);
bool cliSgsnInfoDelete(SgsnInfo *si);
void cliGmmDelete(GmmInfo *gmm);
}; // namespace
#endif