1068 lines
37 KiB
C
1068 lines
37 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 _GPRSL3MESSAGES_H_
|
||
|
#define _GPRSL3MESSAGES_H_
|
||
|
|
||
|
#include <stdio.h>
|
||
|
//#include "GPRSInternal.h"
|
||
|
#include "Configuration.h"
|
||
|
#include "GSMCommon.h"
|
||
|
#include "GSML3Message.h"
|
||
|
#include "ByteVector.h"
|
||
|
#include "Utils.h"
|
||
|
#include "ScalarTypes.h"
|
||
|
#include "SgsnBase.h"
|
||
|
//#include "GPRSL3New.h"
|
||
|
extern ConfigurationTable gConfig;
|
||
|
using namespace std;
|
||
|
#define DEHEXIFY(c) ((c)>=10 ? (c)-10+'a' : (c)+'0')
|
||
|
|
||
|
namespace SGSN {
|
||
|
struct LlcEntity;
|
||
|
|
||
|
// 10.5.5.18 Update Type uses all four values.
|
||
|
// 10.5.5.17 Update Result uses the first two values.
|
||
|
// RA is for GPRS, and LA is for GSM CS [circuit switched].
|
||
|
enum RAUpdateType {
|
||
|
RAUpdated = 0, CombinedRALAUpdated = 1, CombinedRALAWithImsiAttach = 2, PeriodicUpdating = 3
|
||
|
};
|
||
|
|
||
|
|
||
|
#if 0
|
||
|
// An L3Message as known by the GPRS code.
|
||
|
// The L3Message is not really applicable here, because we do not
|
||
|
// really know enough about this message to set the bodylength etc. yet.
|
||
|
//struct GPRSL3Message : ByteVector {
|
||
|
// unsigned mSkip;
|
||
|
// unsignd mPD;
|
||
|
// unsigned mMITI;
|
||
|
//
|
||
|
// // Parse out the common L3Message header
|
||
|
// GPRSL3Message(ByteVector &vec) : ByteVector(vec.begin(),vec,size())
|
||
|
// {
|
||
|
// mSkip = getByte(0) >> 4;
|
||
|
// mPD = getByte(0) & 0xf;
|
||
|
// mMITI = getByte(1);
|
||
|
// }
|
||
|
//};
|
||
|
#endif
|
||
|
|
||
|
// The L3Message is built on L3Frame which assumes that messages are in BitVectors,
|
||
|
// but for GPRS messages come from RLC (and LLC and LLE) and they are ByteVectors.
|
||
|
class L3GprsMsg : public Text2Str
|
||
|
{ public:
|
||
|
virtual void text(std::ostream &os) const = 0;
|
||
|
virtual int MTI() const =0;
|
||
|
virtual const char *mtname() const =0;
|
||
|
};
|
||
|
|
||
|
// All Gmm and Sm messages need a sense that is either reply or command.
|
||
|
// For SM messages, the sense is encoded in the transaction-identifier field.
|
||
|
// For both types, the sense is also encoded in one of the LLC headers.
|
||
|
// Cant be be too too redundant redundant.
|
||
|
class L3GprsDlMsg : public virtual L3GprsMsg
|
||
|
{ public:
|
||
|
enum MsgSense { senseInvalid, senseReply, senseCmd } mSense;
|
||
|
L3GprsDlMsg(MsgSense wSense) : mSense(wSense) {}
|
||
|
// This dummy constructor is used for uplink messages, when message can be both up and downlink:
|
||
|
L3GprsDlMsg() : mSense(senseInvalid) {}
|
||
|
// void setSense(MsgSense wSense) { mSense = wSense; } //unused.
|
||
|
bool isSenseCmd() {
|
||
|
assert(mSense != senseInvalid);
|
||
|
return mSense == senseCmd;
|
||
|
}
|
||
|
virtual void gWrite(ByteVector &msg) = 0;
|
||
|
};
|
||
|
|
||
|
|
||
|
// An L3Frame for Gprs.
|
||
|
class L3GprsFrame : public ByteVector
|
||
|
{
|
||
|
public:
|
||
|
void dump(std::ostream &os);
|
||
|
L3GprsFrame(ByteVector &vec) : ByteVector(vec) {}
|
||
|
|
||
|
GSM::L3PD getPD() { return (GSM::L3PD)getNibble(0,0); } // protocol descriminator
|
||
|
//unsigned getSkip() { return getNibble(0,1); } // skip indicator
|
||
|
virtual unsigned getMsgType() { assert(0); }
|
||
|
// TODO: Handle extended transaction id 24.007
|
||
|
virtual unsigned getBodyOffset() { assert(0); }
|
||
|
|
||
|
unsigned mIEI; // cache for message parsers.
|
||
|
|
||
|
unsigned readIEI(size_t &rp) {
|
||
|
mIEI = readByte(rp);
|
||
|
return mIEI;
|
||
|
}
|
||
|
|
||
|
ByteVector readLVasBV(size_t &rp) {
|
||
|
int len = readByte(rp);
|
||
|
ByteVector result; result.clone(segment(rp,len));
|
||
|
rp += len;
|
||
|
return result;
|
||
|
}
|
||
|
// Skip over an IE we dont care about:
|
||
|
void skipLV(size_t &rp) {
|
||
|
int len = readByte(rp);
|
||
|
rp += len;
|
||
|
}
|
||
|
|
||
|
// The minlen and maxlen are of the IE itself, excluding to Type (if any) and Length byte.
|
||
|
void skipLV(size_t &rp, int minlen, int maxlen, const char *text) {
|
||
|
int len = readByte(rp);
|
||
|
if (len < minlen || len > maxlen) {
|
||
|
LLCWARN("unexpected message length for iei:"<<mIEI<<":"<<text);
|
||
|
}
|
||
|
rp += len;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class L3GmmFrame : public L3GprsFrame
|
||
|
{ public:
|
||
|
unsigned getSkip() { return getNibble(0,1); } // skip indicator
|
||
|
unsigned getMsgType() { return getByte(1); }
|
||
|
// TODO: Handle extended transaction id 24.007
|
||
|
unsigned getBodyOffset() { return 2; } // Where message specific body begins.
|
||
|
L3GmmFrame(ByteVector &vec) : L3GprsFrame(vec) {}
|
||
|
};
|
||
|
|
||
|
class L3SmFrame: public L3GprsFrame
|
||
|
{ public:
|
||
|
// 24.007 11.2.3.1.3: Transaction identifier - it may have an extension byte,
|
||
|
// which will also move the message type and the body location in the message.
|
||
|
unsigned getTIflag() { return getField(0,1); } // First bit in message.
|
||
|
bool isTiExt() { return getField(1,3) == 7; }
|
||
|
unsigned getTI() {
|
||
|
unsigned ti = getField(1,3);
|
||
|
if (ti == 7) { // special value indicates extension.
|
||
|
ti = getField2(1,1,7);
|
||
|
}
|
||
|
return ti;
|
||
|
}
|
||
|
// 24.008 10.4: Top 2 bits do something else.
|
||
|
unsigned getMsgType() {
|
||
|
return getByte(isTiExt() ? 2 : 1);
|
||
|
}
|
||
|
unsigned getBodyOffset() { // Where message specific body begins.
|
||
|
return isTiExt() ? 3 : 2;
|
||
|
}
|
||
|
L3SmFrame(ByteVector &vec) : L3GprsFrame(vec) {}
|
||
|
};
|
||
|
|
||
|
// This is defined as an L3Message, however, the functions in L3Message
|
||
|
// are generally inapplicable because the transport for these messages
|
||
|
// uses an LLC layer wrapper inside an RLCControlMessage wrapper.
|
||
|
// The BTS does not worry about these, however, these are here to allow
|
||
|
// debug inspection of LLC packets passing through the BTS.
|
||
|
struct L3GmmMsg : public virtual L3GprsMsg //, public Text2Str
|
||
|
{
|
||
|
// Protocol Discriminator GSM 04.07 11.2.3.1.1
|
||
|
static const unsigned mPD = GSM::L3GPRSMobilityManagementPD; // 8
|
||
|
// This should be declared static, but it is not static in L3Message.
|
||
|
GSM::L3PD PD() const { return GSM::L3GPRSMobilityManagementPD; }
|
||
|
|
||
|
// GSM 04.08, 24.008 table 10.4: Message Types for GPRS Mobility Management.
|
||
|
// Note that the IdentityRequest/IdentityResponse are DIFFERENT NUMBERS
|
||
|
// than the identically named messages in Table 10.2: Message Types
|
||
|
// for Mobility Management.
|
||
|
// Note that mobility management is handled inside the SGSN and the MS,
|
||
|
// and these messages are usually transferred opaquely through the BTS,
|
||
|
// so the BTS does not have to worry about this.
|
||
|
enum MessageType {
|
||
|
AttachRequest = 0x01,
|
||
|
AttachAccept = 0x02,
|
||
|
AttachComplete = 0x03,
|
||
|
AttachReject = 0x04,
|
||
|
DetachRequest = 0x05,
|
||
|
DetachAccept = 0x06,
|
||
|
RoutingAreaUpdateRequest = 0x08,
|
||
|
RoutingAreaUpdateAccept = 0x09,
|
||
|
RoutingAreaUpdateComplete = 0x0a,
|
||
|
RoutingAreaUpdateReject = 0x0b,
|
||
|
ServiceRequest = 0x0c,
|
||
|
ServiceAccept = 0x0d,
|
||
|
ServiceReject = 0x0e,
|
||
|
PTMSIReallocationCommand = 0x10,
|
||
|
PTMSIReallocationComplete = 0x11,
|
||
|
AuthenticationAndCipheringReq = 0x12,
|
||
|
AuthenticationAndCipheringResp = 0x13,
|
||
|
AuthenticationAndCipheringRej = 0x14,
|
||
|
AuthenticationAndCipheringFailure = 0x1c,
|
||
|
IdentityRequest = 0x15,
|
||
|
IdentityResponse = 0x16,
|
||
|
GMMStatus = 0x20,
|
||
|
GMMInformation = 0x21,
|
||
|
};
|
||
|
const char *mtname() const { return name(MTI()); }
|
||
|
static const char *name(unsigned mt, bool ornull=0);
|
||
|
static void dump(L3GmmFrame &frame, std::ostream &os);
|
||
|
virtual void textBody(std::ostream&os) const = 0;
|
||
|
void text(std::ostream&os) const;
|
||
|
};
|
||
|
std::ostream& operator<<(std::ostream& os, L3GmmMsg::MessageType mt);
|
||
|
|
||
|
class L3GmmDlMsg : public virtual L3GmmMsg, public L3GprsDlMsg
|
||
|
{ protected:
|
||
|
virtual void gmmWriteBody(ByteVector &msg) = 0;
|
||
|
public:
|
||
|
L3GmmDlMsg(MsgSense wSense) : L3GprsDlMsg(wSense) {} // For a downlink message
|
||
|
L3GmmDlMsg() {} // For uplink messages
|
||
|
void gWrite(ByteVector &msg);
|
||
|
};
|
||
|
|
||
|
class L3GmmUlMsg : public virtual L3GmmMsg
|
||
|
{ protected:
|
||
|
virtual void gmmParseBody(L3GmmFrame &src, size_t &rp) = 0;
|
||
|
public:
|
||
|
void gmmParse(L3GmmFrame &frame);
|
||
|
};
|
||
|
|
||
|
///@name GSM04.08 table 10.4a: Message types for GPRS session management.
|
||
|
// GSM 24.008 10.4 has a more complete list.
|
||
|
// The BTS does not worry about these, however, these are here to allow
|
||
|
// debug inspection of LLC packets passing through the BTS.
|
||
|
class L3SmMsg : public virtual L3GprsMsg //, public Text2Str
|
||
|
{ public:
|
||
|
// Init transaction id to invalid value. It must be specified for every downlink SM message.
|
||
|
int mTransactionId;
|
||
|
L3SmMsg() : mTransactionId(-1) {}
|
||
|
|
||
|
static const unsigned mPD = GSM::L3GPRSSessionManagementPD; // 10
|
||
|
GSM::L3PD PD() const { return GSM::L3GPRSSessionManagementPD; }
|
||
|
|
||
|
enum MessageType {
|
||
|
ActivatePDPContextRequest = 0x41,
|
||
|
ActivatePDPContextAccept = 0x42,
|
||
|
ActivatePDPContextReject = 0x43,
|
||
|
RequestPDPContextActivation = 0x44,
|
||
|
RequestPDPContextActivationReject = 0x45,
|
||
|
DeactivatePDPContextRequest = 0x46, // not a very symmetric naming system here.
|
||
|
DeactivatePDPContextAccept = 0x47,
|
||
|
ModifyPDPContextRequest = 0x48, // network to MS direction.
|
||
|
ModifyPDPContextAccept = 0x49, // MS to netowrk direction.
|
||
|
ModifyPDPContextRequestMS = 0x4a, // MS to network direction.
|
||
|
ModifyPDPContextAcceptMS = 0x4b, // netowrk to MS direction.
|
||
|
ModifyPDPContextReject = 0x4c,
|
||
|
ActivateSecondaryPDPContextRequest = 0x4d,
|
||
|
ActivateSecondaryPDPContextAccept = 0x4e,
|
||
|
ActivateSecondaryPDPContextReject = 0x4f,
|
||
|
|
||
|
// GSM 24.008 says: 0x50 - 0x54: "Reserved: was allocated in
|
||
|
// earlier phases of the protocol"
|
||
|
// But here they are anyway:
|
||
|
ActivateAAPDPContextRequest = 0x50, // Anonymous Access PDP.
|
||
|
ActivateAAPDPContextAccept = 0x51,
|
||
|
ActivateAAPDPContextReject = 0x52,
|
||
|
DeactivateAAPDPContextRequest = 0x53,
|
||
|
DeactivateAAPDPContextAccept = 0x54,
|
||
|
|
||
|
SMStatus = 0x55,
|
||
|
// From GSM 24.008:
|
||
|
ActivateMBMSContextRequest = 0x56,
|
||
|
ActivateMBMSContextAccept = 0x57,
|
||
|
ActivateMBMSContextReject = 0x58,
|
||
|
RequestMBMSContextActivation = 0x59,
|
||
|
RequestMBMSContextActivationReject = 0x5a,
|
||
|
|
||
|
RequestSecondaryPDPContextActivation = 0x5b,
|
||
|
RequestSecondaryPDPContextActivationReject = 0x5c,
|
||
|
Notification = 0x5d,
|
||
|
};
|
||
|
const char *mtname() const { return name(MTI()); }
|
||
|
static const char *name(unsigned mt, bool ornull=0); // convert mt to a string name of the message.
|
||
|
static void dump(L3SmFrame &frame, std::ostream &os); // just dump the header. old routine.
|
||
|
virtual void textBody(std::ostream&os) const = 0;
|
||
|
void text(std::ostream&os) const;
|
||
|
};
|
||
|
std::ostream& operator<<(std::ostream& os, L3SmMsg::MessageType mt);
|
||
|
|
||
|
class L3SmDlMsg : public virtual L3SmMsg, public L3GprsDlMsg
|
||
|
{ protected:
|
||
|
// All SM downlink messages must have a ti [Transaction Identifier.]
|
||
|
// The assertion in smWrite guarantees that the correct constructor was called.
|
||
|
L3SmDlMsg(unsigned ti,MsgSense wSense) : L3GprsDlMsg(wSense) { mTransactionId = ti; }
|
||
|
// This dummy constructor is used for uplink messages, when message can be both up and downlink:
|
||
|
L3SmDlMsg() {}
|
||
|
void appendTiPd(ByteVector &msg); // Append Transaction Id and PD to msg.
|
||
|
virtual void smWriteBody(ByteVector &msg) = 0;
|
||
|
public:
|
||
|
void gWrite(ByteVector &msg);
|
||
|
};
|
||
|
|
||
|
class L3SmUlMsg : public virtual L3SmMsg
|
||
|
{ protected:
|
||
|
virtual void smParseBody(L3SmFrame &src, size_t &rp) = 0;
|
||
|
void smParse(L3SmFrame &frame);
|
||
|
};
|
||
|
|
||
|
const char *L3GprsMsgType2Name(unsigned pd, unsigned mt);
|
||
|
const char *L3GprsMsgType2Name(ByteVector &vec);
|
||
|
|
||
|
// 3GPP 24.008 10.5.7.3 Gprs Timer IE
|
||
|
// The timers themselves are in 24.008 11.2.2
|
||
|
struct GprsTimerIE
|
||
|
{
|
||
|
unsigned mUnits; // => 0 (2-sec interval) or 1 (minutes) or 2 (deci-hours) or 7 deactivated
|
||
|
unsigned mValue; // 5 bit range.
|
||
|
GprsTimerIE() : mUnits(7), mValue(0) {} // Deactivate.
|
||
|
void setSeconds(unsigned numSeconds) {
|
||
|
if (numSeconds > 62) { setMinutes((numSeconds+59)/60); return; }
|
||
|
mUnits = 0; // 2-second increments.
|
||
|
mValue = numSeconds/2; //
|
||
|
}
|
||
|
void setMinutes(unsigned numMinutes) {
|
||
|
if (numMinutes > 31) {
|
||
|
mUnits = 2; // deci-hours
|
||
|
mValue = (numMinutes+5)/6;
|
||
|
} else {
|
||
|
mUnits = 1; // minutes
|
||
|
mValue = numMinutes;
|
||
|
}
|
||
|
}
|
||
|
unsigned getSeconds() const {
|
||
|
switch (mUnits) {
|
||
|
case 0: return mValue*2;
|
||
|
case 1: return mValue*60;
|
||
|
case 2: return mValue*600;
|
||
|
default: return 0x7fffffff; // positive infinity
|
||
|
}
|
||
|
}
|
||
|
unsigned getIEValue() const { return (mUnits << 5) | (mValue & 0x1f); }
|
||
|
void appendElement(ByteVector &msg) { msg.appendByte(getIEValue()); }
|
||
|
};
|
||
|
|
||
|
// 24.008 10.5.5.15
|
||
|
// See also GSM::L3LocationAreaIdentity
|
||
|
struct GMMRoutingAreaIdIE : Text2Str
|
||
|
{
|
||
|
unsigned char mMCC[3], mMNC[3];
|
||
|
uint16_t mLAC;
|
||
|
uint8_t mRAC;
|
||
|
void parseElement(ByteVector &pp, size_t &rp);
|
||
|
void raLoad();
|
||
|
void appendElement(ByteVector &msg);
|
||
|
void text(std::ostream&os) const;
|
||
|
GMMRoutingAreaIdIE(); // constructor zeros mMCC and mMNC
|
||
|
bool valid() { return mMCC[0] || mMCC[1] || mMCC[2]; } // Has IE been set?
|
||
|
};
|
||
|
|
||
|
// 24.008 10.5.1.4 Mobile Identity
|
||
|
// We dont really care what the MS/UE tells us except for TMSI,
|
||
|
// so treat TMSI specially, and otherwise just save the whole thing.
|
||
|
struct GmmMobileIdentityIE : Text2Str
|
||
|
{
|
||
|
Field_z<3> mTypeOfId; // From the first byte.
|
||
|
// Either mTmsi or mIdData+mLen is valid.
|
||
|
uint32_t mTmsi; // tmsi or ptmsi
|
||
|
unsigned char mIdData[8]; // Original data from the IEI.
|
||
|
unsigned mLen; // Length of mIdData = length of IE.
|
||
|
bool mPresent;
|
||
|
GmmMobileIdentityIE() : mPresent(false) {}
|
||
|
|
||
|
bool isImsi() const { return mTypeOfId == 1; }
|
||
|
bool isTmsi() const { return mTypeOfId == 4; } // TMSI or P-TMSI
|
||
|
|
||
|
// Parse out an IMSI from the data, and return it in the ByteVector, which must have room to hold it,
|
||
|
// and return true if it was found, false otherwise.
|
||
|
ByteVector getImsi() const;
|
||
|
uint32_t getTmsi() const { assert(isTmsi()); return mTmsi; }
|
||
|
void decodeIM(ByteVector &result) const;
|
||
|
void parseLV(ByteVector &pp, size_t &rp);
|
||
|
const string getAsBcd() const; // its not bcd but readable
|
||
|
void text(std::ostream&os) const;
|
||
|
// write the length and value, but not the IEI type.
|
||
|
void appendLV(ByteVector &msg);
|
||
|
|
||
|
void setTmsi(uint32_t tmsi) {
|
||
|
mPresent = true;
|
||
|
mTypeOfId = 4;
|
||
|
mTmsi = tmsi; // TMSI or P-TMSI
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// We keep only the ByteVector of the QoS IE itself,
|
||
|
// but the description in 24.008 of the IE numbers octets starting at 3,
|
||
|
// and bits 8..1, so we will do the same:
|
||
|
#define DEFINE_QOS_FIELD(name,octet,bitr,length) \
|
||
|
unsigned get##name() { return getField2(octet-3,8-bitr,length); } \
|
||
|
void set##name(unsigned val) { setField2(octet-3,8-bitr,val,length); }
|
||
|
|
||
|
// 3GPP 24.008 10.5.6.5 Quality of Service
|
||
|
// We leave it as a ByteVector, read the values out of it.
|
||
|
struct SmQoS : public ByteVector {
|
||
|
// We dont fill in every bit, and the unused ones are defined as zero,
|
||
|
// so just zero the whole thing before starting.
|
||
|
SmQoS(ByteVector &bv) : ByteVector(bv) {}
|
||
|
SmQoS(unsigned size) : ByteVector(size) {fill(0);}
|
||
|
// Octet 3 (meaning first byte in the QoS ByteVector.)
|
||
|
DEFINE_QOS_FIELD(DelayClass,3,6,3)
|
||
|
DEFINE_QOS_FIELD(ReliabilityClass,3,3,3)
|
||
|
// Octet 4:
|
||
|
DEFINE_QOS_FIELD(PeakThroughputCode,4,8,4)
|
||
|
DEFINE_QOS_FIELD(PrecedenceClass,4,3,3)
|
||
|
// Octet 5:
|
||
|
DEFINE_QOS_FIELD(MeanThroughputCode,5,5,5)
|
||
|
// Octet 6:
|
||
|
DEFINE_QOS_FIELD(TrafficClass,6,8,3)
|
||
|
DEFINE_QOS_FIELD(DeliveryOrder,6,5,2)
|
||
|
DEFINE_QOS_FIELD(DeliveryOfErrSdu,6,3,3)
|
||
|
// Octet 7:
|
||
|
DEFINE_QOS_FIELD(MaxSduSize,7,8,8)
|
||
|
// Octet 8,9: (actual 0-based bytes number 5 and 6)
|
||
|
DEFINE_QOS_FIELD(MaxBitRateUplinkCode,8,8,8)
|
||
|
DEFINE_QOS_FIELD(MaxBitRateDownlinkCode,9,8,8)
|
||
|
// Octet 10:
|
||
|
DEFINE_QOS_FIELD(ResidualBER,10,8,4)
|
||
|
DEFINE_QOS_FIELD(SduErrorRatio,10,4,4)
|
||
|
// Octet 11:
|
||
|
DEFINE_QOS_FIELD(TransferDelay,11,8,6)
|
||
|
DEFINE_QOS_FIELD(TrafficHandlingPriority,11,2,2)
|
||
|
// Octet 12,13:
|
||
|
// "The Guaranteed Bit Rate is ignored if the Traffic Class is
|
||
|
// Interactive or Background class or the max bit rate for downlink is 0 kBps
|
||
|
// 0xff implies 0kBps, ie, unspecified.
|
||
|
DEFINE_QOS_FIELD(GuaranteedBitRateUplinkCode,12,8,8)
|
||
|
DEFINE_QOS_FIELD(GuaranteedBitRateDownlinkCode,13,8,8)
|
||
|
// Octet 14:
|
||
|
DEFINE_QOS_FIELD(SignalingIndication,14,5,1)
|
||
|
DEFINE_QOS_FIELD(SourceStatisticsDescriptor,14,4,4)
|
||
|
// Optional Octets 15-18 are for extented Max and Guaranteed bit-rates.
|
||
|
|
||
|
// Encoding methods using raw methods above:
|
||
|
|
||
|
// Maximum and Guaranteed bit rate for uplink and downlink have
|
||
|
// normal and extended versions, where the extended versions
|
||
|
// add yet another extra octet at the end of the QoS IE.
|
||
|
// Return -1 if not specified in the IE, because it was too short,
|
||
|
// in which case the caller must fall back on PeakThroughput.
|
||
|
void setMaxBitRate(unsigned val, bool uplink);
|
||
|
int getMaxBitRate(bool uplink);
|
||
|
|
||
|
unsigned getPeakThroughput();
|
||
|
void setPeakThroughput(unsigned bytePSec);
|
||
|
// We probably dont ever need mean throughput, because you can set it to 'best effort',
|
||
|
// implying that only peak throughput is significant.
|
||
|
unsigned getMeanThroughput();
|
||
|
void setMeanThroughput(unsigned bytePHour);
|
||
|
void defaultPS(unsigned rateDownlink, unsigned rateUplink);
|
||
|
};
|
||
|
|
||
|
// 24.008 10.5.5.12a MS Radio Access Capability
|
||
|
// 5-15-2015, David says Range supports:
|
||
|
// 850: GSM 850, 900: GSM E, 1800: GSM 1800, 1900: GSM 1900
|
||
|
enum AccessTechnologyType {
|
||
|
GSM_P = 0,
|
||
|
GSM_E = 1,
|
||
|
GSM_R = 2,
|
||
|
GSM_1800 = 3,
|
||
|
GSM_1900 = 4,
|
||
|
GSM_450 = 5,
|
||
|
GSM_480 = 6,
|
||
|
GSM_850 = 7,
|
||
|
GSM_750 = 8,
|
||
|
GSM_T380 = 9,
|
||
|
GSM_T410 = 10,
|
||
|
GSM_UNUSED = 11,
|
||
|
GSM_710 = 12,
|
||
|
GSM_T810 = 13
|
||
|
};
|
||
|
const char *AccessTechnologyType2Name(AccessTechnologyType type);
|
||
|
|
||
|
// 24.008 10.5.5.12a MS Radio Access Capability
|
||
|
// 45.002 appendix B explains GPRS Multislot Class.
|
||
|
// There is so much junk in here I am taking a new tack.
|
||
|
// Keep the capabilities in a single array indexed by these names.
|
||
|
// A value of -1 indicates not present.
|
||
|
struct AccessCapabilities {
|
||
|
Bool_z mValid;
|
||
|
AccessTechnologyType mTechType;
|
||
|
Bool_z mSameAsPrevious;
|
||
|
enum CapType {
|
||
|
//AccessTechnologyType,
|
||
|
RFPowerCapability,
|
||
|
A5Bits,
|
||
|
ESInd,
|
||
|
PS,
|
||
|
VGCS,
|
||
|
VBS,
|
||
|
// multislot capabilities:
|
||
|
// Warning: parsing code assumes multislot caps are in the order below.
|
||
|
HSCSDMultislotClass,
|
||
|
GPRSMultislotClass,
|
||
|
GPRSExtendedDynamicAllocationCapability,
|
||
|
SMS_VALUE,
|
||
|
SM_VALUE,
|
||
|
ECSDMultislotClass, // multislot additions in release 99
|
||
|
EGPRSMultislotClass,
|
||
|
EGPRSExtendedDynamicAllocationCapability,
|
||
|
DTMGPRSMultiSlotClass,
|
||
|
SingleSlotDTM,
|
||
|
DTMEGPRSMultiSlotClass,
|
||
|
// Additions in release 99:
|
||
|
EightPSKPowerCapability,
|
||
|
COMPACTInterferenceMeasurementCapability,
|
||
|
RevisionLevelIndicator,
|
||
|
UMTSFDDRadioAccessTechnologyCapability,
|
||
|
UMTS384McpsTDDRadioAccessTechnologyCapability,
|
||
|
CDMA2000RadioAccessTechnologyCapability,
|
||
|
// Additions in release 4:
|
||
|
UMTS128McpsTDDRadioAccessTechnologyCapability,
|
||
|
GERANFeaturePackage1, // Finally, something we care about.
|
||
|
ExtendedDTMGPRSMultiSlotClass,
|
||
|
ExtendedDTMEGPRSMultiSlotClass,
|
||
|
ModulationBasedMultislotClassSupport,
|
||
|
// Addigions in release 5:
|
||
|
HighMultislotCapability,
|
||
|
//GMSKPowerClass,
|
||
|
//8PSKPowerClass,
|
||
|
CapsMax // Here to indicate the length of the required list.
|
||
|
};
|
||
|
// These are the capabilities that are actually of some interest to us,
|
||
|
// and will be printed in the message:
|
||
|
static CapType mPrintList[];
|
||
|
const char *CapName(CapType type) const;
|
||
|
short mCaps[CapsMax];
|
||
|
AccessCapabilities() { for (int i = 0; i < CapsMax; i++) { mCaps[i] = -1; } }
|
||
|
int getCap(CapType captype) { // Return cap value or -1.
|
||
|
if (!mValid) return -1;
|
||
|
assert(captype < CapsMax);
|
||
|
return mCaps[captype];
|
||
|
}
|
||
|
void parseAccessCapabilities(ByteVector &bv,size_t &rp,AccessCapabilities *prev,size_t end);
|
||
|
void text2(std::ostream &os,bool verbose) const;
|
||
|
void text(std::ostream &os) const;
|
||
|
};
|
||
|
|
||
|
// 24.008 10.5.5.12a MS Radio Access Capability
|
||
|
// It is a list of structures, each of which is either an AccessCapabilitiesStruct
|
||
|
// or an AdditionalAccessTechnologiesStruct
|
||
|
struct MsRaCapability : public ByteVector {
|
||
|
static const int sMsRaCapMaxTypes = 4; // Keep the first four.
|
||
|
AccessCapabilities mCList[sMsRaCapMaxTypes]; // Keep first four.
|
||
|
void parseMsRaCapability();
|
||
|
void text2(std::ostream &os,bool verbose) const;
|
||
|
void text(std::ostream &os) const;
|
||
|
MsRaCapability(const ByteVector &bv) : ByteVector(bv) { if (bv.size()) parseMsRaCapability(); }
|
||
|
};
|
||
|
|
||
|
struct PdpContextStatus : public Text2Str
|
||
|
{
|
||
|
unsigned char mStatus[2];
|
||
|
PdpContextStatus() { mStatus[0] = mStatus[1] = 0; }
|
||
|
void text(std::ostream &os) const {
|
||
|
os <<"PdpContextStatus="<<hex<<(int)mStatus[0]<<","<<(int)mStatus[1]<<dec;
|
||
|
}
|
||
|
bool anyDefined() { return !!(mStatus[0] | mStatus[1]); }
|
||
|
};
|
||
|
|
||
|
|
||
|
// The only differences between RoutingAreaUpdateRequest and AttachRequest are:
|
||
|
// The first nibble: for AttachRequest it is Attach Type 10.5.5.2
|
||
|
// RAUpdate it is update type 10.5.5.18
|
||
|
// Mobile Identity: AttachRequest it is P-TMSI or IMSI; RAUpdate it is PTmsi.
|
||
|
// DRXparameter : AttachRequest: required; RAupdate: optional.
|
||
|
// PDP Context Status: RAUpdate: optional, AttachRequest: not present
|
||
|
struct GMMAttach
|
||
|
{
|
||
|
Field_z<4> mCypheringKeySequenceNumber; // Only bottom 3 bits used.
|
||
|
// value 7 from MS means no key available, which it should be
|
||
|
// if we have not sent an Authentication Request.
|
||
|
Field_z<16> mDrxParameter;
|
||
|
GmmMobileIdentityIE mMobileId; // AttachRequest: PtmsiOrImsi; RAUpdate: mPTmsi;
|
||
|
Bool_z mTmsiStatus; // 10.5.5.4: does ms have a valid TMSI?
|
||
|
ByteVector mMsRadioAccessCapability;// Required element in both AttachRequest and RAUpdate.
|
||
|
GMMRoutingAreaIdIE mOldRaId; // Required element in both AttachRequest and RAUpdate.
|
||
|
Field_z<24> mOldPtmsiSignature;
|
||
|
// For an incoming RAUpdate or AttachRequest, this is the mRequestedReadyTimerValue.
|
||
|
Field_z<8> mRequestedReadyTimerValue; // Unit is encoded per 10.5.7.3
|
||
|
|
||
|
ByteVector mMsNetworkCapability;
|
||
|
|
||
|
// For RA update the PDP context status indicates which PDP contexts
|
||
|
// are still active in the GGSN, because you can switch SGSNs
|
||
|
// while still having active PDP contexts.
|
||
|
PdpContextStatus mPdpContextStatus;
|
||
|
GmmMobileIdentityIE mAdditionalMobileId;
|
||
|
|
||
|
void gmParseIEs(L3GmmFrame &src, size_t &rp, const char *culprit);
|
||
|
void text(std::ostream &os) const {
|
||
|
os <<LOGVAR2("mobileId",mMobileId.str())
|
||
|
<<LOGVAR2("addtionalMobileId",mAdditionalMobileId.str())
|
||
|
<<LOGVAR2("drx",mDrxParameter)
|
||
|
<<LOGVAR2("tmsiStatus",mTmsiStatus)
|
||
|
<<LOGVAR2("pdpContextStatus",mPdpContextStatus.str())
|
||
|
<<LOGVAR(mCypheringKeySequenceNumber)
|
||
|
//<<LOGVAR(mMsRadioAccessCapability)
|
||
|
<<LOGVAR2("oldRaId",mOldRaId.str())
|
||
|
<<LOGVAR(mOldPtmsiSignature)
|
||
|
<<LOGVAR(mRequestedReadyTimerValue)
|
||
|
<<LOGVAR(mMsNetworkCapability)
|
||
|
;
|
||
|
MsRaCapability caps(mMsRadioAccessCapability);
|
||
|
os <<"\n";
|
||
|
caps.text(os);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// 24.008 9.4.20 Service Request
|
||
|
struct L3GmmMsgServiceRequest : L3GmmUlMsg
|
||
|
{
|
||
|
Field<4> mCypheringKeySequenceNumber; // Only bottom 3 bits used.
|
||
|
GmmMobileIdentityIE mMobileId; // PTmsi;
|
||
|
Field<4> mServiceType; // Only bottom 3 bits are used
|
||
|
|
||
|
// For RA update the PDP context status indicates which PDP contexts
|
||
|
// are still active in the GGSN, because you can switch SGSNs
|
||
|
// while still having active PDP contexts.
|
||
|
PdpContextStatus mPdpContextStatus;
|
||
|
// MBMS context status -- not implemented yet
|
||
|
// Uplink data status -- not implemented yet;
|
||
|
|
||
|
int MTI() const {return ServiceRequest;}
|
||
|
void gmmParseBody(L3GmmFrame &src, size_t &rp);
|
||
|
void textBody(std::ostream &os) const {
|
||
|
os <<LOGVAR(mCypheringKeySequenceNumber) << LOGVAR(mServiceType)
|
||
|
<<LOGVAR2("PdpContextStatus",mPdpContextStatus.str());
|
||
|
mMobileId.text(os);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// 24.008 9.4.21 Service Accept
|
||
|
struct L3GmmMsgServiceAccept : L3GmmDlMsg
|
||
|
{
|
||
|
// For RA update the PDP context status indicates which PDP contexts
|
||
|
// are still active in the GGSN, because you can switch SGSNs
|
||
|
// while still having active PDP contexts.
|
||
|
PdpContextStatus mPdpContextStatus;
|
||
|
// MBMS context status -- not implemented yet
|
||
|
|
||
|
int MTI() const {return ServiceAccept;}
|
||
|
L3GmmMsgServiceAccept(PdpContextStatus wStatus):
|
||
|
mPdpContextStatus(wStatus)
|
||
|
{
|
||
|
}
|
||
|
void gmmWriteBody(ByteVector &msg);
|
||
|
void textBody(std::ostream &os) const {
|
||
|
os << LOGVAR2("PdpContextStatus",mPdpContextStatus.str());
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// 24.008 9.4.22 Service Reject
|
||
|
struct L3GmmMsgServiceReject : L3GmmDlMsg
|
||
|
{
|
||
|
uint8_t mGmmCause; // GMM cause 10.5.5.14
|
||
|
int MTI() const {return ServiceReject;}
|
||
|
L3GmmMsgServiceReject(uint8_t wGmmCause):
|
||
|
mGmmCause(wGmmCause)
|
||
|
{
|
||
|
}
|
||
|
void gmmWriteBody(ByteVector &msg);
|
||
|
void textBody(std::ostream &os) const {
|
||
|
os << LOGVAR2("GmmCause",mGmmCause);
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
struct L3GmmMsgRAUpdateRequest : L3GmmUlMsg, GMMAttach
|
||
|
{
|
||
|
Field<3> mUpdateType; // 10.5.5.18:
|
||
|
// 0 => RA updating, 1 => Combined RA/LA updating
|
||
|
// 2 => Combined RA/LA updating with IMSI attach
|
||
|
// 3 => Periodic updating.
|
||
|
bool mFollowOnRequestPending;
|
||
|
|
||
|
int MTI() const {return RoutingAreaUpdateRequest;}
|
||
|
void gmmParseBody(L3GmmFrame &src, size_t &rp);
|
||
|
void textBody(std::ostream &os) const {
|
||
|
//os <<"RAUpdateRequest" <<LOGVAR(mUpdateType);
|
||
|
os <<LOGVAR(mUpdateType) << LOGVAR(mFollowOnRequestPending);
|
||
|
GMMAttach::text(os);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct L3GmmMsgRAUpdateComplete : L3GmmUlMsg {
|
||
|
int MTI() const {return RoutingAreaUpdateComplete;}
|
||
|
void gmmParseBody(L3GmmFrame &src, size_t &rp) {
|
||
|
// There is nothing interesting inside.
|
||
|
// The fact that it arrived is the message.
|
||
|
}
|
||
|
//void text(std::ostream &os) const { os <<"RaUpdateComplete"; }
|
||
|
void textBody(std::ostream &os) const;
|
||
|
};
|
||
|
|
||
|
// 24.008 9.4.2 Attach Accept
|
||
|
struct L3GmmMsgAttachAccept : public L3GmmDlMsg
|
||
|
{
|
||
|
Field<4> mAttachResult; // 1=> GPRS only attach, 3=>combined GPRS/IMSI attach.
|
||
|
Bool_z mForceToStandby;
|
||
|
GprsTimerIE mPeriodicRAUpdateTimer;
|
||
|
GprsTimerIE mReadyTimer;
|
||
|
uint32_t mPTmsi;
|
||
|
// Per 24.008 9.4.2: Attach Accept Description,
|
||
|
// it sounds like the second mobile identity is included only to set
|
||
|
// a TMSI in case of a combined attach, so we should not include
|
||
|
// this IE unless using NMO 1 and we support the combined attach.
|
||
|
// 6-7-2012: Previously I had been returning the IMSI in this spot,
|
||
|
// and that worked ok, but I am removing it as incorrect.
|
||
|
GmmMobileIdentityIE mMobileId;
|
||
|
|
||
|
int MTI() const {return AttachAccept;}
|
||
|
|
||
|
// Constructor prior to 6-7-2012:
|
||
|
L3GmmMsgAttachAccept(unsigned wAttachResult, uint32_t wPTmsi,
|
||
|
GmmMobileIdentityIE wMobileId) :
|
||
|
L3GmmDlMsg(senseReply),
|
||
|
mAttachResult(wAttachResult),
|
||
|
mPTmsi(wPTmsi)
|
||
|
//mMobileId(wMobileId)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// New constructor, no mobile id.
|
||
|
L3GmmMsgAttachAccept(unsigned wAttachResult, uint32_t wPTmsi):
|
||
|
L3GmmDlMsg(senseReply),
|
||
|
mAttachResult(wAttachResult),
|
||
|
mPTmsi(wPTmsi)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void gmmWriteBody(ByteVector &msg);
|
||
|
void textBody(std::ostream &os) const {
|
||
|
//os <<"AttachAccept ";
|
||
|
os <<LOGVAR(mAttachResult) <<LOGHEX(mPTmsi)
|
||
|
<<LOGVAR(mForceToStandby)
|
||
|
<<LOGVAR2("PeriodicRAUpdateTimer",mPeriodicRAUpdateTimer.getSeconds())
|
||
|
<<LOGVAR2("mobileId",mMobileId.str());
|
||
|
GMMRoutingAreaIdIE mRaId;
|
||
|
mRaId.raLoad();
|
||
|
mRaId.text(os);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// 24.008 9.4.15 Routing Area Update Accept
|
||
|
struct L3GmmMsgRAUpdateAccept : L3GmmDlMsg
|
||
|
{
|
||
|
unsigned mUpdateResult; // 10.5.5.17
|
||
|
Bool_z mForceToStandby; // 0 means no, 1 means force to standby.
|
||
|
GprsTimerIE mPeriodicRAUpdateTimer;
|
||
|
// "This IE may be included to assign or unassign a TMSI to a MS
|
||
|
// in case of a combined routing area updating procedure."
|
||
|
// Note: we do not need the MS id in this message because the L2 Layer takes care
|
||
|
// of delivering it back to the correct MS.
|
||
|
//GmmMobileIdentityIE mMobileId;
|
||
|
PdpContextStatus mPdpContextStatusCurrent;
|
||
|
uint32_t mAllocatedPTmsi; // Allocated p-tmsi, or 0
|
||
|
uint32_t mTmsi; // tmsi or 0, for combined raupdate, sent in the mobile-id ie.
|
||
|
|
||
|
L3GmmMsgRAUpdateAccept(RAUpdateType updatetype, PdpContextStatus wPdpContextStatus, uint32_t ptmsi,uint32_t tmsi) :
|
||
|
L3GmmDlMsg(senseReply),
|
||
|
mUpdateResult((unsigned)updatetype),
|
||
|
mPdpContextStatusCurrent(wPdpContextStatus),
|
||
|
mAllocatedPTmsi(ptmsi),
|
||
|
mTmsi(tmsi)
|
||
|
//mMobileId(mid),
|
||
|
{
|
||
|
mPeriodicRAUpdateTimer.setSeconds(gConfig.getNum("SGSN.Timer.RAUpdate"));
|
||
|
}
|
||
|
|
||
|
int MTI() const {return RoutingAreaUpdateAccept;}
|
||
|
void gmmWriteBody(ByteVector &msg);
|
||
|
void textBody(std::ostream &os) const {
|
||
|
//os <<"RaUpdateAccept"
|
||
|
os<<LOGVAR(mUpdateResult) <<LOGVAR(mForceToStandby)
|
||
|
// We did not bother to print out the GMMRoutingAreaIdIE
|
||
|
<<LOGHEX2("ptmsi",mAllocatedPTmsi) <<LOGHEX2("MSIdentity(mTmsi)",mTmsi)
|
||
|
<<LOGVAR2("PeriodicRAUpdateTimer",mPeriodicRAUpdateTimer.getSeconds())
|
||
|
<<LOGVAR2("PdpContextStatusCurrent",mPdpContextStatusCurrent.str());
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
// 24.008 9.4.17 Routing Area Update Reject
|
||
|
// Note there is no MS id here - the L2 Layer takes care of delivering it back to the correct MS.
|
||
|
struct L3GmmMsgRAUpdateReject : L3GmmDlMsg
|
||
|
{
|
||
|
uint8_t mGmmCause; // GMM cause 10.5.5.14
|
||
|
L3GmmMsgRAUpdateReject(unsigned cause) :
|
||
|
L3GmmDlMsg(senseReply),
|
||
|
mGmmCause(cause)
|
||
|
{}
|
||
|
int MTI() const {return RoutingAreaUpdateReject;}
|
||
|
void gmmWriteBody(ByteVector &msg);
|
||
|
void textBody(std::ostream &os) const;
|
||
|
};
|
||
|
|
||
|
|
||
|
struct L3GmmMsgAttachRequest : L3GmmUlMsg, GMMAttach
|
||
|
{
|
||
|
Field<4> mAttachType; // 10.5.5.2
|
||
|
|
||
|
int MTI() const {return AttachRequest;}
|
||
|
void gmmParseBody(L3GmmFrame &src, size_t &rp);
|
||
|
|
||
|
void textBody(std::ostream &os) const {
|
||
|
os <<LOGVAR(mAttachType);
|
||
|
//<<LOGVAR2("mobileId",mMobileId.str())
|
||
|
//<<LOGVAR2("drx",mDrxParameter)
|
||
|
//<<LOGVAR2("tmsiStatus",mTmsiStatus);
|
||
|
GMMAttach::text(os);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct L3GmmMsgAttachComplete : L3GmmUlMsg
|
||
|
{
|
||
|
int MTI() const {return AttachComplete;}
|
||
|
void gmmParseBody(L3GmmFrame &src, size_t &rp) {
|
||
|
// There is nothing interesting inside the message.
|
||
|
// The fact that it arrived is the message.
|
||
|
}
|
||
|
void textBody(std::ostream &os) const {/*nothing*/}
|
||
|
};
|
||
|
|
||
|
// 3GPP 24.008 9.4.5.1 Detach Request Network Originated
|
||
|
// and 9.5.4.2 Detach Request Mobile Originated.
|
||
|
struct L3GmmMsgDetachRequest : L3GmmDlMsg, L3GmmUlMsg
|
||
|
{
|
||
|
int MTI() const {return DetachRequest;}
|
||
|
|
||
|
Field_z<4> mDetachType, mForceToStandby;
|
||
|
// The GmmCause and ForceToStandby are only present in the downlink direction.
|
||
|
uint16_t mGmmCause;
|
||
|
Bool_z mGmmCausePresent;
|
||
|
|
||
|
// The tmsi is only present in the uplink direction.
|
||
|
GmmMobileIdentityIE mMobileId; // This is supposed to be a PTMSI only, so why encoded as a MobileIdentity IE?
|
||
|
Bool_z mMobileIdPresent;
|
||
|
|
||
|
// In the uplink direction there is also a P-TMSI signature that we dont use, so we dont parse.
|
||
|
// In fact, the MS is not supposed to include it if we didnt sent it one in an earlier message.
|
||
|
|
||
|
// This message is bidirectional. This constructor is for reading one in:
|
||
|
L3GmmMsgDetachRequest() {}
|
||
|
void gmmParseBody(L3GmmFrame &src, size_t &rp);
|
||
|
|
||
|
// This message is bidirectional. This constructor is for making one to send downstream:
|
||
|
L3GmmMsgDetachRequest(unsigned type, unsigned cause) :
|
||
|
L3GmmDlMsg(senseCmd),
|
||
|
mDetachType(type),
|
||
|
mForceToStandby(0),
|
||
|
mGmmCause(cause),
|
||
|
mGmmCausePresent(cause != 0)
|
||
|
{}
|
||
|
void gmmWriteBody(ByteVector &msg);
|
||
|
|
||
|
void textBody(std::ostream &os) const;
|
||
|
};
|
||
|
|
||
|
// 3GPP 24.008 9.4.6 Detach Accept
|
||
|
struct L3GmmMsgDetachAccept : L3GmmUlMsg, L3GmmDlMsg
|
||
|
{
|
||
|
int MTI() const {return DetachAccept;}
|
||
|
Field_z<4> mForceToStandby;
|
||
|
|
||
|
// This message is bidirectional. This constructor is for reading one in:
|
||
|
L3GmmMsgDetachAccept() {}
|
||
|
void gmmParseBody(L3GmmFrame &src, size_t &rp) {
|
||
|
// Nothing at all. The presence of this message is the indication.
|
||
|
}
|
||
|
|
||
|
// This message is bidirectional. This constructor is for making one to send downstream.
|
||
|
// Good old C++ requires us to make the constructor arguments unique so we will pass in the useless ForceToStandby.
|
||
|
L3GmmMsgDetachAccept(unsigned wForceToStandby) :
|
||
|
L3GmmDlMsg(senseReply),
|
||
|
mForceToStandby(wForceToStandby)
|
||
|
{}
|
||
|
void gmmWriteBody(ByteVector &msg);
|
||
|
|
||
|
void textBody(std::ostream &os) const;
|
||
|
};
|
||
|
|
||
|
struct L3GmmMsgIdentityRequest : L3GmmDlMsg
|
||
|
{
|
||
|
Field<4> mIdentityType, mForceToStandby;
|
||
|
int MTI() const {return IdentityRequest;}
|
||
|
L3GmmMsgIdentityRequest() :
|
||
|
L3GmmDlMsg(senseCmd),
|
||
|
mIdentityType(1), // 1 means IMSI, the only kind we ever ask for.
|
||
|
mForceToStandby(0)
|
||
|
{}
|
||
|
void gmmWriteBody(ByteVector &msg);
|
||
|
void textBody(std::ostream &os) const;
|
||
|
};
|
||
|
|
||
|
// 24.008 9.4.9 Authenticaion and ciphering request.
|
||
|
struct L3GmmMsgAuthentication : L3GmmDlMsg
|
||
|
{
|
||
|
int MTI() const {return AuthenticationAndCipheringReq;}
|
||
|
// We wont use any of the IEs:
|
||
|
// Ciphering algorithm - always 0
|
||
|
// IMEISV request - request IMEI in response, nope.
|
||
|
// Force to standyby - nope
|
||
|
// A&C reference number - just used to match up Authentication Response to this message.
|
||
|
ByteVector mRand; // 128 bit random number.
|
||
|
// GPRS ciphering key sequence - nope
|
||
|
// AUTN - if specified, it is a UMTS type challenge. nope.
|
||
|
void gmmWriteBody(ByteVector &msg);
|
||
|
L3GmmMsgAuthentication(ByteVector &rand) : L3GmmDlMsg(senseCmd), mRand(rand)
|
||
|
{
|
||
|
assert(rand.size() == 16);
|
||
|
}
|
||
|
void textBody(std::ostream &os) const {
|
||
|
os <<LOGVAR(mRand);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// 24.008 9.4.9 Authenticaion and ciphering request.
|
||
|
struct L3GmmMsgAuthenticationResponse : L3GmmUlMsg
|
||
|
{
|
||
|
int MTI() const {return AuthenticationAndCipheringResp;}
|
||
|
ByteVector mSRES; // 32 bit authentication result.
|
||
|
void gmmParseBody(L3GmmFrame &src, size_t &rp);
|
||
|
void textBody(std::ostream &os) const {
|
||
|
os <<LOGVAR(mSRES);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
struct L3GmmMsgIdentityResponse : L3GmmUlMsg
|
||
|
{
|
||
|
int MTI() const {return IdentityResponse;}
|
||
|
GmmMobileIdentityIE mMobileId;
|
||
|
void gmmParseBody(L3GmmFrame &src, size_t &rp);
|
||
|
void textBody(std::ostream &os) const;
|
||
|
};
|
||
|
|
||
|
struct L3SmMsgActivatePdpContextRequest : L3SmUlMsg
|
||
|
{
|
||
|
unsigned mNSapi;
|
||
|
unsigned mLlcSapi;
|
||
|
unsigned mRequestType;
|
||
|
ByteVector mQoS;
|
||
|
ByteVector mPdpAddress;
|
||
|
ByteVector mApName;
|
||
|
ByteVector mPco;
|
||
|
|
||
|
L3SmMsgActivatePdpContextRequest() {};
|
||
|
L3SmMsgActivatePdpContextRequest(L3SmFrame &src) { smParse(src); }
|
||
|
int MTI() const {return ActivatePDPContextRequest;}
|
||
|
void smParseBody(L3SmFrame &src, size_t &rp);
|
||
|
void textBody(std::ostream &os) const {
|
||
|
os<<LOGVAR(mNSapi)<<LOGVAR(mLlcSapi) <<LOGVAR(mRequestType)
|
||
|
<<LOGVAR(mPdpAddress)<<LOGVAR(mQoS)<<LOGVAR(mApName) <<LOGVAR(mPco);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct L3SmMsgActivatePdpContextAccept : L3SmDlMsg
|
||
|
{
|
||
|
unsigned mLlcSapi;
|
||
|
ByteVector mQoS;
|
||
|
unsigned mRadioPriority;
|
||
|
ByteVector mPdpAddress; // Would be optional if we supported static ips, but we dont.
|
||
|
ByteVector mPco; // If you dont pass these down, get: SM Status: Invalid Mandatory information.
|
||
|
|
||
|
L3SmMsgActivatePdpContextAccept(unsigned wti) : L3SmDlMsg(wti,senseReply) {} // Other fields filled in by caller.
|
||
|
int MTI() const {return ActivatePDPContextAccept;}
|
||
|
void smWriteBody(ByteVector &msg);
|
||
|
void textBody(std::ostream &os) const {
|
||
|
os<<LOGVAR(mLlcSapi)<<LOGVAR(mPdpAddress)<<LOGVAR(mQoS)<<LOGVAR(mRadioPriority)<<LOGVAR(mPco);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct L3SmMsgActivatePdpContextReject : L3SmDlMsg
|
||
|
{
|
||
|
unsigned mCause; // type SmCause::Cause
|
||
|
L3SmMsgActivatePdpContextReject(unsigned wti, unsigned wcause) : L3SmDlMsg(wti,senseReply), mCause(wcause) {}
|
||
|
int MTI() const {return ActivatePDPContextReject;}
|
||
|
void smWriteBody(ByteVector &msg);
|
||
|
void textBody(std::ostream &os) const;
|
||
|
};
|
||
|
|
||
|
// TODO: How do you deactivate the contexts? The PDP context is identified by its TI,
|
||
|
// but if we just get a packet for an unconfigured NSAPI, then we dont have a TI...
|
||
|
// I added sendPdpDeactivateAll to just deactivate them all.
|
||
|
struct L3SmMsgDeactivatePdpContextRequest : L3SmUlMsg, L3SmDlMsg
|
||
|
{
|
||
|
unsigned mCause; // This is an SmCause
|
||
|
Bool_z mTearDownIndicator;
|
||
|
ByteVector mPco; // Option PCO. Added this so I can see what is in there.
|
||
|
|
||
|
// This message is bidirectional. This constructor is for reading one in:
|
||
|
L3SmMsgDeactivatePdpContextRequest(L3SmFrame &src) { smParse(src); }
|
||
|
void smParseBody(L3SmFrame &src,size_t &rp);
|
||
|
|
||
|
// This message is bidirectional. This constructor is for making one to send downstream:
|
||
|
L3SmMsgDeactivatePdpContextRequest(unsigned ti, SmCause::Cause cause, bool wTearDownIndicator) :
|
||
|
L3SmDlMsg(ti,senseCmd), mCause(cause), mTearDownIndicator(wTearDownIndicator) {}
|
||
|
int MTI() const {return DeactivatePDPContextRequest;}
|
||
|
void smWriteBody(ByteVector &msg);
|
||
|
void textBody(std::ostream &os) const;
|
||
|
};
|
||
|
|
||
|
// 3GPP 24.008 9.5.15
|
||
|
struct L3SmMsgDeactivatePdpContextAccept : L3SmDlMsg, L3SmUlMsg
|
||
|
{
|
||
|
// Message has optional PCO and MBMS IEs that we ignore.
|
||
|
// This message is bidirectional. This constructor is for reading one in:
|
||
|
L3SmMsgDeactivatePdpContextAccept(L3SmFrame &src) { smParse(src); }
|
||
|
void smParseBody(L3SmFrame &src,size_t &rp) {/*nothing*/}
|
||
|
|
||
|
// This message is bidirectional. This constructor is for making one to send downstream:
|
||
|
L3SmMsgDeactivatePdpContextAccept(unsigned ti) : L3SmDlMsg(ti,senseReply) {}
|
||
|
int MTI() const {return DeactivatePDPContextAccept;}
|
||
|
void smWriteBody(ByteVector &msg) {/*nothing*/}
|
||
|
void textBody(std::ostream &os) const;
|
||
|
};
|
||
|
|
||
|
|
||
|
struct L3SmMsgSmStatus : L3SmUlMsg, L3SmDlMsg
|
||
|
{
|
||
|
unsigned mCause;
|
||
|
int MTI() const {return SMStatus;}
|
||
|
// This message is bidirectional. This constructor is for reading one in:
|
||
|
L3SmMsgSmStatus(L3SmFrame &src) { smParse(src); }
|
||
|
void smParseBody(L3SmFrame &src,size_t &rp) {
|
||
|
mCause = src.getByte(rp);
|
||
|
}
|
||
|
|
||
|
// This message is bidirectional. This constructor is for making one to send downstream:
|
||
|
L3SmMsgSmStatus(unsigned tid, SmCause::Cause cause) : L3SmDlMsg(tid,senseReply), mCause(cause) {}
|
||
|
void smWriteBody(ByteVector &msg);
|
||
|
|
||
|
void textBody(std::ostream &os) const;
|
||
|
};
|
||
|
|
||
|
|
||
|
// This message is birectional.
|
||
|
struct L3GmmMsgGmmStatus : L3GmmUlMsg, L3GmmDlMsg
|
||
|
{
|
||
|
unsigned mCause;
|
||
|
int MTI() const {return GMMStatus;}
|
||
|
void gmmParseBody(L3GmmFrame &src, size_t &rp) {
|
||
|
mCause = src.getByte(rp);
|
||
|
}
|
||
|
void gmmWriteBody(ByteVector &msg) {
|
||
|
msg.appendByte(mCause);
|
||
|
}
|
||
|
void textBody(std::ostream &os) const;
|
||
|
// This message is bidirectional. This constructor is for reading one in:
|
||
|
L3GmmMsgGmmStatus() {}
|
||
|
// This message is bidirectional. This constructor is for making one to send downstream.
|
||
|
L3GmmMsgGmmStatus(unsigned wCause): L3GmmDlMsg(senseCmd), mCause(wCause) {}
|
||
|
};
|
||
|
|
||
|
}; // namespace GPRS
|
||
|
#endif
|