622 lines
21 KiB
C++
622 lines
21 KiB
C++
/**@file LLC objects, from GSM 04.64. */
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
//#include "GPRSInternal.h" // for LLCWARN
|
|
|
|
#ifndef LLC_H
|
|
#define LLC_H
|
|
|
|
#include <ByteVector.h>
|
|
#include "SgsnBase.h"
|
|
#include "GPRSL3Messages.h"
|
|
#include <MemoryLeak.h>
|
|
//#include "TBF.h"
|
|
|
|
namespace GPRS { class MSInfo; }
|
|
|
|
namespace SGSN {
|
|
struct LlcEntity;
|
|
class SgsnInfo;
|
|
struct Sgsn;
|
|
class PdpContext;
|
|
struct Sndcp;
|
|
|
|
// GSM04.64 6.2.3 table 2.
|
|
struct LlcSapi {
|
|
enum type {
|
|
GPRSMM = 1, // receives all control messages, both GMM an SM protocols.
|
|
TOM2 = 2,
|
|
UserData3 = 3,
|
|
UserData5 = 5,
|
|
SMS = 7,
|
|
TOM8 = 8,
|
|
UserData9 = 9,
|
|
UserData11 = 11,
|
|
};
|
|
static const char *name(type sapi);
|
|
};
|
|
|
|
struct LLCFormat {
|
|
enum type {
|
|
Invalid, I, S, U, UI, ISack, SSack
|
|
};
|
|
static const char *name(type format);
|
|
};
|
|
|
|
// (pat) This is a generic parity generator for up to 32 bit parity.
|
|
|
|
// Pats Notes: The generator-based algorithm in BitVector.h did not work
|
|
// for the LLC FCS when I tried to pre-invert the remainder.
|
|
// Here is a new one based on table lookups, which is better for ByteVectors.
|
|
// An N-bit CRC is always N+1 bits long where the first and last bits are 1.
|
|
// The CRC is the remainder of division of the input by the generator.
|
|
// The complicated description of the LLC FCS is describing a normal 24-bit CRC
|
|
// but they are setting the remainder to all ones beforehand (to catch the input
|
|
// error of leading 0s) and inverting it after (to catch the input error of trailing 0s.)
|
|
// Algorithm here is based on LSB-first algorithm from wikipedia "Computation of CRC"
|
|
// So we have to pre-reverse the bits of the CRC generator.
|
|
// The top bit of the CRC sets the shift-register output to 0 for the division,
|
|
// so the division result comes out 0, but those bits are not relevant to the CRC,
|
|
// which is the remainder, because they are all shifted away.
|
|
// So we chop the top bit off.
|
|
class Parity32
|
|
{
|
|
uint32_t mTab[256]; // Precomputed crc remainders.
|
|
uint32_t mInvertedGenerator;
|
|
uint32_t mInitialRemainder;
|
|
uint32_t mMask;
|
|
public:
|
|
Parity32(uint32_t generator, unsigned width, bool invertFirst);
|
|
uint32_t computeCrc(unsigned char *str, int len);
|
|
uint32_t computeCrc(ByteVector &bv);
|
|
};
|
|
|
|
|
|
// 04.64 5.5 FCS [Frame Check Sequence] field, aka parity.
|
|
// The CRC shall be the ones complement of the sum (modulo 2) of:
|
|
// the remainder of xk (x23 + x22 + x21 +... + x2 + x + 1) divided (modulo 2) by
|
|
// the generator polynomial, where k is the number of bits of the dividend;
|
|
// plus the remainder of the division (modulo 2) by the generator polynomial
|
|
// of the product of x24 by the dividend.
|
|
// The CRC-24 generator polynomial is:
|
|
// G(x) = x24 + x23 + x21 + x20 + x19 + x17 + x16 + x15 + x13 + x8 + x7 + x5 + x4 + x2 + 1
|
|
class LlcParity : public Parity32
|
|
{
|
|
static const uint32_t sFCSGenerator =
|
|
(1<<24) + (1<<23) + (1<<21) + (1<<20) + (1<<19) + (1<<17) + (1<<16) +
|
|
(1<<15) + (1<<13) + (1<<8) + (1<<7) + (1<<5) + (1<<4) + (1<<2) + 1;
|
|
public:
|
|
LlcParity() : Parity32(sFCSGenerator,24,true) {};
|
|
void appendFCS(ByteVector &bv);
|
|
bool checkFCS(ByteVector &bv); // true if parity ok.
|
|
};
|
|
extern LlcParity gLlcParity;
|
|
|
|
struct LlcDefs {
|
|
// sec 6.4 LLC commands for U-format frames, passed in LlcFrame::mM above.
|
|
enum U_M_Commands { // Some are commands and some are responses.
|
|
UCMD_NULL = 0, // null command
|
|
UCMD_DM = 1, // DM response: Disconnected Mode Response.
|
|
// "An LLE shall transmit a DM response to any valid command
|
|
// received that it cannot action."
|
|
// However, I think the multitech modem sends this as a command.
|
|
UCMD_DISC = 4, // DISC command: Disconnect - terminate ABM mode
|
|
UCMD_UA = 6, // UA response: acknowledge SABM or DISC command
|
|
UCMD_SABM = 7, // SABM command: Set Async Balanced (aka acknowledged) Mode
|
|
UCMD_FRMR = 8, // FRMR response: Frame Reject response - includes a bunch of info; see spec.
|
|
UCMD_XID = 0xb // XID command or response: Exchange Identification - used to set parameters.
|
|
};
|
|
// sec 6.4 LLC commands for S-format frames, passed in LlcFrame::mS above.
|
|
enum U_S_Commands {
|
|
SCMD_RR = 0,
|
|
SCMD_ACK = 1,
|
|
SCMD_RNR = 2,
|
|
SCMD_SACK = 3
|
|
};
|
|
};
|
|
|
|
struct LlcMsg {
|
|
virtual void llcProcess(LlcEntity *lle) = 0;
|
|
virtual const char *typeName() { return "generic"; }
|
|
};
|
|
|
|
// Note: The frame formats are in 04.64 6.3.
|
|
struct LlcFrame : public LlcDefs, public ByteVector
|
|
{
|
|
static const unsigned addrOffset = 0;
|
|
static const unsigned controlOffset = 1;
|
|
static const unsigned UIHeaderLength = 3; // 1 byte for addr, 2 for header.
|
|
|
|
// Address fields:
|
|
unsigned getSapi() { return getByte(addrOffset) & 0xf; }
|
|
bool getLlcPD() { return getBitR1(addrOffset,8); } // 1 means LLC frame. Good grief.
|
|
bool getCR() { return getBitR1(addrOffset,7); }
|
|
LLCFormat::type getFormat(); // get the format from the ByteVector.
|
|
|
|
LlcFrame(const ByteVector &vec) : ByteVector(vec) { }
|
|
LlcFrame(unsigned size) : ByteVector(size) { }
|
|
|
|
//LlcFrame(const LlcFrame &other) { *this = other; }
|
|
|
|
// Return nth control byte. 0 is the first byte after the address byte,
|
|
// ie, LLC header byte 1.
|
|
unsigned getControl(unsigned nth) {
|
|
unsigned w = nth + controlOffset;
|
|
return size() <= w ? 0 : getByte(w);
|
|
}
|
|
|
|
// Write the LLC header.
|
|
void writeAddrHeader(unsigned sapi, bool isCmd) {
|
|
setField(0,0,1); // PD bit always 0.
|
|
setField(1,isCmd,1); // Command/Response set to 1 for a downlink command.
|
|
setField(2,0,2); // 2 unused bits.
|
|
setField(4,sapi,4);
|
|
}
|
|
void appendAddrHeader(unsigned sapi, bool isCmd) {
|
|
writeAddrHeader(sapi, isCmd);
|
|
setAppendP(1);
|
|
}
|
|
|
|
LlcMsg *switchFrame();
|
|
void llcProcess1(LlcEntity *lle);
|
|
};
|
|
|
|
struct LlcDlFrame : public LlcFrame
|
|
{
|
|
// Allocate room for all the downlink headers that will be needed:
|
|
// The size is the needed payload size.
|
|
// Add room for all the downstream headers and trailers:
|
|
// sndcp header up to 4 bytes, but it allocates its own, so all we need are:
|
|
// llc header 3 bytes
|
|
// fcs trailer 3 bytes
|
|
// We will overkill it a bit, and and let the downstream prepend their headers.
|
|
LlcDlFrame(unsigned size) : LlcFrame(size+12) {
|
|
trimLeft(8); // Room for headers.
|
|
setAppendP(0);
|
|
}
|
|
};
|
|
|
|
// 05.64 6.3
|
|
struct LlcFrameI : public LlcFrame, public LlcMsg
|
|
{
|
|
LlcFrameI(ByteVector &f) : LlcFrame(f) {}
|
|
|
|
bool getA() { return getBit2(controlOffset,1); }
|
|
unsigned getNS() { return getField2(controlOffset,3,9); }
|
|
unsigned getNR() { return getField2(controlOffset+1,5,9); }
|
|
unsigned getS() { return getField2(controlOffset+2,6,2); } // S1 and S2 bits.
|
|
|
|
void llcProcess(LlcEntity *lle) { // We dont handle them.
|
|
LLCWARN("LLC unexpected I frame ignored"<<LOGVAR2("S",getS()));
|
|
}
|
|
};
|
|
|
|
// 05.64 6.3
|
|
struct LlcFrameS : public LlcFrame, public LlcMsg
|
|
{
|
|
LlcFrameS(ByteVector &f) : LlcFrame(f) {}
|
|
|
|
bool getA() { return getBit2(controlOffset,2); }
|
|
unsigned getNR() { return getField2(controlOffset,5,9); }
|
|
unsigned getS() { return getField2(controlOffset+1,6,2); } // S1 and S2 bits.
|
|
|
|
void llcProcess(LlcEntity *lle) { // We dont handle them.
|
|
LLCWARN("LLC unexpected S frame ignored"<<LOGVAR2("S",getS()));
|
|
}
|
|
};
|
|
|
|
// 05.64 6.3
|
|
struct LlcFrameUI : public LlcFrame, public LlcMsg
|
|
{
|
|
const char *typeName() { return "FrameUI"; }
|
|
LlcFrameUI(ByteVector &f) : LlcFrame(f) {}
|
|
unsigned getNU() { return getField2(controlOffset,5,9); } // frame number
|
|
bool getE() { return getField2(controlOffset+1,6,1); } // encryption bit
|
|
bool getPM() { return getField2(controlOffset+1,7,1); } // protected mode (crc data too?)
|
|
|
|
void llcProcess(LlcEntity *lle);
|
|
void writeUIHeader(unsigned wNU /*, bool pf*/);
|
|
};
|
|
|
|
// 05.64 6.3
|
|
// These are commands using the S bits, mostly for ABM aka acknowledged mode.
|
|
struct LlcFrameU : public LlcFrame, public LlcMsg
|
|
{
|
|
LlcFrameU(ByteVector &f) : LlcFrame(f) {}
|
|
LlcFrameU(unsigned size) : LlcFrame(size) {}
|
|
bool getUPF() { return getBit2(controlOffset,3); }
|
|
unsigned getUM() { return getField2(controlOffset,4,4); }
|
|
|
|
void appendUHeader(unsigned ucmd, bool pf) {
|
|
// Write the header.
|
|
appendField(0x7,3); // Identifies U-format frame.
|
|
appendField(pf,1); // P/F should nearly always be 1.
|
|
appendField(ucmd,4); // The U-format command.
|
|
}
|
|
void llcProcess(LlcEntity *lle);
|
|
};
|
|
|
|
struct LlcFrameSack : public LlcFrame , public LlcMsg
|
|
{
|
|
LlcFrameSack(ByteVector &f) : LlcFrame(f) {}
|
|
void llcProcess(LlcEntity *lle) {
|
|
LOG(ERR) << "LLC SSACK frame ignored";
|
|
LLCWARN("LLC SSACK frame ignored");
|
|
}
|
|
};
|
|
|
|
// XID params are in 6.4.1.6 table 6
|
|
// The "Layer-3 Parameters" are the SNDCP XID params used for compression, and vary by SAPI.
|
|
// The LLC default values are in 8.9.9, and vary by SAPI:
|
|
// Defaults for user SAPIS: N201-I=1503, N201-U=500.
|
|
struct LlcFrameXid : public LlcFrameU
|
|
{
|
|
enum XID_Type {
|
|
Version = 0, // value 0-15
|
|
IOV_UI = 1, // Ciphering input offset value for UI frames, for all SAPIs.
|
|
IOV_I = 2, // Ciphering input offset value for I frames, for SAPI under negotiation
|
|
T200 = 3,
|
|
N200 = 4,
|
|
N201_U = 5, // U frame info length 140-1520
|
|
N201_I = 6, // I frame info length 140-1520
|
|
mD = 7, // I frame buffer in downlink direction, 2 bytes, 0, 9 -24320
|
|
mU = 8, // I frame buffer in uplink direction
|
|
kD = 9, // window size, downlink
|
|
kU = 10, // window size uplink
|
|
layer3 = 11, // These are SNDCP XID commands, see 3GPP 04.65.
|
|
// They must go to one of the data SAPIs.
|
|
reset = 12 // Does a reset.
|
|
};
|
|
LlcFrameXid(unsigned size) : LlcFrameU(size) { }
|
|
//void writeXID(ByteVector bv, bool pf) {
|
|
//writeUHeader(bv,UCMD_XID,1); // poll/final bit is always 1 for XID frame.
|
|
//}
|
|
void appendXidItem(unsigned xidtype, unsigned len, unsigned value)
|
|
{
|
|
if (len <= 3) {
|
|
appendField(0,1); // XL - item length < 4.
|
|
appendField(xidtype,5);
|
|
appendField(len,2);
|
|
appendField(value,8*len);
|
|
} else {
|
|
appendField(1,1); // XL - item length >= 4.
|
|
appendField(xidtype,5);
|
|
appendField(len,8);
|
|
appendField(0,2); // 2 unused bits.
|
|
appendField(value,8*len);
|
|
}
|
|
}
|
|
};
|
|
|
|
// 3GPP 04.64 Logical Link Entity part of LLC.
|
|
// There is one of these for each data LLC SAPI for each MS.
|
|
// The LLC SAPIs are supposed to correspond to QoS [Quality of Service] classes;
|
|
// the final NSAPI [Network SAPIs] that are connected to PDPContexts
|
|
// are the on the high side of the SNDCP entity for user-data LLC SAPIs only.
|
|
// There can be a many-to-one mapping of SNDCP entity to LLC SAPI.
|
|
// The pdu number in the header is used to discard duplicates (04.64 8.4.2),
|
|
// with a memory of 32 frames, which is redundant for data SAPIs because
|
|
// SNDCP also discards PDUs, as well as assembling and reordering.
|
|
// LLC has three states which affect LLC Entity:
|
|
// o TLLI Unassigned state. Can only use UI and XID frames for SAPI = 1.
|
|
// o TLLI assigned (by the SGSN, from higher layers) - affects all entities.
|
|
// o ABM state - acknowledged data state - per entity.
|
|
// Unacknowledged frames may also be sent in ABM mode.
|
|
// The SAP [Service Access Points] are defined in LlcSapi above.
|
|
struct LlcEntity
|
|
{
|
|
// 6.3.5.5 Unacknowledged mode state variables:
|
|
static const unsigned mSNS = 512; // modulo arithment
|
|
unsigned mVU; // Unconfirmed send state variable
|
|
unsigned mVUR; // Unconfirmed receive state variable.
|
|
unsigned mN201U; // Max number of bytes in UI data field.
|
|
// This is used by the SNDCP to split the data.
|
|
//GPRS::MSInfo *mMS; // The MS who ultimately owns us.
|
|
//LlcEntity(GPRS::MSInfo *ms) : mMS(ms) { reset(); }
|
|
//GPRS::MSInfo *getMS() { return mMS; }
|
|
SgsnInfo *mSI; // The SgsnInfo in which we reside.
|
|
LlcEntity(SgsnInfo *wSI) : mSI(wSI) {}
|
|
|
|
//SgsnInfo *getSgsnInfo();
|
|
|
|
void reset() {
|
|
mVU = mVUR = 0;
|
|
// 8.9.8: LLC layer parameter default values.
|
|
// It varies by SAPI, but for user data default is 500, max 1520.
|
|
// For other sapis length wont be exceeded anyway so dont worry about them.
|
|
mN201U = 500;
|
|
}
|
|
|
|
virtual void lleUplinkData(ByteVector &payload) = 0;
|
|
virtual unsigned getLlcSapi() = 0;
|
|
//Sndcp *getSndcp(unsigned nsapi);
|
|
//void setSndcp(unsigned nsapi,Sndcp*);
|
|
void lleWriteLowSide(LlcFrame &frame);
|
|
void lleWriteHighSide(LlcDlFrame &frame, bool isCmd, const char *descr);
|
|
void lleWriteHighSide(L3GprsDlMsg &msg);
|
|
void lleWriteRaw(ByteVector &frame, const char *descr);
|
|
};
|
|
#if LLC_IMPLEMENTATION
|
|
//SgsnInfo *LlcEntity::getSgsnInfo() { return mSI; }
|
|
#endif
|
|
|
|
// Attached to one of the user-data SAPIs for an MS
|
|
struct LlcEntityUserData : public LlcEntity
|
|
{
|
|
unsigned mLlcSapi; // The LLC sapi of this entity.
|
|
unsigned mN201U;
|
|
LlcEntityUserData(unsigned wLlcSapi, SgsnInfo *si) :
|
|
LlcEntity(si),
|
|
mLlcSapi(wLlcSapi)
|
|
{
|
|
mN201U = 500; // Default max size for pdus.
|
|
}
|
|
|
|
unsigned getLlcSapi() { return mLlcSapi; }
|
|
unsigned getMaxPduSize() { return mN201U; }
|
|
Sndcp *getSndcp(unsigned nsapi);
|
|
void setSndcp(unsigned nsapi, Sndcp*ptr);
|
|
void lleUplinkData(ByteVector &payload);
|
|
};
|
|
#if LLC_IMPLEMENTATION
|
|
#endif
|
|
|
|
// Attached to the LLC GPRSMM sapi for an MS.
|
|
struct LlcEntityGmm : public LlcEntity
|
|
{
|
|
LlcEntityGmm(SgsnInfo *si) : LlcEntity(si) {}
|
|
// The payload is in L3 message.
|
|
void lleUplinkData(ByteVector &payload); // calls: Sgsn::handleL3Msg(this,&payload);
|
|
unsigned getLlcSapi() { return 1; }
|
|
};
|
|
|
|
// 3GPP 04.64: SNDCP, with yet another stupid header.
|
|
// It is a miracle any data gets through at all.
|
|
// The NSAPI are on the high (network) side, and the SAPI are the low side at LLC.
|
|
// There can be multiple NSSAPI, each with a PDP context, attached to each SAPI.
|
|
// The L3 Activate PDP context message specifies both NSAPI and LLC SAPI.
|
|
struct SndcpFrame : public ByteVector
|
|
{
|
|
|
|
SndcpFrame(ByteVector &bv) : ByteVector(bv) { }
|
|
|
|
bool getF() { return getBit(1); } // First segment indicator.
|
|
bool getT() { return getBit(2); } // 0 - DATA(acked) 1 - UNITDATA
|
|
bool getM() { return getBit(3); } // More bit: 1 => more segments.
|
|
unsigned getNSapi() { return getField(4,4); } // NSAPI on sndcp network high side.
|
|
// Dcomp and Pcomp are only extent if F bit is set.
|
|
unsigned getDcomp() { return getField2(1,0,4); } // data compression
|
|
unsigned getPcomp() { return getField2(1,4,4); } // protocol compression
|
|
ByteVector getPayload() { return tail((getT() ? 3 : 2)+(getF()?1:0)); }
|
|
|
|
// For UNITDATA (unacknowledged mode) - T bit == 1
|
|
unsigned getSegmentNumber() { return getField2(getF()?2:1,0,4); }
|
|
unsigned getPduNumber() {
|
|
if (getT()) { // unacknowledged mode.
|
|
return getField2(getF()?2:1,4,12);
|
|
} else { // acknowledged mode.
|
|
return getField2(getF()?2:1,0,8);
|
|
}
|
|
}
|
|
|
|
// For DATA (acknowled mode) - T bit == 0
|
|
//unsigned getAMPduNumber() { return getField2(2,0,8); }
|
|
};
|
|
|
|
class Sndcp
|
|
{
|
|
enum { // Address field bits
|
|
F_BIT = 0x40,
|
|
T_BIT = 0x20,
|
|
M_BIT = 0x10
|
|
};
|
|
// Our identifiers:
|
|
static const unsigned sUmSNS = 4096; // Module for Unacknowledged Mode, sequence number space.
|
|
static const unsigned sAmSNS = 256; // Module for Acknowledged Mode, sequence number space.
|
|
unsigned mSNS; // Current modulo
|
|
unsigned mNSapi; // 5..16
|
|
unsigned mLlcSapi; // One of the LLC UserData sapis.
|
|
UInt_z mRecvNPdu;
|
|
UInt_z mSendNPdu;
|
|
LlcEntityUserData *mlle;
|
|
|
|
public:
|
|
//PdpContext *mPdp;
|
|
//void setPdp(PdpContext *pdp) { mPdp = pdp; }
|
|
//PdpContext *getPdp() { return mPdp; }
|
|
SgsnInfo *getSgsnInfo();
|
|
|
|
// I dont think it is possible in our case for an sndcp to exist in pdp-inactive state.
|
|
//bool isPdpInactive();
|
|
|
|
// Data reassembly Queues:
|
|
// We are supposed to reorder pdus.
|
|
// We dont care about the order, since they are going to the internet, but the packets may
|
|
// arrive in multiple segments that have become unordered, possibly due to multi-slot transmission,
|
|
// so we need to handle it. We will only reorder the last sMemory pdus.
|
|
static const unsigned sMemory = 32;
|
|
struct OneSdu {
|
|
UInt_z mSegCount; // Number of segs, derived from 'm' bit.
|
|
ByteVector segs[16]; // The segments.
|
|
};
|
|
OneSdu mSegs[sMemory]; // This stuff is all deleted automatically.
|
|
|
|
Sndcp(unsigned wNSapi, unsigned wLlcSapi, LlcEntityUserData *mlle);
|
|
~Sndcp();
|
|
|
|
private:
|
|
// If we have all the segments for pdu num, send it off.
|
|
// If force, delete it even if incomplete.
|
|
void flush(unsigned num, bool force);
|
|
int diffSNS(int v1, int v2);
|
|
// SDU segmented to this size. May be negotiated using XID command, which we dont implement.
|
|
unsigned getMaxPduSize();
|
|
void sndcpWriteSegment(ByteVector &pduSeg, unsigned segnum, unsigned flags);
|
|
|
|
public:
|
|
// downlink data from internet comes in here.
|
|
// It needs to be segmented and sent to LLC.
|
|
void sndcpWriteHighSide(ByteVector &sdu);
|
|
// uplink data from MS comes in here.
|
|
void sndcpWriteLowSide(SndcpFrame &frame);
|
|
};
|
|
#if LLC_IMPLEMENTATION
|
|
Sndcp::Sndcp(unsigned wNSapi, unsigned wLlcSapi, LlcEntityUserData *wlle) :
|
|
mSNS(sUmSNS),
|
|
mNSapi(wNSapi),
|
|
mLlcSapi(wLlcSapi),
|
|
mlle(wlle)
|
|
//,mPdp(0)
|
|
{
|
|
mlle->setSndcp(mNSapi,this);
|
|
}
|
|
|
|
Sndcp::~Sndcp()
|
|
{
|
|
// Test mlle and mPdp and setting to 0 after are cautious overkill.
|
|
// The setSndcp() is also redundant, since our caller does it too.
|
|
if (mlle) {mlle->setSndcp(mNSapi,0); mlle = 0;}
|
|
//if (mPdp) {delete mPdp; mPdp = 0;}
|
|
}
|
|
#endif
|
|
|
|
// A connection is identified by DLCI [Data Link Connection Identifier] which is a TLLI+SAP.
|
|
// However, our LlcEngine corresponds directly with an SgsnInfo which corresponds
|
|
// directly with the MSInfo, so we dont use this.
|
|
//struct LlcDlci {
|
|
// MSInfo *ms;
|
|
// unsigned char mSapi;
|
|
//};
|
|
|
|
// An LLC engine for use with a single MS.
|
|
struct LlcEngine {
|
|
// A set of LLC LLE [logical link entities] for use in an MS.
|
|
// These use predefined SAPIs, and here they are.
|
|
// The different user-data ones are supposed to correspond to different priorities,
|
|
// but we ignore the distinction.
|
|
LlcEntityGmm mLleGmm;
|
|
LlcEntityUserData mLleUserData3;
|
|
LlcEntityUserData mLleUserData5;
|
|
LlcEntityUserData mLleUserData9;
|
|
LlcEntityUserData mLleUserData11;
|
|
|
|
// An sndcp entity for each NSAPI that is in use, ie, for each allocated pdpcontext.
|
|
// Allocated/deallocated on demand and at the same time as the PdpContext they connect with.
|
|
// The LlcEntityUserData are connected to these in a one-to-many mapping,
|
|
// ie, each LlcEntity may be tied to one or more Sndcp.
|
|
#if 0==SNDCP_IN_PDP
|
|
Sndcp *mSndcp[16]; // 0-4 are reserved (same as UMTS), but we just allocate the whole array
|
|
// and index it directly with nsapi [Network SAPI]
|
|
#endif
|
|
|
|
LlcEngine(SgsnInfo *si) :
|
|
mLleGmm(si),
|
|
mLleUserData3(3,si),
|
|
mLleUserData5(5,si),
|
|
mLleUserData9(9,si),
|
|
mLleUserData11(11,si)
|
|
{
|
|
RN_MEMCHKNEW(LlcEngine)
|
|
#if 0==SNDCP_IN_PDP
|
|
memset(mSndcp,0,sizeof(mSndcp));
|
|
#endif
|
|
}
|
|
|
|
~LlcEngine() { RN_MEMCHKDEL(LlcEngine) }
|
|
|
|
static bool isValidDataSapi(unsigned sapi) {
|
|
switch (sapi) {
|
|
case LlcSapi::UserData3:
|
|
case LlcSapi::UserData5:
|
|
case LlcSapi::UserData9:
|
|
case LlcSapi::UserData11:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LlcEntity *getLlcEntity(unsigned sapi) {
|
|
switch (sapi) {
|
|
case LlcSapi::GPRSMM: return &mLleGmm;
|
|
case LlcSapi::UserData3: return &mLleUserData3;
|
|
case LlcSapi::UserData5: return &mLleUserData5;
|
|
case LlcSapi::UserData9: return &mLleUserData9;
|
|
case LlcSapi::UserData11: return &mLleUserData11;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
// The LLC Entities. The GMM ones process L3 messages and the UserData ones are for...guess what?
|
|
// The LlcEntityUserData communicates to one or more Sndcp on its high side.
|
|
LlcEntityGmm *getLlcGmm();
|
|
LlcEntityUserData *getLlcEntityUserData(unsigned llcSapi);
|
|
|
|
// The bv is an LLC message.
|
|
// It may cause the LLC to send some download messages,
|
|
// or it may be an information frame on a UserData sapi
|
|
// whose payload goes to GGSN via an Sndcp entity.
|
|
void llcWriteLowSide(ByteVector &bv,SgsnInfo *si);
|
|
|
|
void llcWriteHighSide(ByteVector &sdu,int nsapi);
|
|
|
|
void allocSndcp(SgsnInfo *si, unsigned nsapi, unsigned llcsapi);
|
|
void freeSndcp(unsigned nsapi);
|
|
};
|
|
|
|
|
|
// GSM04.64 6.2
|
|
// (pat) The RLC/MAC layer knows nothing about the contents of an LlcFrame; it just passes data
|
|
// back and forth between the MS and SGSN.
|
|
// This is used to dump out the complete header.
|
|
struct LlcFrameDump : public LlcFrame
|
|
{
|
|
LLCFormat::type mFormat;
|
|
|
|
// I, I+S format is for acknowledged mode.
|
|
// UI format is for unacknowledged mode.
|
|
// U format is for LLC control only, includes no data.
|
|
// Control fields. Which are valid depends on format:
|
|
bool mE; // UI format only
|
|
bool mPM; // UI format only
|
|
bool mA; // I or S format
|
|
bool mPF; // U format only
|
|
unsigned mM; // U format only
|
|
unsigned mS; // I or S formats.
|
|
unsigned mK; // I Sack format bitmap length.
|
|
unsigned mNS, mNR, mNU;
|
|
|
|
// Return -1 if we dont know how long, which is S SACK format.
|
|
int headerLength();
|
|
|
|
//LlcFrameDump(const ByteVector &vec) : LlcFrame(vec)
|
|
LlcFrameDump(const ByteVector &vec) : LlcFrame(vec)
|
|
{
|
|
mFormat = getFormat();
|
|
llcParseDump();
|
|
}
|
|
void llcParseDump();
|
|
|
|
void textHeader(std::ostream &os);
|
|
void textContent(std::ostream &os, bool verbose);
|
|
void text(std::ostream &os);
|
|
};
|
|
|
|
}; // namespace GPRS
|
|
|
|
#endif
|
|
|