OpenBTS-UMTS/SGSNGGSN/LLC.h

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