mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-22 23:32:00 +00:00
620 lines
16 KiB
C++
620 lines
16 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.
|
|
*/
|
|
|
|
#define LOG_GROUP LogGroup::GPRS // Can set Log.Level.GPRS for debugging
|
|
|
|
#include "BSSG.h"
|
|
#include "BSSGMessages.h"
|
|
#include "GPRSInternal.h"
|
|
#include "Globals.h"
|
|
#include "LLC.h"
|
|
#define CASENAME(x) case x: return #x;
|
|
|
|
namespace BSSG {
|
|
|
|
const char *BSPDUType::name(int val)
|
|
{
|
|
switch ((type)val) {
|
|
CASENAME(DL_UNITDATA)
|
|
CASENAME(UL_UNITDATA)
|
|
CASENAME(RA_CAPABILITY)
|
|
CASENAME(PTM_UNITDATA)
|
|
CASENAME(PAGING_PS)
|
|
CASENAME(PAGING_CS)
|
|
CASENAME(RA_CAPABILITY_UPDATE)
|
|
CASENAME(RA_CAPABILITY_UPDATE_ACK)
|
|
CASENAME(RADIO_STATUS)
|
|
CASENAME(SUSPEND)
|
|
CASENAME(SUSPEND_ACK)
|
|
CASENAME(SUSPEND_NACK)
|
|
CASENAME(RESUME)
|
|
CASENAME(RESUME_ACK)
|
|
CASENAME(RESUME_NACK)
|
|
CASENAME(BVC_BLOCK)
|
|
CASENAME(BVC_BLOCK_ACK)
|
|
CASENAME(BVC_RESET)
|
|
CASENAME(BVC_RESET_ACK)
|
|
CASENAME(BVC_UNBLOCK)
|
|
CASENAME(BVC_UNBLOCK_ACK)
|
|
CASENAME(FLOW_CONTROL_BVC)
|
|
CASENAME(FLOW_CONTROL_BVC_ACK)
|
|
CASENAME(FLOW_CONTROL_MS)
|
|
CASENAME(FLOW_CONTROL_MS_ACK)
|
|
CASENAME(FLUSH_LL)
|
|
CASENAME(FLUSH_LL_ACK)
|
|
CASENAME(LLC_DISCARDED)
|
|
CASENAME(SGSN_INVOKE_TRACE)
|
|
CASENAME(STATUS)
|
|
CASENAME(DOWNLOAD_BSS_PFC)
|
|
CASENAME(CREATE_BSS_PFC)
|
|
CASENAME(CREATE_BSS_PFC_ACK)
|
|
CASENAME(CREATE_BSS_PFC_NACK)
|
|
CASENAME(MODIFY_BSS_PFC)
|
|
CASENAME(MODIFY_BSS_PFC_ACK)
|
|
CASENAME(DELETE_BSS_PFC)
|
|
CASENAME(DELETE_BSS_PFC_ACK)
|
|
}
|
|
return "unrecognized PDU";
|
|
}
|
|
std::ostream& operator<<(std::ostream& os, const BSPDUType::type val)
|
|
{
|
|
os << "PDU_Type=" <<(int)val <<"=" <<BSPDUType::name(val);
|
|
return os;
|
|
}
|
|
|
|
// GSM 08.18 table 5.4 says what BVCI to use for each message.
|
|
// This is kinda dopey, but we have to do it.
|
|
// We are ignoring PTM messages.
|
|
// There is a note the PAGING_PS and PAGING_CS may be BVCI::SIGNALLING
|
|
const unsigned BSPDUType::LookupBVCI(BSPDUType::type bstype)
|
|
{
|
|
switch (bstype) {
|
|
case BSPDUType::SUSPEND:
|
|
case BSPDUType::SUSPEND_ACK:
|
|
case BSPDUType::SUSPEND_NACK:
|
|
case BSPDUType::RESUME:
|
|
case BSPDUType::RESUME_ACK:
|
|
case BSPDUType::RESUME_NACK:
|
|
case BSPDUType::FLUSH_LL:
|
|
case BSPDUType::FLUSH_LL_ACK:
|
|
case BSPDUType::LLC_DISCARDED:
|
|
case BSPDUType::BVC_BLOCK:
|
|
case BSPDUType::BVC_BLOCK_ACK:
|
|
case BSPDUType::BVC_UNBLOCK:
|
|
case BSPDUType::BVC_UNBLOCK_ACK:
|
|
case BSPDUType::BVC_RESET:
|
|
case BSPDUType::BVC_RESET_ACK:
|
|
case BSPDUType::SGSN_INVOKE_TRACE:
|
|
return BVCI::SIGNALLING;
|
|
default:
|
|
return gBSSG.mbsBVCI;
|
|
}
|
|
}
|
|
|
|
const char *IEIType::name(int val)
|
|
{
|
|
switch ((type)val) {
|
|
CASENAME(AlignmentOctets)
|
|
CASENAME(BmaxDefaultMS)
|
|
CASENAME(BSSAreaIndication)
|
|
CASENAME(BucketLeakRate)
|
|
CASENAME(BVCI)
|
|
CASENAME(BVCBucketSize)
|
|
CASENAME(BVCMeasurement)
|
|
CASENAME(Cause)
|
|
CASENAME(CellIdentifier)
|
|
CASENAME(ChannelNeeded)
|
|
CASENAME(DRXParameters)
|
|
CASENAME(eMLPPPriority)
|
|
CASENAME(FlushAction)
|
|
CASENAME(IMSI)
|
|
CASENAME(LLCPDU)
|
|
CASENAME(LLCFramesDiscarded)
|
|
CASENAME(LocationArea)
|
|
CASENAME(MobileId)
|
|
CASENAME(MSBucketSize)
|
|
CASENAME(MSRadioAccessCapability)
|
|
CASENAME(OMCId)
|
|
CASENAME(PDUInError)
|
|
CASENAME(PDULifetime)
|
|
CASENAME(Priority)
|
|
CASENAME(QoSProfile)
|
|
CASENAME(RadioCause)
|
|
CASENAME(RACapUPDCause)
|
|
CASENAME(RouteingArea)
|
|
CASENAME(RDefaultMS)
|
|
CASENAME(SuspendReferenceNumber)
|
|
CASENAME(Tag)
|
|
CASENAME(TLLI)
|
|
CASENAME(TMSI)
|
|
CASENAME(TraceReference)
|
|
CASENAME(TraceType)
|
|
CASENAME(TransactionId)
|
|
CASENAME(TriggerId)
|
|
CASENAME(NumberOfOctetsAffected)
|
|
CASENAME(LSAIdentifierList)
|
|
CASENAME(LSAInformation)
|
|
CASENAME(PacketFlowIdentifier)
|
|
CASENAME(PacketFlowTimer)
|
|
CASENAME(AggregateBSSQoSProfile)
|
|
CASENAME(FeatureBitmap)
|
|
CASENAME(BucketFullRatio)
|
|
CASENAME(ServiceUTRANCCO)
|
|
}
|
|
return "unrecognized IEI";
|
|
}
|
|
std::ostream& operator<<(std::ostream& os, const IEIType::type val)
|
|
{
|
|
os << "IEI_Type=" <<(int)val <<"=" <<IEIType::name(val);
|
|
return os;
|
|
}
|
|
|
|
const char *NSPDUType::name(int val)
|
|
{
|
|
switch ((type)val) {
|
|
CASENAME(NS_UNITDATA)
|
|
CASENAME(NS_RESET)
|
|
CASENAME(NS_RESET_ACK)
|
|
CASENAME(NS_BLOCK)
|
|
CASENAME(NS_BLOCK_ACK)
|
|
CASENAME(NS_UNBLOCK)
|
|
CASENAME(NS_UNBLOCK_ACK)
|
|
CASENAME(NS_STATUS)
|
|
CASENAME(NS_ALIVE)
|
|
CASENAME(NS_ALIVE_ACK)
|
|
}
|
|
return "unrecognized NSPDUType";
|
|
}
|
|
std::ostream& operator<<(std::ostream& os, const NSPDUType::type val)
|
|
{
|
|
os << "NSPDU_Type=" <<(int)val <<"=" <<NSPDUType::name(val);
|
|
return os;
|
|
}
|
|
|
|
#if 0
|
|
/** GSM 04.60 11.2 */
|
|
BSSGDownlinkMsg* BSSGDownlinkParse(ByteVector &src)
|
|
{
|
|
BSSGDownlinkMsg *result = NULL;
|
|
|
|
// NS PDUType is byte 0.
|
|
NSPDUType::type nstype = (NSPDUType::type) src.getByte(0);
|
|
if (nstype != NSPDUType::NS_UNITDATA) {
|
|
LOG(INFO) << "Unrecognized NS PDU Type=" << nstype ;
|
|
return NULL;
|
|
}
|
|
|
|
// BSSG PDUType is byte 5.
|
|
BSPDUType::type msgType = (BSPDUType::type) src.getByte(5);
|
|
|
|
switch (msgType) {
|
|
case BSPDUType::DL_UNITDATA:
|
|
result = new BSSGMsgDLUnitData(src);
|
|
break;
|
|
case BSPDUType::RA_CAPABILITY:
|
|
case BSPDUType::PAGING_PS:
|
|
case BSPDUType::PAGING_CS:
|
|
case BSPDUType::RA_CAPABILITY_UPDATE_ACK:
|
|
case BSPDUType::SUSPEND_ACK:
|
|
case BSPDUType::SUSPEND_NACK:
|
|
case BSPDUType::RESUME_ACK:
|
|
case BSPDUType::RESUME_NACK:
|
|
default:
|
|
LOG(INFO) << "unimplemented BSSG downlink message, type=" << msgType;
|
|
return NULL;
|
|
}
|
|
return result;
|
|
};
|
|
BSSGDownlinkMsg* BSSGDownlinkMessageParse(ByteVector&src)
|
|
{
|
|
BSSGDownlinkMsg *result = NULL;
|
|
unsigned pdutype = src.getByte(sizeof(NSMsg::UnitDataHeaderLen));
|
|
switch (pdutype) {
|
|
case BSPDUType::DL_UNITDATA:
|
|
result = new BSSGMsgDLUnitData(src);
|
|
break;
|
|
case BSPDUType::RA_CAPABILITY_UPDATE_ACK:
|
|
case BSPDUType::RA_CAPABILITY:
|
|
case BSPDUType::FLUSH_LL:
|
|
// todo
|
|
LOG(INFO) << "unsuppported BSSG downlink PDU type=" << pdutype;
|
|
break;
|
|
default:
|
|
LOG(INFO) << "unsuppported BSSG downlink PDU type=" << pdutype;
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
void BSSGMsgDLUnitData::parseDLUnitDataBody(ByteVector &src, size_t &wp)
|
|
{
|
|
wp++; // Skip over the pdutype.
|
|
mbdTLLI = src.getUInt32(wp); wp+=4;
|
|
mbdQoS.qosRead(src,wp);
|
|
// The rest of the fields use TLV format:
|
|
unsigned len = src.size() - 1; // wp cant actually get anywhere near size() because
|
|
// the TLV headers take at least 2 bytes.
|
|
while (wp < len) {
|
|
unsigned iei = src.getByte(wp++);
|
|
unsigned length = src.readLI(wp);
|
|
unsigned nextwp = wp + length;
|
|
switch (iei) {
|
|
case IEIType::PDULifetime:
|
|
mbdPDULifetime = src.getUInt16(wp);
|
|
break;
|
|
case IEIType::IMSI:
|
|
mbdIMSI.set(src.segment(wp,length));
|
|
break;
|
|
case IEIType::MSRadioAccessCapability:
|
|
mbdRACap.set(src.segment(wp,length));
|
|
break;
|
|
case IEIType::LLCPDU:
|
|
// Finally, here is the data:
|
|
mbdPDU.set(src.segment(wp,length));
|
|
goto done;
|
|
case IEIType::TLLI: // old TLLI
|
|
mbdHaveOldTLLI = true;
|
|
mbdOldTLLI = src.getUInt32(wp);
|
|
break;
|
|
|
|
case IEIType::Priority:
|
|
case IEIType::DRXParameters:
|
|
case IEIType::PacketFlowIdentifier:
|
|
case IEIType::LSAInformation:
|
|
case IEIType::AlignmentOctets:
|
|
default:
|
|
// ignored.
|
|
break;
|
|
}
|
|
wp = nextwp;
|
|
}
|
|
done:;
|
|
//...
|
|
}
|
|
|
|
//std::ostream& operator<<(std::ostream& os, const BSSGMsgDLUnitData &val)
|
|
//{
|
|
//val.text(os);
|
|
//return os;
|
|
//}
|
|
|
|
static void NsAddCause(ByteVector *vec, unsigned cause)
|
|
{
|
|
vec->appendByte(NsIEIType::IEINsCause);
|
|
vec->appendLI(1);
|
|
vec->appendByte(cause);
|
|
}
|
|
|
|
static void NsAddBVCI(ByteVector *vec, BVCI::type bvci)
|
|
{
|
|
vec->appendByte(NsIEIType::IEINsBVCI);
|
|
vec->appendLI(2);
|
|
vec->appendUInt16(bvci);
|
|
}
|
|
|
|
static void NsAddVCI(ByteVector *vec)
|
|
{
|
|
vec->appendByte(NsIEIType::IEINsVCI);
|
|
vec->appendLI(2);
|
|
vec->appendUInt16(gBSSG.mbsNSVCI);
|
|
}
|
|
|
|
static void NsAddNSEI(ByteVector *vec)
|
|
{
|
|
vec->appendByte(NsIEIType::IEINsNSEI);
|
|
vec->appendLI(2);
|
|
vec->appendUInt16(gBSSG.mbsNSEI);
|
|
}
|
|
|
|
NSMsg *NsFactory(NSPDUType::type nstype, int cause)
|
|
{
|
|
NSMsg *vec = new NSMsg(80); // Big enough for any message.
|
|
|
|
vec->setAppendP(0); // Setup vec for appending.
|
|
vec->appendByte(nstype); // First byte of message is NS PDUType.
|
|
|
|
switch (nstype) {
|
|
case NSPDUType::NS_RESET:
|
|
NsAddCause(vec,cause);
|
|
NsAddVCI(vec);
|
|
NsAddNSEI(vec);
|
|
break;
|
|
case NSPDUType::NS_RESET_ACK:
|
|
NsAddVCI(vec);
|
|
NsAddNSEI(vec);
|
|
break;
|
|
case NSPDUType::NS_BLOCK:
|
|
NsAddCause(vec,cause);
|
|
NsAddVCI(vec);
|
|
break;
|
|
case NSPDUType::NS_BLOCK_ACK:
|
|
NsAddVCI(vec);
|
|
break;
|
|
case NSPDUType::NS_UNBLOCK:
|
|
break; // 1 byte messages is finished.
|
|
case NSPDUType::NS_UNBLOCK_ACK:
|
|
break; // 1 byte messages is finished.
|
|
case NSPDUType::NS_STATUS:
|
|
NsAddCause(vec,cause);
|
|
switch (cause) {
|
|
case NsCause::NSVCBlocked:
|
|
case NsCause::NSVCUnknown:
|
|
NsAddVCI(vec);
|
|
break;
|
|
case NsCause::SemanticallyIncorrectPDU:
|
|
case NsCause::PduNotCompatible:
|
|
case NsCause::ProtocolError:
|
|
case NsCause::InvalidEssentialIE:
|
|
case NsCause::MissingEssentialIE:
|
|
// unimplemented.
|
|
assert(0); // In these cases need to append PDU.
|
|
|
|
case NsCause::BVCIUnknown:
|
|
// We dont really know what the cause was,
|
|
// and this wont happen, so just make up a BVCI
|
|
NsAddBVCI(vec,BVCI::PTP);
|
|
break;
|
|
case NsCause::TransitNetworkFailure:
|
|
case NsCause::OAndMIntervention:
|
|
case NsCause::EquipmentFailure:
|
|
break; // nothing more needed.
|
|
}
|
|
break;
|
|
case NSPDUType::NS_ALIVE:
|
|
break; // 1 byte messages is finished.
|
|
case NSPDUType::NS_ALIVE_ACK:
|
|
break; // 1 byte messages is finished.
|
|
|
|
case NSPDUType::NS_UNITDATA:
|
|
assert(0); // Not handled by this routine.
|
|
default: assert(0);
|
|
}
|
|
return vec;
|
|
}
|
|
|
|
static void BVCAddBVCI(ByteVector *vec, BSPDUType::type bstype)
|
|
{
|
|
vec->appendByte(IEIType::BVCI);
|
|
vec->appendLI(2);
|
|
vec->appendUInt16(BSPDUType::LookupBVCI(bstype));
|
|
}
|
|
|
|
static void BVCAddCause(ByteVector *vec, int cause)
|
|
{
|
|
vec->appendByte(IEIType::Cause);
|
|
vec->appendLI(1);
|
|
vec->appendByte(cause);
|
|
}
|
|
|
|
static void BVCAddTag(ByteVector *vec, int tag)
|
|
{
|
|
vec->appendByte(IEIType::Tag);
|
|
vec->appendLI(1);
|
|
vec->appendByte(tag);
|
|
}
|
|
|
|
// GSM 08.18 10.4.12 and 11.3.9
|
|
static void BVCAddCellIdentifier(ByteVector *vec)
|
|
{
|
|
vec->appendByte(IEIType::CellIdentifier);
|
|
vec->appendLI(8);
|
|
// Add Routing Area Identification IE from GSM 04.08 10.5.5.15, excluding IEI type and length bytes.
|
|
// Another fine example of Object Oriented programming preventing sharing of code:
|
|
// this information is wrapped up in the middle of an inapplicable class hierarchy.
|
|
const char*mMCC = gConfig.getStr("GSM.Identity.MCC").c_str();
|
|
const char*mMNC = gConfig.getStr("GSM.Identity.MNC").c_str();
|
|
unsigned mLAC = gConfig.getNum("GSM.Identity.LAC");
|
|
unsigned mRAC = gConfig.getNum("GPRS.RAC");
|
|
vec->appendByte(((mMCC[1]-'0')<<4) | (mMCC[0]-'0')); // MCC digit 2, MCC digit 1
|
|
vec->appendByte(((mMNC[2]-'0')<<4) | (mMCC[2]-'0')); // MNC digit 3, MCC digit 3
|
|
vec->appendByte(((mMNC[1]-'0')<<4) | (mMNC[0]-'0')); // MNC digit 2, MNC digit 1
|
|
vec->appendUInt16(mLAC);
|
|
vec->appendByte(mRAC);
|
|
// Add Routing Area Identification IE from GSM 04.08 10.5.5.15, excluding IEI type and length bytes.
|
|
// Add Cell Identity IE GSM 04.08 10.5.1.1, excluding IEI type
|
|
unsigned mCI = gConfig.getNum("GSM.Identity.CI");
|
|
vec->appendUInt16(mCI);
|
|
}
|
|
|
|
// GSM 08.18 sec 10 describes the PDU messages that the SGSN can send to the BSS.
|
|
// Cause values: 08.18 sec 11.3.8
|
|
BSSGUplinkMsg *BVCFactory(BSPDUType::type bstype,
|
|
int arg1) // For reset, the bvci to reset; for others may be cause or tag .
|
|
{
|
|
BSSGUplinkMsg *vec = new BSSGUplinkMsg(80); // Big enough for any message.
|
|
BVCI::type bvci;
|
|
|
|
vec->setAppendP(0); // Setup vec for appending.
|
|
|
|
// Add the NS header.
|
|
vec->appendByte(NSPDUType::NS_UNITDATA);
|
|
vec->appendByte(0); // unused byte.
|
|
vec->appendUInt16(gBSSG.mbsNSEI);
|
|
// Add the BSSG message type
|
|
vec->appendByte((ByteType)bstype);
|
|
|
|
switch (bstype) {
|
|
case BSPDUType::BVC_RESET:
|
|
// See GSM 08.18 sec 8.4: BVC-RESET procedure; and 10.4.12: BVC-RESET message.
|
|
bvci = (BVCI::type) arg1;
|
|
vec->appendByte(IEIType::BVCI);
|
|
vec->appendLI(2);
|
|
vec->appendUInt16(bvci);
|
|
BVCAddCause(vec,0x8); // Cause 8: O&M Intervention
|
|
if (bvci != BVCI::SIGNALLING) {
|
|
BVCAddCellIdentifier(vec);
|
|
}
|
|
// We dont use the feature bitmap.
|
|
break;
|
|
case BSPDUType::BVC_RESET_ACK:
|
|
BVCAddBVCI(vec,bstype);
|
|
// There could be a cell identifier
|
|
// There coulde be a feature bitmap.
|
|
break;
|
|
case BSPDUType::BVC_BLOCK:
|
|
BVCAddBVCI(vec,bstype);
|
|
BVCAddCause(vec,arg1);
|
|
break;
|
|
case BSPDUType::BVC_BLOCK_ACK: // fall through
|
|
case BSPDUType::BVC_UNBLOCK: // fall through
|
|
case BSPDUType::BVC_UNBLOCK_ACK:
|
|
BVCAddBVCI(vec,bstype);
|
|
break;
|
|
case BSPDUType::FLOW_CONTROL_BVC_ACK:
|
|
BVCAddTag(vec,arg1);
|
|
break;
|
|
default: assert(0);
|
|
}
|
|
return vec;
|
|
}
|
|
|
|
// Length Indicator GSM 08.16 10.1.2
|
|
// And I quote:
|
|
// "The BSS or SGSN shall not consider the presence of octet 2a in a received IE
|
|
// as an error when the IE is short enough for the length to be coded
|
|
// in octet 2 only."
|
|
// (pat) If the length is longer than 127, it is written simply
|
|
// as a 16 bit number in network order, which is high byte first,
|
|
// so the upper most bit is 0 if the value is <= 32767
|
|
static unsigned IEILength(unsigned int len)
|
|
{
|
|
if (len < 127) return 0x80 + len;
|
|
assert(0);
|
|
}
|
|
|
|
|
|
void NSMsg::textNSHeader(std::ostream&os) const
|
|
{
|
|
int nstype = (int)getNSPDUType();
|
|
if (nstype == NSPDUType::NS_UNITDATA) {
|
|
os <<"NSPDUType="<<nstype <<"="<<NSPDUType::name(nstype)<<" BVCI="<<getUInt16(2);
|
|
} else {
|
|
os <<"NSPDUType="<<nstype <<"="<<NSPDUType::name(nstype);
|
|
// The rest of the message is not interesting to us, so dont bother.
|
|
}
|
|
}
|
|
|
|
void NSMsg::text(std::ostream&os) const
|
|
{
|
|
os <<" NSMsg:"; textNSHeader(os);
|
|
}
|
|
|
|
void BSSGMsg::text(std::ostream&os) const
|
|
{
|
|
textNSHeader(os);
|
|
os << " BSPDUType="<<getPDUType() <<" size="<<size();
|
|
}
|
|
|
|
std::string BSSGMsg::briefDescription() const
|
|
{
|
|
return BSPDUType::name(getPDUType());
|
|
}
|
|
|
|
BSSGMsgULUnitData::BSSGMsgULUnitData(unsigned wLen, // Length of the PDU to be sent.
|
|
uint32_t wTLLI)
|
|
: BSSGUplinkMsg(wLen + HeaderLength)
|
|
{
|
|
QoSProfile qos; // Both we and the SGSN ignore the contents of this.
|
|
// Note there is a different QoS format included in
|
|
// PDP Context activation messages.
|
|
|
|
setAppendP(0); // Setup vec for appending.
|
|
// Write the NS header.
|
|
appendByte(NSPDUType::NS_UNITDATA);
|
|
appendByte(0); // unused byte.
|
|
// The NS UNITDATA message puts the BVCI in the NS header where the NSEI normally goes.
|
|
appendUInt16(gBSSG.mbsBVCI);
|
|
// End of NS Header, start of BSSG message.
|
|
appendByte(BSPDUType::UL_UNITDATA);
|
|
appendUInt32(wTLLI);
|
|
qos.qosAppend(this);
|
|
BVCAddCellIdentifier(this);
|
|
// We need a two byte alignment IEI to get to a 32 bit boundary.
|
|
appendByte(IEIType::AlignmentOctets);
|
|
appendByte(IEILength(0));
|
|
|
|
// The PDU IEI itself comes next.
|
|
appendByte(IEIType::LLCPDU);
|
|
// We are allowed to use a 16 bit length IEI even for elements
|
|
// less than 128 bytes long, so we will.
|
|
mLengthPosition = size(); // Where the length goes.
|
|
appendUInt16(0); // A spot for the length.
|
|
// Followed by the PDU data.
|
|
assert(size() == HeaderLength);
|
|
}
|
|
|
|
// Call this when the PDU is finished to set the length in the BSSG header.
|
|
void BSSGMsgULUnitData::setLength()
|
|
{
|
|
// The PDU begins immediately after the 2 byte length.
|
|
assert(size() >= HeaderLength);
|
|
unsigned pdulen = size() - HeaderLength;
|
|
GPRSLOG(1) << "setLength:"<<LOGVAR(pdulen) << LOGVAR(mLengthPosition) << LOGVAR(size());
|
|
setUInt16(mLengthPosition,pdulen);
|
|
}
|
|
|
|
void BSSGMsgULUnitData::text(std::ostream&os) const
|
|
{
|
|
os <<"BSSGMsgULUnitData=(";
|
|
os <<"tbf=TBF#"<<mTBFId<<" ";
|
|
BSSGUplinkMsg::text(os);
|
|
unsigned TLLI = getTLLI();
|
|
os << LOGHEX(TLLI);
|
|
// The payload is 0 length only during debugging when we send in empty messages.
|
|
ByteVector payload(tail(HeaderLength));
|
|
if (payload.size()) {
|
|
//GPRS::LLCFrame llcmsg(*const_cast<BSSGMsgULUnitData*>(this));
|
|
os << " LLC UL payload="<<payload;
|
|
SGSN::LlcFrameDump llcmsg(payload);
|
|
os << " ";
|
|
llcmsg.text(os);
|
|
}
|
|
//os << " payload=" <<payload;
|
|
os <<")";
|
|
}
|
|
|
|
std::string BSSGMsgDLUnitData::briefDescription() const
|
|
{
|
|
std::ostringstream ss;
|
|
if (mbdPDU.size()) {
|
|
SGSN::LlcFrameDump llcmsg(mbdPDU);
|
|
llcmsg.textContent(ss,false);
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
void BSSGMsgDLUnitData::text(std::ostream &os) const
|
|
{
|
|
os << "BSSGMsgDLUnitData:";
|
|
BSSGDownlinkMsg::text(os);
|
|
os << " BSPDUType="<<getPDUType();
|
|
os<<LOGHEX(mbdTLLI) <<LOGVAR(mbdPDULifetime);
|
|
os <<" QoS skipped"; // Skip qos for now.
|
|
if (mbdHaveOldTLLI) { os << LOGHEX(mbdOldTLLI); }
|
|
os <<" RACap=(" <<mbdRACap <<")";
|
|
os <<" IMSI=(" <<mbdIMSI <<")";
|
|
//os <<" PDU=(" <<mbdPDU <<")";
|
|
if (mbdPDU.size()) {
|
|
os << " LLC DL payload="<<mbdPDU;
|
|
SGSN::LlcFrameDump llcmsg(mbdPDU);
|
|
os << " ";
|
|
llcmsg.text(os);
|
|
}
|
|
os <<"\n";
|
|
}
|
|
|
|
};
|