Added GSM library.

Work in progress on adding Radio Layer 3 message decoders.



git-svn-id: http://voip.null.ro/svn/yate@5692 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
oana 2013-12-13 11:27:09 +00:00
parent 7a9887cd70
commit 74aca45854
5 changed files with 723 additions and 1 deletions

View File

@ -24,7 +24,8 @@ SLIBS:= $(YLIB) libyate.so \
libyatescript.so.@PACKAGE_VERSION@ libyatescript.so \
libyatesig.so.@PACKAGE_VERSION@ libyatesig.so \
libyatemgcp.so.@PACKAGE_VERSION@ libyatemgcp.so \
libyatejabber.so.@PACKAGE_VERSION@ libyatejabber.so
libyatejabber.so.@PACKAGE_VERSION@ libyatejabber.so \
libyategsm.so.@PACKAGE_VERSION@ libyategsm.so
INCS := yateclass.h yatemime.h yatengine.h yatephone.h yatecbase.h
GENS := yateversn.h
LIBS :=

View File

@ -1614,6 +1614,7 @@ AC_CONFIG_FILES([packing/rpm/yate.spec
libs/yasn/Makefile
libs/ysnmp/Makefile
libs/miniwebrtc/Makefile
libs/ygsm/Makefile
share/Makefile
share/scripts/Makefile
share/skins/Makefile

72
libs/ygsm/Makefile.in Normal file
View File

@ -0,0 +1,72 @@
# Makefile
# This file holds the make rules for the libyategsm
DEBUG :=
CXX := @CXX@ -Wall
AR := ar
DEFS :=
INCLUDES := -I@top_srcdir@ -I../.. -I@srcdir@/../yxml -I@srcdir@
CFLAGS := @CFLAGS@ @MODULE_CPPFLAGS@ @INLINE_FLAGS@
LDFLAGS:= @LDFLAGS@
SONAME_OPT := @SONAME_OPT@
YATELIBS := -L../.. -lyate @LIBS@
INCFILES := @top_srcdir@/yateclass.h @srcdir@/../yxml/yatexml.h @srcdir@/yategsm.h
PROGS=
LIBS = libyategsm.a
OBJS = codec.o
LIBD_DEV:= libyategsm.so
LIBD_VER:= $(LIBD_DEV).@PACKAGE_VERSION@
LIBD:= ../../$(LIBD_VER) ../../$(LIBD_DEV)
YXML:= ../yxml/libyatexml.a
LOCALFLAGS =
LOCALLIBS =
COMPILE = $(CXX) $(DEFS) $(DEBUG) $(INCLUDES) $(CFLAGS)
LINK = $(CXX) $(LDFLAGS)
prefix = @prefix@
exec_prefix = @exec_prefix@
# include optional local make rules
-include YateLocal.mak
.PHONY: all debug ddebug xdebug
all: $(LIBS) $(LIBD) $(PROGS)
debug:
$(MAKE) all DEBUG=-g3 MODSTRIP=
ddebug:
$(MAKE) all DEBUG='-g3 -DDEBUG' MODSTRIP=
xdebug:
$(MAKE) all DEBUG='-g3 -DXDEBUG' MODSTRIP=
.PHONY: strip
strip: all
strip --strip-debug --discard-locals $(PROGS)
.PHONY: clean
clean:
@-$(RM) $(PROGS) $(LIBS) $(LIBD) $(OBJS) core 2>/dev/null
%.o: @srcdir@/%.cpp $(INCFILES)
$(COMPILE) -c $<
Makefile: @srcdir@/Makefile.in ../../config.status
cd ../.. && ./config.status
../../$(LIBD_VER): $(OBJS) $(YXML)
$(LINK) -o $@ $(SONAME_OPT)$(LIBD_VER) $^ $(YATELIBS)
../../$(LIBD_DEV): ../../$(LIBD_VER)
cd ../.. && ln -sf $(LIBD_VER) $(LIBD_DEV)
$(LIBS): $(OBJS)
$(AR) rcs $@ $^
$(YXML):
$(MAKE) -C ../yxml

493
libs/ygsm/codec.cpp Normal file
View File

@ -0,0 +1,493 @@
/**
* 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-2013 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 <yategsm.h>
using namespace TelEngine;
struct IEParam;
struct RL3ProtoMessage;
struct IEParam {
RL3Codec::Type type;
RL3Codec::XmlType xmlType;
uint16_t iei;
const String name;
bool isOptional;
uint16_t length; // in bits
bool lowerBits;
unsigned int (*decoder)(const RL3Codec*,uint8_t,const IEParam*,const uint8_t*&, unsigned int&, XmlElement*&);
unsigned int (*encoder)(const RL3Codec*,uint8_t,const IEParam*,XmlElement*,DataBlock&);
const void* data;
};
struct RL3Message {
uint16_t value;
const String name;
const IEParam* params;
};
static unsigned int decodeParams(const RL3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len,
XmlElement*& out, const IEParam* param);
static 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);
}
len--;
return *in++;
}
static void addXMLElement(XmlElement*& dst, XmlElement* what)
{
if (!what)
return;
if (!dst)
dst = what;
else
dst->addChildSafe(what);
}
static unsigned int decodeMsgType(const RL3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out)
{
//TODO
return RL3Codec::NoError;
}
static unsigned int encodeMsgType(const RL3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,DataBlock& out)
{
//TODO
return RL3Codec::NoError;
}
static 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 unsigned int decodePD(const RL3Codec* codec, uint8_t proto, const IEParam* param, const uint8_t*& in,
unsigned int& len, XmlElement*& out)
{
if (!(codec && in && len && param))
return RL3Codec::ParserErr;
DDebug(codec->dbg(),DebugAll,"decodePD(param=%s(%p),in=%p,len=%u,out=%p [%p]",param->name.c_str(),param,
in,len,out);
XmlElement* payload = 0;
XmlElement* xml = 0;
if (codec->flags() & RL3Codec::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 = RL3Codec::NoError;
const RL3Message* msg = static_cast<const RL3Message*>(param->data);
msg = findRL3Msg(val,msg);
if (!msg) {
xml = new XmlElement(param->name ? param->name : YSTRING("ie"));
xml->setText(String(val));
}
else {
xml = new XmlElement(msg->name);
if (msg->params)
status = decodeParams(codec,proto,in,len,xml,msg->params);
}
addXMLElement(out,xml);
if (payload)
xml->addChildSafe(payload);
return status;
}
static unsigned int encodePD(const RL3Codec* codec, uint8_t proto, const IEParam* param, XmlElement* in,DataBlock& out)
{
//TODO
return RL3Codec::NoError;
}
static const RL3Message s_mmMsgs[] = {
//TODO
};
static const IEParam s_mmMessage[] = {
{RL3Codec::V, RL3Codec::Skip, 0, "SkipIndicator", false, 4, false, 0, 0, 0},
{RL3Codec::V, RL3Codec::XmlElem, 0, "MessageType", false, 8, false, decodeMsgType, encodeMsgType, s_mmMsgs},
{RL3Codec::NoType, RL3Codec::Skip, 0, "", 0, 0, 0, 0, 0, 0 },
};
static const RL3Message s_protoMsg[] = {
{RL3Codec::GCC, "GCC", 0},
{RL3Codec::BCC, "BCC", 0},
{RL3Codec::EPS_SM, "EPS_SM", 0},
{RL3Codec::CC, "CC", 0},
{RL3Codec::GTTP, "GTTP", 0},
{RL3Codec::MM, "MM", s_mmMessage},
{RL3Codec::RRM, "RRM", 0},
{RL3Codec::EPS_MM, "EPS_MM", 0},
{RL3Codec::GPRS_MM, "GPRS_MM", 0},
{RL3Codec::SMS, "SMS", 0},
{RL3Codec::GPRS_SM, "GPRS_SM", 0},
{RL3Codec::SS, "SS", 0},
{RL3Codec::LCS, "LCS", 0},
{RL3Codec::Extension, "EXT", 0},
{RL3Codec::Test, "TEST", 0},
{RL3Codec::Unknown, "", 0},
};
static const IEParam s_rl3Message[] = {
{RL3Codec::V, RL3Codec::XmlElem, 0, "PD", false, 4, true, decodePD, encodePD, s_protoMsg},
{RL3Codec::NoType, RL3Codec::Skip, 0, "", 0, 0, 0, 0, 0, 0},
};
static unsigned int skipParam(const RL3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len,const IEParam* param)
{
if (!(codec && in && len && param))
return RL3Codec::ParserErr;
DDebug(codec->dbg(),DebugAll,"skipParam() param=%s(%p) of type %s [%p]",param->name.c_str(),param,
lookup(param->type,RL3Codec::s_typeDict,""),codec->ptr());
switch (param->type) {
case RL3Codec::V:
case RL3Codec::T:
if (param->length == 4) {
if (!param->lowerBits) {
len--;
in++;
}
break;
}
// intentional fall through
case RL3Codec::TV:
if (len * 8 < param->length)
return RL3Codec::MsgTooShort;
len -= param->length / 8;
in += param->length / 8;
break;
case RL3Codec::TLV:
if (len < 2)
return RL3Codec::MsgTooShort;
in++;
len--;
// intentional fall through
case RL3Codec::LV:
{
if (len < 1)
return RL3Codec::MsgTooShort;
uint8_t l = *in++;
len--;
if (len < l)
return RL3Codec::MsgTooShort;
in += l;
len -= l;
break;
}
case RL3Codec::TLVE:
if (len < 3)
return RL3Codec::MsgTooShort;
in++;
len--;
// intentional fall through
case RL3Codec::LVE:
{
if (len < 2)
return RL3Codec::MsgTooShort;
uint16_t l = in[0];
l = (l << 8) | in[1];
in += 2;
len -= 2;
if (len < l)
return RL3Codec::MsgTooShort;
in += l;
len -= l;
break;
}
case RL3Codec::NoType:
break;
}
return RL3Codec::NoError;
}
static unsigned int dumpUnknownIE(const RL3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, XmlElement*& out)
{
if (!(codec))
return RL3Codec::ParserErr;
if (!(in && len))
return RL3Codec::NoError;
DDebug(codec->dbg(),DebugAll,"dumpUnknownIE(in=%p,len=%u) in protocol=%s [%p]",in,len,
lookup(proto,RL3Codec::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 == RL3Codec::EPS_MM || proto == RL3Codec::EPS_SM) && ((iei & 0x78) == 0x78)) {
if (len < 3)
dumpOctets = len;
else {
uint16_t l = in[1];
l = (l << 8) | in[2];
dumpOctets = (len < (l + 3) ? len : l + 3);
}
}
else
dumpOctets = (len < (in[1] + 2) ? len : in[1] + 2);
}
if (dumpOctets) {
XmlElement* xml = new XmlElement("ie");
addXMLElement(out,xml);
String dumpStr;
dumpStr.hexify((void*)in,dumpOctets);
xml->setText(dumpStr);
in += dumpOctets;
len -= dumpOctets;
}
return RL3Codec::NoError;
}
static unsigned int dumpParamValue(const RL3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, const IEParam* param,
XmlElement*& out)
{
if (!(codec && in && len))
return RL3Codec::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 RL3Codec::T:
// there's no value to dump
break;
case RL3Codec::V:
{
uint8_t val = 0;
if (param->length == 4) {
if (!param->lowerBits) {
val |= *in & 0xf0;
len--;
in++;
}
else
val |= *in & 0x0f;
}
else {
val = *in;
len--;
in++;
}
dumpStr.hexify(&val,1);
break;
}
case RL3Codec::TV:
{
if (param->length == 8) {
uint8_t val = *in & 0x0f;
len--;
in++;
dumpStr.hexify(&val,1);
}
else
skipOctets = 1;
break;
}
case RL3Codec::TLV:
skipOctets = 2;
break;
case RL3Codec::LV:
skipOctets = 1;
break;
case RL3Codec::TLVE:
skipOctets = 3;
break;
case RL3Codec::LVE:
skipOctets = 2;
break;
case RL3Codec::NoType:
break;
}
if (skipOctets) {
const uint8_t* buff = in;
unsigned int lbuff = len;
if (int status = skipParam(codec,proto,in,len,param))
return status;
if (len + skipOctets <= lbuff)
dumpStr.hexify((void*)(buff + skipOctets), lbuff - len - skipOctets);
}
XmlElement* xml = new XmlElement(param->name);
addXMLElement(out,xml);
if (dumpStr)
xml->setText(dumpStr);
}
else
return dumpUnknownIE(codec,proto,in,len,out);
return RL3Codec::NoError;
}
static unsigned int decodeV(const RL3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, XmlElement*& out,
const IEParam* param)
{
if (!(codec && in && len && param))
return RL3Codec::ParserErr;
if (len * 8 < param->length)
return RL3Codec::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 RL3Codec::Skip:
return skipParam(codec,proto,in,len,param);
case RL3Codec::XmlElem:
if (!(param->decoder || (param->data && param->name)))
return dumpParamValue(codec,proto,in,len,param,out);
if (param->decoder)
return param->decoder(codec,proto,param,in,len,out);
// decode an 1 byte value from a dictionary
if (param->data && 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->data);
if (!dict)
xml->setText(String(val));
else
xml->setText(lookup(val,dict,String(val)));
return RL3Codec::NoError;
}
default:
return RL3Codec::ParserErr;
}
return RL3Codec::NoError;
}
static unsigned int decodeParams(const RL3Codec* codec, uint8_t proto, const uint8_t*& in, unsigned int& len, XmlElement*& out,
const IEParam* param)
{
if (!(codec && in && len > 2 && param))
return RL3Codec::ParserErr;
while (param) {
int status = RL3Codec::NoError;
switch (param->type) {
case RL3Codec::V:
status = decodeV(codec,proto,in,len,out,param);
break;
//TODO
case RL3Codec::T:
case RL3Codec::TV:
case RL3Codec::LV:
case RL3Codec::TLV:
case RL3Codec::LVE:
case RL3Codec::TLVE:
case RL3Codec::NoType:
break;
}
if (status)
return status;
}
return RL3Codec::NoError;
};
const TokenDict RL3Codec::s_typeDict[] = {
{"T", RL3Codec::T},
{"V", RL3Codec::V},
{"TV", RL3Codec::TV},
{"LV", RL3Codec::LV},
{"TLV", RL3Codec::TLV},
{"LVE", RL3Codec::LVE},
{"TLVE", RL3Codec::TLVE},
{0, 0},
};
const TokenDict RL3Codec::s_protoDict[] = {
{"GCC", RL3Codec::GCC},
{"BCC", RL3Codec::BCC},
{"EPS_SM", RL3Codec::EPS_SM},
{"CC", RL3Codec::CC},
{"GTTP", RL3Codec::GTTP},
{"MM", RL3Codec::MM},
{"RRM", RL3Codec::RRM},
{"EPS_MM", RL3Codec::EPS_MM},
{"GPRS_MM", RL3Codec::GPRS_MM},
{"SMS", RL3Codec::SMS},
{"GPRS_SM", RL3Codec::GPRS_SM},
{"SS", RL3Codec::SS},
{"LCS", RL3Codec::LCS},
{"Extension", RL3Codec::Extension},
{"Test", RL3Codec::Test},
{"Unknown", RL3Codec::Unknown},
{0, 0},
};
RL3Codec::RL3Codec(DebugEnabler* dbg)
: m_flags(0),
m_dbg(0),
m_ptr(0)
{
DDebug(DebugAll,"Created RL3Codec [%p]",this);
setCodecDebug(dbg);
}
unsigned int RL3Codec::decode(const uint8_t* in, unsigned int len, XmlElement*& out)
{
if (!in || len < 2)
return MsgTooShort;
const uint8_t* buff = in;
unsigned int l = len;
return decodeParams(this,RL3Codec::Unknown,buff,l,out,s_rl3Message);
}
unsigned int RL3Codec::encode(XmlElement* in, DataBlock& out)
{
//TODO
return NoError;
}
void RL3Codec::setCodecDebug(DebugEnabler* enabler, void* ptr)
{
m_dbg = enabler ? enabler : m_dbg;
m_ptr = ptr ? ptr : (void*)this;
}

155
libs/ygsm/yategsm.h Normal file
View File

@ -0,0 +1,155 @@
/*
* yategsm.h
* GSM Radio Layer 3 library
* This file is part of the YATE Project http://YATE.null.ro
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2011-2013 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.
*/
#ifndef __YATEGSM_H
#define __YATEGSM_H
#include <yateclass.h>
#include <yatexml.h>
#ifdef _WINDOWS
#ifdef LIBYGSM_EXPORTS
#define YGSM_API __declspec(dllexport)
#else
#ifndef LIBYGSM_STATIC
#define YGSM_API __declspec(dllimport)
#endif
#endif
#endif /* _WINDOWS */
#ifndef YGSM_API
#define YGSM_API
#endif
namespace TelEngine {
class YGSM_API RL3Codec
{
YNOCOPY(RL3Codec);
public:
enum Flags {
XmlDumpMsg = 0x01,
XmlDumpIEs = 0x02,
};
enum Status {
NoError = 0,
MsgTooShort,
UnknownProto,
ParserErr,
};
// Protocol discriminator according to ETSI TS 124 007 V11.0.0, section 11.2.3.1.1
enum Protocol {
GCC = 0x00, // Group Call Control
BCC = 0x01, // Broadcast Call Control
EPS_SM = 0x02, // EPS Session Management
CC = 0x03, // Call Control; Call Related SS messages
GTTP = 0x04, // GPRS Transparent Transport Protocol (GTTP)
MM = 0x05, // Mobility Management
RRM = 0x06, // Radio Resources Management
EPS_MM = 0x07, // EPS Mobility Management
GPRS_MM = 0x08, // GPRS Mobility Management
SMS = 0x09, // SMS
GPRS_SM = 0x0a, // GPRS Session Management
SS = 0x0b, // Non Call Related SS messages
LCS = 0x0c, // Location services
Extension = 0x0e, // reserved for extension of the PD to one octet length
Test = 0x0f, // used by tests procedures described in 3GPP TS 44.014, 3GPP TS 34.109 and 3GPP TS 36.509
Unknown = 0xff,
};
enum Type {
NoType = 0,
T,
V,
TV,
LV,
TLV,
LVE,
TLVE,
};
enum XmlType {
Skip,
XmlElem,
};
RL3Codec(DebugEnabler* dbg);
unsigned int decode(const uint8_t* in, unsigned int len, XmlElement*& out);
unsigned int encode(XmlElement* in, DataBlock& out);
/**
* Set data used in debug
* @param enabler The DebugEnabler to use (0 to to use the engine)
* @param ptr Pointer to print, 0 to use the codec pointer
*/
void setCodecDebug(DebugEnabler* enabler = 0, void* ptr = 0);
/**
* Retrieve codec flags
* @return Codec flags
*/
inline uint8_t flags() const
{ return m_flags; }
/**
* Set codec flags
* @param flgs Flags to set
* @param reset Reset flags before setting these ones
*/
inline void setFlags(uint8_t flgs, bool reset = false)
{
if (reset)
resetFlags();
m_flags |= flgs;
}
/**
* Reset codec flags
* @param flgs Flags to reset. If 0, all flags are reset
*/
inline void resetFlags(uint8_t flgs = 0)
{
if (flgs)
m_flags &= ~flgs;
else
m_flags = 0;
}
inline DebugEnabler* dbg() const
{ return m_dbg; }
inline void* ptr() const
{ return m_ptr; }
static const TokenDict s_typeDict[];
static const TokenDict s_protoDict[];
private:
uint8_t m_flags; // Codec flags
// data used for debugging messages
DebugEnabler* m_dbg;
void* m_ptr;
};
}; // namespace TelEngine
#endif /* __YATEGSM_H */
/* vi: set ts=8 sw=4 sts=4 noet: */