yate/libs/yradio/codec.cpp

5185 lines
203 KiB
C++

/**
* codec.cpp
* This file is part of the YATE Project http://YATE.null.ro
*
* GSM Radio Layer 3 messages coder and decoder
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
*
* 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.
*/
#include <yateradio.h>
using namespace TelEngine;
struct IEParam;
struct IEType;
struct RL3ProtoMessage;
struct IEType {
unsigned int (*decoder)(const GSML3Codec*,uint8_t,const IEParam*,const uint8_t*&, unsigned int&, XmlElement*&,
const NamedList&);
unsigned int (*encoder)(const GSML3Codec*,uint8_t,const IEParam*,XmlElement*,DataBlock&,const NamedList&);
const void* data;
};
struct IEParam {
GSML3Codec::Type type;
GSML3Codec::XmlType xmlType;
uint8_t iei;
const String name;
bool isOptional;
uint16_t length; // in bits
bool lowerBits;
const IEType& ieType;
};
struct RL3Message {
uint16_t value;
const String name;
const IEParam* params;
const IEParam* toMSParams;
};
static const String s_pduCodec = "codecTag";
static const String s_epsSequenceNumber = "SequenceNumber";
static const String s_encAttr = "enc";
static const String s_typeAttr = "type";
static const String s_flags = "Flags";
static const String s_data = "data";
static const String s_PD = "PD";
static const String s_SAPI = "SAPI";
static const char s_digits[] = "0123456789";
#define GET_DIGIT(val,str,err,odd) \
if ((val > 9 && val != 0x0f) || (!odd && val == 0x0f) || (odd && val != 0x0f)) {\
Debug(DebugWarn,"GET_DIGIT: Invalid digit=%u",val);\
return err;\
}\
else if (val != 0x0f) \
str << s_digits[val];
#define SET_DIGIT(c,b,idx,highOctet,err) \
{ \
if (!(('0' <= c) && (c <= '9'))) { \
Debug(DebugWarn,"SET_DIGIT: Invalid digit=%c",c);\
return err; \
}\
*(b + idx) |= (highOctet ? ((c - '0') << 4) : (c - '0')); \
}
#define CONDITIONAL_ERROR(param,x,y) (param ? (param->isOptional ? GSML3Codec::x : GSML3Codec::y) : GSML3Codec::y)
static unsigned int decodeParams(const GSML3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len,
XmlElement*& out, const IEParam* param, const NamedList& params = NamedList::empty());
static unsigned int encodeParams(const GSML3Codec* codec, uint8_t proto, XmlElement* in, DataBlock& out,
const IEParam* param, const NamedList& params);
static unsigned int decodeSecHeader(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params);
static unsigned int encodeSecHeader(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params);
static unsigned int decodeRL3Msg(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params);
static unsigned int encodeRL3Msg(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params);
static void unpackGSM7Bit(unsigned char* in, unsigned int length, DataBlock& out)
{
if (!(in && length))
return;
unsigned int len = length * 8 / 7;
out.assign(0,len);
unsigned char* outData = out.data(0);
uint8_t bits = 0;
uint16_t buf = 0;
for (unsigned int i = 0; i < length; i++) {
buf |= ((uint16_t)*in) << bits;
in++;
bits += 8;
while (bits >= 7) {
*outData = (buf & 0x7f);
outData++;
buf >>= 7;
bits -= 7;
}
}
if ((bits == 0) && (out[len - 1] == '\r'))
out.assign(out.data(),out.length() - 1);
}
void packGSM7Bit(unsigned char* in, unsigned int length, DataBlock& out)
{
if (!(in && length))
return;
int len = (length + 1) * 7 / 8;
out.assign(0,len);
unsigned char* outData = out.data(0);
uint8_t bits = 0;
uint32_t buf = 0;
uint8_t code = 0;
for (unsigned int i = 0; i < length; i++) {
code = *in++;
buf |= (code << bits);
bits += 7;
while (bits >= 8) {
*outData = buf & 0xff;
outData++;
buf >>= 8;
bits -= 8;
}
}
if (bits) {
*outData = buf & 0xff;
// if just 1 bit use a shifted \r as filler
if (bits == 1)
*outData |= 0x1a;
}
else if (code == '\r') {
code = 0x0d;
// last char was \r, add another \r
out.append(&code,sizeof(code));
}
}
static bool getBCDDigits(const uint8_t*& in, unsigned int& len, String& digits)
{
if (!(in && len))
return true;
static const char s_bcdDigits[] = "0123456789*#ABC";
while (len) {
digits += s_bcdDigits[(*in & 0x0f)];
uint8_t odd = (*in >> 4);
if ((odd & 0x0f) != 0x0f)
digits += s_bcdDigits[odd];
else if (len > 1)
return false;
in++;
len--;
}
XDebug(DebugAll,"Decoded BCD digits=%s",digits.c_str());
return true;
}
static bool setBCDDigits(uint8_t* in, unsigned int len, unsigned int& idx, const String& digits)
{
if (!digits)
return true;
if (!(in && len))
return false;
bool odd = false;
const char* chars = digits.c_str();
char c = 0;
while((c = *chars++) && (idx < len)) {
uint8_t d = 0;
if (('0' <= c) && (c <= '9'))
d = c - '0';
else if ('*' == c)
d = 10;
else if ('#' == c)
d = 11;
else if ('a' == c || 'A' == c)
d = 12;
else if ('b' == c || 'B' == c)
d = 13;
else if ('c' == c || 'C' == c)
d = 14;
else {
Debug(DebugWarn,"Invalid char=%c in BCD String",c);
return false;
}
odd = !odd;
if (odd)
in[idx] = d;
else
in[idx++] |= (d << 4);
}
if (odd)
in[idx++] |= 0xf0;
return true;
}
static inline uint8_t getUINT8(const uint8_t*& in, unsigned int& len, const IEParam* param)
{
if (!(in && len && param))
return 0;
if (param->length == 4) {
if (param->lowerBits)
return *in & 0x0f;
len--;
return (*in++ >> 4);
}
if (param->length == 8 && param->type == GSML3Codec::TV) {
len--;
return (*in++ & 0x0f);
}
len--;
return *in++;
}
static inline void setUINT8(uint8_t val, DataBlock& where, const IEParam* param)
{
if (!param)
return;
if (param->length == 4 && !param->lowerBits)
*(((uint8_t*) where.data()) + where.length() - 1) |= (uint8_t)(val << 4);
else
where.append(&val,1);
}
static inline void addXMLElement(XmlElement*& dst, XmlElement* what)
{
if (!what)
return;
if (!dst)
dst = what;
else
dst->addChildSafe(what);
}
static inline void advanceBuffer(unsigned int bytes, const uint8_t*& in, unsigned int& len)
{
if (!(in && len))
return;
bytes = (bytes > len ? len : bytes);
in += bytes;
len -= bytes;
}
static inline uint16_t getUINT16(const uint8_t* in, unsigned int len)
{
if (!(in && len >= 2))
return 0;
uint16_t l = in[0];
l = (l << 8) | in[1];
return l;
}
static inline uint16_t getUINT16(const uint8_t*& in, unsigned int& len, bool advance)
{
uint16_t l = getUINT16(in,len);
if (advance)
advanceBuffer(2,in,len);
return l;
}
static inline bool setUINT16(uint16_t val, uint8_t* in, unsigned int len)
{
if (!(in && len >= 2))
return false;
*in++ = val >> 8;
*in++ = val;
return true;
}
static inline void setUINT16(uint16_t val, uint8_t*& in, unsigned int& len, bool advance)
{
if (!setUINT16(val,in,len))
return;
if (advance)
len -= 2;
else
in -= 2;
}
static inline void getFlags(unsigned int bitmask, const TokenDict* dict, String& out)
{
if (!dict)
return;
for (; dict->token; dict++)
if (dict->value & bitmask)
out.append(dict->token,",");
}
static inline unsigned int setFlags(ObjList* flags, const TokenDict* dict)
{
if (!(flags && dict))
return 0;
unsigned int bits = 0;
for (; dict->token; dict++)
if (flags->find(dict->token))
bits |= dict->value;
return bits;
}
static inline unsigned int setFlags(const String& str, const TokenDict* dict)
{
if (!(dict && str))
return 0;
ObjList* list = str.split(',');
unsigned int bits = setFlags(list,dict);
TelEngine::destruct(list);
return bits;
}
static inline const RL3Message* findRL3Msg(uint16_t val, const RL3Message* where)
{
if (!where)
return 0;
while (where->name) {
if (where->value == val)
return where;
where++;
}
return 0;
}
static inline const RL3Message* findRL3Msg(const char* in, const RL3Message* where)
{
if (!(where && in))
return 0;
while (where->name) {
if (where->name == in)
return where;
where++;
}
return 0;
}
static inline const IEParam* getParams(const GSML3Codec* codec, const RL3Message* msg, bool encode = false)
{
if (!(codec && msg))
return 0;
XDebug(codec->dbg(),DebugAll,"getParams(msg=%s,encode=%s) [%p]",msg->name.c_str(),String::boolText(encode),codec->ptr());
if (!msg->toMSParams)
return msg->params;
switch ((codec->flags() & GSML3Codec::MSCoder)) {
case 0:
// we are the network
if (encode)
return msg->toMSParams;
return msg->params;
case GSML3Codec::MSCoder:
// we have the role of a mobile station
if (encode)
return msg->params;
return msg->toMSParams;
default:
return 0;
}
return 0;
}
static void dumpData(const uint8_t*& in, unsigned int& len, XmlElement* xml, bool advance = true)
{
if (!(in && len))
return;
String str;
str.hexify((void*)in,len);
XmlElement* child = new XmlElement(s_data,str);
child->setAttribute(s_encAttr,"hex");
xml->addChildSafe(child);
if (advance)
advanceBuffer(len,in,len);
}
static void getData(DataBlock& out, XmlElement* xml)
{
if (!xml)
return;
XmlElement* data = xml->findFirstChild(&s_data);
if (!data)
return;
DataBlock d;
if (!d.unHexify(data->getText())) {
Debug(DebugWarn,"Failed to unhexify data in xml=%s",xml->tag());
return;
}
out.append(d);
}
static inline unsigned int getMCCMNC(const uint8_t*& in, unsigned int& len, XmlElement* xml, bool advance = true)
{
if (len < 3 || !xml)
return GSML3Codec::ParserErr;
if ((in[0] == 0xff && in[1] == 0xff && in[2] == 0xff) || (in[0] == 0 && (in[1] & 0x0f) == 0)){
if (advance)
advanceBuffer(3,in,len);
return GSML3Codec::NoError;
}
String out;
// get MCC
GET_DIGIT((in[0] & 0x0f),out,GSML3Codec::ParserErr,false);
GET_DIGIT(((in[0] >> 4) & 0x0f),out,GSML3Codec::ParserErr,false);
GET_DIGIT((in[1] & 0x0f),out,GSML3Codec::ParserErr,false);
// get MNC
GET_DIGIT((in[2] & 0x0f),out,GSML3Codec::ParserErr,false);
GET_DIGIT(((in[2] >> 4) & 0x0f),out,GSML3Codec::ParserErr,false);
if ((in[1] & 0xf0) != 0xf0) {
GET_DIGIT(((in[1] >> 4) & 0x0f),out,GSML3Codec::ParserErr,false);
}
xml->addChildSafe(new XmlElement("PLMNidentity",out));
if (advance)
advanceBuffer(3,in,len);
return GSML3Codec::NoError;
}
static inline unsigned int setMCCMNC(XmlElement* in, uint8_t*& out, unsigned int& len, bool advance = true,
bool findChild = true)
{
if (!(in && out && len >= 3))
return GSML3Codec::ParserErr;
XmlElement* xml = findChild ? in->findFirstChild(&YSTRING("PLMNidentity")) : in;
if (!xml) {
*out = *(out+1) = *(out+2) =0xff;
if (advance) {
out += 3;
len -= 3;
}
return GSML3Codec::NoError;
}
if (!(xml && (xml->getText().length() == 5 || xml->getText().length() == 6)))
return GSML3Codec::ParserErr;
const String& text = xml->getText();
// MCC
SET_DIGIT(text[0],out,0,false,GSML3Codec::ParserErr)
SET_DIGIT(text[1],out,0,true,GSML3Codec::ParserErr)
SET_DIGIT(text[2],out,1,false,GSML3Codec::ParserErr)
// MNC
SET_DIGIT(text[3],out,2,false,GSML3Codec::ParserErr)
SET_DIGIT(text[4],out,2,true,GSML3Codec::ParserErr)
if (text.length() == 6)
SET_DIGIT(text[5],out,1,true,GSML3Codec::ParserErr)
else
*(out + 1) |= 0xf0;
if (advance) {
out += 3;
len -= 3;
}
return GSML3Codec::NoError;
}
static bool getInt(const GSML3Codec* codec, const IEParam* param, const uint8_t*& in, unsigned int& len, unsigned int& val)
{
if (!(codec && in && param))
return false;
switch (len) {
case 1:
val = getUINT8(in,len,param);
break;
case 2:
val = getUINT16(in,len,true);
break;
default:
Debug(codec->dbg(),DebugStub,"Please implement decoding of integer on %u bytes, skipping data [%p]",len,codec->ptr());
advanceBuffer(len,in,len);
break;
}
return true;
}
static bool setInt(const GSML3Codec* codec, const IEParam* param, unsigned int val, DataBlock& out)
{
if (!(codec && param))
return false;
unsigned int encLen = param->length;
bool minLen = false;
switch (param->type) {
case GSML3Codec::V:
case GSML3Codec::T:
encLen = param->length;
break;
case GSML3Codec::LV:
encLen = param->length - 8;
break;
case GSML3Codec::LVE:
case GSML3Codec::TLV:
minLen = true;
encLen = param->length - 16;
break;
case GSML3Codec::TLVE:
minLen = true;
encLen = param->length - 24;
break;
case GSML3Codec::TV:
encLen = param->length - (param->length <= 8 ? 0 : 8);
break;
default:
Debug(codec->dbg(),DebugWarn,"Cannot encode integer value=%u for param=%s [%p]",
val,param->name.c_str(),codec->ptr());
return false;
}
if (encLen <= 8 || (minLen && val <= 0xff))
setUINT8(val,out,param);
else if (encLen <= 16 || (minLen && val <= 0xffff)) {
uint8_t l[2];
setUINT16(val,l,2);
out.append(l,2);
}
else {
Debug(codec->dbg(),DebugWarn,"Cannot encode integer value=%u for param=%s [%p]",
val,param->name.c_str(),codec->ptr());
return false;
}
return true;
}
static unsigned int decodeInt(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param && out))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeInt(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
unsigned int val = 0;
if (!getInt(codec,param,in,len,val))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
XmlElement* xml = new XmlElement(param->name,String(val));
addXMLElement(out,xml);
return GSML3Codec::NoError;
}
static unsigned int encodeInt(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && param && in))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeInt(param=%s(%p),xml=%s(%p)) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
const String* valStr = in->childText(param->name);
const unsigned int* defVal = static_cast<const unsigned int*>(param->ieType.data);
unsigned int val = (defVal ? *defVal : 0);
if (TelEngine::null(valStr) && !defVal)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
if (valStr)
val = valStr->toInteger(val);
if (!setInt(codec,param,val,out))
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
return GSML3Codec::NoError;
}
static unsigned int decodeEnum(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param && out))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeEnum(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
unsigned int val = 0;
if (!getInt(codec,param,in,len,val))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
const TokenDict* dict = static_cast<const TokenDict*>(param->ieType.data);
xml->setText(lookup(val,dict,String(val)));
return GSML3Codec::NoError;
}
static unsigned int encodeEnum(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && param && in))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeEnum(param=%s(%p),xml=%s(%p)) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
const String* valStr = in->childText(param->name);
if (TelEngine::null(valStr))
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
const TokenDict* dict = static_cast<const TokenDict*>(param->ieType.data);
unsigned int val = lookup(*valStr,dict,0);
if (!setInt(codec,param,val,out))
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
return GSML3Codec::NoError;
}
static unsigned int decodeFlags(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param && out))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeFlags(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
unsigned int val = 0;
if (!getInt(codec,param,in,len,val))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
const TokenDict* dict = static_cast<const TokenDict*>(param->ieType.data);
String flags;
getFlags(val,dict,flags);
XmlElement* xml = new XmlElement(param->name,flags);
addXMLElement(out,xml);
return GSML3Codec::NoError;
}
static unsigned int encodeFlags(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && param && in))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeFlags(param=%s(%p),xml=%s(%p)) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
const String* valStr = in->childText(param->name);
if (TelEngine::null(valStr))
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
const TokenDict* dict = static_cast<const TokenDict*>(param->ieType.data);
unsigned int val = setFlags(*valStr,dict);
if (!setInt(codec,param,val,out))
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
return GSML3Codec::NoError;
}
// reference ETSI TS 124 007 V11.0.0, section11.2.3.2 Message type octet
static const String s_NSD = "NSD";
static unsigned int decodeMsgType(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param && out))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeMsgType(param=%s(%p),in=%p,len=%u,out=%p [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
uint8_t val = getUINT8(in,len,param);
switch (proto) {
case GSML3Codec::GCC:
case GSML3Codec::BCC:
case GSML3Codec::LCS:
if (val & 0x80)
return GSML3Codec::UnknownMsgType;
// intentional fall through
case GSML3Codec::MM:
case GSML3Codec::CC:
case GSML3Codec::SS:
{
uint8_t nsd = (val >> 6);
out->addChildSafe(new XmlElement(s_NSD,String(nsd)));
val &= 0x3f;
break;
}
default:
break;
}
const RL3Message* msg = static_cast<const RL3Message*>(param->ieType.data);
msg = findRL3Msg(val,msg);
XmlElement* xml = new XmlElement(param->name);
if (msg)
xml->setAttribute(s_typeAttr,msg->name);
else
xml->setAttribute(s_typeAttr,String(val));
addXMLElement(out,xml);
if (!msg) {
dumpData(in,len,xml);
return GSML3Codec::UnknownMsgType;
}
if (const IEParam* msgParams = getParams(codec,msg))
return decodeParams(codec,proto,in,len,xml,msgParams,params);
else
dumpData(in,len,xml);
return GSML3Codec::NoError;
}
static unsigned int encodeMsgType(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && param && in))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeMsgType(param=%s(%p),xml=%s(%p)) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
uint8_t val = 0;
switch (proto) {
case GSML3Codec::GCC:
case GSML3Codec::BCC:
case GSML3Codec::LCS:
case GSML3Codec::MM:
case GSML3Codec::CC:
case GSML3Codec::SS:
{
const String* nsd = in->childText(s_NSD);
if (!TelEngine::null(nsd)) {
uint8_t sd = nsd->toInteger();
if ((proto == GSML3Codec::GCC || proto == GSML3Codec::BCC || proto == GSML3Codec::LCS) && sd > 1)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
val |= (sd << 6);
}
break;
}
default:
break;
}
const RL3Message* msg = static_cast<const RL3Message*>(param->ieType.data);
in = in->findFirstChild(&param->name);
if (!in)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
msg = findRL3Msg(in->attribute(s_typeAttr),msg);
if (!msg) {
DataBlock d;
if (!d.unHexify(in->getText())) {
Debug(codec->dbg(),DebugWarn,"Failed to unhexify message payload in xml=%s(%p) [%p]",in->tag(),in, codec->ptr());
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
}
out.append(d);
return GSML3Codec::NoError;
}
val |= (msg->value & 0x3f);
setUINT8(val,out,param);
if (const IEParam* msgParams = getParams(codec,msg,true))
return encodeParams(codec,proto,in,out,msgParams,params);
else
getData(out,in);
return GSML3Codec::NoError;
}
// reference ETSI TS 124 007 V11.0.0, section 11.2.3.1.1 Protocol discriminator
static unsigned int decodePD(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodePD(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
XmlElement* payload = 0;
if (codec->flags() & GSML3Codec::XmlDumpMsg) {
String s;
s.hexify((void*)in,len);
payload = new XmlElement(YSTRING("message_payload"),s);
}
uint8_t val = getUINT8(in,len,param);
unsigned int status = GSML3Codec::NoError;
const RL3Message* msg = static_cast<const RL3Message*>(param->ieType.data);
msg = findRL3Msg(val,msg);
if (!msg) {
Debug(codec->dbg(),DebugWarn,"Failed to decode Protocol Discriminator %s [%p]",
lookup(val,GSML3Codec::s_protoDict,String(val)),codec->ptr());
return GSML3Codec::UnknownProto;
}
XmlElement* xml = new XmlElement(msg->name);
if (const IEParam* msgParams = getParams(codec,msg))
status = decodeParams(codec,msg->value,in,len,xml,msgParams,params);
addXMLElement(out,xml);
if (payload)
xml->addChildSafe(payload);
return status;
}
static unsigned int encodePD(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && param && in))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodePD(param=%s(%p),xml=%s(%p)) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
const RL3Message* msg = static_cast<const RL3Message*>(param->ieType.data);
msg = findRL3Msg(in->tag(),msg);
if (!msg) {
Debug(codec->dbg(),DebugWarn,"Failed to encode Protocol Discriminator %s [%p]",in->tag(),codec->ptr());
return GSML3Codec::UnknownProto;
}
setUINT8(msg->value,out,param);
String str;
str.hexify(out.data(),out.length());
if (const IEParam* msgParams = getParams(codec,msg,true))
return encodeParams(codec,msg->value,in,out,msgParams,params);
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 301 V11.8.0, section 9.9.3.21 NAS key set identifier
static const String s_TSC = "TSC";
static const String s_NASKeySetId = "NASKeySetId";
static const String s_NASKeyMapCtxt = "mapped-security-context-for-KSI_SGSN";
static const String s_NASKeyNativCtxt = "native-security-context-for-KSI_ASME";
static unsigned int decodeNASKeyId(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeNASKeyId(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
uint8_t val = getUINT8(in,len,param);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
if (val & 0x08)
xml->addChildSafe(new XmlElement(s_TSC,s_NASKeyMapCtxt));
else
xml->addChildSafe(new XmlElement(s_TSC,s_NASKeyNativCtxt));
xml->addChildSafe(new XmlElement(s_NASKeySetId,String((val & 0x07))));
return GSML3Codec::NoError;
}
static unsigned int encodeNASKeyId(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeNASKeyId(param=%s(%p),in=%s(%p)) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
uint8_t val = 0;
XmlElement* child = xml->findFirstChild(&s_TSC);
if (!child)
Debug(codec->dbg(),DebugMild,"Missing '%s' element for encoding %s, assuming default [%p]",s_TSC.c_str(),
param->name.c_str(),codec->ptr());
if (child && (child->getText() == s_NASKeyMapCtxt || child->getText().toBoolean() || child->getText() == "1"))
val |= 0x080;
child = xml->findFirstChild(&s_NASKeySetId);
if (!child)
Debug(codec->dbg(),DebugMild,"Missing '%s' element for encoding %s, assuming default [%p]",s_NASKeySetId.c_str(),
param->name.c_str(),codec->ptr());
else
val |= (child->getText().toInteger() & 0x07);
setUINT8(val,out,param);
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 301 V11.8.0, section 9.9.3.12 EPS mobile identity
static const TokenDict s_epsMobileIdentType[] = {
{"IMSI", 1},
{"IMEI", 3},
{"GUTI", 6},
{0, 0},
};
static unsigned int decodeEPSMobileIdent(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeEPSMobileIdent(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
uint8_t type = in[0] & 0x07;
switch (type) {
case 1:
case 3:
{
// IMSI / IMEI
XmlElement* child = new XmlElement(lookup(type,s_epsMobileIdentType,(type == 1 ? "IMSI" : "IMEI")));
xml->addChildSafe(child);
String digits;
digits.clear();
bool odd = (in[0] & 0x08);
GSML3Codec::Status err = CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
GET_DIGIT((in[0] >> 4),digits,err,(len == 1));
unsigned int index = 1;
while (index < len) {
GET_DIGIT((in[index] & 0x0f),digits,err,false);
GET_DIGIT((in[index] >> 4),digits,err,(index == len - 1 ? !odd : false));
index++;
}
advanceBuffer(index,in,len);
child->addText(digits);
break;
}
case 6:
{
// GUTI
if (len < 11)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
advanceBuffer(1,in,len);
XmlElement* child = new XmlElement("GUTI");
xml->addChildSafe(child);
// get MCC_MNC
if (getMCCMNC(in,len,child))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
// get MME Group ID (16 bits)
uint16_t groupID = getUINT16(in,len,true);
child->addChildSafe(new XmlElement("MMEGroupID",String(groupID)));
// get MME Code (8 bits)
child->addChildSafe(new XmlElement("MMECode",String(in[0])));
advanceBuffer(1,in,len);
// get M-TMSI (32 bits)
String str = "";
str.hexify((void*)in,4);
child->addChildSafe(new XmlElement("M_TMSI",str));
advanceBuffer(4,in,len);
break;
}
default:
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
}
if (len) {
String str;
str.hexify((void*)in,len);
xml->addChildSafe(new XmlElement("extraneous_data",str));
}
return GSML3Codec::NoError;
}
static unsigned int encodeEPSMobileIdent(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeEPSMobileIdent(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!(xml && (xml = xml->findFirstChild())))
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
uint8_t type = lookup(xml->getTag(),s_epsMobileIdentType,0xff);
switch (type) {
case 1:
case 3:
break;
case 6:
{
// GUTI
DataBlock d(0,7);
uint8_t* buf = (uint8_t*)d.data();
*buf++ = 0xf6;
unsigned int len = 6;
if (setMCCMNC(xml,buf,len))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
// MMEGroupID
XmlElement* child = xml->findFirstChild(&YSTRING("MMEGroupID"));
unsigned int val = (unsigned int)-1;
if (!(child && ((val = child->getText().toInteger(val)) <= 0xffff)))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
setUINT16(val,buf,len,true);
// MME Code
child = xml->findFirstChild(&YSTRING("MMECode"));
if (!(child && ((val = child->getText().toInteger(-1)) <= 0xff)))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
*buf++ = (uint8_t) val;
out.append(d);
// M-TMSI
d.clear();
child = xml->findFirstChild(&YSTRING("M_TMSI"));
if (!(child && d.unHexify(child->getText()) && d.length() == 4))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
out.append(d);
break;
}
default:
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
}
return GSML3Codec::NoError;
}
static const TokenDict s_UENetworkCapabMandatory[] =
{
{"EIA7", 0x0001},
{"EIA6", 0x0002},
{"EIA5", 0x0004},
{"EIA4", 0x0008},
{"128-EIA3", 0x0010},
{"128-EIA2", 0x0020},
{"128-EIA1", 0x0040},
{"EIA0", 0x0080},
{"EEA7", 0x0100},
{"EEA6", 0x0200},
{"EEA5", 0x0400},
{"EEIA4", 0x0800},
{"128-EEA3", 0x1000},
{"128-EEA2", 0x2000},
{"128-EEA1", 0x4000},
{"EEA0", 0x8000},
{0, 0},
};
static const TokenDict s_UENetworkCapabOptional[] =
{
{"UEA7", 0x000001},
{"UEA6", 0x000002},
{"UEA5", 0x000004},
{"UEA4", 0x000008},
{"UEA3", 0x000010},
{"UEA2", 0x000020},
{"UEA1", 0x000040},
{"UEA0", 0x000080},
{"UIA7", 0x000100},
{"UIA6", 0x000200},
{"UIA5", 0x000400},
{"UIA4", 0x000800},
{"UIA3", 0x001000},
{"UIA2", 0x002000},
{"UIA1", 0x004000},
{"UCS2", 0x008000},
{"NF", 0x010000},
{"1xSRVCC", 0x020000},
{"LCS", 0x040000},
{"LPP", 0x080000},
{"ACC-CSFB", 0x100000},
{"H.245-ASH", 0x200000},
{0, 0},
};
// reference: ETSI TS 124 301 V11.8.0, section 9.9.3.34 UE network capability
static unsigned int decodeUENetworkCapab(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeUENetworkCapab(param=%s(%p),in=%p,len=%u,out=%p [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
if (len < 2)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
uint16_t mandBytes = getUINT16(in,len,true);
String flags;
getFlags(mandBytes,s_UENetworkCapabMandatory,flags);
if (len) {
// optional bytes are present (only 3 defined). If length is longer, those are spare octets and will be ignored
unsigned int bitmask = 0;
for (unsigned int i = 0; i < (len < 3 ? len : 3); i++) {
bitmask |= (in[i] << 8 * i);
}
getFlags(bitmask,s_UENetworkCapabOptional,flags);
}
xml->addText(flags);
return GSML3Codec::NoError;
}
static unsigned int encodeUENetworkCapab(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
//TODO
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 301 V11.8.0, section 9.9.3.32 Tracking area identity
static unsigned int decodeTAI(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeTAI(param=%s(%p),in=%p,len=%u,out=%p [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
if (len < 5)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
// get MCC MNC
if (getMCCMNC(in,len,xml))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
// get TAC
String str;
str.hexify((void*)in,len);
xml->addChildSafe(new XmlElement("TAC",str));
advanceBuffer(len,in,len);
return GSML3Codec::NoError;
}
static unsigned int encodeTAI(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
//TODO
return GSML3Codec::NoError;
}
static const TokenDict s_splitPgCycle[] = {
{"704", 0},
{"71", 65},
{"72", 66},
{"74", 67},
{"75", 68},
{"77", 69},
{"79", 70},
{"80", 71},
{"83", 72},
{"86", 73},
{"88", 74},
{"90", 75},
{"92", 76},
{"96", 77},
{"101", 78},
{"103", 79},
{"107", 80},
{"112", 81},
{"116", 82},
{"118", 83},
{"128", 84},
{"141", 85},
{"144", 86},
{"150", 87},
{"160", 88},
{"171", 89},
{"176", 90},
{"192", 91},
{"214", 92},
{"224", 93},
{"235", 94},
{"256", 95},
{"288", 96},
{"320", 97},
{"352", 98},
{0, 0},
};
static const TokenDict s_nonDRXTimer[] = {
{"no-non-DRX-mode" , 0},
{"max-1-sec-non-DRX-mode", 1},
{"max-2-sec-non-DRX-mode", 2},
{"max-4-sec-non-DRX-mode", 3},
{"max-8-sec-non-DRX-mode", 4},
{"max-16-sec-non-DRX-mode", 5},
{"max-32-sec-non-DRX-mode", 6},
{"max-64-sec-non-DRX-mode", 7},
{0, 0},
};
static const TokenDict s_drxCycleLength[] = {
{"not-specified-by-the-MS", 0},
{"coefficient-6-and-T", 6},
{"coefficient-7-and-T", 7},
{"coefficient-8-and-T", 8},
{"coefficient-9-and-T", 9},
{0, 0},
};
// reference: ETSI TS 124 008 V11.8.0, 10.5.5.6 DRX parameter
static unsigned int decodeDRX(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeDRX(param=%s(%p),in=%p,len=%u,out=%p [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
if (len < 2)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
uint8_t splitCode = *in;
String splitValue = "1";
if (splitCode && splitCode < 65)
splitValue = splitCode;
else
splitValue = lookup(splitCode,s_splitPgCycle,splitValue);
xml->addChildSafe(new XmlElement("SplitPGCycleCode",splitValue));
xml->addChildSafe(new XmlElement("NonDRXTimer",lookup((in[1] & 0x03),s_nonDRXTimer)));
xml->addChildSafe(new XmlElement("SplitOnCCCH",String::boolText((in[1] & 0x04))));
xml->addChildSafe(new XmlElement("CNSpecificDRXCycleLength",lookup((in[1] & 0xf0),
s_drxCycleLength,s_drxCycleLength[0].token)));
advanceBuffer(2,in,len);
return GSML3Codec::NoError;
}
static unsigned int encodeDRX(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
//TODO
return GSML3Codec::NoError;
}
static const TokenDict s_voiceDomPref[] = {
{"CS-voice-only", 0},
{"IMS-PS-voice-only", 1},
{"CS-voice-preferred", 2},
{"IMS-PS-voice-preferred", 3},
{0, 0},
};
// reference: ETSI TS 124 008 V11.8.0, section 10.5.5.28 Voice domain preference and UE's usage setting
static unsigned int decodeVoicePref(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeVoicePref(param=%s(%p),in=%p,len=%u,out=%p [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
if (*in & 0x04)
xml->addChildSafe(new XmlElement("UEUsageSetting","data-centric"));
else
xml->addChildSafe(new XmlElement("UEUsageSetting","voice-centric"));
uint8_t vd = (*in & 0x03);
xml->addChildSafe(new XmlElement("VoiceDomainPreference",lookup(vd,s_voiceDomPref,String(vd))));
return GSML3Codec::NoError;
}
static unsigned int encodeVoicePref(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
//TODO
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 008 V11.6.0, section 10.5.3.5 Location updating type
static const String s_mmFORFlag = "FOR";
static const String s_mmLUT = "LUT";
static const TokenDict s_mmLUTypes[] = {
{"normal-location-updating", 0},
{"periodic-updating", 1},
{"IMSI-attach", 2},
{"reserved", 3},
{0, 0},
};
static unsigned int decodeLocUpdType(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeLocUpdType(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
uint8_t val = getUINT8(in,len,param);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
xml->addChildSafe(new XmlElement(s_mmFORFlag,String::boolText(val & 0x08)));
xml->addChildSafe(new XmlElement(s_mmLUT,lookup(val & 0x03,s_mmLUTypes,"normal-location-updating")));
return GSML3Codec::NoError;
}
static unsigned int encodeLocUpdType(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeLocUpdType(param=%s(%p),xml=%s(%p)) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml && !param->isOptional)
return GSML3Codec::MissingMandatoryIE;
const String* forFlag = xml->childText(s_mmFORFlag);
const String* lut = xml->childText(s_mmLUT);
uint8_t val = (TelEngine::null(forFlag) ? 0 : (forFlag->toBoolean() ? 0x80 : 0));
if (!TelEngine::null(lut))
val |= (lut->toInteger(s_mmLUTypes) & 0x03);
setUINT8(val,out,param);
return GSML3Codec::NoError;
}
static const TokenDict s_ciphKeySN[] = {
{"0", 0},
{"1", 1},
{"2", 2},
{"3", 3},
{"4", 4},
{"5", 5},
{"6", 6},
{"no-key/reserved", 7},
{0, 0},
};
// reference: ETSI TS 124 008 V11.6.0, section 10.5.1.3 Location area identification
const String& s_LAC = "LAC";
static inline bool getPLMN_LAC(const uint8_t*& in, unsigned int& len, XmlElement* xml)
{
if (!(in && len >= 5 && xml))
return false;
// get MCC_MNC
if (getMCCMNC(in,len,xml))
return false;
// get LAC(16 bits)
String lac;
lac.hexify((void*)in,2);
advanceBuffer(2,in,len);
xml->addChildSafe(new XmlElement(s_LAC,lac));
return true;
}
static inline bool setPLMN_LAC(XmlElement* xml, DataBlock& d)
{
if (!xml)
return false;
uint8_t buf[3] = {0, 0, 0};
uint8_t* b = buf;
unsigned int len = 3;
if (setMCCMNC(xml,b,len,false))
return false;
d.append(buf,len);
DataBlock l;
const String* lac = xml->childText(s_LAC);
if (TelEngine::null(lac) || !l.unHexify(*lac))
return false;
d.append(l);
return true;
}
static unsigned int decodeLAI(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeLAI(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
if (len != 5)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
if (!getPLMN_LAC(in,len,xml))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
return GSML3Codec::NoError;
}
static unsigned int encodeLAI(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeLAI(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
DataBlock d;
if (!setPLMN_LAC(xml,d))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
out.append(d);
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 008 V11.6.0, section 10.5.5.15 Routing area identification
// reference: ETSI TS 124 008 V11.6.0, section 10.5.5.15a Routing area identification 2
const String s_RAC = "RAC";
static unsigned int decodeRAI(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeRAI(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
if (len != 6)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
// get MCC_MNC & LAC
if (!getPLMN_LAC(in,len,xml))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
// get RAC(8 bits)
String rac;
rac.hexify((void*)in,len);
advanceBuffer(len,in,len);
xml->addChildSafe(new XmlElement(s_RAC,rac));
return GSML3Codec::NoError;
}
static unsigned int encodeRAI(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeRAI(param=%s(%p),xml=%s(%p)) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
DataBlock d;
if (!setPLMN_LAC(xml,d))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
out.append(d);
d.clear();
// encode RAC
const String* lac = xml->childText(s_RAC);
if (TelEngine::null(lac) || !d.unHexify(*lac))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
out.append(d);
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 008 V11.6.0, section 10.5.1.4 Mobile identity
static const TokenDict s_mobileIdentType[] = {
{"no-identity", 0},
{"IMSI", 1},
{"IMEI", 2},
{"IMEISV", 3},
{"TMSI", 4},
{"TMGI", 5},
{0, 0},
};
static unsigned int decodeMobileIdent(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeMobileIdent(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
uint8_t type = in[0] & 0x07;
XmlElement* child = new XmlElement(lookup(type,s_mobileIdentType,String(type)));
xml->addChildSafe(child);
switch (type) {
case 0:
case 1:
case 2:
case 3:
{
String digits;
bool odd = (in[0] & 0x08);
GSML3Codec::Status err = CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
GET_DIGIT((in[0] >> 4),digits,err,(len == 1));
unsigned int index = 1;
while (index < len) {
GET_DIGIT((in[index] & 0x0f),digits,err,false);
GET_DIGIT((in[index] >> 4),digits,err,(index == len - 1 ? !odd : false));
index++;
}
advanceBuffer(index,in,len);
child->addText(digits);
break;
}
case 4:
{
advanceBuffer(1,in,len);
String str = "";
str.hexify((void*)in,len);
child->addText(str);
advanceBuffer(len,in,len);
break;
}
case 5:
{
// TMGI
if (len < 4)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
bool mncMccInd = (in[0] & 0x10);
bool sessIdInd = (in[0] & 0x20);
advanceBuffer(1,in,len);
// get MBMS Service ID (24 bits) - TODO dump as octet string for now
String str = "";
str.hexify((void*)in,3);
child->addChildSafe(new XmlElement("MBMSServiceID",str));
advanceBuffer(3,in,len);
if (mncMccInd && len < 3)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
// get MCC_MNC
if (getMCCMNC(in,len,child))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
if (sessIdInd && len < 1)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
child->addChildSafe(new XmlElement("MBMSSessionIdentity",*in));
advanceBuffer(1,in,len);
break;
}
default:
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
}
return GSML3Codec::NoError;
}
static unsigned int encodeMobileIdent(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeMobileIdent(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!(xml && (xml = xml->findFirstChild())))
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
uint8_t type = lookup(xml->getTag(),s_mobileIdentType,0xff);
switch (type) {
case 4:
{
// TMSI
type |= 0xf0;
out.append(&type,1);
DataBlock d;
if (!d.unHexify(xml->getText())) {
DDebug(codec->dbg(),DebugWarn,"Failed to unhexify TMSI while encoding mobile identity [%p]",codec->ptr());
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
}
out.append(d);
break;
}
case 0:
case 1:
case 2:
case 3:
{
const String& digits = xml->getText();
if (!digits)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
DataBlock d(0, digits.length() / 2 + 1);
uint8_t* buf = (uint8_t*)d.data();
*buf |= (type & 0x07);
bool odd = (digits.length() % 2);
if (odd)
*buf |= 0x08;
GSML3Codec::Status err = CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
const char* str = digits.c_str();
SET_DIGIT(*str,buf,0,true,err)
str++;
buf++;
bool high = false;
while (char c = *str) {
SET_DIGIT(c,buf,0,high,err);
if (high)
buf++;
high = !high;
str++;
}
if (!odd)
*buf |= 0xf0;
out.append(d);
break;
}
case 5:
Debug(DebugStub,"Please implement encoding of TMGI for mobile identity [%p]",codec->ptr());
default:
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
}
return GSML3Codec::NoError;
}
static unsigned int decodeMobileTD(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeMobileTD(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
if (len != 3)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
uint32_t val = (((uint32_t)in[0]) << 13) | (((uint32_t)in[1]) << 5) | (in[2] >> 3);
addXMLElement(out,new XmlElement(param->name,String(val)));
return GSML3Codec::NoError;
}
static unsigned int encodeMobileTD(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeMobileTD(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
const String* valStr = in->childText(param->name);
if (TelEngine::null(valStr))
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
int val = valStr->toInteger(-1);
if (val < 0 || val > 0x1fffff)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
uint8_t buf[3];
buf[0] = (uint8_t)(val >> 13);
buf[1] = (uint8_t)(val >> 5);
buf[2] = (uint8_t)(val << 3);
out.append(buf,3);
return GSML3Codec::NoError;
}
static unsigned int decodeMobileTDHyper(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeMobileTDHyper(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
if (len != 5)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
uint64_t val = (((uint64_t)in[0]) << 25) | (((uint64_t)in[1]) << 17)
| (((uint64_t)in[2]) << 9) | (((uint64_t)in[3]) << 1) | (((uint64_t)in[4]) >> 7);
addXMLElement(out,new XmlElement(param->name,String(val)));
return GSML3Codec::NoError;
}
static unsigned int encodeMobileTDHyper(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeMobileTDHyper(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
const String* valStr = in->childText(param->name);
if (TelEngine::null(valStr))
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
int64_t val = valStr->toInt64(-1);
if (val < 0 || val > 0x1ffffffffLL)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
uint8_t buf[5];
buf[0] = (uint8_t)(val >> 25);
buf[1] = (uint8_t)(val >> 17);
buf[2] = (uint8_t)(val >> 9);
buf[3] = (uint8_t)(val >> 1);
buf[4] = (uint8_t)(val << 7);
out.append(buf,5);
return GSML3Codec::NoError;
}
static const TokenDict s_msNetworkFeatSupport[] = {
{"MS-does-not-support-the-extended-periodic-timer-in-this-domain", 0},
{"MS-supports-the-extended-periodic-timer-in-this-domain", 1},
{0, 0},
};
// reference: ETSI TS 124 008 V11.6.0, section 10.5.3.4 Identity type
static const TokenDict s_mmIdentType[] = {
{"IMSI", 1},
{"IMEI", 2},
{"IMEISV", 3},
{"TMSI", 4},
{"TMGI", 5},
{0, 0},
};
// reference: ETSI TS 124 008 V11.6.0, section 10.5.5.29 P-TMSI type
static const TokenDict s_pTMSIType[] = {
{"native-P_TMSI", 0},
{"mapped-P_TMSI", 1},
{0, 0},
};
const TokenDict GSML3Codec::s_mmRejectCause[] = {
{"IMSI-unknown-in-HLR", 0x02},
{"illegal-MS", 0x03},
{"IMSI-unknown-in-VLR", 0x04},
{"IMEI-not-accepted", 0x05},
{"illegal-ME", 0x06},
{"PLMN-not-allowed", 0x0b},
{"location-area-not-allowed", 0x0c},
{"roaming-not-allowed-in-this-location-area", 0x0d},
{"no-suitable-cells-in-location-area", 0x0f},
{"network-failure", 0x11},
{"MAC-failure", 0x14},
{"synch-failure", 0x15},
{"congestion", 0x16},
{"GSM-authentication-unacceptable", 0x17},
{"not-authorized-for-this-CSG", 0x19},
{"service-option-not-supported", 0x20},
{"requested-service-option-not-subscribed", 0x21},
{"service-option-temporarily-out-of-order", 0x22},
{"call-cannot-be-identified", 0x26},
{"retry-upon-entry-into-a-new-cell", 0x30},
{"retry-upon-entry-into-a-new-cell", 0x31},
{"retry-upon-entry-into-a-new-cell", 0x32},
{"retry-upon-entry-into-a-new-cell", 0x33},
{"retry-upon-entry-into-a-new-cell", 0x34},
{"retry-upon-entry-into-a-new-cell", 0x35},
{"retry-upon-entry-into-a-new-cell", 0x36},
{"retry-upon-entry-into-a-new-cell", 0x37},
{"retry-upon-entry-into-a-new-cell", 0x38},
{"retry-upon-entry-into-a-new-cell", 0x38},
{"retry-upon-entry-into-a-new-cell", 0x3a},
{"retry-upon-entry-into-a-new-cell", 0x3b},
{"retry-upon-entry-into-a-new-cell", 0x3c},
{"retry-upon-entry-into-a-new-cell", 0x3d},
{"retry-upon-entry-into-a-new-cell", 0x3e},
{"retry-upon-entry-into-a-new-cell", 0x3f},
{"semantically-incorrect-message", 0x5f},
{"invalid-mandatory-information", 0x60},
{"message-type-non-existent-or-not-implemented", 0x61},
{"message-type-not-compatible-with-the-protocol-state", 0x62},
{"information-element-non-existent-or-not-implemented", 0x63},
{"conditional-IE-error", 0x64},
{"message-not-compatible-with-the-protocol-state", 0x65},
{"protocol-error-unspecified", 0x6f},
{0, 0},
};
// reference: ETSI TS 124 008 V11.6.0, section 10.5.3.3 CM service type
static const TokenDict s_mmCMServType[] = {
{"MO-call-establishment-or-PM-connection-establishment", 0x01},
{"emergency-call-establishment", 0x02},
{"SMS", 0x04},
{"SS-activation", 0x08},
{"voice-group-call-establishment", 0x09},
{"voice-broadcast-call-establishment", 0x0a},
{"location-services", 0x0b},
{0, 0},
};
// reference: ETSI TS 124 008 V11.6.0, 10.5.1.11 Priority Level
static const TokenDict s_mmPriorityLevel[] = {
{"no-priority-applied", 0x00},
{"call-priority-level-4", 0x01},
{"call-priority-level-3", 0x02},
{"call-priority-level-2", 0x03},
{"call-priority-level-1", 0x04},
{"call-priority-level-0", 0x05},
{"call-priority-level-B", 0x06},
{"call-priority-level-A", 0x07},
{0, 0},
};
// reference ETSI TS 124 007 V11.0.0, section 11.2.3.1.3 Transaction identifier
static const String s_TIFlag = "TIFlag";
static unsigned int decodeTID(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeTID(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
uint8_t val = getUINT8(in,len,param);
xml->setAttribute(s_TIFlag,String::boolText(val & 0x08));
val &= 0x07;
if (val == 7) {
if (!len)
return GSML3Codec::MsgTooShort;
val = *in++;
len--;
if (!(val & 0x80)) {
Debug(codec->dbg(),DebugWarn,"Decoding extended TIDs longer than 1 octet not implemented [%p]",codec->ptr());
return GSML3Codec::ParserErr;
}
xml->setText(String(val & 0x7f));
}
else
xml->setText(String(val));
return GSML3Codec::NoError;
}
static unsigned int encodeTID(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeTID(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
const String* str = xml->getAttribute(s_TIFlag);
if (TelEngine::null(str))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
uint8_t val = 0 | (str->toBoolean() ? 0x08 : 0);
const String& tiStr = xml->getText();
if (TelEngine::null(tiStr))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
unsigned int ti = tiStr.toInteger();
if (ti > 0x7f) {
Debug(codec->dbg(),DebugWarn,"Encoding extended TIDs longer than 1 octet not implemented [%p]",codec->ptr());
return GSML3Codec::ParserErr;
}
else if (ti >= 7) {
val |= 0x07;
setUINT8(val,out,param);
val = ti;
out.append(&val,1);
}
else {
val |= ti;
setUINT8(val,out,param);
}
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 008 V11.6.0, 10.5.4.21 Progress indicator
static const String s_progIndCoding = "coding";
static const String s_progIndLocation = "location";
static const String s_progInd = "progress";
static const TokenDict s_progIndCoding_dict[] = {
{"CCITT", 0x00},
{"reserved", 0x20},
{"national", 0x40},
{"GSM-PLMN", 0x60},
{0, 0},
};
static const TokenDict s_progIndLocation_dict[] = {
{"U", 0x00}, // User
{"LPN", 0x01}, // Private network serving the local user
{"LN", 0x02}, // Public network serving the local user
{"RLN", 0x04}, // Public network serving the remote user
{"RPN", 0x05}, // Private network serving the remote user
{"BI", 0x0a}, // Network beyond the interworking point
{0, 0},
};
static const TokenDict s_progInd_dict[] = {
{"call-is-not-end-to-end-PLMN/ISDN", 1},
{"destination-address-in-non-PLMN/ISDN", 2},
{"origination-address-in-non-PLMN/ISDN", 3},
{"call-has-returned-to-the-PLMN/ISDN", 4},
{"in-band-information-available", 8},
{"in-band-multimedia-CAT-available", 9},
{"call-is-end-to-end-PLMN/ISDN", 32},
{"queueing", 64},
{0, 0},
};
static unsigned int decodeProgressInd(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeProgressInd(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
if (len < 2 || !(in[0] & 0x80) || !(in[1] & 0x80))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
xml->setAttribute(s_progIndCoding,lookup(in[0] & 0x60,s_progIndCoding_dict,"unknown"));
xml->setAttribute(s_progIndLocation,lookup(in[0] & 0x0f,s_progIndLocation_dict,"unknown"));
xml->setText(lookup(in[1] & 0x7f,s_progInd_dict,"unspecified"));
return GSML3Codec::NoError;
}
static unsigned int encodeProgressInd(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeProgressInd(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
const String* coding = xml->getAttribute(s_progIndCoding);
const String* loc = xml->getAttribute(s_progIndLocation);
const String& prog = xml->getText();
if (TelEngine::null(prog))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
uint8_t buf[2] = {0x80,0x80};
if (TelEngine::null(coding)) // set coding GSM PLMN if not provided
buf[0] |= 0x60;
else
buf[0] |= lookup(*coding,s_progIndCoding_dict,0x60) & 0x60;
if (TelEngine::null(loc)) // set location LPN if not provided
buf[0] |= 0x01;
else
buf[0] |= lookup(*loc,s_progIndLocation_dict,0x01) & 0x0f;
buf[1] |= lookup(prog,s_progInd_dict,0x7f) & 0x7f;
out.append(buf,2);
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 008 V11.6.0, section 10.5.4.7 Called Party BCD Number
// reference: ETSI TS 124 008 V11.6.0, section 10.5.4.9 Calling party BCD number
static const String s_numberPlan = "plan";
static const String s_numberNature = "nature";
static const String s_numberScreened = "screened";
static const String s_numberRestrict = "restrict";
static const TokenDict s_dict_numNature[] = {
{ "unknown", 0x00 },
{ "international", 0x10 },
{ "national", 0x20 },
{ "network-specific", 0x30 },
{ "dedicated-access", 0x40 },
{ "reserved", 0x50 },
{ "abbreviated", 0x60 },
{ "extension-reserved", 0x70 },
{ 0, 0 },
};
// Numbering Plan Indicator
static const TokenDict s_dict_numPlan[] = {
{ "unknown", 0 },
{ "isdn", 1 },
{ "data", 3 },
{ "telex", 4 },
{ "national", 8 },
{ "private", 9 },
{ "CTS-reserved", 11},
{ "extension-reserved", 15 },
{ 0, 0 },
};
// Address Presentation
static const TokenDict s_dict_presentation[] = {
{ "allowed", 0 },
{ "restricted", 1 },
{ "unavailable", 2 },
{ "reserved", 3 },
// aliases for restrict=...
{ "no", 0 },
{ "false", 0 },
{ "yes", 1 },
{ "true", 1 },
{ 0, 0 }
};
// Screening Indicator
static const TokenDict s_dict_screening[] = {
{ "user-provided", 0 },
{ "user-provided-passed", 1 },
{ "user-provided-failed", 2 },
{ "network-provided", 3 },
// aliases for screened=...
{ "no", 0 },
{ "false", 0 },
{ "yes", 1 },
{ "true", 1 },
{ 0, 0 }
};
static unsigned int decodeBCDNumber(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeBCDNumber(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
xml->setAttribute(s_numberNature,lookup((in[0] & 0x70),s_dict_numNature,"unknown"));
xml->setAttribute(s_numberPlan,lookup((in[0] & 0x0f),s_dict_numPlan,"unknown"));
if (!(in[0] & 0x80)) {
advanceBuffer(1,in,len);
if (!(len && (in[0] & 0x80)))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
xml->setAttribute(s_numberScreened,lookup((in[0] & 0x03),s_dict_screening,"unknown"));
xml->setAttribute(s_numberRestrict,lookup((in[0] & 0x60),s_dict_presentation,"unknown"));
}
advanceBuffer(1,in,len);
String bcdDigits;
if (!getBCDDigits(in,len,bcdDigits))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
xml->setText(bcdDigits);
return GSML3Codec::NoError;
}
static unsigned int encodeBCDNumber(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeBCDNumber(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
const String& digits = xml->getText();
const String* nature = xml->getAttribute(s_numberNature);
const String* plan = xml->getAttribute(s_numberPlan);
const String* screen = xml->getAttribute(s_numberScreened);
const String* pres = xml->getAttribute(s_numberRestrict);
unsigned int len = 2 + digits.length() / 2 + (digits.length() % 2 ? 1 : 0);
uint8_t buff[len];
unsigned int idx = 0;
buff[idx] = ((nature ? lookup(*nature,s_dict_numNature,0) : 0) & 0x70);
buff[idx] |= (( plan ? lookup(*plan,s_dict_numPlan,0) :0) & 0x0f);
if (TelEngine::null(screen) && TelEngine::null(pres))
buff[idx++] |= 0x80;
else {
buff[++idx] = 0x80;
buff[idx] |= ((screen ? lookup(*screen,s_dict_screening,0) : 0) & 0x03);
buff[idx++] |= ((pres ? lookup(*pres,s_dict_presentation,0) : 0) & 0x60);
}
if (!setBCDDigits(buff,len,idx,digits))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
out.append(buff,idx);
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 008 V11.6.0, section 10.5.4.11 Cause
static const TokenDict s_causeGSM_dict[] = {
// normal-event class
{"normal-event", 0x00},
{"unallocated", 0x01}, // Unallocated (unassigned) number
{"noroute", 0x03}, // No route to destination
{"channel-unacceptable", 0x06}, // Channel unacceptable
{"operator-determined-barring", 0x08}, // Operator determined barring
{"normal-clearing", 0x10}, // Normal Clearing
{"busy", 0x11}, // User busy
{"noresponse", 0x12}, // No user responding
{"noanswer", 0x13}, // No answer from user (user alerted)
{"rejected", 0x15}, // Call Rejected
{"moved", 0x16}, // Number changed
{"rejected-by-feature", 0x18}, // Call rejected due to feature at the destination
{"preemption", 0x19}, // Preemption
{"answered", 0x1a}, // Non-selected user clearing (answered elsewhere)
{"out-of-order", 0x1b}, // Destination out of order
{"invalid-number", 0x1c}, // Invalid number format
{"facility-rejected", 0x1d}, // Facility rejected
{"status-enquiry-rsp", 0x1e}, // Response to STATUS ENQUIRY
{"normal", 0x1f}, // Normal, unspecified
// resource-unavailable class
{"congestion", 0x22}, // No circuit/channel available
{"channel-congestion", 0x22},
{"net-out-of-order", 0x26}, // Network out of order
{"noconn", 0x29},
{"temporary-failure", 0x29}, // Temporary failure
{"congestion", 0x2a}, // Switching equipment congestion
{"switch-congestion", 0x2a},
{"access-info-discarded", 0x2b}, // Access information discarded
{"channel-unavailable", 0x2c}, // Requested channel not available
{"noresource", 0x2f}, // Resource unavailable, unspecified
// service-unavailable class
{"qos-unavailable", 0x31}, // Quality of service unavailable
{"facility-not-subscribed", 0x32}, // Requested facility not subscribed
{"forbidden-in", 0x37}, // Incoming call barred within CUG
{"bearer-cap-not-auth", 0x39}, // Bearer capability not authorized
{"bearer-cap-not-available", 0x3a}, // Bearer capability not presently available
{"nomedia", 0x3a},
{"service-unavailable", 0x3f}, // Service or option not available
// service-not-implemented class
{"bearer-cap-not-implemented", 0x41}, // Bearer capability not implemented
{"acm-equal-or-greater-ACM-max", 0x44}, // ACM equal to or greater than ACMmax
{"facility-not-implemented", 0x45}, // Requested facility not implemented
{"restrict-bearer-cap-avail", 0x46}, // Only restricted digital information bearer capability is available
{"service-not-implemented", 0x4f}, // Service or option not implemented, unspecified
// invalid-message class
{"invalid-callref", 0x51}, // Invalid call reference value
{"not-subscribed", 0x57}, // User not member of CUG
{"incompatible-dest", 0x58}, // Incompatible destination
{"invalid-transit-net", 0x5b}, // Invalid transit network selection
{"invalid-message", 0x5f}, // Invalid message, unspecified
// protocol-error class
{"missing-mandatory-ie", 0x60}, // Mandatory information element is missing
{"unknown-message", 0x61}, // Message type non-existent or not implemented
{"wrong-message", 0x62}, // Message not compatible with call state, non-existent or not implemented
{"unknown-ie", 0x63}, // Information element non-existent or not implemented
{"invalid-ie", 0x64}, // Invalid information element contents
{"wrong-state-message", 0x65}, // Message not compatible with call state
{"timeout", 0x66}, // Recovery on timer expiry
{"protocol-error", 0x6f}, // Protocol error, unspecified
// interworking class
{"interworking", 0x7f}, // Interworking, unspecified
{0,0}
};
// Q.850 2.2.5. Cause class: Bits 4-6
// Q.850 Table 1. Cause value: Bits 0-6
// Defined for CCITT coding standard
static const TokenDict s_causeCCITT_dict[] = {
// normal-event class
{"normal-event", 0x00},
{"unallocated", 0x01}, // Unallocated (unassigned) number
{"noroute-to-network", 0x02}, // No route to specified transit network
{"noroute", 0x03}, // No route to destination
{"send-info-tone", 0x04}, // Send special information tone
{"misdialed-trunk-prefix", 0x05}, // Misdialed trunk prefix
{"channel-unacceptable", 0x06}, // Channel unacceptable
{"call-delivered", 0x07}, // Call awarded and being delivered in an established channel
{"preemption", 0x08}, // Preemption
{"preemption-circuit-reserved", 0x09}, // Preemption circuit reserved for re-use
{"ported-number", 0x0e}, // QoR: ported number Q.850 Addendum 1 (06/2000)
{"excess-digits", 0x0e}, // Excess digits received, call is proceeding
{"normal-clearing", 0x10}, // Normal Clearing
{"busy", 0x11}, // User busy
{"noresponse", 0x12}, // No user responding
{"noanswer", 0x13}, // No answer from user (user alerted)
{"offline", 0x14}, // Subscriber absent
{"rejected", 0x15}, // Call Rejected
{"moved", 0x16}, // Number changed
{"redirection", 0x17}, // Redirection to new destination Q.850 05/98
{"rejected-by-feature", 0x18}, // Call rejected due to feature at the destination Q.850 Amendment 1 (07/2001)
{"looping", 0x19}, // Exchange routing error (hop counter) Q.850 05/98
{"answered", 0x1a}, // Non-selected user clearing (answered elsewhere)
{"out-of-order", 0x1b}, // Destination out of order
{"invalid-number", 0x1c}, // Invalid number format
{"facility-rejected", 0x1d}, // Facility rejected
{"status-enquiry-rsp", 0x1e}, // Response to STATUS ENQUIRY
{"normal", 0x1f}, // Normal, unspecified
// resource-unavailable class
{"resource-unavailable", 0x20}, // Resource unavailable
{"congestion", 0x22}, // No circuit/channel available
{"channel-congestion", 0x22},
{"net-out-of-order", 0x26}, // Network out of order
{"frame-mode-conn-down", 0x27}, // Permanent frame mode connection out of service
{"frame-mode-conn-up", 0x28}, // Permanent frame mode connection operational
{"noconn", 0x29},
{"temporary-failure", 0x29}, // Temporary failure
{"congestion", 0x2a}, // Switching equipment congestion
{"switch-congestion", 0x2a},
{"access-info-discarded", 0x2b}, // Access information discarded
{"channel-unavailable", 0x2c}, // Requested channel not available
{"preemption-congestion", 0x2e}, // Precedence call blocked
{"noresource", 0x2f}, // Resource unavailable, unspecified
{"service-unavailable", 0x30}, // Service or option not available
{"qos-unavailable", 0x31}, // Quality of service unavailable
{"facility-not-subscribed", 0x32}, // Requested facility not subscribed
{"forbidden-out", 0x35}, // Outgoing call barred within CUG
{"forbidden-in", 0x37}, // Incoming call barred within CUG
{"bearer-cap-not-auth", 0x39}, // Bearer capability not authorized
{"bearer-cap-not-available", 0x3a}, // Bearer capability not presently available
{"nomedia", 0x3a},
{"invalid-access-info-out", 0x3e}, // Inconsistency in designated outgoing access information and subscriber class
{"service-unavailable", 0x3f}, // Service or option not available
// service-not-implemented class
{"bearer-cap-not-implemented", 0x41}, // Bearer capability not implemented
{"channel-type-not-implemented", 0x42}, // Channel type not implemented
{"facility-not-implemented", 0x45}, // Requested facility not implemented
{"restrict-bearer-cap-avail", 0x46}, // Only restricted digital information bearer capability is available
{"service-not-implemented", 0x4f}, // Service or option not implemented, unspecified
// invalid-message class
{"invalid-callref", 0x51}, // Invalid call reference value
{"unknown-channel", 0x52}, // Identified channel does not exist
{"unknown-callid", 0x53}, // A suspended call exists, but this call identity does not
{"duplicate-callid", 0x54}, // Call identity in use
{"no-call-suspended", 0x55}, // No call suspended
{"suspended-call-cleared", 0x56}, // Call having the requested call identity has been cleared
{"not-subscribed", 0x57}, // User not member of CUG
{"incompatible-dest", 0x58}, // Incompatible destination
{"unknown-group", 0x5a}, // Non-existent CUG
{"invalid-transit-net", 0x5b}, // Invalid transit network selection
{"invalid-message", 0x5f}, // Invalid message, unspecified
// protocol-error class
{"missing-mandatory-ie", 0x60}, // Mandatory information element is missing
{"unknown-message", 0x61}, // Message type non-existent or not implemented
{"wrong-message", 0x62}, // Message not compatible with call state, non-existent or not implemented
{"unknown-ie", 0x63}, // Information element non-existent or not implemented
{"invalid-ie", 0x64}, // Invalid information element contents
{"wrong-state-message", 0x65}, // Message not compatible with call state
{"timeout", 0x66}, // Recovery on timer expiry
{"unknown-param-passed-on", 0x67}, // Parameter non-existent or not implemented, passed on
{"unknown-param-message-droppped", 0x6e}, // Message with unrecognized parameter, discarded
{"protocol-error", 0x6f}, // Protocol error, unspecified
// interworking class
{"interworking", 0x7f}, // Interworking, unspecified
{0,0}
};
static const String s_causeRec = "rec";
static const String s_causeDiag = "diagnostic";
static unsigned int decodeCause(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeCause(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
if (len < 2)
return CONDITIONAL_ERROR(param,NoError,IncorrectMandatoryIE);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
uint8_t coding = in[0] & 0x60;
xml->setAttribute(s_progIndCoding,lookup(coding,s_progIndCoding_dict,"unknown"));
xml->setAttribute(s_progIndLocation,lookup(in[0] & 0x0f,s_progIndLocation_dict,"unknown"));
if (!(coding == 0x60 /* GSM PLMN */ || coding == 0x00 /* Q.931 */)) {
Debug(codec->dbg(),DebugNote,"Unknown Cause coding standard=%s (%u), dumping data [%p]",
lookup(coding,s_progIndCoding_dict,"unknown"),coding >> 5,codec->ptr());
advanceBuffer(1,in,len);
dumpData(in,len,xml);
return GSML3Codec::NoError;
}
if (!(in[0] & 0x80)) {
advanceBuffer(1,in,len);
xml->setAttribute(s_causeRec,String(in[0] & 0x7f));
}
advanceBuffer(1,in,len);
if (!len)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
xml->setText(lookup(in[0] & 0x7f,(coding == 0 ? s_causeCCITT_dict : s_causeGSM_dict),"unspecified"));
advanceBuffer(1,in,len);
if (len) {
String s;
s.hexify((void*)in,len);
advanceBuffer(len,in,len);
xml->setAttribute(s_causeDiag,s);
}
return GSML3Codec::NoError;
}
static unsigned int encodeCause(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeCause(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
const String* coding = xml->getAttribute(s_progIndCoding);
const String* loc = xml->getAttribute(s_progIndLocation);
uint8_t buf[4] = {0x80,0x80,0x80};
uint8_t idx = 0;
uint8_t cdg = 0x60; // set coding GSM PLMN if not provided
if (!TelEngine::null(coding))
cdg = lookup(*coding,s_progIndCoding_dict,0x60) & 0x60;
buf[idx] |= cdg;
if (TelEngine::null(loc)) // set location LPN if not provided
buf[idx] |= 0x01;
else
buf[idx] |= lookup(*loc,s_progIndLocation_dict,0x01) & 0x0f;
if (!(cdg == 0x00 || cdg == 0x60)) {
Debug(codec->dbg(),DebugNote,"Unknown Cause coding standard=%s (%u), encoding from hexified <data> element [%p]",
lookup(cdg,s_progIndCoding_dict,"unknown"),cdg >> 5,codec->ptr());
out.append(buf,idx+1);
getData(out,xml);
return GSML3Codec::NoError;
}
const String& cause = xml->getText();
if (TelEngine::null(cause))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
const String* rec = xml->getAttribute(s_causeRec);
const String* diag = xml->getAttribute(s_causeDiag);
if (!TelEngine::null(rec)) {
buf[idx++] &= 0x7f;
buf[idx] |= rec->toInteger() & 0x7f;
}
idx++;
buf[idx] |= lookup(cause,(coding == 0 ? s_causeCCITT_dict : s_causeGSM_dict),0) & 0x7f;
out.append(buf,idx+1);
if (!TelEngine::null(diag)) {
DataBlock d;
if (!d.unHexify(*diag))
Debug(codec->dbg(),DebugWarn,"Failed to unhexify Cause diagnostic, not encoding it");
else
out.append(d);
}
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 008 V11.6.0, section 10.5.4.5a Call Control Capabilities
const String s_maxSuppBearers = "MaxSupportedBearers";
const String s_maxSpeechBearers = "MaxSpeechBearers";
static const TokenDict s_CCCapab_flags[] = {
{"DTMF", 1},
{"PCP", 2},
{"ENICM", 4},
{"MCAT", 8},
{0, 0},
};
static unsigned int decodeCCCapab(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeCCCapab(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
if (len < 2)
return CONDITIONAL_ERROR(param,NoError,IncorrectMandatoryIE);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
String flags;
getFlags(*in & 0x0f,s_CCCapab_flags,flags);
xml->addChildSafe(new XmlElement(s_flags,flags));
xml->addChildSafe(new XmlElement(s_maxSuppBearers,String((*in & 0xf0) >> 4)));
xml->addChildSafe(new XmlElement(s_maxSpeechBearers,String(*(in + 1) & 0x0f)));
advanceBuffer(2,in,len);
return GSML3Codec::NoError;
}
static unsigned int encodeCCCapab(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeCCCapab(param=%s(%p),xml=%s(%p)) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
uint8_t buf[2] = {0,0};
const String* str = xml->childText(s_flags);
buf[0] = (TelEngine::null(str) ? 0 : (setFlags(*str,s_CCCapab_flags) & 0x0f));
str = xml->childText(s_maxSuppBearers);
buf[0] |= (TelEngine::null(str) ? 0 : (0xf0 & (str->toInteger() << 4)));
str = xml->childText(s_maxSpeechBearers);
buf[1] |= (TelEngine::null(str) ? 0 : (0x0f & str->toInteger()));
out.append(buf,2);
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 008 V11.6.0, section 10.5.4.5 Bearer Capability
static const String s_bearerCapabITC = "ITC";
static const String s_bearerTransfMode = "TransferMode";
static const String s_codingStd = "CodingStandard";
static const String s_radioChanReq = "RadioChannelRequirement";
static const String s_ctmTxtTel = "CTMTextTelephony";
static const String s_speechVers = "SpeechVersions";
static const String s_bearerCapabNIRR = "NIRR";
static const String s_bearerCapabNIRRStr = "data-to-and-including-4.8kb/s,FR,non-transparent,6kb/s-radio-interface-requested";
static const TokenDict s_bearerCapabITC_types[] = {
{"speech", 0}, // speech
{"udi", 1}, // unrestricted digital information
{"3.1khz-audio", 2}, // 3.1kHz audio, ex PLMN
{"facsimile-group3", 3}, // facsimile group 3
{"other-ITC", 5}, // Other ITC (see octet 5a)
{"reserved", 7}, // reserved, to be used in the network
{0, 0},
};
static const TokenDict s_bearerTransfMode_types[] = {
{"circuit-mode", 0x00},
{"packet-mode", 0x08},
{0, 0},
};
static const TokenDict s_bearerCodingStd_types[] = {
{"GSM", 0x00},
{"reserved", 0x10},
{0, 0},
};
static const TokenDict s_radioChanNonSpeech[] = {
{"reserved", 0x00},
{"FR-support-only-MS", 0x01},
{"DR-support-MS/HR-preferred", 0x02},
{"DR-support-MS/FR-preferred", 0x03},
{0, 0},
};
static const TokenDict s_radioChanSpeech[] = {
{"reserved", 0x00},
{"FR-support-only-MS/FR-speech-version1-supported", 0x01},
{"DR-support-MS/HR-speech-version1-preferred", 0x02},
{"DR-support-MS/FR-speech-version1-preferred", 0x03},
{0, 0},
};
static const TokenDict s_radioChanSpeechExt[] = {
{"reserved", 0x00},
{"FR-speech-version1-supported", 0x01},
{"FR-and-HR-speech-version1-supported/HR-speech-preferred", 0x02},
{"FR-and-HR-speech-version1-supported/FR-speech-preferred", 0x03},
{0, 0},
};
static const TokenDict s_speechVers_types[] = {
{"GSM-FR-speech-version1", 0x00},
{"GSM-FR-speech-version2", 0x02},
{"GSM-FR-speech-version3", 0x04},
{"GSM-FR-speech-version4", 0x06},
{"GSM-FR-speech-version5", 0x08},
{"GSM-HR-speech-version1", 0x01},
{"GSM-HR-speech-version3", 0x05},
{"GSM-HR-speech-version4", 0x07},
{"GSM-FR-speech-version6", 0x0b},
{"no-speech-version-for-GERAN", 0x0f},
{0, 0},
};
static const TokenDict s_bearerCapabStruct[] = {
{"service-data-unit-integrity", 0x00},
{"unstructured", 0x30},
{0, 0},
};
static unsigned int decodeBearerCapab(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeBearerCapab(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
if (len < 1)
return CONDITIONAL_ERROR(param,NoError,IncorrectMandatoryIE);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
// octet 3
uint8_t itc = *in & 0x07;
bool ext = ((*in & 0x80) == 0);
xml->addChildSafe(new XmlElement(s_bearerCapabITC,lookup(itc,s_bearerCapabITC_types,"unknown")));
xml->addChildSafe(new XmlElement(s_bearerTransfMode,lookup(*in & 0x08,s_bearerTransfMode_types,"unknown")));
xml->addChildSafe(new XmlElement(s_codingStd,lookup(*in & 0x10,s_bearerCodingStd_types,"unknown")));
uint8_t rcr = (*in & 0x60) >> 5;
advanceBuffer(1,in,len);
switch (itc) {
case 0: // speech
{
if (!ext)
xml->addChildSafe(new XmlElement(s_radioChanReq,lookup(rcr,s_radioChanSpeech,"unknown")));
else {
xml->addChildSafe(new XmlElement(s_radioChanReq,lookup(rcr,s_radioChanSpeechExt,"unknown")));
XmlElement* spVersInd = new XmlElement(s_speechVers);
xml->addChildSafe(spVersInd);
String speechVers;
// octet 3a*
while (ext) {
if (!len) {
Debug(codec->dbg(),DebugWarn,"Invalid payload length for extended BearerCapability type [%p]",
codec->ptr());
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
}
if (!(*in & 0x40)) {
if (!speechVers && (*in & 0x20))
spVersInd->setAttribute(s_ctmTxtTel,"true");
speechVers.append(lookup(*in & 0x0f,s_speechVers_types,"TBD"),",");
}
ext = ((*in & 0x80) == 0);
advanceBuffer(1,in,len);
}
spVersInd->addText(speechVers);
}
break;
}
default: // non-speech
xml->addChildSafe(new XmlElement(s_radioChanReq,lookup(rcr,s_radioChanNonSpeech,"unknown")));
break;
}
// octet 4
if (len) {
ext = ((*in & 0x80) == 0);
if (ext) {
Debug(codec->dbg(),DebugWarn,"Extension bit set for specification octet 4 of Bearer Capability type [%p]",
codec->ptr());
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
}
xml->addChildSafe(new XmlElement("Establishment",(*in & 0x01) ? "reserved" : "demand"));
if (*in & 0x02)
xml->addChildSafe(new XmlElement(s_bearerCapabNIRR,s_bearerCapabNIRRStr));
xml->addChildSafe(new XmlElement("Configuration",(*in & 0x04) ? "reserved" : "point-to-point"));
xml->addChildSafe(new XmlElement("DuplexMode",(*in & 0x08) ? "full-duplex" : "half-duplex"));
xml->addChildSafe(new XmlElement("Compression",(*in & 0x40) ? "allowed" : "not-allowed"));
xml->addChildSafe(new XmlElement("Structure",lookup(*in & 0x30,s_bearerCapabStruct,"reserved")));
advanceBuffer(1,in,len);
}
// TODO - continue decoding of octets 5*, 6* and 7
if (len)
dumpData(in,len,xml);
return GSML3Codec::NoError;
}
static unsigned int encodeBearerCapab(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeBearerCapab(param=%s(%p),xml=%s(%p)) [%p]",param->name.c_str(),
param,in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
const String* str = xml->childText(s_bearerCapabITC);
if (TelEngine::null(str))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
// does it need extension ?
XmlElement* speechVers = xml->findFirstChild(&s_speechVers);
bool ext = speechVers && !TelEngine::null(speechVers->getText());
// encode bits 3-1
uint8_t itc = lookup(*str,s_bearerCapabITC_types,0) & 0x07;
// set bits 7-6
str = xml->childText(s_radioChanReq);
if (itc)
itc |= ((str ? lookup(*str,s_radioChanNonSpeech,0) : 0) << 5) & 0x60;
else if (ext)
itc |= ((str ? lookup(*str,s_radioChanSpeechExt,0) : 0) << 5) & 0x60;
else
itc |= ((str ? lookup(*str,s_radioChanSpeech,0) : 0) << 5) & 0x60;
// set extension bit (bit 8)
itc |= (ext ? 0 : 0x80);
// set transfer mode (bit 4)
str = xml->childText(s_bearerTransfMode);
itc |= (TelEngine::null(str) ? 0 : lookup(*str,s_bearerTransfMode_types,0));
// set coding standard (bit 5)
str = xml->childText(s_codingStd);
itc |= (TelEngine::null(str) ? 0 : lookup(*str,s_bearerCodingStd_types,0));
// append octet
out.append(&itc,1);
// set octets 3a* 3b*
if (ext) { // speech versions
ObjList* list = speechVers->getText().split(',');
unsigned int len = list->count();
uint8_t buf[len];
unsigned int idx = 0;
for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
String* str = static_cast<String*>(o->get());
buf[idx] = 0x00 | (str ? lookup(*str,s_speechVers_types,0x03) : 0x03);
if (!idx) {
const String* ctm = speechVers->getAttribute(s_ctmTxtTel);
if (!TelEngine::null(ctm) && ctm->toBoolean())
buf[idx] |= 0x20;
}
if (idx == len - 1)
buf[idx] |= 0x80;
idx++;
}
out.append(buf,len);
TelEngine::destruct(list);
}
// TODO set octet 4,5*,6*,7
getData(out,xml);
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 008 V11.6.0, 10.5.4.17 Keypad facility
static unsigned int decodeIA5Chars(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeIA5Chars(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
String s;
while (len) {
s << (char)(*in & 0x7f);
advanceBuffer(1,in,len);
}
xml->addText(s);
return GSML3Codec::NoError;
}
static unsigned int encodeIA5Chars(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeIA5Chars(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
const String& txt = xml->getText();
unsigned int len = txt.length();
uint8_t buf[len];
for (unsigned int i = 0; i < len; i++)
buf[0] = txt[i] & 0x7f;
out.append(buf,len);
return GSML3Codec::NoError;
}
// reference ETSI TS 124 080 V11.0.0, section 3.7.1 Supplementary service screening indicator
static const TokenDict s_ssScreenInd_dict[] = {
{"phase1", 0},
{"ellipsis-notation-and-phase2-error-handling", 1},
{0, 0},
};
// reference: ETSI TS 124 008 V11.6.0, 10.5.1.5 Mobile Station Classmark 1
static const String s_revisionLevel = "RevisionLevel";
static const String s_rfPowerCapab = "RFPowerCapability";
static const TokenDict s_classmarkRFPowerCapab_dict[] = {
{"class1", 0},
{"class2", 1},
{"class3", 2},
{"class4", 3},
{"class5", 4},
{"irrelevant", 7},
{0, 0},
};
static const TokenDict s_classmarkRevLevel_dict[] = {
{"GSM-phase1", 0x00},
{"GSM-phase2", 0x20},
{"R99-or-later", 0x40},
{"reserved", 0x60},
{0, 0},
};
static const TokenDict s_msClassmarkOct1_flags[] = {
{"no-A5/1", 0x08},
{"ES-IND", 0x10},
{0, 0},
};
static unsigned int decodeMSClassmarkOctet1(XmlElement* xml, const uint8_t*& in, unsigned int& len, String& flags)
{
if (!(xml && in && len))
return GSML3Codec::ParserErr;
// MS Classmark 1 & 2 first octet
xml->addChildSafe(new XmlElement(s_rfPowerCapab,lookup(*in & 0x07,s_classmarkRFPowerCapab_dict,"reserved")));
xml->addChildSafe(new XmlElement(s_revisionLevel,lookup(*in & 0x60,s_classmarkRevLevel_dict,"reserved")));
getFlags(*in,s_msClassmarkOct1_flags,flags);
advanceBuffer(1,in,len);
return GSML3Codec::NoError;
}
static unsigned int encodeMSClassmarkOctet1(XmlElement* xml, DataBlock& out, ObjList* flags = 0)
{
if (!xml)
return GSML3Codec::ParserErr;
// MS Classmark 1 & 2 first octet
uint8_t val = 0;
if (!flags) {
const String* flgs = xml->childText(s_flags);
val = TelEngine::null(flgs) ? 0 : setFlags(*flgs,s_msClassmarkOct1_flags);
}
else
val = setFlags(flags,s_msClassmarkOct1_flags);
const String* str = xml->childText(s_rfPowerCapab);
if (!TelEngine::null(str))
val |= lookup(*str,s_classmarkRFPowerCapab_dict,5) & 0x07; // reserved value if not found in dictionary
str = xml->childText(s_revisionLevel);
if (!TelEngine::null(str))
val |= lookup(*str,s_classmarkRevLevel_dict,0x60) ;
out.append(&val,1);
return GSML3Codec::NoError;
}
static unsigned int decodeMSClassmark1(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeMSClassmark1(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
String flgs;
if (decodeMSClassmarkOctet1(xml,in,len,flgs))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
xml->addChildSafe(new XmlElement(s_flags,flgs));
return GSML3Codec::NoError;
}
static unsigned int encodeMSClassmark1(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeMSClassmark1(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
if (encodeMSClassmarkOctet1(xml,out))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 008 V11.6.0, 10.5.1.6 Mobile Station Classmark 2
static const String s_ssScreenInd = "SSScreeningIndicator";
static const TokenDict s_msClassmark2Oct2_flags[] = {
{"E-GSM-and-R-GSM-support", 0x01},
{"VGCS-capability", 0x02},
{"VBS-capability", 0x04},
{"MT-sms-point-to-point-capability", 0x08},
{"pseudo-sync-capability", 0x40},
{0, 0},
};
static const TokenDict s_msClassmark2Oct3_flags[] = {
{"A5/2-support", 0x01},
{"A5/3-support", 0x02},
{"CMSP-support", 0x04},
{"SoLSA-support", 0x08},
{"no-preference-between-default-alphabet-and-UCS2", 0x10},
{"LCS-VA-support", 0x20},
{"CM3-support", 0x80},
{0, 0},
};
static unsigned int decodeMSClassmark2(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeMSClassmark2(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
String flgs;
if (decodeMSClassmarkOctet1(xml,in,len,flgs))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
if (len != 2) {
Debug(codec->dbg(),DebugWarn,"Invalid length %u for MS Classmark2 [%p]",len+1,codec->ptr());
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
}
// octet 2
getFlags(*in,s_msClassmark2Oct2_flags,flgs);
xml->addChildSafe(new XmlElement(s_ssScreenInd,lookup((*in & 0x30) >> 4,s_ssScreenInd_dict,String(*in & 0x30))));
advanceBuffer(1,in,len);
// octet 3
getFlags(*in,s_msClassmark2Oct3_flags,flgs);
xml->addChildSafe(new XmlElement(s_flags,flgs));
advanceBuffer(1,in,len);
return GSML3Codec::NoError;
}
static unsigned int encodeMSClassmark2(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeMSClassmark2(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
const String* str = xml->childText(s_flags);
ObjList* flgs = (TelEngine::null(str) ? 0 : str->split(','));
// octet 1
if (encodeMSClassmarkOctet1(xml,out,flgs)) {
TelEngine::destruct(flgs);
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
}
// octet 2
str = xml->childText(s_ssScreenInd);
uint8_t buf[2];
buf[0] = setFlags(flgs,s_msClassmark2Oct2_flags);
buf[0] |= (str && *str ? (lookup(*str,s_ssScreenInd_dict,3) << 4) : 0x30) & 0x30;
// octet 3
buf[1] = setFlags(flgs,s_msClassmark2Oct3_flags);
out.append(buf,2);
TelEngine::destruct(flgs);
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 008 V11.6.0, 10.5.1.10a PD and SAPI $(CCBS)$
static unsigned int decodePDAndSAPI(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodePDAndSAPI(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
xml->addChildSafe(new XmlElement(s_PD,lookup(*in & 0x0f,GSML3Codec::s_protoDict,String(*in & 0x0f))));
xml->addChildSafe(new XmlElement(s_SAPI,String((*in & 0x30) >> 4)));
advanceBuffer(1,in,len);
return GSML3Codec::NoError;
}
static unsigned int encodePDAndSAPI(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodePDAndSAPI(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
uint8_t val = 0;
// encode PD
const String* str = xml->childText(s_PD);
if (TelEngine::null(str))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
val |= lookup(*str,GSML3Codec::s_protoDict,0) & 0x0f;
// encode SAPI
str = xml->childText(s_SAPI);
if (!TelEngine::null(str))
val |= (str->toInteger() & 0x03) << 4;
out.append(&val,1);
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 008 V11.6.0, 10.5.1.13 PLMNList
static unsigned int decodePLMNList(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len >= 3 && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodePLMNList(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
while (len)
if (getMCCMNC(in,len,xml))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
return GSML3Codec::NoError;
}
static unsigned int encodePLMNList(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodePLMNList(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
XmlElement* c = xml->findFirstChild();
if (!c)
return GSML3Codec::NoError;
while (c) {
uint8_t buf[3] = {0,0,0};
uint8_t* b = buf;
unsigned int len = 3;
if (setMCCMNC(c,b,len,false,false))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
out.append(buf,len);
c = xml->findNextChild(c);
}
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 008 V11.6.0, 10.5.3.16
static const String s_timerUnit = "unit";
static const TokenDict s_mmTimerUnit_dict[] = {
{"2-seconds", 0x00},
{"1-minute", 0x20},
{"decihours", 0x60},
{"deactivated", 0xe0},
{0, 0},
};
static unsigned int decodeMMTimer(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeMMTimer(param=%s(%p),in=%p,len=%u,out=%p) [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
XmlElement* xml = new XmlElement(param->name,String(*in & 0x1f));
addXMLElement(out,xml);
xml->setAttribute(s_timerUnit,lookup((*in & 0xe0) ,s_mmTimerUnit_dict,"1-minute"));
return GSML3Codec::NoError;
}
static unsigned int encodeMMTimer(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeMMTimer(param=%s(%p),xml=%s(%p)) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
uint8_t val = (xml->getText().toInteger() & 0x1f);
const String* str = xml->getAttribute(s_timerUnit);
if (TelEngine::null(str))
val |= 0x20;
else
val |= lookup(*str,s_mmTimerUnit_dict,0x20);
out.append(&val,1);
return GSML3Codec::NoError;
}
// reference: ETSI TS 124 008 V11.6.0, 10.5.4.20 Notification Indicator
static const TokenDict s_notifIndicatorType[] = {
{"user-suspended", 0x80},
{"user-resumed", 0x81},
{"bearer-changed", 0x82},
{0, 0},
};
// reference: ETSI TS 124 008 V11.6.0, 10.5.4.22 Repeat Indicator
static const TokenDict s_repeatIndType[] = {
{"circular", 0x01},
{"fallback", 0x02},
{"reserved", 0x03},
{"service-change-and-fallback", 0x04},
{0, 0},
};
// reference: ETSI TS 124 008 V11.6.0, 10.5.4.29 Network Call Control Capabilities
static const TokenDict s_networkCCCapabType[] = {
{"no-MCS", 0x00},
{"MCS", 0x01},
{0, 0},
};
// reference: ETSI TS 124 008 V11.6.0, 10.5.4.23 Signal
static const TokenDict s_signalType[] = {
{"dial-tone-on", 0x00},
{"ringback-tone-on", 0x01},
{"intercept-tone-on", 0x02},
{"network-congestion-tone-on", 0x03},
{"busy-tone-on", 0x04},
{"confirm-tone-on", 0x05},
{"answer-tone-on", 0x06},
{"call-waiting-tone-on", 0x07},
{"off-hook-warning-tone-on", 0x08},
{"tones-off", 0x3f},
{"alerting-off", 0x4f},
{0, 0},
};
// reference: ETSI TS 124 008 V11.6.0, 10.5.4.26 Alerting Pattern
static const TokenDict s_alertPattern[] = {
{"alertingLevel-0", 0x00},// Alerting Pattern 1
{"alertingLevel-1", 0x01},// Alerting Pattern 2
{"alertingLevel-2", 0x02},// Alerting Pattern 3
{"alertingCategory-1", 0x04},// Alerting Pattern 4
{"alertingCategory-2", 0x05},// Alerting Pattern 5
{"alertingCategory-3", 0x06},// Alerting Pattern 6
{"alertingCategory-4", 0x07},// Alerting Pattern 7
{"alertingCategory-5", 0x08},// Alerting Pattern 8
{0,0},
};
// reference: ETSI TS 124 008 V11.6.0, 10.5.4.30 Cause of No CLI
static const TokenDict s_causeNoCLIType[] = {
{"unavailable", 0x00},
{"user-reject", 0x01},
{"interaction-with-other-service", 0x02},
{"payphone", 0x03},
{0,0},
};
// reference: ETSI TS 124 008 V11.6.0,section 10.5.4.12 Congestion level
static const TokenDict s_congestLvl_type[] = {
{"receiver-ready", 0x00},
{"receiver-not-ready", 0x0f},
{0,0},
};
// reference: ETSI TS 124 008 V11.6.0,section 10.5.4.21a Recall type $(CCBS)$
static const TokenDict s_recallType[] = {
{"CCBS", 0x00},
{"reserved", 0x07},
{0,0},
};
// reference: ETSI TS 124 008 V11.6.0,section 10.5.3.14 Additional update parameters
static const TokenDict s_additionalUpdateParams_type[] = {
{"CSMT", 0x01},
{"CSMO", 0x02},
{0,0},
};
// reference: ETSI TS 124 008 V11.6.0,section 10.5.7.8 Device properties
static const TokenDict s_DeviceProperties[] = {
{"NAS-low-priority", 0x01},
{0,0},
};
// reference: ETSI TS 124 301 V11.8.0, section 9.9.4.14 Request type =>
// section 10.5.6.17 in 3GPP TS 24.008
static const TokenDict s_epsReqType[] = {
{"initialRequest", 1},
{"handover", 2},
{"unused", 3},
{"emergency", 4},
{0, 0},
};
// reference: ETSI TS 124 301 V11.8.0, section 9.9.4.10 PDN type
static const TokenDict s_epsPdnType[] = {
{"ipv4", 1},
{"ipv6", 2},
{"ipv4v6", 3},
{"unused", 4},
{0, 0},
};
// reference: ETSI TS 124 301 V11.8.0, section 9.9.4.10 PDN type
static const TokenDict s_esmEITFlag[] = {
{"security-protected-ESM-information-transfer-not-required", 0},
{"security-protected-ESM-information-transfer-required", 1},
{0, 0},
};
// reference: ETSI TS 124 301 V11.8.0, section 9.9.3.11
static const TokenDict s_epsAttachTypes[] = {
{"EPS-Attach", 1},
{"combined-EPS-IMSI-attach", 2},
{"EPS-emergency-attach", 6},
{"reserved", 7},
{0, 0},
};
// reference: ETSI TS 124 008 V11.8.0, section 10.5.5.4 TMSI status
static const TokenDict s_tmsiStatus[] = {
{"no-valid-TMSI-available", 0},
{"valid-TMSI-available", 1},
{0, 0},
};
// reference: ETSI TS 124 301 V11.8.0,9.9.3.0B Additional update type
static const TokenDict s_additionalUpdateType[] = {
{"no-additional-information", 0},
{"SMS-only", 1},
{0, 0},
};
// reference: ETSI TS 124 301 V11.8.0, section 9.9.3.45 GUTI type
static const TokenDict s_epsGUTIType[] = {
{"native-GUTI", 0},
{"mapped-GUTI", 1},
{0, 0},
};
// reference: ETSI TS 124 080 V11.0.0, section 3.7.2 Supplementary service version indicator
static const TokenDict s_ssVersionType[] = {
{"phase2-service,ellipsis-notation-and-phase2-error-handling-supported", 0},
{"SS-protocol-version-3-and-phase2-error-handling-supported", 1},
{0, 0},
};
// reference: ETSI TS 124 011 V11.1.0, section 8.1.4.2 CP-Cause element
static const TokenDict s_cpCauseType[] = {
{"network-failure", 0x11},
{"congestion", 0x16},
{"invalid-tid", 0x51},
{"semantically-incorrect-message", 0x5f},
{"invalid-mandatory-info", 0x60},
{"message-type-non-existent-or-not-implemented", 0x61},
{"message-not-compatible-with-SM-protocol-state", 0x62},
{"information-element-non-existent-or-not-implemented", 0x63},
{"protocol-error-unspecified", 0x6f},
{0, 0},
};
// reference: ETSI TS 144 018 V11.5.0,, section 10.5.2.31 RR Cause
static const TokenDict s_rrCauseType[] = {
{"normal-event", 0x00}, // Normal event
{"unspecified", 0x01}, // Abnormal release, unspecified
{"channel-unacceptable", 0x02}, // Abnormal release, channel unacceptable
{"timeout", 0x03}, // Abnormal release, timer expired
{"no-activity-on-radio-path", 0x04}, // Abnormal release, no activity on the radio path
{"preeemtive-release", 0x05}, // Preemptive release
{"UTRAN-config-unknown", 0x06}, // UTRAN configuration unknown
{"ho-impossible", 0x08}, // Handover impossible, timing advance out of range
{"channel-mode-unacceptable", 0x09}, // Channel mode unacceptable
{"frequency-not-implemented", 0x0a}, // Frequency not implemented
{"talker-leaving-GC-area", 0x0b}, // Originator or talker leaving group call area
{"lower-layer-failure", 0x0c}, // Lower layer failure
{"call-already-cleared", 0x41}, // Call already cleared
{"semantically-incorrect-message", 0x5f}, // Semantically incorrect message
{"invalid-mandatory-information", 0x60}, // Invalid mandatory information
{"message-type-non-existent-or-not-implemented", 0x61}, // Message type non-existent or not implemented
{"message-type-not-compatible-with-the-protocol-state", 0x62}, // Message type not compatible with protocol state
{"conditional-IE-error", 0x64}, // Conditional IE error
{"no-cell-allocation-available", 0x65}, // No cell allocation available
{"protocol-error-unspecified", 0x6f}, // Protocol error unspecified
{0, 0},
};
// reference: ETSI TS 144 018 V11.5.0,, section 10.5.2.67 PS Cause
static const TokenDict s_psCauseType[] = {
{"DTM-multislot-capabilities-violated", 0x00},
{"no-uplink-TBFs", 0x01},
{"too-many-TBFs", 0x02},
{0, 0},
};
// IE Types
#define MAKE_IE_TYPE(x,decoder,encoder,data) const IEType s_type_##x = {decoder,encoder,data};
MAKE_IE_TYPE(Undef,0,0,0)
MAKE_IE_TYPE(Hex,0,0,0) // Use it to distinguish octet string types from undefined types
MAKE_IE_TYPE(MobileIdent,decodeMobileIdent, encodeMobileIdent,0)
MAKE_IE_TYPE(LAI,decodeLAI,encodeLAI,0)
MAKE_IE_TYPE(MMRejectCause,decodeEnum,encodeEnum,GSML3Codec::s_mmRejectCause)
MAKE_IE_TYPE(LocUpdType,decodeLocUpdType,encodeLocUpdType,0)
MAKE_IE_TYPE(CiphKeySN,decodeEnum,encodeEnum,s_ciphKeySN)
MAKE_IE_TYPE(MSNetFeatSupp,decodeEnum,encodeEnum,s_msNetworkFeatSupport)
MAKE_IE_TYPE(MMIdentType,decodeEnum,encodeEnum,s_mmIdentType)
MAKE_IE_TYPE(PTMSIType,decodeEnum,encodeEnum,s_pTMSIType)
MAKE_IE_TYPE(CMServType,decodeEnum,encodeEnum,s_mmCMServType)
MAKE_IE_TYPE(PrioLevel,decodeEnum,encodeEnum,s_mmPriorityLevel)
MAKE_IE_TYPE(ProgressInd,decodeProgressInd,encodeProgressInd,0)
MAKE_IE_TYPE(BCDNumber,decodeBCDNumber,encodeBCDNumber,0)
MAKE_IE_TYPE(Cause,decodeCause,encodeCause,0)
MAKE_IE_TYPE(CCCapabilities,decodeCCCapab,encodeCCCapab,0)
MAKE_IE_TYPE(BearerCapab,decodeBearerCapab,encodeBearerCapab,0)
MAKE_IE_TYPE(IA5Chars,decodeIA5Chars,encodeIA5Chars,0)
MAKE_IE_TYPE(NotifIndicator,decodeEnum,encodeEnum,s_notifIndicatorType)
MAKE_IE_TYPE(RepeatInd,decodeEnum,encodeEnum,s_repeatIndType)
MAKE_IE_TYPE(SSVersion,decodeEnum,encodeEnum,s_ssVersionType)
MAKE_IE_TYPE(NetworkCCCapab,decodeEnum,encodeEnum,s_networkCCCapabType)
MAKE_IE_TYPE(Signal,decodeEnum,encodeEnum,s_signalType)
MAKE_IE_TYPE(AlertPattern,decodeEnum,encodeEnum,s_alertPattern)
MAKE_IE_TYPE(CauseNoCLI,decodeEnum,encodeEnum,s_causeNoCLIType)
MAKE_IE_TYPE(MSClassmark1,decodeMSClassmark1,encodeMSClassmark1,0)
MAKE_IE_TYPE(MSClassmark2,decodeMSClassmark2,encodeMSClassmark2,0)
MAKE_IE_TYPE(PDAndSAPI,decodePDAndSAPI,encodePDAndSAPI,0)
MAKE_IE_TYPE(CongestLvl,decodeEnum,encodeEnum,s_congestLvl_type)
MAKE_IE_TYPE(RecallType,decodeEnum,encodeEnum,s_recallType)
MAKE_IE_TYPE(AdditUpdParams,decodeFlags,encodeFlags,s_additionalUpdateParams_type)
MAKE_IE_TYPE(DevProperties,decodeFlags,encodeFlags,s_DeviceProperties)
MAKE_IE_TYPE(PLMNList,decodePLMNList,encodePLMNList,0)
MAKE_IE_TYPE(MMTimer,decodeMMTimer,encodeMMTimer,0)
MAKE_IE_TYPE(RAI,decodeRAI,encodeRAI,0)
const int s_skipIndDefVal = 0;
MAKE_IE_TYPE(Int,decodeInt,encodeInt,&s_skipIndDefVal)
MAKE_IE_TYPE(TID,decodeTID,encodeTID,0)
MAKE_IE_TYPE(EpsReqType,0,0,s_epsReqType)
MAKE_IE_TYPE(EpsPdnType,0,0,s_epsPdnType)
MAKE_IE_TYPE(EsmEITFlag,0,0,s_esmEITFlag)
MAKE_IE_TYPE(EpsAttachTypes,0,0,s_epsAttachTypes)
MAKE_IE_TYPE(NASKeySetId,decodeNASKeyId,encodeNASKeyId,0)
MAKE_IE_TYPE(EPSMobileIdent,decodeEPSMobileIdent,encodeEPSMobileIdent,0)
MAKE_IE_TYPE(UENetworkCapab,decodeUENetworkCapab,encodeUENetworkCapab,0)
MAKE_IE_TYPE(RL3Msg,decodeRL3Msg,encodeRL3Msg,0)
MAKE_IE_TYPE(TAI,decodeTAI,encodeTAI,0)
MAKE_IE_TYPE(DRX,decodeDRX,encodeDRX,0)
MAKE_IE_TYPE(TMSIStatus,0,0,s_tmsiStatus)
MAKE_IE_TYPE(AdditionalUpdateType,0,0,s_additionalUpdateType)
MAKE_IE_TYPE(VoicePreference,decodeVoicePref,encodeVoicePref,0)
MAKE_IE_TYPE(GUTIType,0,0,s_epsGUTIType)
MAKE_IE_TYPE(SecurityHeader,decodeSecHeader,encodeSecHeader,0)
// SMS types
MAKE_IE_TYPE(CPCause,decodeEnum,encodeEnum,s_cpCauseType)
// RR types
MAKE_IE_TYPE(RRCause,decodeEnum,encodeEnum,s_rrCauseType)
MAKE_IE_TYPE(PSCause,decodeEnum,encodeEnum,s_psCauseType)
MAKE_IE_TYPE(MTDiff,decodeMobileTD,encodeMobileTD,0);
MAKE_IE_TYPE(MTDiffHyper,decodeMobileTDHyper,encodeMobileTDHyper,0);
#define MAKE_IE_PARAM(type,xml,iei,name,optional,length,lowerBits,ieType) \
{GSML3Codec::type,GSML3Codec::xml,iei,name,optional,length,lowerBits,ieType}
const IEParam s_ie_EndDef = MAKE_IE_PARAM(NoType, Skip, 0, "", 0, 0, 0, s_type_Undef);
// Mobility management message definitions
// reference: ETSI TS 124 008 V11.6.0, section 9.2.12 IMSI detach indication
static const IEParam s_mmIMSIDetachIndParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "MobileStationClassmark", false, 8, true, s_type_MSClassmark1),
MAKE_IE_PARAM(LV, XmlElem, 0, "MobileIdentity", false, 9 * 8, true, s_type_MobileIdent),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.2.13 Location updating Accept
static const IEParam s_mmLocationUpdateAckParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "LAI", false, 5 * 8, true, s_type_LAI),
MAKE_IE_PARAM(TLV, XmlElem, 0x17, "MobileIdentity", true, 10 * 8, true, s_type_MobileIdent),
MAKE_IE_PARAM(T, XmlElem, 0xA1, "FollowOnProceed", true, 8, true, s_type_Hex),
MAKE_IE_PARAM(T, XmlElem, 0xA2, "CTSPermission", true, 8, true, s_type_Hex),
MAKE_IE_PARAM(TLV, XmlElem, 0x4A, "EquivalentPLMNs", true, 47 * 8, true, s_type_PLMNList),
MAKE_IE_PARAM(TLV, XmlElem, 0x34, "EmergencyNumberList", true, 50 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x35, "PerMST3212", true, 3 * 8, true, s_type_Undef),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.2.14 Location updating reject
// reference: ETSI TS 124 008 V11.6.0, section 9.2.6 CM Service reject
static const IEParam s_mmLocationUpdateRejParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "RejectCause", false, 8, true, s_type_MMRejectCause),
MAKE_IE_PARAM(TLV, XmlElem, 0x36, "T3246Value", true, 3 * 8, true, s_type_MMTimer),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.2.15 Location updating request
static const IEParam s_mmLocationUpdateReqParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "LocationUpdatingType", false, 4, true, s_type_LocUpdType),
MAKE_IE_PARAM(V, XmlElem, 0, "CipheringKeySequenceNumber", false, 4, false, s_type_CiphKeySN),
MAKE_IE_PARAM(V, XmlElem, 0, "LAI", false, 5 * 8, true, s_type_LAI),
MAKE_IE_PARAM(V, XmlElem, 0, "MobileStationClassmark", false, 8, true, s_type_MSClassmark1),
MAKE_IE_PARAM(LV, XmlElem, 0, "MobileIdentity", false, 9 * 8, true, s_type_MobileIdent),
MAKE_IE_PARAM(TLV, XmlElem, 0x33, "MobileStationClassmark2", true, 5 * 8, true, s_type_MSClassmark2),
MAKE_IE_PARAM(TV, XmlElem, 0xC0, "AdditionalUpdateParameters", true, 8, true, s_type_AdditUpdParams),
MAKE_IE_PARAM(TV, XmlElem, 0xD0, "DeviceProperties", true, 8, true, s_type_DevProperties),
MAKE_IE_PARAM(TV, XmlElem, 0xE0, "MSNetworkFeatureSupport", true, 8, true, s_type_MSNetFeatSupp),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.2.2 Authentication request
static const IEParam s_mmAuthReqParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "CipheringKeySequenceNumber", false, 4, true, s_type_CiphKeySN),
MAKE_IE_PARAM(V, Skip, 0, "SpareHalfOctet", false, 4, false, s_type_Undef),
MAKE_IE_PARAM(V, XmlElem, 0, "rand", false, 16 * 8, false, s_type_Hex),
MAKE_IE_PARAM(TLV, XmlElem, 0x20, "autn", true, 18 * 8, false, s_type_Hex),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.2.3 Authentication Response
static const IEParam s_mmAuthRespParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "res", false, 4 * 8, false, s_type_Hex),
MAKE_IE_PARAM(TLV, XmlElem, 0x21, "xres2", true, 14 * 8, false, s_type_Hex),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.2.3a Authentication Failure
static const IEParam s_mmAuthFailParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "RejectCause", false, 8, true, s_type_MMRejectCause),
MAKE_IE_PARAM(TLV, XmlElem, 0x22, "auts", true, 16 * 8, false, s_type_Hex),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.2.10 Identity Request
static const IEParam s_mmIdentityReqParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "IdentityType", false, 8, true, s_type_MMIdentType),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.2.11 Identity Response
static const IEParam s_mmIdentityRespParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "MobileIdentity", false, 10 * 8, true, s_type_MobileIdent),
MAKE_IE_PARAM(TV, XmlElem, 0xE0, "P_TMSIType", true, 8, true, s_type_PTMSIType),
MAKE_IE_PARAM(TLV, XmlElem, 0x1B, "RAI", true, 8 * 8, true, s_type_RAI),
MAKE_IE_PARAM(TLV, XmlElem, 0x19, "P_TMSISignature", true, 5 * 8, true, s_type_Hex),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.2.17 TMSI reallocation command
static const IEParam s_mmTMSIReallocCmdParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "LAI", false, 5 * 8, true, s_type_LAI),
MAKE_IE_PARAM(LV, XmlElem, 0, "MobileIdentity", false, 9 * 8, true, s_type_MobileIdent),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.2.9 CM service request
static const IEParam s_mmCMServiceReqParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "CMServiceType", false, 4, true, s_type_CMServType),
MAKE_IE_PARAM(V, XmlElem, 0, "CipheringKeySequenceNumber", false, 4, false, s_type_CiphKeySN),
MAKE_IE_PARAM(LV, XmlElem, 0, "MobileStationClassmark2", false, 4 * 8, true, s_type_MSClassmark2),
MAKE_IE_PARAM(LV, XmlElem, 0, "MobileIdentity", false, 9 * 8, true, s_type_MobileIdent),
MAKE_IE_PARAM(TV, XmlElem, 0x80, "Priority", true, 8, true, s_type_PrioLevel),
MAKE_IE_PARAM(TV, XmlElem, 0xC0, "AdditionalUpdateParameters", true, 8, true, s_type_AdditUpdParams),
MAKE_IE_PARAM(TV, XmlElem, 0xD0, "DeviceProperties", true, 8, true, s_type_DevProperties),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.2.5a CM service prompt $(CCBS)$
static const IEParam s_mmCMServicePromptParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "PDAndSAPI", false, 8, true, s_type_PDAndSAPI),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.2.4 CM Re-establishment request
static const IEParam s_mmCMReEstablishReqParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "CipheringKeySequenceNumber", false, 4, true, s_type_CiphKeySN),
MAKE_IE_PARAM(V, Skip, 0, "SpareHalfOctet", false, 4, false, s_type_Undef),
MAKE_IE_PARAM(LV, XmlElem, 0, "MobileStationClassmark2", false, 4 * 8, true, s_type_MSClassmark2),
MAKE_IE_PARAM(LV, XmlElem, 0, "MobileIdentity", false, 9 * 8, true, s_type_MobileIdent),
MAKE_IE_PARAM(TV, XmlElem, 0x13, "LAI", true, 6 * 8, true, s_type_LAI),
MAKE_IE_PARAM(TV, XmlElem, 0xD0, "DeviceProperties", true, 8, true, s_type_DevProperties),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.2.8 Abort
// reference: ETSI TS 124 008 V11.6.0, section 9.2.16 MM Status
static const IEParam s_mmAbortParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "RejectCause", false, 8, true, s_type_MMRejectCause),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.2.15a MM information
static const IEParam s_mmInformationParams[] = {
MAKE_IE_PARAM(TLV, XmlElem, 0x43, "NetworkFullName", true, 255 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x45, "NetworkShortName", true, 255 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TV, XmlElem, 0x46, "LocalTimezone", true, 2 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TV, XmlElem, 0x47, "UniversalTimeAndTimezone", true, 8 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x48, "LSAIdentity", true, 5 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x49, "NetworkDST", true, 3 * 8, true, s_type_Undef),
s_ie_EndDef,
};
static const RL3Message s_mmMsgs[] = {
// Registration messages
{0x01, "IMSIDetachIndication", s_mmIMSIDetachIndParams, 0},
{0x02, "LocationUpdatingAccept", s_mmLocationUpdateAckParams, 0},
{0x04, "LocationUpdatingReject", s_mmLocationUpdateRejParams, 0},
{0x08, "LocationUpdatingRequest", s_mmLocationUpdateReqParams, 0},
// Security messages
{0x11, "AuthenticationReject", 0, 0},
{0x12, "AuthenticationRequest", s_mmAuthReqParams, 0},
{0x14, "AuthenticationResponse", s_mmAuthRespParams, 0},
{0x1c, "AuthenticationFailure", s_mmAuthFailParams, 0},
{0x18, "IdentityRequest", s_mmIdentityReqParams, 0},
{0x19, "IdentityResponse", s_mmIdentityRespParams, 0},
{0x1a, "TMSIReallocationCommand", s_mmTMSIReallocCmdParams, 0},
{0x1b, "TMSIReallocationComplete", 0, 0},
// Connection management messages
{0x21, "CMServiceAccept", 0, 0},
{0x22, "CMServiceReject", s_mmLocationUpdateRejParams, 0},
{0x23, "CMServiceAbort", 0, 0},
{0x24, "CMServiceRequest", s_mmCMServiceReqParams, 0},
{0x25, "CMServicePrompt", s_mmCMServicePromptParams, 0},
{0x28, "CMReEstablishmentRequest", s_mmCMReEstablishReqParams, 0},
// CM re-establishment request 0x28
{0x29, "Abort", s_mmAbortParams, 0},
// Miscellaneous messages
{0x30, "MMNull", 0, 0},
{0x31, "MMStatus", s_mmAbortParams, 0},
{0x32, "MMInformation", s_mmInformationParams, 0},
{0xff, "", 0, 0},
};
// Call control message definitions
// reference: ETSI TS 124 008 V11.6.0, section 9.3.1.2 Alerting (mobile station to network direction)
static const IEParam s_ccAlertFromMSParams[] = {
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x7E, "UserUser", true, 131 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7F, "SSVersion", true, 3 * 8, true, s_type_SSVersion),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.1.1 Alerting (network to mobile station direction)
static const IEParam s_ccAlertToMSParams[] = {
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x1E, "ProgressIndicator", true, 4 * 8, true, s_type_ProgressInd),
MAKE_IE_PARAM(TLV, XmlElem, 0x7E, "UserUser", true, 131 * 8, true, s_type_Undef),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, 9.3.3 Call proceeding
static const IEParam s_ccCallProceedParams[] = {
MAKE_IE_PARAM(TV, XmlElem, 0xD0, "BCRepeatIndicator", true, 8, true, s_type_RepeatInd),
MAKE_IE_PARAM(TLV, XmlElem, 0x04, "BearerCapability1", true, 16 * 8, true, s_type_BearerCapab),
MAKE_IE_PARAM(TLV, XmlElem, 0x04, "BearerCapability2", true, 16 * 8, true, s_type_BearerCapab),
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x1E, "ProgressIndicator", true, 4 * 8, true, s_type_ProgressInd),
MAKE_IE_PARAM(TV, XmlElem, 0x80, "Priority", true, 8, true, s_type_PrioLevel),
MAKE_IE_PARAM(TLV, XmlElem, 0x2F, "NetworkCCCapabilities", true, 3 * 8, true, s_type_NetworkCCCapab),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, 9.3.17 Progress
static const IEParam s_ccProgressParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "ProgressIndicator", false, 3 * 8, true, s_type_ProgressInd),
MAKE_IE_PARAM(TLV, XmlElem, 0x7E, "UserUser", true, 131 * 8, true, s_type_Undef),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, 9.3.17a CC-Establishment $(CCBS)$
static const IEParam s_ccEstablishmentParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "SetupContainer", false, 255 * 8, true, s_type_RL3Msg),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, 9.3.23.2 Setup (mobile originating call establishment)
static const IEParam s_ccSetupFromMSParams[] = {
MAKE_IE_PARAM(TV, XmlElem, 0xD0, "BCRepeatIndicator", true, 8, true, s_type_RepeatInd),
MAKE_IE_PARAM(TLV, XmlElem, 0x04, "BearerCapability1", false, 16 * 8, true, s_type_BearerCapab),
MAKE_IE_PARAM(TLV, XmlElem, 0x04, "BearerCapability2", true, 16 * 8, true, s_type_BearerCapab),
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x5D, "CallingPartySubAddress", true, 23 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x5E, "CalledPartyBCDNumber", false, 43 * 8, true, s_type_BCDNumber),
MAKE_IE_PARAM(TLV, XmlElem, 0x6D, "CalledPartySubAddress", true, 23 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TV, XmlElem, 0xD0, "LLCRepeatIndicator", true, 8, true, s_type_RepeatInd),
MAKE_IE_PARAM(TLV, XmlElem, 0x7C, "LowLayerCompatibility1", true, 18 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7C, "LowLayerCompatibility2", true, 18 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TV, XmlElem, 0xD0, "HLCRepeatIndicator", true, 8, true, s_type_RepeatInd),
MAKE_IE_PARAM(TLV, XmlElem, 0x7D, "HighLayerCompatibility1",true, 5 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7D, "HighLayerCompatibility2",true, 5 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7E, "UserUser", true, 35 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7F, "SSVersion", true, 3 * 8, true, s_type_SSVersion),
MAKE_IE_PARAM(T, XmlElem, 0xA1, "CLIRSuppresion", true, 8, true, s_type_Hex),
MAKE_IE_PARAM(T, XmlElem, 0xA2, "CLIRInvocation", true, 8, true, s_type_Hex),
MAKE_IE_PARAM(TLV, XmlElem, 0x15, "CCCapabilities", true, 4 * 8, true, s_type_CCCapabilities),
MAKE_IE_PARAM(TLV, XmlElem, 0x1D, "FacilityCCBSAdvRA", true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x1B, "FacilityCCBSRANotEssent",true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x2D, "StreamIdentifier", true, 3 * 8, true, s_type_Int),
MAKE_IE_PARAM(TLV, XmlElem, 0x40, "SupportedCodecs", true, 255 * 8, true, s_type_Undef),
MAKE_IE_PARAM(T, XmlElem, 0xA3, "Redial", true, 8, true, s_type_Hex),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, 9.3.23.1 Setup (mobile terminated call establishment)
static const IEParam s_ccSetupToMSParams[] = {
MAKE_IE_PARAM(TV, XmlElem, 0xD0, "BCRepeatIndicator", true, 8, true, s_type_RepeatInd),
MAKE_IE_PARAM(TLV, XmlElem, 0x04, "BearerCapability1", true, 16 * 8, true, s_type_BearerCapab),
MAKE_IE_PARAM(TLV, XmlElem, 0x04, "BearerCapability2", true, 16 * 8, true, s_type_BearerCapab),
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x1E, "ProgressIndicator", true, 4 * 8, true, s_type_ProgressInd),
MAKE_IE_PARAM(TV, XmlElem, 0x34, "Signal", true, 2 * 8, true, s_type_Signal),
MAKE_IE_PARAM(TLV, XmlElem, 0x5C, "CallingPartyBCDNumber", true, 14 * 8, true, s_type_BCDNumber),
MAKE_IE_PARAM(TLV, XmlElem, 0x5D, "CallingPartySubAddress", true, 23 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x5E, "CalledPartyBCDNumber", true, 19 * 8, true, s_type_BCDNumber),
MAKE_IE_PARAM(TLV, XmlElem, 0x6D, "CalledPartySubAddress", true, 23 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x74, "RedirectingPartyBCDNumber", true, 19 * 8, true, s_type_BCDNumber),
MAKE_IE_PARAM(TLV, XmlElem, 0x75, "RedirectingPartySubAddress", true, 23 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TV, XmlElem, 0xD0, "LLCRepeatIndicator", true, 8, true, s_type_RepeatInd),
MAKE_IE_PARAM(TLV, XmlElem, 0x7C, "LowLayerCompatibility1", true, 18 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7C, "LowLayerCompatibility2", true, 18 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TV, XmlElem, 0xD0, "HLCRepeatIndicator", true, 8, true, s_type_RepeatInd),
MAKE_IE_PARAM(TLV, XmlElem, 0x7D, "HighLayerCompatibility1", true, 5 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7D, "HighLayerCompatibility2", true, 5 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7E, "UserUser", true, 35 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TV, XmlElem, 0x80, "Priority", true, 8, true, s_type_PrioLevel),
MAKE_IE_PARAM(TLV, XmlElem, 0x19, "Alert", true, 3 * 8, true, s_type_AlertPattern),
MAKE_IE_PARAM(TLV, XmlElem, 0x2F, "NetworkCCCapabilities", true, 3 * 8, true, s_type_NetworkCCCapab),
MAKE_IE_PARAM(TLV, XmlElem, 0x3A, "CauseOfNoCLI", true, 3 * 8, true, s_type_CauseNoCLI),
MAKE_IE_PARAM(TLV, XmlElem, 0x41, "BackupBearerCapability", true, 15 * 8, true, s_type_Undef),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.17b CC-Establishment confirmed $(CCBS)$
static const IEParam s_ccEstablCnfParams[] = {
MAKE_IE_PARAM(TV, XmlElem, 0xD0, "BCRepeatIndicator", true, 8, true, s_type_RepeatInd),
MAKE_IE_PARAM(TLV, XmlElem, 0x04, "BearerCapability1", true, 16 * 8, true, s_type_BearerCapab),
MAKE_IE_PARAM(TLV, XmlElem, 0x04, "BearerCapability2", true, 16 * 8, true, s_type_BearerCapab),
MAKE_IE_PARAM(TLV, XmlElem, 0x08, "Cause", true, 32 * 8, true, s_type_Cause),
MAKE_IE_PARAM(TLV, XmlElem, 0x40, "SupportedCodecs", true, 255 * 8, true, s_type_Undef),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, sectin 9.3.5.2 Connect (mobile station to network direction)
static const IEParam s_ccConnFromMSParams[] = {
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x4D, "ConnectedSubAddress", true, 23 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7E, "UserUser", true, 131 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7F, "SSVersion", true, 3 * 8, true, s_type_SSVersion),
MAKE_IE_PARAM(TLV, XmlElem, 0x2D, "StreamIdentifier", true, 3 * 8, true, s_type_Int),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.5.1 Connect (network to mobile station direction)
static const IEParam s_ccConnToMSParams[] = {
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x1E, "ProgressIndicator", true, 4 * 8, true, s_type_ProgressInd),
MAKE_IE_PARAM(TLV, XmlElem, 0x4C, "ConnectedNumber", true, 14 * 8, true, s_type_BCDNumber),
MAKE_IE_PARAM(TLV, XmlElem, 0x4D, "ConnectedSubAddress", true, 23 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7E, "UserUser", true, 131 * 8, true, s_type_Undef),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.7.2 Disconnect (mobile station to network direction)
static const IEParam s_ccDisconnFromMSParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "Cause", false, 31 * 8, true, s_type_Cause),
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x7E, "UserUser", true, 131 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7F, "SSVersion", true, 3 * 8, true, s_type_SSVersion),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.7.1 Disconnect (network to mobile station direction)
static const IEParam s_ccDisconnToMSParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "Cause", false, 31 * 8, true, s_type_Cause),
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x1E, "ProgressIndicator", true, 4 * 8, true, s_type_ProgressInd),
MAKE_IE_PARAM(TLV, XmlElem, 0x7E, "UserUser", true, 131 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7B, "AllowedActions", true, 3 * 8, true, s_type_Undef),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.18.2 Release (mobile station to network direction)
static const IEParam s_ccRelFromMSParams[] = {
MAKE_IE_PARAM(TLV, XmlElem, 0x08, "Cause", true, 32 * 8, true, s_type_Cause),
MAKE_IE_PARAM(TLV, XmlElem, 0x08, "SecondCause", true, 32 * 8, true, s_type_Cause),
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x7E, "UserUser", true, 131 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7F, "SSVersion", true, 3 * 8, true, s_type_SSVersion),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.18.1 Release (network to mobile station direction)
static const IEParam s_ccRelToMSParams[] = {
MAKE_IE_PARAM(TLV, XmlElem, 0x08, "Cause", true, 32 * 8, true, s_type_Cause),
MAKE_IE_PARAM(TLV, XmlElem, 0x08, "SecondCause", true, 32 * 8, true, s_type_Cause),
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x7E, "UserUser", true, 131 * 8, true, s_type_Undef),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.19.2 Release complete (mobile station to network direction)
static const IEParam s_ccRelComplFromMSParams[] = {
MAKE_IE_PARAM(TLV, XmlElem, 0x08, "Cause", true, 32 * 8, true, s_type_Cause),
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x7E, "UserUser", true, 131 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7F, "SSVersion", true, 3 * 8, true, s_type_SSVersion),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.19.1 Release complete (network to mobile station direction)
static const IEParam s_ccRelComplToMSParams[] = {
MAKE_IE_PARAM(TLV, XmlElem, 0x08, "Cause", true, 32 * 8, true, s_type_Cause),
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", true, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem, 0x7E, "UserUser", true, 131 * 8, true, s_type_Undef),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.2 Call confirmed
static const IEParam s_ccCallConfirmParams[] = {
MAKE_IE_PARAM(TV, XmlElem, 0xD0, "BCRepeatIndicator", true, 8, true, s_type_RepeatInd),
MAKE_IE_PARAM(TLV, XmlElem, 0x04, "BearerCapability1", true, 16 * 8, true, s_type_BearerCapab),
MAKE_IE_PARAM(TLV, XmlElem, 0x04, "BearerCapability2", true, 16 * 8, true, s_type_BearerCapab),
MAKE_IE_PARAM(TLV, XmlElem, 0x08, "Cause", true, 32 * 8, true, s_type_Cause),
MAKE_IE_PARAM(TLV, XmlElem, 0x15, "CCCapabilities", true, 4 * 8, true, s_type_CCCapabilities),
MAKE_IE_PARAM(TLV, XmlElem, 0x2D, "StreamIdentifier", true, 3 * 8, true, s_type_Int),
MAKE_IE_PARAM(TLV, XmlElem, 0x40, "SupportedCodecs", true, 255 * 8, true, s_type_Undef),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.23a Start CC $(CCBS)$
static const IEParam s_ccStartCCParams[] = {
MAKE_IE_PARAM(TLV, XmlElem, 0x15, "CCCapabilities", true, 4 * 8, true, s_type_CCCapabilities),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, sections 9.3.18a Recall $(CCBS)$
static const IEParam s_ccRecallParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "RecallType", false, 8, true, s_type_RecallType),
MAKE_IE_PARAM(LV, XmlElem, 0, "Facility", false, 255 * 8, true, s_type_RL3Msg),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.8 Emergency setup
static const IEParam s_ccEmergencySetupParams[] = {
MAKE_IE_PARAM(TLV, XmlElem, 0x04, "BearerCapability", true, 11 * 8, true, s_type_BearerCapab),
MAKE_IE_PARAM(TLV, XmlElem, 0x2D, "StreamIdentifier", true, 3 * 8, true, s_type_Int),
MAKE_IE_PARAM(TLV, XmlElem, 0x40, "SupportedCodecs", true, 255 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x2D, "EmergencyCategory", true, 3 * 8, true, s_type_Undef),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, 9.3.31 User information
static const IEParam s_ccUserInfoParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "UserUser", false, 130 * 8, true, s_type_Undef),
MAKE_IE_PARAM(T, XmlElem, 0xA0, "MoreData", true, 8, true, s_type_Hex),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.13 Modify
static const IEParam s_ccModifyParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "BearerCapability", false, 15 * 8, true, s_type_BearerCapab),
MAKE_IE_PARAM(TLV, XmlElem, 0x7C, "LowLayerCompatibility", true, 18 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7D, "HighLayerCompatibility", true, 5 * 8, true, s_type_Undef),
MAKE_IE_PARAM(T, XmlElem, 0xA3, "ReverseCallSetupDirection", true, 8, true, s_type_Hex),
MAKE_IE_PARAM(T, XmlElem, 0xA4, "NIServiceUpgradeIndicator", true, 8, true, s_type_Hex),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.14 Modify Complete
static const IEParam s_ccModifyComplParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "BearerCapability", false, 15 * 8, true, s_type_BearerCapab),
MAKE_IE_PARAM(TLV, XmlElem, 0x7C, "LowLayerCompatibility", true, 18 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7D, "HighLayerCompatibility", true, 5 * 8, true, s_type_Undef),
MAKE_IE_PARAM(T, XmlElem, 0xA3, "ReverseCallSetupDirection", true, 8, true, s_type_Hex),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.15 Modify Reject
static const IEParam s_ccModifyRejParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "Cause", false, 31 * 8, true, s_type_Cause),
MAKE_IE_PARAM(TLV, XmlElem, 0x7C, "LowLayerCompatibility", true, 18 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x7D, "HighLayerCompatibility", true, 5 * 8, true, s_type_Undef),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.4 Congestion control
static const IEParam s_ccCongestionCtrlParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "CongestionLevel", false, 4, true, s_type_CongestLvl),
MAKE_IE_PARAM(V, Skip, 0, "SpareHalfOctet", false, 4, false, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x08, "Cause", true, 32 * 8, true, s_type_Cause),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.16 Notify
static const IEParam s_ccNotifyParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "NotificationIndicator", false, 8, true, s_type_NotifIndicator),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.19.1 Release complete (network to mobile station direction)
static const IEParam s_ccStatusParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "Cause", false, 31 * 8, true, s_type_Cause),
MAKE_IE_PARAM(V, XmlElem, 0, "CallState", false, 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x24, "AuxiliaryStates", true, 3 * 8, true, s_type_Undef),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.12 Hold Reject
// reference: ETSI TS 124 008 V11.6.0, section 9.3.21 Retrieve Reject
static const IEParam s_ccCauseRejParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "Cause", false, 31 * 8, true, s_type_Cause),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.24 Start DTMF
// reference: ETSI TS 124 008 V11.6.0, section 9.3.25 Start DTMF Acknowledge
static const IEParam s_ccStartDTMFParams[] = {
MAKE_IE_PARAM(TV, XmlElem, 0x2C, "KeypadFacility", false, 2 * 8, true, s_type_IA5Chars),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.9.2 Facility (mobile Station to network direction)
static const IEParam s_ccFacilityFromMSParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "Facility", false, 255 * 8, true, s_type_RL3Msg),
MAKE_IE_PARAM(TLV, XmlElem,0x7F, "SSVersion", true, 3 * 8, true, s_type_SSVersion),
s_ie_EndDef,
};
// reference: ETSI TS 124 008 V11.6.0, section 9.3.9.1 Facility (network to mobile Station direction)
static const IEParam s_ccFacilityToMSParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "Facility", false, 255 * 8, true, s_type_RL3Msg),
s_ie_EndDef,
};
static const RL3Message s_ccMsgs[] = {
// Call establishment messages
{0x01, "Alerting", s_ccAlertFromMSParams, s_ccAlertToMSParams},
{0x02, "CallProceeding", s_ccCallProceedParams, 0},
{0x03, "Progress", s_ccProgressParams, 0},
{0x04, "CCEstablishment", s_ccEstablishmentParams, 0},
{0x05, "Setup", s_ccSetupFromMSParams, s_ccSetupToMSParams},
{0x06, "CCEstablishmentConfirmed", s_ccEstablCnfParams, 0},
{0x07, "Connect", s_ccConnFromMSParams, s_ccConnToMSParams},
{0x08, "CallConfirmed", s_ccCallConfirmParams, 0},
{0x09, "StartCC", s_ccStartCCParams, 0},
{0x0b, "Recall", s_ccRecallParams, 0},
{0x0e, "EmergencySetup", s_ccEmergencySetupParams, 0},
{0x0f, "ConnectAcknowledge", 0, 0},
// Call information phase messages
{0x10, "UserInformation", s_ccUserInfoParams, 0},
{0x17, "Modify", s_ccModifyParams, 0},
{0x1f, "ModifyComplete", s_ccModifyComplParams, 0},
{0x13, "ModifyReject", s_ccModifyRejParams, 0},
{0x18, "Hold", 0, 0},
{0x19, "HoldAck", 0, 0},
{0x1a, "HoldReject", s_ccCauseRejParams, 0},
{0x1c, "Retrieve", 0, 0},
{0x1d, "RetrieveAck", 0, 0},
{0x1e, "RetrieveReject", s_ccCauseRejParams, 0},
// Call clearing messages
{0x25, "Disconnect", s_ccDisconnFromMSParams, s_ccDisconnToMSParams},
{0x2d, "Release", s_ccRelFromMSParams, s_ccRelToMSParams},
{0x2a, "ReleaseComplete", s_ccRelComplFromMSParams, s_ccRelComplToMSParams},
// Miscellaneous messages
{0x39, "CongestionControl", s_ccCongestionCtrlParams, 0},
{0x3e, "Notify", s_ccNotifyParams, 0},
{0x34, "StatusEnquiry", 0, 0},
{0x3d, "Status", s_ccStatusParams, 0},
{0x35, "StartDTMF", s_ccStartDTMFParams, 0},
{0x36, "StartDTMFAck", s_ccStartDTMFParams, 0},
{0x37, "StartDTMFReject", s_ccCauseRejParams, 0},
{0x31, "StopDTMF", 0, 0},
{0x32, "StopDTMFAck", 0, 0},
{0x3a, "Facility", s_ccFacilityFromMSParams, s_ccFacilityToMSParams},
{0xff, "", 0, 0},
};
// EPS Session Management message definitions
// reference: ETSI TS 124 301 V11.8.0, section 8.3.20 PDN connectivity request
static const IEParam s_epsPdnConnReqParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "RequestType", false, 4, true, s_type_EpsReqType),
MAKE_IE_PARAM(V, XmlElem, 0, "PDNType", false, 4, false, s_type_EpsPdnType),
MAKE_IE_PARAM(TV, XmlElem, 0xD0, "ESMInformationTransferFlag", true, 8, true, s_type_EsmEITFlag),
MAKE_IE_PARAM(TLV, XmlElem, 0x28, "AccessPointName", true, 102 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x27, "ProtocolConfigurationOptions", true, 253 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TV, XmlElem, 0xC0, "DeviceProperties", true, 8, true, s_type_DevProperties),
s_ie_EndDef,
};
// EPS Session Management Messages
// reference: ETSI TS 124 301 V11.8.0, section 9.8
static const RL3Message s_epsSmMsgs[] = {
{0xc1, "ActivateDefaultEPSBearerContextRequest", 0, 0},
{0xc2, "ActivateDefaultEPSBearerContextAccept", 0, 0},
{0xc3, "ActivateDefaultEPSBearerContextReject", 0, 0},
{0xc5, "ActivateDedicatedEPSBearerContextRequest", 0, 0},
{0xc6, "ActivateDedicatedEPSBearerContextAccept", 0, 0},
{0xc7, "ActivateDedicatedEPSBearerContextReject", 0, 0},
{0xc9, "ModifyEPSBearerContextRequest", 0, 0},
{0xca, "ModifyEPSBearerContextAccept", 0, 0},
{0xcb, "ModifyEPSBearerContextReject", 0, 0},
{0xcd, "DeactivateEPSBearerContextRequest", 0, 0},
{0xce, "DeactivateEPSBearerContextaccept", 0, 0},
{0xd0, "PDNConnectivityRequest", s_epsPdnConnReqParams, 0},
{0xd1, "PDNConnectivityReject", 0, 0},
{0xd2, "PDNDisconnectRequest", 0, 0},
{0xd3, "PDNDisconnectReject", 0, 0},
{0xd4, "BearerResourceAllocationRequest", 0, 0},
{0xd5, "BearerResourceAllocationReject", 0, 0},
{0xd6, "BearerResourceModificationRequest", 0, 0},
{0xd7, "BearerResourceModificationReject", 0, 0},
{0xd9, "ESMInformationRequest", 0, 0},
{0xda, "ESMInformationResponse", 0, 0},
{0xdb, "Notification", 0, 0},
{0xe8, "ESMStatus", 0, 0},
{0xff, "", 0, 0},
};
// EPS Mobile Management message defitions
// reference: ETSI TS 124 301 V11.8.0, section 8.2.4 Attach request
static const IEParam s_epsAttachRequestParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "EPSAttachType", false, 4, true, s_type_EpsAttachTypes),
MAKE_IE_PARAM(V, XmlElem, 0, "NASKeySetIdentifier", false, 4, false, s_type_NASKeySetId),
MAKE_IE_PARAM(LV, XmlElem, 0, "EPSMobileIdentity", false, 12 * 8, true, s_type_EPSMobileIdent),
MAKE_IE_PARAM(LV, XmlElem, 0, "UENetworkCapability", false, 14 * 8, true, s_type_UENetworkCapab),
MAKE_IE_PARAM(LVE, XmlElem, 0, "ESMMessageContainer", false, 0, true, s_type_RL3Msg),
MAKE_IE_PARAM(TV, XmlElem, 0x19,"OldPTMSISignature", true, 4 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x50,"AdditionalGUTI", true, 13 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TV, XmlElem, 0x52,"LastVisitedRegisteredTAI", true, 6 * 8, true, s_type_TAI),
MAKE_IE_PARAM(TV, XmlElem, 0x5C,"DRXParameter", true, 3 * 8, true, s_type_DRX),
MAKE_IE_PARAM(TLV, XmlElem, 0x31,"MSNetworkCapability", true, 10 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TV, XmlElem, 0x13,"OldLocationAreaIdentification", true, 6 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TV, XmlElem, 0x90,"TMSIStatus", true, 8, true, s_type_TMSIStatus),
MAKE_IE_PARAM(TLV, XmlElem, 0x11,"MobileStationClassmark2", true, 5 * 8, true, s_type_MSClassmark2),
MAKE_IE_PARAM(TLV, XmlElem, 0x20,"MobileStationClassmark3", true, 34 * 8, true, s_type_Undef),
MAKE_IE_PARAM(TLV, XmlElem, 0x40,"SupportedCodecs", true, 0, true, s_type_Undef),
MAKE_IE_PARAM(TV, XmlElem, 0xF0,"AdditionalUpdateType", true, 8, true, s_type_AdditionalUpdateType),
MAKE_IE_PARAM(TLV, XmlElem, 0x5D,"VoiceDomainPreferenceAndUEsUsageSetting", true, 3 * 8, true, s_type_VoicePreference),
MAKE_IE_PARAM(TV, XmlElem, 0xD0,"DeviceProperties", true, 8, true, s_type_DevProperties),
MAKE_IE_PARAM(TV, XmlElem, 0xE0,"OldGUTIType", true, 8, true, s_type_GUTIType),
MAKE_IE_PARAM(TV, XmlElem, 0xC0,"MSNetworkFeatureSupport", true, 8, true, s_type_MSNetFeatSupp),
MAKE_IE_PARAM(TLV, XmlElem, 0x10,"TMSIBasedNRIContainer", true, 4 * 8, true, s_type_Undef),
s_ie_EndDef,
};
static const RL3Message s_epsMmMsgs[] = {
// TODO
{0x41, "AttachRequest", s_epsAttachRequestParams, 0},
{0xff, "", 0, 0},
};
// SS (Supplementary services) message definitions
// reference ETSI TS 124 080 V11.0.0, section 2.5 Release complete
static const IEParam s_ssRelCompleteParams[] = {
MAKE_IE_PARAM(TLV, XmlElem, 0x08, "Cause", true, 32 * 8, true, s_type_Cause),
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", true, 255 * 8, true, s_type_Hex),
s_ie_EndDef,
};
// reference ETSI TS 124 080 V11.0.0, section 2.3 Facility
static const IEParam s_ssFacilityParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "Facility", false, 255 * 8, true, s_type_Hex),
s_ie_EndDef,
};
// reference ETSI TS 124 080 V11.0.0, section 2.4.2 Register (MS to network direction)
static const IEParam s_ssRegistFromMSParams[] = {
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", false, 255 * 8, true, s_type_Hex),
MAKE_IE_PARAM(TLV, XmlElem, 0x7F, "SSVersion", true, 3 * 8, true, s_type_SSVersion),
s_ie_EndDef,
};
// reference ETSI TS 124 080 V11.0.0, section 2.4.1 Register (network to MS direction)
static const IEParam s_ssRegistToMSParams[] = {
MAKE_IE_PARAM(TLV, XmlElem, 0x1C, "Facility", false, 255 * 8, true, s_type_Hex),
s_ie_EndDef,
};
// SS message types
// reference ETSI TS 124 080 V11.0.0, section 3.4 Message type
static const RL3Message s_ssMsgs[] = {
{0x2a, "ReleaseComplete", s_ssRelCompleteParams, 0},
{0x3a, "Facility", s_ssFacilityParams, 0},
{0x3b, "Register", s_ssRegistFromMSParams, s_ssRegistToMSParams},
{0xff, "", 0, 0},
};
// SMS message definitions
// reference ETSI TS 124 011 V11.1.0, section 7.2.1 CP-DATA
static const IEParam s_smsCPDataParams[] = {
MAKE_IE_PARAM(LV, XmlElem, 0, "RPDU", false, 249 * 8, true, s_type_Hex),
s_ie_EndDef,
};
// reference ETSI TS 124 011 V11.1.0, section 7.2.3 CP-ERROR
static const IEParam s_smsCPErrorParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "CP-Cause", false, 8, true, s_type_CPCause),
s_ie_EndDef,
};
// SMS message types
// reference ETSI TS 124 011 V11.1.0, section 8.1.3 Message type
static const RL3Message s_smsMsgs[] = {
{0x01, "CP-Data", s_smsCPDataParams, 0},
{0x04, "CP-Ack", 0, 0},
{0x10, "CP-Error", s_smsCPErrorParams, 0},
{0xff, "", 0, 0},
};
// RR message definitions
// reference ETSI TS 144 018 V11.5.0, section 9.1.25 Paging response
static const IEParam s_rrPagingRespParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "CipheringKeySequenceNumber", false, 4, true, s_type_CiphKeySN),
MAKE_IE_PARAM(V, Skip, 0, "SpareHalfOctet", false, 4, false, s_type_Undef),
MAKE_IE_PARAM(LV, XmlElem, 0, "MobileStationClassmark2", false, 4 * 8, true, s_type_MSClassmark2),
MAKE_IE_PARAM(LV, XmlElem, 0, "MobileIdentity", false, 9 * 8, true, s_type_MobileIdent),
MAKE_IE_PARAM(TV, XmlElem, 0xC0, "AdditionalUpdateParameters", true, 8, true, s_type_AdditUpdParams),
s_ie_EndDef,
};
// reference ETSI TS 144 018 V11.5.0, section 9.1.17 Handover failure
static const IEParam s_rrHoFailureParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "RRCause", false, 8, true, s_type_RRCause),
MAKE_IE_PARAM(TV, XmlElem, 0x90, "PSCause", true, 8, true, s_type_PSCause),
s_ie_EndDef,
};
// reference ETSI TS 144 018 V11.5.0, section 9.1.16 Handover complete
static const IEParam s_rrHoCompleteParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "RRCause", false, 8, true, s_type_RRCause),
MAKE_IE_PARAM(TLV, XmlElem, 0x77, "MobileTimeDifference", true, 5 * 8, true, s_type_MTDiff),
MAKE_IE_PARAM(TLV, XmlElem, 0x67, "MobileTimeDifferenceHyperframe", true, 7 * 8, true, s_type_MTDiffHyper),
s_ie_EndDef,
};
// reference ETSI TS 144 018 V11.5.0, section 9.1.29 RR Status
static const IEParam s_rrStatusParams[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "RRCause", false, 8, true, s_type_RRCause),
s_ie_EndDef,
};
// Radio Resource Management message types
// reference ETSI TS 144 018 V11.5.0, section 10.4 Message type
static const RL3Message s_rrMsgs[] = {
// Paging and Notification messages
{0x27, "PagingResponse", s_rrPagingRespParams, 0},
// Handover messages
{0x28, "HandoverFailure", s_rrHoFailureParams, 0},
{0x2c, "HandoverComplete", s_rrHoCompleteParams, 0},
// Miscellaneous messages
{0x12, "RRStatus", s_rrStatusParams, 0},
{0xff, "", 0, 0},
};
// Message definitions according to protocol discriminator type
MAKE_IE_TYPE(MM_Msg,decodeMsgType,encodeMsgType,s_mmMsgs)
MAKE_IE_TYPE(CC_Msg,decodeMsgType,encodeMsgType,s_ccMsgs)
MAKE_IE_TYPE(EPS_SM_Msg,decodeMsgType,encodeMsgType,s_epsSmMsgs)
MAKE_IE_TYPE(EPS_MM_Msg,decodeMsgType,encodeMsgType,s_epsMmMsgs)
MAKE_IE_TYPE(SS_Msg,decodeMsgType,encodeMsgType,s_ssMsgs)
MAKE_IE_TYPE(SMS_Msg,decodeMsgType,encodeMsgType,s_smsMsgs)
MAKE_IE_TYPE(RR_Msg,decodeMsgType,encodeMsgType,s_rrMsgs)
static const IEParam s_mmMessage[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "SkipIndicator", false, 4, false, s_type_Int),
MAKE_IE_PARAM(V, XmlRoot, 0, "Message", false, 8, false, s_type_MM_Msg),
s_ie_EndDef,
};
// reference ETSI TS 144 018 V11.5.0
static const IEParam s_rrMessage[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "SkipIndicator", false, 4, false, s_type_Int),
MAKE_IE_PARAM(V, XmlRoot, 0, "Message", false, 8, false, s_type_RR_Msg),
s_ie_EndDef,
};
static const IEParam s_ccMessage[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "TID", false, 4, false, s_type_TID),
MAKE_IE_PARAM(V, XmlRoot, 0, "Message", false, 8, false, s_type_CC_Msg),
s_ie_EndDef,
};
// reference: ETSI TS 124 301 V11.8.0,section 8.3
static const IEParam s_epsSmMessage[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "EPSBearerIdentity", false, 4, false, s_type_Undef),
MAKE_IE_PARAM(V, XmlElem, 0, "PTID", false, 8, false, s_type_Undef),
MAKE_IE_PARAM(V, XmlRoot, 0, "Message", false, 8, false, s_type_EPS_SM_Msg),
s_ie_EndDef,
};
static const IEParam s_epsMMMessagePDU = MAKE_IE_PARAM(V,XmlRoot,0,"Message",false,8,false,s_type_EPS_MM_Msg);
static const IEParam s_epsMmMessage[] = {
MAKE_IE_PARAM(V, XmlRoot, 0, "SecurityHeader", false, 4, false, s_type_SecurityHeader),
s_ie_EndDef,
};
// reference ETSI TS 124 080 V11.0.0
static const IEParam s_SsMessage[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "TID", false, 4, false, s_type_TID),
MAKE_IE_PARAM(V, XmlRoot, 0, "Message", false, 8, false, s_type_SS_Msg),
s_ie_EndDef,
};
// reference ETSI TS 124 011 V11.1.0
static const IEParam s_smsMessage[] = {
MAKE_IE_PARAM(V, XmlElem, 0, "TID", false, 4, false, s_type_TID),
MAKE_IE_PARAM(V, XmlRoot, 0, "Message", false, 8, false, s_type_SMS_Msg),
s_ie_EndDef,
};
// reference ETSI TS 124 007 V11.0.0, section 11.2.3.1.1 Protocol discriminator
static const RL3Message s_protoMsg[] = {
{GSML3Codec::GCC, "GCC", 0, 0},
{GSML3Codec::BCC, "BCC", 0, 0},
{GSML3Codec::EPS_SM, "EPS_SM", s_epsSmMessage, 0},
{GSML3Codec::CC, "CC", s_ccMessage, 0},
{GSML3Codec::GTTP, "GTTP", 0, 0},
{GSML3Codec::MM, "MM", s_mmMessage, 0},
{GSML3Codec::RRM, "RRM", s_rrMessage, 0},
{GSML3Codec::EPS_MM, "EPS_MM", s_epsMmMessage, 0},
{GSML3Codec::GPRS_MM, "GPRS_MM", 0, 0},
{GSML3Codec::SMS, "SMS", s_smsMessage, 0},
{GSML3Codec::GPRS_SM, "GPRS_SM", 0, 0},
{GSML3Codec::SS, "SS", s_SsMessage, 0},
{GSML3Codec::LCS, "LCS", 0, 0},
{GSML3Codec::Extension, "EXT", 0, 0},
{GSML3Codec::Test, "TEST", 0, 0},
{GSML3Codec::Unknown, "", 0, 0},
};
MAKE_IE_TYPE(PD,decodePD,encodePD,s_protoMsg)
static const IEParam s_rl3Message[] = {
MAKE_IE_PARAM(V, XmlRoot, 0, "PD", false, 4, true, s_type_PD),
s_ie_EndDef,
};
#undef MAKE_IE_TYPE
#undef MAKE_IE_PARAM
static unsigned int checkIntegrity(const GSML3Codec* codec, const String& mac, uint8_t seq, const uint8_t*& in,
unsigned int& len, const NamedList& params)
{
// TODO
return GSML3Codec::NoError;
}
static unsigned int addIntegrity(const GSML3Codec* codec, uint8_t seq, DataBlock& data, const NamedList& params)
{
// TODO - code just to add the octets now
uint32_t mac = 0;
data.insert(DataBlock(&mac,sizeof(mac)));
return GSML3Codec::NoError;
}
static unsigned int decipherNASPdu(const GSML3Codec* codec, const String& mac, uint8_t seq, const uint8_t*& in,
unsigned int& len, const NamedList& params)
{
// TODO
return GSML3Codec::NoError;
}
static unsigned int cipherNASPdu(const GSML3Codec* codec, uint8_t seq, DataBlock& data, const NamedList& params)
{
// TODO
return GSML3Codec::NoError;
}
static unsigned int decodeSecHeader(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param && out))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeSecHeader(param=%s(%p),in=%p,len=%u,out=%p [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
uint8_t secVal = getUINT8(in,len,param);
XmlElement* xml = new XmlElement(param->name,lookup(secVal,GSML3Codec::s_securityHeaders,String(secVal)));
out->addChildSafe(xml);
switch (secVal) {
case GSML3Codec::PlainNAS:
{
if (len < 1)
return GSML3Codec::MsgTooShort;
return decodeMsgType(codec,proto,&s_epsMMMessagePDU,in,len,out,params);
}
case GSML3Codec::IntegrityProtect:
case GSML3Codec::IntegrityProtectNewEPSCtxt:
case GSML3Codec::IntegrityProtectCiphered:
case GSML3Codec::IntegrityProtectCipheredNewEPSCtxt:
{
if (len < 5)
return GSML3Codec::MsgTooShort;
String mac;
mac.hexify((void*)in,4);
out->addChildSafe(new XmlElement("MAC",mac));
uint8_t seq = in[4];
out->addChildSafe(new XmlElement("SequenceNumber",String(seq)));
// skip over MAC
advanceBuffer(4,in,len);
if (unsigned int ok = checkIntegrity(codec,mac,seq,in,len,params))
return ok;
// skip over Sequence Number
advanceBuffer(1,in,len);
if (secVal == GSML3Codec::IntegrityProtectCiphered || secVal == GSML3Codec::IntegrityProtectCiphered)
decipherNASPdu(codec,mac,seq,in,len,params);
return decodeParams(codec,proto,in,len,out,s_rl3Message);
}
default:
if (secVal >= GSML3Codec::ServiceRequestHeader) {
//TODO
DDebug(codec->dbg(),DebugStub,"decodeSecHeader() for ServiceRequestHeader not implemented [%p]",codec->ptr());
}
break;
}
return GSML3Codec::NoError;
}
static unsigned int encodeSecHeader(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeSecHeader(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* child = in->findFirstChild(&param->name);
if (!child)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
uint8_t secVal = lookup(child->getText(),GSML3Codec::s_securityHeaders,0xff);
switch (secVal) {
case GSML3Codec::PlainNAS:
{
setUINT8(secVal,out,param);
return encodeMsgType(codec,proto,&s_epsMMMessagePDU,in,out,params);
}
case GSML3Codec::IntegrityProtect:
case GSML3Codec::IntegrityProtectNewEPSCtxt:
case GSML3Codec::IntegrityProtectCiphered:
case GSML3Codec::IntegrityProtectCipheredNewEPSCtxt:
{
setUINT8(secVal,out,param);
uint8_t seq = 0;
const String& seqParam = params[s_epsSequenceNumber];
if (seqParam)
seq = seqParam.toInteger();
else {
child = in->findFirstChild(&s_epsSequenceNumber);
if (!(child && child->getText())) {
Debug(codec->dbg(),DebugWarn,"Missing SequenceNumber param [%p]",codec->ptr());
return GSML3Codec::MissingMandatoryIE;
}
seq = child->getText().toInteger();
}
child = in->findNextChild(child);
DataBlock d;
if (unsigned int stat = encodeParams(codec,proto,child,d,s_rl3Message,params))
return stat;
if (secVal == GSML3Codec::IntegrityProtectCiphered || secVal == GSML3Codec::IntegrityProtectCiphered)
if (unsigned int stat = cipherNASPdu(codec,seq,d,params))
return stat;
d.insert(DataBlock(&seq,1));
if (unsigned int stat = addIntegrity(codec,seq,d,params))
return stat;
out.append(d);
return GSML3Codec::NoError;
}
default:
if (secVal >= GSML3Codec::ServiceRequestHeader) {
//TODO
DDebug(codec->dbg(),DebugStub,"encodeSecHeader() for ServiceRequestHeader not implemented [%p]",codec->ptr());
}
break;
}
return GSML3Codec::NoError;
}
static unsigned int decodeRL3Msg(const GSML3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeRL3Msg(param=%s(%p),in=%p,len=%u,out=%p [%p]",param->name.c_str(),param,
in,len,out,codec->ptr());
if (len < 2)
return GSML3Codec::MsgTooShort;
XmlElement* xml = 0;
if (param->name)
xml = new XmlElement(param->name);
unsigned int stat = decodeParams(codec,proto,in,len,xml,s_rl3Message);
addXMLElement(out,xml);
return stat;
}
static unsigned int encodeRL3Msg(const GSML3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,
DataBlock& out, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeRL3Msg(param=%s(%p),xml=%s(%p) [%p]",param->name.c_str(),param,
in->tag(),in,codec->ptr());
XmlElement* child = in->findFirstChild(&param->name);
if (!(child && (child = child->findFirstChild())))
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
return encodeParams(codec,proto,child,out,s_rl3Message,params);
}
static unsigned int skipParam(const GSML3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len,const IEParam* param)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"skipParam() param=%s(%p) of type %s [%p]",param->name.c_str(),param,
lookup(param->type,GSML3Codec::s_typeDict,""),codec->ptr());
switch (param->type) {
case GSML3Codec::V:
case GSML3Codec::T:
if (param->length == 4) {
if (!param->lowerBits)
advanceBuffer(1,in,len);
break;
}
// intentional fall through
case GSML3Codec::TV:
if (len * 8 < param->length)
return GSML3Codec::MsgTooShort;
advanceBuffer(param->length / 8,in,len);
break;
case GSML3Codec::TLV:
if (len < 2)
return GSML3Codec::MsgTooShort;
advanceBuffer(1,in,len);
// intentional fall through
case GSML3Codec::LV:
{
if (len < 1)
return GSML3Codec::MsgTooShort;
uint8_t l = *in;
advanceBuffer(1,in,len);
if (len < l)
return GSML3Codec::MsgTooShort;
advanceBuffer(l,in,len);
break;
}
case GSML3Codec::TLVE:
if (len < 3)
return GSML3Codec::MsgTooShort;
advanceBuffer(1,in,len);
// intentional fall through
case GSML3Codec::LVE:
{
if (len < 2)
return GSML3Codec::MsgTooShort;
uint16_t l = getUINT16(in,len,true);
if (len < l)
return GSML3Codec::MsgTooShort;
advanceBuffer(l,in,len);
break;
}
case GSML3Codec::NoType:
break;
}
return GSML3Codec::NoError;
}
static unsigned int dumpUnknownIE(const GSML3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, XmlElement*& out)
{
if (!codec)
return GSML3Codec::ParserErr;
if (!(in && len))
return GSML3Codec::NoError;
DDebug(codec->dbg(),DebugAll,"dumpUnknownIE(in=%p,len=%u) in protocol=%s [%p]",in,len,
lookup(proto,GSML3Codec::s_protoDict,"Unknown"),codec->ptr());
uint8_t iei = *in;
// bit 8 on 1 indicates one octet length IE of type V/T/TV
unsigned int dumpOctets = len;
if (iei & 0x80 || len < 2)
dumpOctets = len;
else {
// it's either TLV or TLVE
// in EPS MM and EPS MM, if bits 7 to 4 are set on 1 => means IEI is TLVE
if ((proto == GSML3Codec::EPS_MM || proto == GSML3Codec::EPS_SM) && ((iei & 0x78) == 0x78)) {
if (len < 3)
dumpOctets = len;
else {
uint16_t l = getUINT16(in + 1u,len) + 3u;
dumpOctets = (len < l ? len : l);
}
}
else
dumpOctets = (len < (in[1] + 2u) ? len : in[1] + 2u);
}
if (dumpOctets) {
XmlElement* xml = new XmlElement("ie");
addXMLElement(out,xml);
String dumpStr;
dumpStr.hexify((void*)in,dumpOctets);
xml->setText(dumpStr);
xml->setAttribute(s_encAttr,"hex");
advanceBuffer(dumpOctets,in,len);
}
return GSML3Codec::NoError;
}
static unsigned int encodeUnknownIE(const GSML3Codec* codec, uint8_t proto, XmlElement* in, DataBlock& out)
{
if (!codec)
return GSML3Codec::ParserErr;
if (!in)
return GSML3Codec::NoError;
DDebug(codec->dbg(),DebugAll,"encodeUnknownIE(in=%p) in protocol=%s [%p]",in,
lookup(proto,GSML3Codec::s_protoDict,"Unknown"),codec->ptr());
DataBlock d;
if (!d.unHexify(in->getText())) {
Debug(codec->dbg(),DebugMild,"Failed to unhexify unknown param=%s(%p) [%p]",in->tag(),in,codec->ptr());
return GSML3Codec::NoError;
}
out.append(d);
return GSML3Codec::NoError;
}
static unsigned int dumpParamValue(const GSML3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, const IEParam* param,
XmlElement*& out)
{
if (!(codec && in && len))
return GSML3Codec::ParserErr;
DDebug(codec->dbg(),DebugAll,"dumpParamValue(in=%p,len=%u) for %sparam%s%s(%p) [%p]",in,len,
(!param ? "unknown " : ""),(param ? "=" : ""), (param ? param->name.c_str() : ""),param,codec->ptr());
if (param) {
String dumpStr;
uint8_t skipOctets = 0;
switch (param->type) {
case GSML3Codec::T:
// there's no value to dump
break;
case GSML3Codec::V:
{
if (param->length == 4) {
uint8_t val = 0;
if (!param->lowerBits) {
val |= *in & 0xf0;
advanceBuffer(1,in,len);
}
else
val |= *in & 0x0f;
dumpStr.hexify(&val,1);
}
break;
}
case GSML3Codec::TV:
{
if (param->length == 8) {
uint8_t val = *in & 0x0f;
advanceBuffer(1,in,len);
dumpStr.hexify(&val,1);
}
else
skipOctets = 1;
break;
}
case GSML3Codec::TLV:
skipOctets = 2;
break;
case GSML3Codec::LV:
skipOctets = 1;
break;
case GSML3Codec::TLVE:
skipOctets = 3;
break;
case GSML3Codec::LVE:
skipOctets = 2;
break;
case GSML3Codec::NoType:
break;
}
const uint8_t* buff = in;
unsigned int lbuff = len;
if (int status = skipParam(codec,proto,in,len,param))
return status;
if (len <= lbuff)
dumpStr.hexify((void*)(buff + skipOctets), lbuff - len - skipOctets);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
if (dumpStr) {
xml->setText(dumpStr);
xml->setAttribute(s_encAttr,"hex");
}
}
else
return dumpUnknownIE(codec,proto,in,len,out);
return GSML3Codec::NoError;
}
static unsigned int encodeHexParam(const GSML3Codec* codec, uint8_t proto, XmlElement* in, DataBlock& out,
const IEParam* param)
{
if (!(codec && in))
return GSML3Codec::ParserErr;
DDebug(codec->dbg(),DebugAll,"encodeHexParam() xml=%s(%p) for %sparam%s%s(%p) [%p]",in->tag(),in,
(!param ? "unknown " : ""),(param ? "=" : ""), (param ? param->name.c_str() : ""),param,codec->ptr());
if (param) {
DataBlock d;
if (!d.unHexify(in->getText())) {
Debug(codec->dbg(),DebugMild,"Failed to unhexify param=%s(%p) [%p]",in->tag(),in,codec->ptr());
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
}
// mask for encoding, 1 is for T, 2 is for L, 4 is for LE
uint8_t mask = 0;
uint8_t iei = param->iei;
switch (param->type) {
case GSML3Codec::T:
out.append(&iei,1);
return GSML3Codec::NoError;
case GSML3Codec::V:
{
if (!d.length())
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
if (param->length == 4) {
uint8_t val = d[0];
if (!param->lowerBits)
val = val >> 4;
setUINT8(val,out,param);
d.clear();
}
break;
}
case GSML3Codec::TV:
{
if (!d.length())
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
if (param->length == 8) {
uint8_t val = d[0] & 0x0f;
val |= param->iei;
out.append(&val,1);
}
else
mask |= 1;
break;
}
case GSML3Codec::TLV:
mask |= 1;
// intentional fall through
case GSML3Codec::LV:
if (d.length() > 0xff)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
mask |= 2;
break;
case GSML3Codec::TLVE:
mask |= 1;
// intentional fall through
case GSML3Codec::LVE:
if (d.length() > 0xffff)
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
mask |= 4;
break;
case GSML3Codec::NoType:
return GSML3Codec::NoError;
}
if (mask & 1) // T
out.append(&iei,1);
if (mask & 2) { // L
iei = d.length();
out.append(&iei,1);
}
else if (mask & 4) { // LE
uint16_t len = d.length();
uint8_t l[2];
setUINT16(len,l,len);
out.append(l,2);
}
if (d.length())
out.append(d);
}
else
return encodeUnknownIE(codec,proto,in,out);
return GSML3Codec::NoError;
}
static unsigned int decodeV(const GSML3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, XmlElement*& out,
const IEParam* param, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
if (len * 8 < param->length)
return GSML3Codec::MsgTooShort;
DDebug(codec->dbg(),DebugAll,"decodeV(in=%p,len=%u,out=%p,param=%s[%p]) [%p]",in,len,out,param->name.c_str(),param,codec->ptr());
switch (param->xmlType) {
case GSML3Codec::Skip:
return skipParam(codec,proto,in,len,param);
case GSML3Codec::XmlRoot:
if (param->ieType.decoder)
return param->ieType.decoder(codec,proto,param,in,len,out,params);
case GSML3Codec::XmlElem:
if (!(param->ieType.decoder || (param->name && param->length <= 8)))
return dumpParamValue(codec,proto,in,len,param,out);
if (param->ieType.decoder) {
const uint8_t* buf = in;
unsigned int l = (param->length <= 8 ? 1 : param->length / 8);
if (!(param->length < 8 && param->lowerBits))
advanceBuffer(l,in,len);
return param->ieType.decoder(codec,proto,param,buf,l,out,params);
}
// decode an 1 byte value from a dictionary
if (param->name) {
if (param->length > 8) {
DDebug(codec->dbg(),DebugMild,"decodeV() - decoding for values longer than 1 byte not supported, dumping param=%s(%p) [%p]",
param->name.c_str(),param,codec->ptr());
return dumpParamValue(codec,proto,in,len,param,out);
}
uint8_t val = getUINT8(in,len,param);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
const TokenDict* dict = static_cast<const TokenDict*>(param->ieType.data);
const char* valStr = lookup(val,dict,0);
if (!valStr) {
String defValStr;
defValStr.hexify(&val,1);
xml->setText(defValStr);
xml->setAttribute(s_encAttr,"hex");
}
else
xml->setText(valStr);
return GSML3Codec::NoError;
}
default:
return GSML3Codec::ParserErr;
}
return GSML3Codec::NoError;
}
static unsigned int encodeV(const GSML3Codec* codec, uint8_t proto, XmlElement* in, DataBlock& out,
const IEParam* param, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeV(in=%s(%p),out=%p,param=%s[%p]) [%p]",in->tag(),in,&out,
param->name.c_str(),param,codec->ptr());
switch (param->xmlType) {
case GSML3Codec::Skip:
{
if (param->length > 8) {
DDebug(codec->dbg(),DebugMild,"encodeV() - encoding skipped param=%s(%p) longer than 1 byte not implemented[%p]",
param->name.c_str(),param,codec->ptr());
return GSML3Codec::ParserErr;
}
setUINT8(param->iei,out,param);
return GSML3Codec::NoError;
}
case GSML3Codec::XmlElem:
case GSML3Codec::XmlRoot:
{
if (param->ieType.encoder)
return param->ieType.encoder(codec,proto,param,in,out,params);
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
if (!(param->ieType.encoder || (param->name && param->length <= 8)))
return encodeHexParam(codec,proto,xml,out,param);
// decode an 1 byte value from a dictionary
if (param->name) {
const TokenDict* dict = static_cast<const TokenDict*>(param->ieType.data);
uint8_t val = 0;
if (!dict)
val = xml->getText().toInteger(0,16);
else
val = xml->getText().toInteger(dict,0,16);
setUINT8(val,out,param);
return GSML3Codec::NoError;
}
break;
}
default:
return GSML3Codec::ParserErr;
}
return GSML3Codec::NoError;
}
static unsigned int decodeLV_LVE(const GSML3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, XmlElement*& out,
const IEParam* param, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeLV_LVE(in=%p,len=%u,out=%p,param=%s[%p]) [%p]",in,len,out,param->name.c_str(),param,codec->ptr());
switch (param->xmlType) {
case GSML3Codec::Skip:
return skipParam(codec,proto,in,len,param);
case GSML3Codec::XmlElem:
case GSML3Codec::XmlRoot:
{
if (!param->ieType.decoder)
return dumpParamValue(codec,proto,in,len,param,out);
bool ext = (param->type == GSML3Codec::LVE);
if (len < (ext ? 2 : 1))
return GSML3Codec::MsgTooShort;
unsigned int l = in[0];
unsigned int advBytes = 1;
if (ext) {
l = getUINT16(in,len);
advBytes = 2;
}
if (l > len - advBytes)
return GSML3Codec::MsgTooShort;
if (param->length && ((l + advBytes)* 8 > param->length))
return (param->isOptional ? GSML3Codec::IncorrectOptionalIE : GSML3Codec::IncorrectMandatoryIE);
if (param->ieType.decoder) {
const uint8_t* buf = in + advBytes;
advanceBuffer(l + advBytes,in,len);
return param->ieType.decoder(codec,proto,param,buf,l,out,params);
}
break;
}
default:
return GSML3Codec::ParserErr;
}
return GSML3Codec::NoError;
}
static unsigned int encodeLV_LVE(const GSML3Codec* codec, uint8_t proto, XmlElement* in, DataBlock& out,
const IEParam* param, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeLV_LVE(in=%s(%p),out=%p,param=%s[%p]) [%p]",in->tag(),in,&out,
param->name.c_str(),param,codec->ptr());
switch (param->xmlType) {
case GSML3Codec::Skip:
{
// TODO
return GSML3Codec::NoError;
}
case GSML3Codec::XmlElem:
case GSML3Codec::XmlRoot:
{
DataBlock d;
if (param->ieType.encoder) {
if (unsigned int status = param->ieType.encoder(codec,proto,param,in,d,params))
return status;
}
else {
XmlElement* xml = in->findFirstChild(&param->name);
if (!(xml && d.unHexify(xml->getText())))
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
}
if (param->isOptional && !d.length())
return GSML3Codec::NoError;
if (param->type == GSML3Codec::LVE) {
uint16_t len = d.length();
uint8_t l[2];
setUINT16(len,l,2);
out.append(l,2);
}
else {
uint8_t len = d.length();
out.append(&len,1);
}
out.append(d);
break;
}
default:
return GSML3Codec::ParserErr;
}
return GSML3Codec::NoError;
}
static unsigned int decodeT(const GSML3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, XmlElement*& out,
const IEParam* param, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeT(in=%p,len=%u,out=%p,param=%s[%p]) [%p]",in,len,out,param->name.c_str(),param,codec->ptr());
if (param->iei != *in)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
switch (param->xmlType) {
case GSML3Codec::Skip:
return skipParam(codec,proto,in,len,param);
case GSML3Codec::XmlElem:
case GSML3Codec::XmlRoot:
{
advanceBuffer(1,in,len);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
break;
}
default:
return GSML3Codec::ParserErr;
}
return GSML3Codec::NoError;
}
static unsigned int encodeT(const GSML3Codec* codec, uint8_t proto, XmlElement* in, DataBlock& out,
const IEParam* param, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeT(in=%s(%p),out=%p,param=%s[%p]) [%p]",in->tag(),in,&out,
param->name.c_str(),param,codec->ptr());
switch (param->xmlType) {
case GSML3Codec::Skip:
{
if (!param->isOptional)
setUINT8(param->iei,out,param);
return GSML3Codec::NoError;
}
case GSML3Codec::XmlElem:
case GSML3Codec::XmlRoot:
{
XmlElement* xml = in->findFirstChild(&param->name);
if (xml)
setUINT8(param->iei,out,param);
else if (!param->isOptional)
return GSML3Codec::MissingMandatoryIE;
break;
}
default:
return GSML3Codec::ParserErr;
}
return GSML3Codec::NoError;
}
static unsigned int decodeTV(const GSML3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, XmlElement*& out,
const IEParam* param, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeTV(in=%p,len=%u,out=%p,param=%s[%p]) [%p]",in,len,out,param->name.c_str(),param,codec->ptr());
if (param->type == GSML3Codec::TV && param->length == 8) {
if ((*in & 0xf0) != param->iei)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
}
else if (param->iei != *in)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
if (param->length && (len * 8 < param->length))
return (param->isOptional ? GSML3Codec::IncorrectOptionalIE : GSML3Codec::IncorrectMandatoryIE);
switch (param->xmlType) {
case GSML3Codec::Skip:
return skipParam(codec,proto,in,len,param);
case GSML3Codec::XmlElem:
case GSML3Codec::XmlRoot:
{
if (!(param->ieType.decoder || (param->name && param->length <= 8)))
return dumpParamValue(codec,proto,in,len,param,out);
if (param->ieType.decoder) {
uint8_t skip = (param->length == 8 ? 0u : 1u);
const uint8_t* buf = in + skip;
unsigned int l = param->length / 8 - skip;
advanceBuffer(l + skip,in,len);
return param->ieType.decoder(codec,proto,param,buf,l,out,params);
}
// decode a max 1 byte value from a dictionary
if (param->name) {
if (param->length > 8) {
DDebug(codec->dbg(),DebugMild,"decodeTV() - decoding for TV longer than 1 byte not supported, dumping param=%s(%p) [%p]",
param->name.c_str(),param,codec->ptr());
return dumpParamValue(codec,proto,in,len,param,out);
}
uint8_t val = getUINT8(in,len,param);
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
const TokenDict* dict = static_cast<const TokenDict*>(param->ieType.data);
const char* valStr = lookup(val,dict,0);
if (!valStr) {
String defValStr;
defValStr.hexify(&val,1);
xml->setText(defValStr);
xml->setAttribute(s_encAttr,"hex");
}
else
xml->setText(valStr);
return GSML3Codec::NoError;
}
break;
}
default:
return GSML3Codec::ParserErr;
}
return GSML3Codec::NoError;
}
static unsigned int encodeTV(const GSML3Codec* codec, uint8_t proto, XmlElement* in, DataBlock& out,
const IEParam* param, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeTV(in=%s(%p),out=%p,param=%s[%p]) [%p]",in->tag(),in,&out,
param->name.c_str(),param,codec->ptr());
switch (param->xmlType) {
case GSML3Codec::Skip:
{
if (param->length > 8) {
DDebug(codec->dbg(),DebugMild,"encodeTV() - encoding skipped param=%s(%p) longer than 1 byte not implemented[%p]",
param->name.c_str(),param,codec->ptr());
return GSML3Codec::ParserErr;
}
setUINT8(param->iei,out,param);
return GSML3Codec::NoError;
}
case GSML3Codec::XmlElem:
case GSML3Codec::XmlRoot:
{
if (param->ieType.encoder) {
DataBlock d;
if (unsigned int status = param->ieType.encoder(codec,proto,param,in,d,params))
return status;
if (param->isOptional && !d.length())
return GSML3Codec::NoError;
uint8_t iei = param->iei;
if (param->length == 8) {
iei |= d[0];
out.append(&iei,1);
}
else {
out.append(&iei,1);
out.append(d);
}
}
else {
XmlElement* xml = in->findFirstChild(&param->name);
if (!xml)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
if (!(param->name && param->length <= 8))
return encodeHexParam(codec,proto,xml,out,param);
const TokenDict* dict = static_cast<const TokenDict*>(param->ieType.data);
uint8_t val = param->iei;
if (!dict)
val |= (xml->getText().toInteger(0,16) & 0x0f);
else
val |= xml->getText().toInteger(dict,0,16);
out.append(&val,1);
}
return GSML3Codec::NoError;
}
default:
return GSML3Codec::ParserErr;
}
return GSML3Codec::NoError;
}
static unsigned int decodeTLV_TLVE(const GSML3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, XmlElement*& out,
const IEParam* param, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"decodeTLV_TLVE(in=%p,len=%u,out=%p,param=%s[%p]) [%p]",in,len,out,param->name.c_str(),param,codec->ptr());
if (param->iei != *in)
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
bool ext = (param->type == GSML3Codec::TLVE);
if (len < (ext ? 3 : 2))
return GSML3Codec::MsgTooShort;
switch (param->xmlType) {
case GSML3Codec::Skip:
return skipParam(codec,proto,in,len,param);
case GSML3Codec::XmlElem:
case GSML3Codec::XmlRoot:
{
if (!param->ieType.decoder)
return dumpParamValue(codec,proto,in,len,param,out);
unsigned int l = in[1];
unsigned int advBytes = 2;
if (param->type == GSML3Codec::LVE) {
l = getUINT16(in + 1u,len - 1);
advBytes = 3;
}
if (l > len - advBytes)
return GSML3Codec::MsgTooShort;
if (param->length && ((l + advBytes) * 8 > param->length))
return CONDITIONAL_ERROR(param,IncorrectOptionalIE,IncorrectMandatoryIE);
if (param->ieType.decoder) {
const uint8_t* buf = in + advBytes;
advanceBuffer(l + advBytes,in,len);
return param->ieType.decoder(codec,proto,param,buf,l,out,params);
}
break;
}
default:
return GSML3Codec::ParserErr;
}
return GSML3Codec::NoError;
}
static unsigned int encodeTLV_TLVE(const GSML3Codec* codec, uint8_t proto, XmlElement* in, DataBlock& out,
const IEParam* param, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
DDebug(codec->dbg(),DebugAll,"encodeTLV_TLVE(in=%s(%p),out=%p,param=%s[%p]) [%p]",in->tag(),in,&out,
param->name.c_str(),param,codec->ptr());
switch (param->xmlType) {
case GSML3Codec::Skip:
{
// TODO
return GSML3Codec::NoError;
}
case GSML3Codec::XmlElem:
case GSML3Codec::XmlRoot:
{
DataBlock d;
if (param->ieType.encoder) {
if (unsigned int status = param->ieType.encoder(codec,proto,param,in,d,params))
return status;
}
else {
XmlElement* xml = in->findFirstChild(&param->name);
if (!(xml && d.unHexify(xml->getText())))
return CONDITIONAL_ERROR(param,NoError,MissingMandatoryIE);
}
if (param->isOptional && !d.length())
return GSML3Codec::NoError;
uint8_t iei = param->iei;
out.append(&iei,1);
if (param->type == GSML3Codec::TLVE) {
uint16_t len = d.length();
uint8_t l[2];
setUINT16(len,l,2);
out.append(l,2);
}
else {
uint8_t len = d.length();
out.append(&len,1);
}
out.append(d);
break;
}
default:
return GSML3Codec::ParserErr;
}
return GSML3Codec::NoError;
}
static unsigned int decodeParams(const GSML3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, XmlElement*& out,
const IEParam* param, const NamedList& params)
{
if (!(codec && in && len && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
#ifdef DEBUG
Debugger d(DebugAll,"decodeParams()"," in=%p,len=%u,out=%p,param=%s(%p)",in,len,out,
param->name.c_str(),param,codec->ptr());
#endif
while (param && param->type != GSML3Codec::NoType) {
int status = GSML3Codec::NoError;
switch (param->type) {
case GSML3Codec::V:
status = decodeV(codec,proto,in,len,out,param,params);
break;
case GSML3Codec::T:
status = decodeT(codec,proto,in,len,out,param,params);
break;
case GSML3Codec::TV:
status = decodeTV(codec,proto,in,len,out,param,params);
break;
case GSML3Codec::LV:
case GSML3Codec::LVE:
status = decodeLV_LVE(codec,proto,in,len,out,param,params);
break;
case GSML3Codec::TLV:
case GSML3Codec::TLVE:
status = decodeTLV_TLVE(codec,proto,in,len,out,param,params);
break;
case GSML3Codec::NoType:
break;
}
if (status)
Debug(codec->dbg(),DebugWarn,"Decoding parameter %s failed with status=%s [%p]",param->name.c_str(),
lookup(status,GSML3Codec::s_errorsDict,String(status)),codec->ptr());
else
DDebug(codec->dbg(),DebugAll,"Decoding parameter %s finished with status=%s [%p]",param->name.c_str(),
lookup(status,GSML3Codec::s_errorsDict,String(status)),codec->ptr());
if (status && !param->isOptional)
return status;
param++;
}
if (len && out)
dumpData(in,len,out);
return GSML3Codec::NoError;
};
static unsigned int encodeParams(const GSML3Codec* codec, uint8_t proto, XmlElement* in, DataBlock& out,
const IEParam* param, const NamedList& params)
{
if (!(codec && in && param))
return CONDITIONAL_ERROR(param,NoError,ParserErr);
#ifdef DEBUG
Debugger d(DebugAll,"encodeParams()"," xml=%s(%p),out=%p,param=%s(%p)",in->tag(),in, &out,
param->name.c_str(),param,codec->ptr());
#endif
unsigned int ok = GSML3Codec::NoError;
while (param && param->type != GSML3Codec::NoType) {
int status = GSML3Codec::NoError;
switch (param->type) {
case GSML3Codec::V:
status = encodeV(codec,proto,in,out,param,params);
break;
case GSML3Codec::T:
status = encodeT(codec,proto,in,out,param,params);
break;
case GSML3Codec::TV:
status = encodeTV(codec,proto,in,out,param,params);
break;
case GSML3Codec::LV:
case GSML3Codec::LVE:
status = encodeLV_LVE(codec,proto,in,out,param,params);
break;
case GSML3Codec::TLV:
case GSML3Codec::TLVE:
status = encodeTLV_TLVE(codec,proto,in,out,param,params);
break;
case GSML3Codec::NoType:
break;
}
XDebug(codec->dbg(),DebugAll,"Encoding parameter %s finished with status=%s [%p]",param->name.c_str(),
lookup(status,GSML3Codec::s_errorsDict,String(status)),codec->ptr());
if (status) {
Debug(codec->dbg(),param->isOptional ? DebugMild :DebugWarn,
"Encoding of %s parameter %s finished with status=%s [%p]",(param->isOptional ? "optional" : "mandatory"),
param->name.c_str(),lookup(status,GSML3Codec::s_errorsDict,String(status)),codec->ptr());
if (!param->isOptional)
ok = status;
}
param++;
}
return ok;
};
#undef GET_DIGIT
#undef CONDITIONAL_ERROR
const TokenDict GSML3Codec::s_typeDict[] = {
{"T", GSML3Codec::T},
{"V", GSML3Codec::V},
{"TV", GSML3Codec::TV},
{"LV", GSML3Codec::LV},
{"TLV", GSML3Codec::TLV},
{"LVE", GSML3Codec::LVE},
{"TLVE", GSML3Codec::TLVE},
{0, 0},
};
const TokenDict GSML3Codec::s_protoDict[] = {
{"GCC", GSML3Codec::GCC},
{"BCC", GSML3Codec::BCC},
{"EPS_SM", GSML3Codec::EPS_SM},
{"CC", GSML3Codec::CC},
{"GTTP", GSML3Codec::GTTP},
{"MM", GSML3Codec::MM},
{"RRM", GSML3Codec::RRM},
{"EPS_MM", GSML3Codec::EPS_MM},
{"GPRS_MM", GSML3Codec::GPRS_MM},
{"SMS", GSML3Codec::SMS},
{"GPRS_SM", GSML3Codec::GPRS_SM},
{"SS", GSML3Codec::SS},
{"LCS", GSML3Codec::LCS},
{"Extension", GSML3Codec::Extension},
{"Test", GSML3Codec::Test},
{"Unknown", GSML3Codec::Unknown},
{0, 0},
};
const TokenDict GSML3Codec::s_securityHeaders[] = {
{"plain-NAS-message", GSML3Codec::PlainNAS},
{"integrity-protected", GSML3Codec::IntegrityProtect},
{"integrity-protected-and-ciphered", GSML3Codec::IntegrityProtectCiphered},
{"integrity-protected-with-new-EPS-security-context", GSML3Codec::IntegrityProtectNewEPSCtxt},
{"integrity-protected-and-ciphered-with-new-EPS-security-context", GSML3Codec::IntegrityProtectCipheredNewEPSCtxt},
{"security-header-for-the-SERVICE-REQUEST-message", GSML3Codec::ServiceRequestHeader},
{0, 0},
};
const TokenDict GSML3Codec::s_errorsDict[] = {
{"NoError", GSML3Codec::NoError},
{"MsgTooShort", GSML3Codec::MsgTooShort},
{"UnknownProto", GSML3Codec::UnknownProto},
{"ParserErr", GSML3Codec::ParserErr},
{"MissingParam", GSML3Codec::MissingParam},
{"IncorrectOptionalIE", GSML3Codec::IncorrectOptionalIE},
{"IncorrectMandatoryIE", GSML3Codec::IncorrectMandatoryIE},
{"MissingMandatoryIE", GSML3Codec::MissingMandatoryIE},
{"UnknownMsgType", GSML3Codec::UnknownMsgType},
{0, 0},
};
GSML3Codec::GSML3Codec(DebugEnabler* dbg)
: m_flags(0),
m_dbg(0),
m_ptr(0),
m_printDbg(false)
{
DDebug(DebugAll,"Created GSML3Codec [%p]",this);
setCodecDebug(dbg);
}
unsigned int GSML3Codec::decode(const uint8_t* in, unsigned int len, XmlElement*& out, const NamedList& params)
{
if (!in || len < 2)
return MsgTooShort;
const uint8_t* buff = in;
unsigned int l = len;
unsigned int stat = decodeParams(this,GSML3Codec::Unknown,buff,l,out,s_rl3Message,params);
printDbg(DebugInfo,in,len,out);
return stat;
}
unsigned int GSML3Codec::encode(const XmlElement* in, DataBlock& out, const NamedList& params)
{
if (!in)
return NoError;
unsigned int stat = encodeParams(this,GSML3Codec::Unknown,(XmlElement*)in,out,s_rl3Message,params);
printDbg(DebugInfo,(const uint8_t*)out.data(),out.length(),(XmlElement*)in,true);
return stat;
}
unsigned int GSML3Codec::decode(XmlElement* xml, const NamedList& params)
{
const String& pduMark = params[s_pduCodec];
if (!(xml && pduMark))
return MissingParam;
return decodeXml(xml,params,pduMark);
}
unsigned int GSML3Codec::encode(XmlElement* xml, const NamedList& params)
{
const String& pduMark = params[s_pduCodec];
if (!(xml && pduMark))
return MissingParam;
return encodeXml(xml,params,pduMark);
}
// These tables contain embedded UTF-8 characters
static const char* const s_gsm7base[128] = {
"@", "£", "$", "¥", "è", "é", "ù", "ì", "ò", "Ç", "\n", "Ø", "ø", "\r", "Å", "å",
"Δ", "_", "Φ", "Γ", "Λ", "Ω", "Π", "Ψ", "Σ", "Θ", "Ξ", "", "Æ", "æ", "ß", "É",
" ", "!", "\"", "#", "¤", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?",
"¡", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
"P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "Ä", "Ö", "Ñ", "Ü", "§",
"¿", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "ä", "ö", "ñ", "ü", "à"
};
static const char* const s_gsm7esc[128] = {
"", "", "", "", "", "", "", "", "", "", "\f", "", "", "", "", "",
"", "", "", "", "^", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "{", "}", "", "", "", "", "", "\\",
"", "", "", "", "", "", "", "", "", "", "", "", "[", "~", "]", "",
"|", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
};
// Decode GSM 7bit buffer
void GSML3Codec::decodeGSM7Bit(unsigned char* buf, unsigned int len, String& text)
{
if (!(buf && len))
return;
DataBlock out;
unpackGSM7Bit(buf,len,out);
uint8_t* b = (uint8_t*)out.data();
bool esc = false;
for (unsigned int i = 0; i < out.length(); b++, i++) {
if (esc) {
text << s_gsm7esc[*b];
esc = false;
}
else if (*b != 0x1b)
text << s_gsm7base[*b];
else
esc = true;
}
}
// Encode GSM 7bit buffer
void GSML3Codec::encodeGSM7Bit(const String& text, DataBlock& buf)
{
static uint8_t escape = 0x1b;
if (!text)
return;
DataBlock gsm;
String tmp = text;
while (tmp) {
bool notFound = true;
for (uint8_t i = 0; i < 128; i++) {
if (tmp.startSkip(s_gsm7base[i],false)) {
gsm.append(&i,sizeof(i));
notFound = false;
break;
}
}
if (notFound) {
for (uint8_t i = 0; i < 128; i++) {
if (tmp.startSkip(s_gsm7esc[i],false)) {
gsm.append((void*)&escape,sizeof(escape));
gsm.append(&i,sizeof(i));
notFound = false;
break;
}
}
if (notFound) {
UChar c;
tmp >> c;
}
}
}
packGSM7Bit(gsm.data(0),gsm.length(),buf);
}
unsigned int GSML3Codec::decodeXml(XmlElement* xml, const NamedList& params, const String& pduTag)
{
#ifdef DEBUG
Debugger d(DebugAll,"decodeXml()"," xml=%s (%p) pduTag=%s",xml ? xml->tag() : "",xml,pduTag.c_str());
#endif
unsigned int status = NoError;
if (xml->getTag() == pduTag) {
const String& txt = xml->getText();
if (txt && xml->hasAttribute(s_encAttr,YSTRING("hex"))) {
DataBlock d;
if (!d.unHexify(txt)) {
Debug(dbg(),DebugInfo,"Invalid hexified payload in XmlElement '%s'(%p) [%p]",xml->tag(),xml,ptr());
return ParserErr;
}
return decode((const uint8_t*)d.data(),d.length(),xml,params);
}
}
XmlElement* child = xml->findFirstChild();
while (child) {
unsigned int ok = decodeXml(child,params,pduTag);
if (ok != NoError)
status = ok;
child = xml->findNextChild(child);
}
return status;
}
unsigned int GSML3Codec::encodeXml(XmlElement* xml, const NamedList& params, const String& pduTag)
{
#ifdef DEBUG
Debugger d(DebugAll,"encodeXml()"," xml=%s (%p) pduTag=%s",xml ? xml->tag() : "",xml,pduTag.c_str());
#endif
unsigned int status = NoError;
if (xml->getTag() == pduTag) {
if (xml->hasAttribute(s_encAttr,YSTRING("xml"))) {
if (!(xml = xml->findFirstChild())) {
Debug(dbg(),DebugInfo,"No XML to encode in XmlElement '%s'(%p) [%p]",xml->tag(),xml,ptr());
return ParserErr;
}
DataBlock d;
unsigned int status = encode(xml,d,params);
String s;
s.hexify(d.data(),d.length());
if (!status) {
xml->clearChildren();
xml->setAttribute(s_encAttr,YSTRING("hex"));
}
xml->setText(s);
return status;
}
}
XmlElement* child = xml->findFirstChild();
while (child) {
unsigned int ok = encodeXml(child,params,pduTag);
if (ok != NoError)
status = ok;
child = xml->findNextChild(child);
}
return status;
}
void GSML3Codec::setCodecDebug(DebugEnabler* enabler, void* ptr)
{
m_dbg = enabler ? enabler : m_dbg;
m_ptr = ptr ? ptr : (void*)this;
}
void GSML3Codec::printDbg(int dbgLevel, const uint8_t* in, unsigned int len, XmlElement* xml, bool encode)
{
if (!m_printDbg)
return;
String s;
s.hexify((void*)in,len,' ');
String tmp;
if (xml)
xml->toString(tmp,true,"\r\n"," ");
Debug(this->dbg(),dbgLevel,"%s:\r\n---------------\r\n%s='%s'\r\n---------------\r\nto:\r\n"
"---------------\r\n%s='%s'\r\n---------------",
(encode ? "Encoded" : "Decoded"),(encode ? "xml" : "payload"),(encode ? tmp.c_str() : s.c_str()),
(encode ? "payload" : "xml"), (encode ? s.c_str() : tmp.c_str()));
}
/* vi: set ts=8 sw=4 sts=4 noet enc=utf-8: */