Files
openbts/GPRS/BSSGMessages.h
2014-07-16 23:57:22 +02:00

523 lines
17 KiB
C++

/*
* Copyright 2011, 2014 Range Networks, Inc.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef BSSGMESSAGES_H
#define BSSGMESSAGES_H
// This file includes both the NS and BSSG layer messages.
// For a downlink PDU, we know the size when we receive it.
// For an uplink data PDU, which comes from RLCEngine, we will allocate the
// maximum size right off the bat, and let the RLCEngine write the data
// directly into it. There is no real reason to downsize it; these dont last long.
namespace GPRS { extern unsigned GPRSDebug; };
#include "GPRSExport.h"
#include "Defines.h"
#include "ScalarTypes.h"
#include "ByteVector.h"
#include "Utils.h"
//#ifndef OFFSETOF // This is a standard definition in many header files.
//#define OFFSETOF(type, field) ((int) ((char *) &((type *) 0)->field))
//#endif
namespace BSSG {
// BVCI defined GSM 08.18 5.4.1
// See table 5.4 for which BVCI to use with each message.
// The SIGNALLING and PTP numbers are reserved.
// See LookupBVCI(BSPDUType::type bstype);
class BVCI {
public:
enum type {
SIGNALLING = 0, // For BSSG BVC messages other than data.
PTM = 1, // Point to multipoint
PTP = 2 // Any other value is a base station designator for Point-To-Point data.
// We only use one value, gBSSG.mbsBVCI, which must be >- PTP.
};
};
/**********************************************************
BSSGSP Messages we need to support eventually:
DL-UNITDATA
Includes PDU type (DL-UNITDATA), TLLI, QoS Profile, PDU Lifetime, PDU.
optional: IMSI, oldTLLI, PFI (Packet Flow Identifier), etc.
UL-UNITDATA
Includes PDU type (UL-UNITDATA), TLLI, BVCI, Cell Identifier, PDU.
GMM-PAGING-PS/GMM-PAGING-CS (for packet or voice)
Includes PDU type (PAGING-PS), QoS Profile, P-TMSI <or> IMSI.
Note: If TLLI is specified and already exists within a Radio Context in BSS
[because MS has communicated previously] it is used.
BVCI <or> Location Area <or> Routing Area <or> BSSArea Indication
Optional: P-TMSI, BVCI, Location area, Routing area.
GMM-RA-CAPABILITY, GMM-RA-CAPABILITY-UPDATE
Astonishingly, the BSS asks the SGSN for this info.
It is because the MS may be moving from BTS to BTS, so the SGSN
is a slightly more permanent repository, and makes handover easier between BTSs.
But note that the MS can also travel from SGSN to SGSN, so I think the location
of the MS info is arbitrary.
We are talking about information that is only kept around a short while anyway.
GMM-RADIO-STATUS
GMM-SUSPEND
GMM-RESUME
Note that there are alot of messages to control the data-rate on the BSSG connection.
None of them are implemented in the SGSN, so we dont support them either.
**********************************************************/
class BSPDUType {
public:
// GSM08.18 sec11.3.26 table11.27: PDU Types
// GSM08.18 table 5.4 defines the BVCI to be used with each message.
// BVCIs are: PTP, PTM, SIG
enum type {
// PDUs between RL and BSSGP SAPs:
DL_UNITDATA = 0, // PTP network->MS
UL_UNITDATA = 1, // PTP MS->network
// PDUs between GMM SAPs.
RA_CAPABILITY = 2, // PTP network->BSS
PTM_UNITDATA = 3, // PTM not currently used
// PDUs between GMM SAPs:
PAGING_PS = 6, // PTP or SIG network->BSS request to page MS for packet connection.
PAGING_CS = 7, // PTP or SIG network->BSS request to page MS for RR connection.
RA_CAPABILITY_UPDATE = 8, // PTP BSS->network request for MS Radio Access Capabilities.
RA_CAPABILITY_UPDATE_ACK = 9,// PTP network->BSS Radio Access Capability and IMSI.
RADIO_STATUS = 0x0a, // PTP BSS->SGSN notification of error
SUSPEND = 0x0b, // SIG MS->network request to suspend GPRS service.
SUSPEND_ACK = 0x0c, // SIG network->MS ACK
SUSPEND_NACK = 0x0d, // SIG network->MS NACK
RESUME = 0xe, // SIG MS->network request to resume GPRS service.
RESUME_ACK = 0xf, // SIG network->MS ACK
RESUME_NACK = 0x10, // SIG network->MS NACK
// PDUs between NM SAPs:
// We will not use the flow control stuff, block, unblock, etc.
BVC_BLOCK = 0x20, // SIG
BVC_BLOCK_ACK = 0x21, // SIG
BVC_RESET = 0x22, // SIG network->BSS request reset everything.
BVC_RESET_ACK = 0x23, // SIG BSS->network and network->BSS?
BVC_UNBLOCK = 0x24, // SIG
BVC_UNBLOCK_ACK = 0x25, // SIG
FLOW_CONTROL_BVC = 0x26, // PTP BSS->network inform maximum throughput on Gb I/F
FLOW_CONTROL_BVC_ACK = 0x27, // PTP network->BSS
FLOW_CONTROL_MS = 0x28, // PTP BSS->network inform maximum throughput for MS.
FLOW_CONTROL_MS_ACK = 0x29, // PTP network->BSS
FLUSH_LL = 0x2a, // SIG network->BSS forget this MS (it moved to another cell.)
FLUSH_LL_ACK = 0x2b, // SIG BSS->network
LLC_DISCARDED = 0x2c, // SIG BSS->network notification of lost PDUs (probably expired)
// We ignore all these:
SGSN_INVOKE_TRACE = 0x40, // network->BSS request trace an MS
STATUS = 0x41, // SIG BSS->network or network->BSS report error condition.
DOWNLOAD_BSS_PFC = 0x50, // PTP
CREATE_BSS_PFC = 0x51, // PTP
CREATE_BSS_PFC_ACK = 0x52, // PTP
CREATE_BSS_PFC_NACK = 0x53, // PTP
MODIFY_BSS_PFC = 0x54, // PTP
MODIFY_BSS_PFC_ACK = 0x55, // PTP
DELETE_BSS_PFC = 0x56, // PTP
DELETE_BSS_PFC_ACK = 0x57 // PTP
};
static const char *name(int val);
static const unsigned LookupBVCI(BSPDUType::type bstype);
};
std::ostream& operator<<(std::ostream& os, const BSPDUType::type val);
class NsIEIType {
// GSM08.18 sec10.3 NS protocol IEI Types.
// The NS protocol doesnt do much. It specifies a procedure
// to make sure the link is alive, to reset it after failure,
// and to turn the entire link on and off (block/unblock.)
public: enum type {
IEINsCause,
IEINsVCI,
IEINsPDU,
IEINsBVCI,
IEINsNSEI,
};
};
class NsCause {
public: enum type {
TransitNetworkFailure,
OAndMIntervention,
EquipmentFailure,
NSVCBlocked,
NSVCUnknown,
BVCIUnknown,
SemanticallyIncorrectPDU = 8,
PduNotCompatible = 10,
ProtocolError = 11,
InvalidEssentialIE = 12,
MissingEssentialIE = 13
};
};
class IEIType {
public:
// GSM08.18 sec11.3 table11.1: IEI Types
enum type {
AlignmentOctets = 0x00,
BmaxDefaultMS = 0x01,
BSSAreaIndication = 0x02,
BucketLeakRate = 0x03,
BVCI = 0x04,
BVCBucketSize = 0x05,
BVCMeasurement = 0x06,
Cause = 0x07,
CellIdentifier = 0x08,
ChannelNeeded = 0x09,
DRXParameters = 0x0a,
eMLPPPriority = 0x0b,
FlushAction = 0x0c,
IMSI = 0x0d,
LLCPDU = 0x0e,
LLCFramesDiscarded = 0x0f,
LocationArea = 0x10,
MobileId = 0x11,
MSBucketSize = 0x12,
MSRadioAccessCapability = 0x13,
OMCId = 0x14,
PDUInError = 0x15,
PDULifetime = 0x16,
Priority = 0x17,
QoSProfile = 0x18,
RadioCause = 0x19,
RACapUPDCause = 0x1a,
RouteingArea = 0x1b,
RDefaultMS = 0x1c,
SuspendReferenceNumber = 0x1d,
Tag = 0x1e,
TLLI = 0x1f,
TMSI = 0x20,
TraceReference = 0x21,
TraceType = 0x22,
TransactionId = 0x23,
TriggerId = 0x24,
NumberOfOctetsAffected = 0x25,
LSAIdentifierList = 0x26,
LSAInformation = 0x27,
PacketFlowIdentifier = 0x28,
PacketFlowTimer = 0x29,
AggregateBSSQoSProfile = 0x3a, // (ABQP)
FeatureBitmap = 0x3b,
BucketFullRatio = 0x3c,
ServiceUTRANCCO = 0x3d // (Cell Change Order)
};
static const char *name(int val);
};
std::ostream& operator<<(std::ostream& os, const IEIType::type val);
// Notes:
// BVC = BSSG Virtual Connection
// NS SDU = the BSSG data packet transmitted over NS.
// GSM08.16 sec 10.3.7: Network Service PDU Type
class NSPDUType {
public:
enum type {
NS_UNITDATA = 0,
NS_RESET = 2,
NS_RESET_ACK = 3,
NS_BLOCK = 4,
NS_BLOCK_ACK = 5,
NS_UNBLOCK = 6,
NS_UNBLOCK_ACK = 7,
NS_STATUS = 8,
NS_ALIVE = 10,
NS_ALIVE_ACK = 11
};
static const char *name(int val);
};
std::ostream& operator<<(std::ostream& os, const NSPDUType::type val);
// GSM08.16 sec 9.2.10
struct RN_PACKED NSUnitDataHeader {
ByteType mNSPDUType;
ByteType mUnused;
ByteType mBVCI[2];
};
class NSMsg : public ByteVector, public Utils::Text2Str
{
public:
// This is the NS header length only for NS_UNIT_DATA messages,
// which are the only ones we care about because they are the
// only ones that have BSSG and potentially user data in them.
static const unsigned UnitDataHeaderLen = sizeof(struct NSUnitDataHeader);
// wlen should include the NS header + BSSG header + data
NSMsg(ByteType *wdata, int wlen) // Make one from downlink data.
: ByteVector(wdata,wlen)
{ }
// wlen should include the NS header + BSSG header + data
NSMsg(int wlen) // Make one for uplink data.
: ByteVector(wlen + NSMsg::UnitDataHeaderLen) // But we will add it in anyway
{
// Zero out the NS header; the type will be filled in later.
fill(0,0,NSMsg::UnitDataHeaderLen);
}
// Make a new message from some other, taking over the ByteVector.
// Used to change the type of a BSSG message.
NSMsg(NSMsg *src) : ByteVector(*src) {
//assert(src->isOwner());
//move(*src); // Grab the memory from src.
//assert(!src->isOwner()); no longer true with refcnts.
}
// Passify the brain-dead compiler:
#define NSMsgConstructors(type1,type2) \
type1(ByteType *data, int len) : type2(data,len) {} \
type1(int wlen) : type2(wlen) {} \
type1(NSMsg *src) : type2(src) {}
// Fields in the 4 byte NS Header:
void setNSPDUType(NSPDUType::type nstype) { setByte(0,nstype); }
NSPDUType::type getNSPDUType() const { return (NSPDUType::type) getByte(0); }
void textNSHeader(std::ostream&os) const;
virtual void text(std::ostream&os) const;
std::string str() const { return this->Text2Str::str(); } // Disambigute
};
class BSSGMsg : public NSMsg {
public:
NSMsgConstructors(BSSGMsg,NSMsg)
// Common fields in the BSSG Header:
void setPDUType(BSPDUType::type type) { setByte(NSMsg::UnitDataHeaderLen,(ByteType)type); }
BSPDUType::type getPDUType() const { return (BSPDUType::type) getByte(NSMsg::UnitDataHeaderLen); }
virtual void text(std::ostream &os) const;
virtual std::string briefDescription() const;
};
class BSSGUplinkMsgElt {
public:
//TODO: virtual void text(std::ostream&) const;
virtual void parseElement(const char *src, size_t &rp);
};
class BSSGDownlinkMsgElt {
public:
//TODO: virtual void text(std::ostream&) const;
};
// Note that the ByteVector in NSMsg is allocated, and all the other ones in downlink messages
// are segments referring to this one.
//class BSSGDownlinkMsg : public NSMsg, public BSSGMsg
class BSSGDownlinkMsg : public BSSGMsg
{
public:
NSMsgConstructors(BSSGDownlinkMsg,BSSGMsg)
virtual void text(std::ostream &os) const { BSSGMsg::text(os); }
};
class BSSGUplinkMsg : public BSSGMsg
{
public:
NSMsgConstructors(BSSGUplinkMsg,BSSGMsg)
virtual void text(std::ostream &os) const { BSSGMsg::text(os); }
};
// This is the QoS Profile in the header, when not including the 2 byte IEI prefix.
// GSM08.18 sec 11.3.28
struct QoSProfile {
// Coded as value part of Bucket Leak Rate 'R' from sec 11.3.4
// And I quote:
// The R field is the binary encoding of the rate information expressed in 100 bits/sec
// increments starting from 0 x 100 bits/sec until 65535 * 100 bits/sec (6Mbps)
// Note a) Bit Rate 0 means "Best Effort".
unsigned mPeakBitRate:16;
// These are the bits of byte 3.
unsigned mSpare:2;
unsigned mCR:1;
unsigned mT:1;
unsigned mA:1;
unsigned mPrecedence:3;
ByteType getB3() { return (mCR<<5)|(mT<<4)|(mA<<3)|mPrecedence; }
void setB3(ByteType bits) {
mPrecedence = bits & 0x7;
mA = (bits>>3)&1;
mT = (bits>>4)&1;
mCR = (bits>>5)&1;
}
QoSProfile() { // Create a default QoSProfile
mPeakBitRate = 0;
setB3(0);
}
// Get from the ByteVector, which is in network order:
void qosRead(ByteVector &src, size_t &wp) {
mPeakBitRate = src.getUInt16(wp); wp+=2;
ByteType byte3 = src.getByte(wp++);
setB3(byte3);
}
// Write to ByteVector in network order.
void qosAppend(ByteVector *dest) {
dest->appendUInt16(mPeakBitRate);
dest->appendByte(getB3());
}
};
// GSM 08.18 sec 10.2.1 From SGSN to BSS.
class BSSGMsgDLUnitData : public BSSGDownlinkMsg
{
public:
// Mandatory elements:
UInt32_z mbdTLLI;
UInt16_z mbdPDULifetime;
QoSProfile mbdQoS; // 3 bytes
Bool_z mbdHaveOldTLLI;
UInt32_z mbdOldTLLI;
// Optional elements:
//RLCMsgEltMSRACapabilityValuePart mbdRACap;
ByteVector mbdRACap;
ByteVector mbdIMSI;
ByteVector mbdPDU;
BSSGMsgDLUnitData(BSSGDownlinkMsg*src) : BSSGDownlinkMsg(src) {
size_t wp = NSMsg::UnitDataHeaderLen;
parseDLUnitDataBody(*this,wp);
}
// Parse body, excluding the NS header.
void parseDLUnitDataBody(ByteVector &src, size_t &wp);
void text(std::ostream &os) const;
std::string briefDescription() const;
};
BSSGDownlinkMsg* BSSGDownlinkMessageParse(ByteVector&src);
// Routing Area Identification: GSM04.08 sec 10.5.5.15.
// 6 bytes. This is just a magic cookie as far as we are concerned.
struct RN_PACKED RoutingAreaID {
// First three bytes identify MCC Mobile Country Code and MNC Mobile Network Code.
// Weird encoding; see spec.
ByteType mMCCDigits12; // MCC Digit2, MCC Digit1.
ByteType mHighDigits; // MNC Digit3, MCC Digit3.
ByteType mMNCDigits12; // MNC Digit2, MNC Digit1.
unsigned mLAC:16; // Location Area Code.
unsigned mRAC:8; // Routing Area Code.
RoutingAreaID(unsigned MCC, unsigned MNC, unsigned LAC, unsigned RAC) {
// TODO, but maybe no one cares.
}
};
// Cell Identifier: GSM 08.18 sec 11.2.9
struct RN_PACKED CellIdentifier {
// Routing Area Identification: GSM 04.08 sec 10.5.5.15.
uint64_t RoutingAreaIdentification:48;
// Cell Identity: GSM04.08 sec 10.5.1.1
// It is just a two byte int whose value determined by the administrator.
unsigned CellIdentity:16;
};
struct RN_PACKED CellIdentifierIEI {
ByteType iei;
ByteType length;
ByteType RoutingAreaID[6];
ByteType CellIdentity[2];
};
// This is the specific UlUnitData format that we use.
struct RN_PACKED BSSGMsgULUnitDataHeader {
NSUnitDataHeader mbuNS;
ByteType mbuPDUType;
ByteType mbuTLLI[4];
ByteType mbuQoS[3];
ByteType mbuCellIdIEI[10];
ByteType mbuAlignmentIEI[2]; // spacer required to make size div by 4.
ByteType mLLCIEIType;
ByteType mLLCPDULength[2];
// PDU data starts after this..
};
//class BSSGMsgBVCReset {
// NSUnitDataHeader mbuNS;
//};
// GSM 08.18 sec 10.2.2 Packet Data from BSS to SGSN.
// It could be user data or a GMM or other message from a higher
// layer in the MS to the SGSN.
// There are several optional IEIs we do not include.
// Alignment octets are necessary so the start of the PDU IEI
// starts on a 32bit boundary, which is totally dopey because the PDU
// itself is unaligned inside the PDU IEI. Whatever.
// This structure is write-only; the internal fields are in network order and
// we dont need to read them, so we dont.
// The RLCEngine is the primary producer of these things, and always allocates
// a maximum size (1502 bytes plus headers) so it can just append the data in.
// The lifetime of these things is quite short; they live in the BSSG outgoing
// queue until the service thread sends them to the SGSN.
class BSSGMsgULUnitData : public BSSGUplinkMsg
{
public:
static const unsigned HeaderLength = sizeof(BSSGMsgULUnitDataHeader);
int mTBFId; // For debugging, the associated tbf that processed us.
BSSGMsgULUnitData(unsigned wLen, uint32_t wTLLI);
// Return the pointer to the header within the ByteVector (its just 0)
// what a horrible language.
BSSGMsgULUnitDataHeader *getHeader() { return (BSSGMsgULUnitDataHeader*) begin(); }
BSSGMsgULUnitDataHeader *getHeader() const { return const_cast<BSSGMsgULUnitData*>(this)->getHeader(); }
//unused: void setBVCI(int wBVCI) { setUInt16(2,wBVCI); }
unsigned getBVCI() const { return getUInt16(2); }
//unused: void setTLLI(int wTLLI) { sethtonl(getHeader()->mbuTLLI,wTLLI); }
int getTLLI() const { return getntohl(getHeader()->mbuTLLI); }
ByteVector getPayload() {
// Now the return value also owns memory.
//return tail(HeaderLength);
ByteVector result = *this; // Increments refcnt.
result.trimLeft(HeaderLength);
return result;
}
// We set the length after assembling the complete pdu.
unsigned mLengthPosition; // Where the PDU Length goes in the ByteVector.
void setLength();
void text(std::ostream&os) const;
};
// Make a short BSSG Protocol signaling message.
BSSGUplinkMsg *BVCFactory(BSPDUType::type bssgtype, int arg1=0);
// Make a short NS Protocol message.
NSMsg *NsFactory(NSPDUType::type nstype, int cause=0);
//class BSSGMsgULUnitData : public BSSGUplinkMsg {
// unsigned mbPDUType:8;
// unsigned mTLLI:32;
// QoSProfile mQoS;
// CellIdentifierIEI mCellIdentifier; // 10 bytes
//};
};
#endif