Added posibility to set maximum data size that can be transported on a specific route.

Improved SCCP segmentation process. The SCCP now detects the maximum amount of data that can fit in a SCCP  message.
Implemented SCCP message change procedures.


git-svn-id: http://voip.null.ro/svn/yate@4828 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
andrei 2012-01-31 11:50:30 +00:00
parent 521f9a2c1d
commit 4299a791e4
5 changed files with 541 additions and 177 deletions

View File

@ -504,19 +504,37 @@
;netindicator=national
; route: string: Build a destination route for the SS7 network
; The format of this option is pointcodetype,label,priority
; The format of this option is pointcodetype,label,priority,shift,size
; This parameter can be repeated to build multiple destination routes
; The network will notify the router about its destination(s) and priority
;
; If not specified the priority is 100. A zero priority creates an adjacent route
; Example: route=ITU,2-2-2,100
;
; Shift SLS right shift when selecting linkset.
; For example for 2 links shift = 1.
; A value of 8 or higher disables load balancing.
;
; Size represents the maximum data size that can be transported on this route.
; The default vaue is 272 -> maximum MSU size on TDM.
; If the route can transport more data, a value up to 3904 should be set to
; avoid SCCP message fragmentation.
;
; Example: route=ITU,2-2-2,100,1,272
;route=
; adjacent: string: Build an adjacent route for the SS7 network (A, E and F links)
; The format of this option is pointcodetype,label
; The format of this option is pointcodetype,label,size
; This parameter can be repeated to declare multiple adjacent routers
; The network will notify the router about its destination(s) and priority
;
; The priority is always zero so an adjacent route will always match first.
; Example: adjacent=ANSI,40-50-60
;
; Size represents the maximum data size that can be transported on this route.
; The default vaue is 272 -> maximum MSU size on TDM.
; If the route can transport more data, a value up to 3904 should be set to
; avoid SCCP message fragmentation
;
; Example: adjacent=ANSI,40-50-60,272
;adjacent=
; local: string: Declare a local pointcode for the SS7 network

View File

@ -29,6 +29,9 @@
using namespace TelEngine;
#define MAX_TDM_DATA_SIZE 272
#define MAX_SIGTRAN_DATA_SIZE 3904 // Maximum ANSI LUDT(SCCP) length
static const TokenDict s_dict_control[] = {
{ "show", SS7MTP3::Status },
{ "pause", SS7MTP3::Pause },
@ -181,6 +184,7 @@ bool SS7Layer3::buildRoutes(const NamedList& params)
continue;
unsigned int prio = 0;
unsigned int shift = 0;
unsigned int maxLength = MAX_TDM_DATA_SIZE;
bool local = false;
if (ns->name() == YSTRING("local"))
local = true;
@ -200,14 +204,27 @@ bool SS7Layer3::buildRoutes(const NamedList& params)
obj = obj->skipNext();
if (!(obj && pc.assign(obj->get()->toString(),type)))
break;
if (!(obj = obj->skipNext()))
break;
if (prio) {
if (!(obj = obj->skipNext()))
break;
prio = obj->get()->toString().toInteger(prio);
obj = obj->skipNext();
if (obj)
shift = obj->get()->toString().toInteger(0);
}
if (!(obj = obj->skipNext()) || local)
break;
maxLength = obj->get()->toString().toInteger(maxLength);
if (maxLength < MAX_TDM_DATA_SIZE) {
Debug(this,DebugNote,"MaxDataLength is too small %d. Setting it to %d",
maxLength,MAX_TDM_DATA_SIZE);
maxLength = MAX_TDM_DATA_SIZE;
}
if (maxLength > MAX_SIGTRAN_DATA_SIZE) {
Debug(this,DebugNote,"MaxDataLength is too big %d. Setting it to %d",
maxLength,MAX_SIGTRAN_DATA_SIZE);
maxLength = MAX_SIGTRAN_DATA_SIZE;
}
} while (false);
TelEngine::destruct(route);
unsigned int packed = pc.pack(type);
@ -220,10 +237,12 @@ bool SS7Layer3::buildRoutes(const NamedList& params)
m_local[type - 1] = packed;
continue;
}
if (findRoute(type,packed))
if (findRoute(type,packed)) {
Debug(this,DebugWarn,"Duplicate route found %s!!",ns->c_str());
continue;
}
added = true;
m_route[(unsigned int)type - 1].append(new SS7Route(packed,prio,shift));
m_route[(unsigned int)type - 1].append(new SS7Route(packed,type,prio,shift,maxLength));
DDebug(this,DebugAll,"Added route '%s'",ns->c_str());
}
if (!added)
@ -233,6 +252,19 @@ bool SS7Layer3::buildRoutes(const NamedList& params)
return added;
}
// Get the maximum data length that this route can transport
unsigned int SS7Layer3::getRouteMaxLength(SS7PointCode::Type type, unsigned int packedPC)
{
if (type == SS7PointCode::Other || (unsigned int)type > YSS7_PCTYPE_COUNT || !packedPC)
return MAX_TDM_DATA_SIZE;
Lock lock(m_routeMutex);
SS7Route* route = findRoute(type,packedPC);
if (route)
return route->m_maxDataLength;
return MAX_TDM_DATA_SIZE;
}
// Get the priority of a route.
unsigned int SS7Layer3::getRoutePriority(SS7PointCode::Type type, unsigned int packedPC)
{

View File

@ -130,6 +130,11 @@ void SS7Route::attach(SS7Layer3* network, SS7PointCode::Type type)
Lock lock(this);
// Remove from list if already there
detach(network);
SS7Route* route = network->findRoute(m_type,m_packed);
if (route) {
if (m_maxDataLength > route->getMaxDataLength() || m_maxDataLength == 0)
m_maxDataLength = route->getMaxDataLength();
}
// Insert
if (priority == 0) {
m_networks.insert(new L3Pointer(network));
@ -161,6 +166,21 @@ bool SS7Route::detach(SS7Layer3* network)
break;
}
}
m_maxDataLength = 0;
for (o = m_networks.skipNull(); o; o = o->skipNext()) {
L3Pointer* p = static_cast<L3Pointer*>(o->get());
if (!p)
continue;
RefPointer<SS7Layer3> l3 = static_cast<SS7Layer3*>(*p);
if (!l3)
continue;
SS7Route* route = l3->findRoute(m_type,m_packed);
if (route) {
if (m_maxDataLength > route->getMaxDataLength() ||
m_maxDataLength == 0)
m_maxDataLength = route->getMaxDataLength();
}
}
return 0 != m_networks.skipNull();
}
@ -665,7 +685,7 @@ void SS7Router::buildView(SS7PointCode::Type type, ObjList& view, SS7Layer3* net
if (!v) {
DDebug(this,DebugAll,"Creating route to %u from %s in view of %s",
route->packed(),(*p)->toString().c_str(),network->toString().c_str());
view.append(new SS7Route(route->packed()));
view.append(new SS7Route(route->packed(),type));
}
}
}

View File

@ -32,11 +32,16 @@
using namespace TelEngine;
#define MAX_MANDATORY_PARAMS 16
// NOTE Keep MAX_DATA_LEN smaller than the value defined by protocol.
// In case that we have to encode optional parameters the pointer to optionalParameters will be wrapped around 255
#define MAX_DATA_LEN 220 // ITU defines 255 if the calling party address does not include GT, ANSI 252
#define SGM_PADDING 10 // Segmentation padding
// 227 is maximum data length that can be transported by a UDT message
// with 2 full gt present both numbers with 16 digits (bcd encoded)
#define MAX_UDT_LEN 227
#define MAX_INFO_TIMER 1200000 // Maximum interval for sending SST 20 min
// Maximum length of optional parameters: 6 Segmentation, 3 Importance, 1 EOP
#define MAX_OPT_LEN 10
// Minimum data size in a SCCP message
#define MIN_DATA_SIZE 2
static const char* s_userMutexName = "SCCPUserTransport";
static const char* s_sccpMutexName = "SCCPUserList";
@ -946,9 +951,8 @@ static unsigned int encodeData(const SS7SCCP* sccp, SS7MSU& msu, SS7MsgSCCP* msg
}
unsigned int length = data->length();
unsigned char header[2];
bool longData = msg->type() == SS7MsgSCCP::LUDT;
DataBlock tmp;
if (longData) {
if (msg->isLongDataMessage()) {
header[0] = length & 0xff;
header[1] = length >> 8 & 0xff;
tmp.assign(header,2,false);
@ -1954,8 +1958,6 @@ bool SCCPManagement::handleMessage(int msgType, unsigned char ssn, unsigned char
break;
}
lock.drop();
NamedList notif("");
notif.setParam("ssn",String("ssn"));
if (!managementMessage(SCCP::SubsystemStatus,params))
return true;
String* status = params.getParam(YSTRING("subsystem-status"));
@ -2648,7 +2650,7 @@ bool SccpRemote::initialize(const String& params)
String* subsystem = static_cast<String*>(ob->get());
unsigned int ssn = subsystem->toInteger(256);
if (ssn > 255) {
DDebug(DebugConf,"Skipping ssn %d for pointcode %d Value to big!",
DDebug(DebugConf,"Skipping ssn %d for pointcode %d Value too big!",
ssn,m_pointcode.pack(m_pointcodeType));
continue;
}
@ -2766,7 +2768,7 @@ SS7SCCP::SS7SCCP(const NamedList& params)
: SignallingComponent(params,&params), SS7Layer4(SS7MSU::SCCP|SS7MSU::National,&params), Mutex(true,params),
m_type(SS7PointCode::Other), m_localPointCode(0), m_management(0), m_hopCounter(15),
m_msgReturnStatus(""), m_segTimeout(0), m_ignoreUnkDigits(false), m_layer3Up(false),
m_supportLongData(false), m_totalSent(0), m_totalReceived(0), m_errors(0),
m_maxUdtLength(220), m_totalSent(0), m_totalReceived(0), m_errors(0),
m_totalGTTranslations(0), m_gttFailed(0), m_extendedMonitoring(false), m_mgmName("sccp-mgm"),
m_printMsg(false), m_extendedDebug(false), m_endpoint(true)
{
@ -2806,7 +2808,7 @@ SS7SCCP::SS7SCCP(const NamedList& params)
m_printMsg = params.getBoolValue(YSTRING("print-messages"),false);
m_extendedDebug = params.getBoolValue(YSTRING("extended-debug"),false);
m_extendedMonitoring = params.getBoolValue(YSTRING("extended-monitoring"),false);
m_supportLongData = params.getBoolValue(YSTRING("ludt-support"),false);
m_maxUdtLength = params.getIntValue(YSTRING("max-udt-length"),MAX_UDT_LEN);
m_segTimeout = params.getIntValue(YSTRING("segmentation-timeout"),10000);
m_mgmName = params.getValue(YSTRING("management"));
m_endpoint = params.getBoolValue(YSTRING("endpoint"),true);
@ -2854,6 +2856,7 @@ bool SS7SCCP::initialize(const NamedList* config)
m_printMsg = config->getBoolValue(YSTRING("print-messages"),m_printMsg);
m_extendedDebug = config->getBoolValue(YSTRING("extended-debug"),m_extendedDebug);
m_ignoreUnkDigits = config->getBoolValue(YSTRING("ignore-unknown-digits"),m_ignoreUnkDigits);
m_maxUdtLength = config->getIntValue(YSTRING("max-udt-length"),m_maxUdtLength);
m_endpoint = config->getBoolValue(YSTRING("endpoint"),m_endpoint);
int hc = config->getIntValue("hopcounter",m_hopCounter);
if (hc < 1 || hc > 15)
@ -2879,14 +2882,6 @@ void SS7SCCP::attach(SS7Layer3* network)
setNetworkUp(network && network->operational());
}
bool SS7SCCP::canSendLUDT(const SS7Label& label)
{
// TODO Check the maxumum MSU size that can be transmited
// and determine if we can send LUDT messages
// SEE if we should update the MAX_DATA_SIZE!!
return m_supportLongData;
}
bool SS7SCCP::managementStatus(Type type, NamedList& params)
{
if (m_management)
@ -2908,6 +2903,21 @@ void SS7SCCP::timerTick(const Time& when)
}
}
void SS7SCCP::ajustMessageParams(NamedList& params, SS7MsgSCCP::Type type)
{
if (type == SS7MsgSCCP::UDT || type == SS7MsgSCCP::UDTS)
return;
int hopCounter = params.getIntValue(YSTRING("HopCounter"),0);
if (hopCounter < 1 || hopCounter > 15)
params.setParam("HopCounter",String(m_hopCounter));
if (ITU() && params.getParam(YSTRING("Importance"))) {
int importance = params.getIntValue(YSTRING("Importance"));
int temp = checkImportanceLevel(type, importance);
if (importance != temp)
params.setParam(YSTRING("Importance"),String(temp));
}
}
// Called by routing method to send a msu
int SS7SCCP::transmitMessage(SS7MsgSCCP* sccpMsg, bool local)
{
@ -2935,23 +2945,10 @@ int SS7SCCP::transmitMessage(SS7MsgSCCP* sccpMsg, bool local)
}
// Build the routing label
SS7Label outLabel(m_type,dest,orig,sls);
while (!canSendLUDT(outLabel)) {
if (sccpMsg->getData()->length() <= MAX_DATA_LEN)
break;
if (sccpMsg->getData()->length() > m_maxUdtLength) {
lock.drop();
if (sccpMsg->type() == SS7MsgSCCP::UDT || sccpMsg->type() == SS7MsgSCCP::UDTS) {
Debug(this,DebugStub,"Request to segment message %s! Implement message change!",
SS7MsgSCCP::lookup(sccpMsg->type()));
return -1;
}
int sls = segmentMessage(*sccpMsg->getData(),sccpMsg,sccpMsg->type(),local);
if (sls < 0) {
Lock lock(this);
m_errors++;
}
return sls;
return segmentMessage(sccpMsg,outLabel,local);
}
// Check route indicator
if (!sccpMsg->params().getParam("CalledPartyAddress.route")) {
// Set route indicator. If have pointcode and ssn, route on ssn
@ -2963,32 +2960,9 @@ int SS7SCCP::transmitMessage(SS7MsgSCCP* sccpMsg, bool local)
}
// Build the msu
SS7MSU* msu = buildMSU(sccpMsg,outLabel);
if (!msu) {
Debug(this,DebugWarn,"Failed to build msu from sccpMessage %s",
SS7MsgSCCP::lookup(sccpMsg->type()));
return -1;
}
if (m_printMsg && debugAt(DebugInfo)) {
String tmp;
void* data = 0;
unsigned int len = 0;
if (m_extendedDebug && msu) {
unsigned int offs = outLabel.length() + 4;
data = msu->getData(offs);
len = data ? msu->length() - offs : 0;
}
String tmp1;
fillLabelAndReason(tmp1,outLabel,sccpMsg);
sccpMsg->toString(tmp,outLabel,debugAt(DebugAll),data,len);
Debug(this,DebugInfo,"Sending message (%p) '%s' %s %s",sccpMsg,
SS7MsgSCCP::lookup(sccpMsg->type()),tmp1.c_str(),tmp.c_str());
} else if (debugAt(DebugAll)) {
String tmp;
bool debug = fillLabelAndReason(tmp,outLabel,sccpMsg);
Debug(this,debug ? DebugInfo : DebugAll,"Sending message '%s' %s",
sccpMsg->name(),tmp.c_str());
}
if (!msu)
return segmentMessage(sccpMsg,outLabel,local);
printMessage(msu,sccpMsg,outLabel);
lock.drop();
sls = transmitMSU(*msu,outLabel,sls);
#ifdef DEBUG
@ -3100,31 +3074,18 @@ int SS7SCCP::sendMessage(DataBlock& data, const NamedList& params)
Debug(this,DebugAll,"SS7SCCP::sendMessage() [%p]%s",this,tmp.c_str());
#endif
Lock lock1(this);
// Verify the presence of optional parameters to detect the message
// type used to send SCLC data
bool checkImportance = false;
bool checkHopCounter = false;
SS7MsgSCCP* sccpMsg = 0;
if (data.length() > MAX_DATA_LEN) {
// TODO verify if we can send LUDT Have a sigtran under?
// If not segment the message. send multiple XUDT messages
sccpMsg = new SS7MsgSCCP(m_supportLongData ? SS7MsgSCCP::LUDT : SS7MsgSCCP::XUDT);
checkHopCounter = true;
if(params.getParam(YSTRING("Importance")) && m_type == SS7PointCode::ITU)
checkImportance = true;
} else if (params.getParam(YSTRING("Importance")) && m_type == SS7PointCode::ITU) {
// Do not check for data length here! If message data is too long the message
// change procedure will be initiated in segmentMessage method
if (params.getParam(YSTRING("Importance")) && m_type == SS7PointCode::ITU) {
// We have Importance optional parameter. Send XUDT. ITU only
sccpMsg = new SS7MsgSCCP(SS7MsgSCCP::XUDT);
checkHopCounter = true;
checkImportance = true;
} else if ((params.getParam(YSTRING("ISNI")) || params.getParam(YSTRING("INS"))) &&
} else if ((params.getParam(YSTRING("ISNI")) || params.getParam(YSTRING("INS"))) &&
m_type == SS7PointCode::ANSI) {
// XUDT message ANSI only
sccpMsg = new SS7MsgSCCP(SS7MsgSCCP::XUDT);
checkHopCounter = true;
} else if (params.getParam(YSTRING("HopCounter"))) {
sccpMsg = new SS7MsgSCCP(SS7MsgSCCP::XUDT);
checkHopCounter = true;
} else // In rest send Unit Data Messages
sccpMsg = new SS7MsgSCCP(SS7MsgSCCP::UDT);
@ -3134,17 +3095,7 @@ int SS7SCCP::sendMessage(DataBlock& data, const NamedList& params)
return -1;
}
sccpMsg->params().copyParams(params); // Copy the parameters to message
if (checkImportance) {
int importance = sccpMsg->params().getIntValue(YSTRING("Importance"));
int temp = checkImportanceLevel(sccpMsg->type(), importance);
if (importance != temp)
sccpMsg->params().setParam(YSTRING("Importance"),String(temp));
}
if (checkHopCounter) {
int hopCounter = params.getIntValue(YSTRING("HopCounter"),0);
if (hopCounter < 1 || hopCounter > 15) // HopCounter is an mandatory fixed length parameter in XUDT
sccpMsg->params().setParam(YSTRING("HopCounter"),String(m_hopCounter));
}
ajustMessageParams(sccpMsg->params(),sccpMsg->type());
if (params.getBoolValue(YSTRING("CallingPartyAddress.pointcode"),false) && m_localPointCode)
sccpMsg->params().setParam("CallingPartyAddress.pointcode",String(getPackedPointCode()));
// Avoid sending optional parameters that aren't specified by protocol
@ -3165,109 +3116,344 @@ int SS7SCCP::sendMessage(DataBlock& data, const NamedList& params)
return ret;
}
int SS7SCCP::segmentMessage(DataBlock& data, SS7MsgSCCP* origMsg, SS7MsgSCCP::Type type, bool local)
// This method approximates the length of sccp address
unsigned int SS7SCCP::getAddressLength(const NamedList& params, const String& prefix)
{
unsigned int length = 2; // Parameter length + Address information octet
if (params.getParam(prefix + ".ssn"))
length++; // One octet for ssn
if (params.getParam(prefix + ".pointcode"))
length += ITU() ? 2 : 3; // Pointcode has 2 octets on ITU and 3 on ANSI
const NamedString* gtNr = YOBJECT(NamedString,params.getParam(prefix + ".gt"));
if (!gtNr)
return length;
DataBlock data;
if (!data.unHexify(*gtNr,gtNr->length(),' ')) {
length += gtNr->length() / 2 + gtNr->length() % 2;
} else
length += data.length();
const NamedString* nature = YOBJECT(NamedString,params.getParam(prefix + ".gt.nature"));
const NamedString* translation = YOBJECT(NamedString,params.getParam(prefix + ".gt.tt"));
const NamedString* plan = YOBJECT(NamedString,params.getParam(prefix + ".gt.np"));
const NamedString* encoding = YOBJECT(NamedString,params.getParam(prefix + ".gt.encoding"));
if (nature)
length++;
if (translation)
length++;
if (plan && encoding)
length++;
return length;
}
void SS7SCCP::getMaxDataLen(const SS7MsgSCCP* msg, const SS7Label& label,
unsigned int& udt, unsigned int& xudt, unsigned int& ludt)
{
if (!network()) {
Debug(this,DebugGoOn,"No Network Attached!!!");
return;
}
unsigned int maxLen = network()->getRouteMaxLength(m_type,label.dpc().pack(m_type));
if (maxLen < 272) {
DDebug(this,DebugInfo,"Received MSU size (%d) lower than maximum TDM!",
maxLen);
maxLen = 272;
}
bool ludtSupport = maxLen > 272; // 272 maximum msu size
maxLen -= (label.length() + 1); // subtract label length and SIO octet
// Now max length represents the maximum length of SCCP message
// Adjust maxLen to represent maximum data in the message.
unsigned int headerLength = 3; // MsgType + ProtocolClass
// Memorize pointer start to adjust data size.
unsigned int pointerLen = 1;
if (msg->type() == msg->isLongDataMessage())
pointerLen++;
unsigned int pointersStart = headerLength;
maxLen -= headerLength;
// We have 3 mandatory variable parameters CallingAddress, CalledAddress,
// and Data and the pointer to optional parameters
headerLength += 4 * pointerLen;
headerLength += getAddressLength(msg->params(), "CalledPartyAddress");
headerLength += getAddressLength(msg->params(), "CallingPartyAddress");
ludt = 0;
unsigned int sccpParamsSize = headerLength - pointersStart;
// 254 = 255 max data length - 1 hopcounter - 1 optional parameters pointer +
// 1 data length indicator
if (maxLen > 254 + sccpParamsSize)
udt = 255;
else
udt = maxLen - sccpParamsSize;
// Append optional parameters length
headerLength += MAX_OPT_LEN;
if (ludtSupport) {
unsigned int maxSupported = ITU() ? 3952 : 3904;
if (maxLen > maxSupported)
ludt = maxSupported - sccpParamsSize;
else
ludt = maxLen - sccpParamsSize;
}
// 254 represents the maximum value that can be stored
if (maxLen < 254)
xudt = maxLen - sccpParamsSize;
// Adjust data length to make sure that the pointer to optional parameters
// is not bigger than max unsigned char value
xudt = 254 - sccpParamsSize;
}
void SS7SCCP::printMessage(const SS7MSU* msu, const SS7MsgSCCP* sccpMsg, const
SS7Label& label) {
if (m_printMsg && debugAt(DebugInfo)) {
String tmp;
const void* data = 0;
unsigned int len = 0;
if (m_extendedDebug && msu) {
unsigned int offs = label.length() + 4;
data = msu->getData(offs);
len = data ? msu->length() - offs : 0;
}
String tmp1;
fillLabelAndReason(tmp1,label,sccpMsg);
sccpMsg->toString(tmp,label,debugAt(DebugAll),data,len);
Debug(this,DebugInfo,"Sending message (%p) '%s' %s %s",sccpMsg,
SS7MsgSCCP::lookup(sccpMsg->type()),tmp1.c_str(),tmp.c_str());
} else if (debugAt(DebugAll)) {
String tmp;
bool debug = fillLabelAndReason(tmp,label,sccpMsg);
Debug(this,debug ? DebugInfo : DebugAll,"Sending message '%s' %s",
sccpMsg->name(),tmp.c_str());
}
}
ObjList* SS7SCCP::getDataSegments(unsigned int dataLength,
unsigned int maxSegmentSize)
{
DDebug(DebugAll,"getDataSegments(%u,%u)",dataLength,maxSegmentSize);
ObjList* segments = new ObjList();
// The first sccp segment must be the largest
int segmentSize = maxSegmentSize - 1;
int dataLeft = dataLength;
unsigned int totalSent = 0;
int sgSize = maxSegmentSize;
if (dataLength - maxSegmentSize <= MIN_DATA_SIZE)
sgSize = maxSegmentSize - MIN_DATA_SIZE;
segments->append(new SS7SCCPDataSegment(0,sgSize));
dataLeft -= sgSize;
totalSent += sgSize;
while (dataLeft > 0) {
sgSize = 0;
if ((dataLeft - segmentSize) > MIN_DATA_SIZE) { // Make sure that the left segment is longer than 2
sgSize = segmentSize;
} else if (dataLeft > segmentSize) {
sgSize = segmentSize - MIN_DATA_SIZE;
} else {
sgSize = dataLeft;
}
XDebug(this,DebugAll,"Creating new data segment total send %d, segment size %d",
totalSent,sgSize);
segments->append(new SS7SCCPDataSegment(totalSent,sgSize));
dataLeft -= sgSize;
totalSent += sgSize;
}
return segments;
}
SS7SCCPDataSegment* getAndRemoveDataSegment(ObjList* obj)
{
if (!obj)
return 0;
ObjList* o = obj->skipNull();
if (!o)
return 0;
SS7SCCPDataSegment* sgm = static_cast<SS7SCCPDataSegment*>(o->get());
obj->remove(sgm,false);
return sgm;
}
int SS7SCCP::segmentMessage(SS7MsgSCCP* origMsg, const SS7Label& label, bool local)
{
// TODO for the moment we have set the max data length at 220
// Verify if data lenght is between 220 and 252 | 255 and the message does not heva optional parameters
// send a single messages
// TODO calculate dinamically the max data length by pre-encoding the message and determine
// the length of the addresses
// TODO implement a better way to split the data in smaller segments
if (!origMsg)
return -1;
if (data.length() > 16 * (MAX_DATA_LEN - SGM_PADDING)) {
Debug(this,DebugMild,"Can not segment message, data to long! dataLength = %d, max data length = %d",
data.length(),16 * MAX_DATA_LEN);
unsigned int udtLength = 0;
unsigned int xudtLength = 0;
unsigned int ludtLength = 0;
getMaxDataLen(origMsg,label,udtLength,xudtLength,ludtLength);
unsigned int dataLen = 0;
DDebug(this,DebugInfo, "Got max data len : udt (%d) : xudt (%d) ludt (%d)",
udtLength,xudtLength,ludtLength);
if (udtLength < 2 && xudtLength < 2 && ludtLength < 2)
return -1;
int sls = origMsg->params().getIntValue(YSTRING("sls"),-1);
DataBlock* data = origMsg->getData();
if (!data)
return -1;
SS7MsgSCCP::Type msgType = origMsg->type();
if (data->length() < udtLength && origMsg->canBeUDT()) {
msgType = isSCLCMessage(msgType) ? SS7MsgSCCP::UDT : SS7MsgSCCP::UDTS;
dataLen = udtLength;
} else if (data->length() < xudtLength) {
msgType = isSCLCMessage(msgType) ? SS7MsgSCCP::XUDT : SS7MsgSCCP::XUDTS;
dataLen = xudtLength;
} else if (data->length() < ludtLength) {
msgType = isSCLCMessage(msgType) ? SS7MsgSCCP::LUDT : SS7MsgSCCP::LUDTS;
dataLen = ludtLength;
} else { // Segmentation is needed!!!
if (ludtLength > 2) { // send ludt
msgType = isSCLCMessage(msgType) ? SS7MsgSCCP::LUDT : SS7MsgSCCP::LUDTS;
dataLen = ludtLength;
} else if (xudtLength > 2) { // Send Ludt
msgType = isSCLCMessage(msgType) ? SS7MsgSCCP::XUDT : SS7MsgSCCP::XUDTS;
dataLen = xudtLength;
} else {
Debug(this,DebugWarn,
"Unable to segment message!! Invalid data len params! XUDT data len = %d, LUDT data len = %d",
xudtLength,ludtLength);
}
}
origMsg->updateType(msgType);
origMsg->params().clearParam(YSTRING("Segmentation"),'.');
// Send the message if it fits in a single message
if (data->length() < dataLen) {
Lock lock(this);
ajustMessageParams(origMsg->params(),origMsg->type());
SS7MSU* msu = buildMSU(origMsg,label,false);
if (!msu) {
Debug(this,DebugGoOn,"Failed to build msu from sccpMessage %s",
SS7MsgSCCP::lookup(origMsg->type()));
return -1;
}
printMessage(msu,origMsg,label);
lock.drop();
sls = transmitMSU(*msu,label,sls);
#ifdef DEBUG
if (sls < 0)
Debug(this,DebugNote,"Failed to transmit message %s. %d",
SS7MsgSCCP::lookup(origMsg->type()),sls);
#endif
// CleanUp memory
TelEngine::destruct(msu);
return sls;
}
// Verify if we should bother to segment the message
if (data->length() > 16 * (dataLen - 1)) {
Debug(DebugNote,
"Unable to segment SCCP message! Data length (%d) excedes max data allowed (%d)",
data->length(),(16 * (dataLen - 1)));
return -1;
}
// Start segmentation process
lock();
ObjList* listSegments = getDataSegments(data->length(),dataLen);
// Build message params
NamedList msgData("");
msgData.copyParams(origMsg->params());
int hopCounter = msgData.getIntValue(YSTRING("HopCounter"),0);
if (hopCounter < 1 || hopCounter > 15) // HopCounter is an mandatory fixed length parameter in XUDT
msgData.setParam("HopCounter",String(m_hopCounter));
if (ITU() && msgData.getParam(YSTRING("Importance"))) {
int importance = msgData.getIntValue(YSTRING("Importance"));
int temp = checkImportanceLevel(type, importance);
if (importance != temp)
msgData.setParam(YSTRING("Importance"),String(temp));
}
ajustMessageParams(msgData,msgType);
// Set segmentation local reference for this message
msgData.setParam("Segmentation","");
if (!msgData.getParam(YSTRING("Segmentation.SegmentationLocalReference")))
msgData.setParam("Segmentation.SegmentationLocalReference",String((u_int32_t)Random::random()));
int segments = data.length() / (MAX_DATA_LEN - SGM_PADDING);
if (data.length() / (MAX_DATA_LEN - SGM_PADDING) != 0)
segments++;
int segments = listSegments->count();
msgData.setParam("Segmentation.ProtocolClass",msgData.getValue(YSTRING("ProtocolClass")));
if (isSCLCMessage(type))
msgData.setParam("ProtocolClass","1"); // Segmentation is useing in sequence delivery option
if (isSCLCMessage(msgType))
msgData.setParam("ProtocolClass","1"); // Segmentation is using in sequence delivery option
bool msgReturn = msgData.getBoolValue(YSTRING("MessageReturn"),false);
int dataLength = data.length();
int totalSent = 0;
int sls = msgData.getIntValue(YSTRING("sls"),-1);
sls = msgData.getIntValue(YSTRING("sls"),-1);
// Transmit first segment
SS7MsgSCCP* msg = new SS7MsgSCCP(type);
SS7MsgSCCP* msg = new SS7MsgSCCP(msgType);
msg->params().copyParams(msgData);
DataBlock temp;
if (dataLength - MAX_DATA_LEN > 2)
temp.assign(data.data(0,MAX_DATA_LEN),MAX_DATA_LEN,false);
else
temp.assign(data.data(0,MAX_DATA_LEN - SGM_PADDING),MAX_DATA_LEN - SGM_PADDING,false);
SS7SCCPDataSegment* sg = getAndRemoveDataSegment(listSegments);
if (!sg) {
Debug(DebugStub,"Unable to extract first data segment!!!");
TelEngine::destruct(msg);
TelEngine::destruct(listSegments);
return -1;
}
sg->fillSegment(temp,*data);
msg->params().setParam("Segmentation.RemainingSegments",
String(isSCLCMessage(type) ? --segments : 0));
String(isSCLCMessage(msgType) ? --segments : 0));
msg->params().setParam("Segmentation.FirstSegment","true");
msg->setData(&temp);
dataLength -= temp.length();
totalSent += temp.length();
DDebug(this,DebugNote,"Sending first segment sl = %d, dl = %d, ts = %d",temp.length(),dataLength,totalSent);
unlock();
sls = transmitMessage(msg);
SS7MSU* msu = buildMSU(msg,label,false);
msg->removeData();
temp.clear(false);
if (!msu) {
Debug(this,DebugGoOn,"Failed to build msu from sccpMessage %s",
SS7MsgSCCP::lookup(msgType));
TelEngine::destruct(msg);
TelEngine::destruct(listSegments);
return -1;
}
printMessage(msu,msg,label);
unlock();
sls = transmitMSU(*msu,label,sls);
#ifdef DEBUG
if (sls < 0)
Debug(this,DebugNote,"Failed to transmit message %s. %d",
SS7MsgSCCP::lookup(msgType),sls);
#endif
TelEngine::destruct(msu);
TelEngine::destruct(msg);
TelEngine::destruct(sg);
if (sls < 0) {
if (msgReturn && !local)
returnMessage(origMsg,MtpFailure);
Debug(this,DebugNote,"Failed to transmit first segment of message");
TelEngine::destruct(listSegments);
return sls;
}
if (isSCLCSMessage(type))
if (isSCLCSMessage(msgType)) {
TelEngine::destruct(listSegments);
return sls;
}
lock();
msgData.setParam("Segmentation.FirstSegment","false");
// Set message return option only for the first segment
msgData.setParam("MessageReturn","false");
while (dataLength > 0) {
msg = new SS7MsgSCCP(type);
while ((sg = getAndRemoveDataSegment(listSegments))) {
msg = new SS7MsgSCCP(msgType);
msg->params().copyParams(msgData);
if (dataLength - MAX_DATA_LEN - SGM_PADDING > 2) // Make shure that the left segment is longer than 2
temp.assign(data.data(totalSent,MAX_DATA_LEN - SGM_PADDING),MAX_DATA_LEN - SGM_PADDING,false);
else if (dataLength - MAX_DATA_LEN - 2 * SGM_PADDING > 2)
temp.assign(data.data(totalSent,MAX_DATA_LEN - 2* SGM_PADDING),MAX_DATA_LEN - 2* SGM_PADDING,false);
else
temp.assign(data.data(totalSent,dataLength),dataLength,false); // Should be last segment
sg->fillSegment(temp,*data);
TelEngine::destruct(sg);
msg->params().setParam("Segmentation.RemainingSegments",String(--segments));
msg->setData(&temp);
dataLength -= temp.length();
totalSent += temp.length();
DDebug(this,DebugNote,"Sending segment: %d sl = %d, dl = %d, ts = %d",segments,temp.length(),dataLength,totalSent);
unlock();
sls = transmitMessage(msg);
SS7MSU* msu = buildMSU(msg,label,false);
msg->removeData();
temp.clear(false);
if (!msu) {
Debug(this,DebugGoOn,"Failed to build msu from sccpMessage %s",
SS7MsgSCCP::lookup(msgType));
TelEngine::destruct(msg);
TelEngine::destruct(listSegments);
return -1;
}
printMessage(msu,msg,label);
unlock();
sls = transmitMSU(*msu,label,sls);
#ifdef DEBUG
if (sls < 0)
Debug(this,DebugNote,"Failed to transmit message %s. %d",
SS7MsgSCCP::lookup(msgType),sls);
#endif
TelEngine::destruct(msg);
TelEngine::destruct(msu);
if (sls < 0) {
if (msgReturn && !local)
returnMessage(origMsg,MtpFailure);
Debug(this,DebugNote,"Failed to transmit segment of %s message remaining segments %d",
SS7MsgSCCP::lookup(origMsg->type()),segments);
SS7MsgSCCP::lookup(msgType),segments);
return sls;
}
lock();
}
if (segments != 0)
Debug(this,DebugStub,"Bug in segment messsage!! RemainingSegments %d",segments);
Debug(this,DebugStub,"Bug in segment message!! RemainingSegments %d",segments);
TelEngine::destruct(listSegments);
unlock();
return sls;
}
@ -3311,7 +3497,7 @@ SS7MsgSccpReassemble::Return SS7SCCP::reassembleSegment(SS7MsgSCCP* segment,
return ret;
}
SS7MSU* SS7SCCP::buildMSU(SS7MsgSCCP* msg, const SS7Label& label) const
SS7MSU* SS7SCCP::buildMSU(SS7MsgSCCP* msg, const SS7Label& label, bool checkLength) const
{
// see what mandatory parameters we should put in this message
const MsgParams* msgParams = getSccpParams(msg->type());
@ -3341,7 +3527,7 @@ SS7MSU* SS7SCCP::buildMSU(SS7MsgSCCP* msg, const SS7Label& label) const
}
len += param->size;
}
bool ludt = msg->type() == SS7MsgSCCP::LUDT;
bool ludt = msg->isLongDataMessage();
int pointerLen = ludt ? 2 : 1;
// initialize the pointer array offset just past the mandatory fixed part
unsigned int ptr = label.length() + 1 + len;
@ -3395,9 +3581,18 @@ SS7MSU* SS7SCCP::buildMSU(SS7MsgSCCP* msg, const SS7Label& label) const
// remember the offset this parameter will actually get stored
len = msu->length();
unsigned int size = 0;
if (ptype == SS7MsgSCCP::Data || ptype == SS7MsgSCCP::LongData)
if (ptype == SS7MsgSCCP::Data || ptype == SS7MsgSCCP::LongData) {
size = encodeData(this,*msu,msg);
else
if (ptype == SS7MsgSCCP::Data) {
// Data parameter is the last of variable mandatory parameters
// Check if the pointer to variable part may be bigger than 255
// (max unsigned char value)
if (checkLength && ((len + size + MAX_OPT_LEN) > 254)) {
TelEngine::destruct(msu);
return 0;
}
}
} else
size = encodeParam(this,*msu,param,&msg->params(),exclude,prefix);
d = msu->getData(0,len+1);
if (!(size && d)) {
@ -3456,8 +3651,19 @@ SS7MSU* SS7SCCP::buildMSU(SS7MsgSCCP* msg, const SS7Label& label) const
storedLength --;
d[ptr] = storedLength & 0xff;
d[ptr+1] = storedLength >> 8;
} else
} else {
// Do not try to set the pointer to optional parameters
// if is bigger than max unsigned char value because will
// result in a malformed packet!
if (storedLength > 255) {
Debug(this,checkLength ? DebugAll : DebugStub,
"Build MSU the pointer to optional parameters is bigger than 255!!!! %d",
storedLength);
TelEngine::destruct(msu);
return 0;
}
d[ptr] = storedLength;
}
len = 0;
}
}
@ -3547,6 +3753,7 @@ bool SS7SCCP::processMSU(SS7MsgSCCP::Type type, const unsigned char* paramPtr,
m_totalReceived++;
int protocolClass = msg->params().getIntValue(YSTRING("ProtocolClass"), -1);
if (isSCOCMsg(msg->type())) {
Debug(DebugWarn,"Received Connection oriented message!!");
if (msg->type() != SS7MsgSCCP::CR) { // Received Connection Oriented message other than Connect Request
// Drop the message
DDebug(this,DebugNote,"Received message %s without a connection!",SS7MsgSCCP::lookup(msg->type()));
@ -3930,7 +4137,7 @@ bool SS7SCCP::decodeMessage(SS7MsgSCCP* msg, SS7PointCode::Type pcType,
paramLen -= param->size;
} // while ((ptype = *plist++)...
bool mustWarn = true;
bool ludt = msg->type() == SS7MsgSCCP::LUDT;
bool ludt = msg->isLongDataMessage();
// next decode any mandatory variable parameters the message should have
while ((ptype = *plist++) != SS7MsgSCCP::EndOfParameters) {
mustWarn = false;

View File

@ -5756,13 +5756,18 @@ public:
/**
* Constructor
* @param packed The packed value of the destination point code
* @param type The destination point code type
* @param priority Optional value of the network priority
* @param shift SLS right shift to apply for balancing between linksets
* @param maxDataLength The maximum data that can be transported on this
* route
*/
inline SS7Route(unsigned int packed, unsigned int priority = 0, unsigned int shift = 0)
: Mutex(true,"SS7Route"),
m_packed(packed), m_priority(priority), m_shift(shift),
m_state(Unknown), m_buffering(0), m_congCount(0), m_congBytes(0)
inline SS7Route(unsigned int packed, SS7PointCode::Type type,
unsigned int priority = 0, unsigned int shift = 0,
unsigned int maxDataLength = 272)
: Mutex(true,"SS7Route"), m_packed(packed), m_type(type),
m_priority(priority), m_shift(shift),m_maxDataLength(maxDataLength),
m_state(Unknown),m_buffering(0), m_congCount(0),m_congBytes(0)
{ m_networks.setDelete(false); }
/**
@ -5770,10 +5775,10 @@ public:
* @param original The original route
*/
inline SS7Route(const SS7Route& original)
: Mutex(true,"SS7Route"),
m_packed(original.packed()), m_priority(original.priority()),
m_shift(original.shift()), m_state(original.state()),
m_buffering(0), m_congCount(0), m_congBytes(0)
: Mutex(true,"SS7Route"), m_packed(original.packed()),
m_type(original.m_type), m_priority(original.priority()),
m_shift(original.shift()), m_maxDataLength(original.getMaxDataLength()),
m_state(original.state()), m_buffering(0), m_congCount(0), m_congBytes(0)
{ m_networks.setDelete(false); }
/**
@ -5817,6 +5822,13 @@ public:
unsigned int priority() const
{ return m_priority; }
/**
* Get the maximum data length that can be transported on this route
* @return The maximum data length
*/
unsigned int getMaxDataLength() const
{ return m_maxDataLength; }
/**
* Get the packed Point Code of this route
* @return Packed Point Code of the route's destination
@ -5899,8 +5911,10 @@ private:
void rerouteCheck(u_int64_t when);
void rerouteFlush();
unsigned int m_packed; // Packed destination point code
SS7PointCode::Type m_type; // The point code type
unsigned int m_priority; // Network priority for the given destination (used by SS7Layer3)
unsigned int m_shift; // SLS right shift when selecting linkset
unsigned int m_maxDataLength; // The maximum data length that can be transported on this route
ObjList m_networks; // List of networks used to route to the given destination (used by SS7Router)
State m_state; // State of the route
u_int64_t m_buffering; // Time when controlled rerouting ends
@ -6001,6 +6015,7 @@ class YSIG_API SS7Layer3 : virtual public SignallingComponent
YCLASS(SS7Layer3,SignallingComponent)
friend class SS7L3User;
friend class SS7Router; // Access the data members to build the routing table
friend class SS7Route;
public:
/**
* Destructor
@ -6176,6 +6191,15 @@ public:
*/
bool buildRoutes(const NamedList& params);
/**
* Get the maximum data length of a route by packed Point Code.
* This method is thread safe
* @param type Destination point code type
* @param packedPC The packed point code
* @return The maximum data length that can be transported on the route. Maximum msu size (272) if no route to the given point code
*/
unsigned int getRouteMaxLength(SS7PointCode::Type type, unsigned int packedPC);
/**
* Get the priority of a route by packed Point Code.
* This method is thread safe
@ -9435,6 +9459,30 @@ public:
inline Type type() const
{ return m_type; }
/**
* Helper method to change the message type
* @param type The new message type
*/
inline void updateType(Type type)
{ m_type = type; params().assign(lookup(type,"Unknown")); }
/**
* Utility method to verify if this message is a long unit data
* @return True if this message is a long unit data
*/
inline bool isLongDataMessage() const
{ return m_type == LUDT || m_type == LUDTS; }
/**
* Utility method to verify if this message can be a UDT message
* A SCCP message can be an UDT message if it not contains HopCounter parameter
* or other optional parameters
* @return True if this message can be a UDT message
*/
inline bool canBeUDT() const
{ return !(params().getParam(YSTRING("Importance")) ||
params().getParam(YSTRING("HopCounter"))); }
/**
* Fill a string with this message's parameters for debug purposes
* @param dest The destination string
@ -10030,6 +10078,41 @@ public:
};
/**
* Helper class to memorize SCCP data segments
*/
class SS7SCCPDataSegment : public GenObject
{
YCLASS(SS7SCCPDataSegment,GenObject)
public:
/**
* Constructor
* @param index The index in the original DataBlock where this segment starts
* @param length The length of this segment
*/
inline SS7SCCPDataSegment(unsigned int index, unsigned int length)
: m_length(length), m_index(index)
{}
/**
* Destructor
*/
virtual ~SS7SCCPDataSegment()
{}
/**
* Assignees to a DataBlock this segment's data
* @param temp The destination DataBlock segment
* @param orig The original DataBlock where this segment is located
*/
inline void fillSegment(DataBlock& temp, const DataBlock& orig)
{ temp.assign(orig.data(m_index,m_length),m_length,false); }
private:
unsigned int m_length;
unsigned int m_index;
};
/**
* Implementation of SS7 Signalling Connection Control Part
* @short SS7 SCCP implementation
@ -10148,20 +10231,12 @@ public:
/**
* Message changeover procedure for segmentation purpose
* @param data The message data
* @param origMsg The original message
* @param type The destination message type
* @param label MTP3 routing label
* @param local True if the origMsg is local initiated
* @return Negative value if the message failed to be sent
*/
int segmentMessage(DataBlock& data, SS7MsgSCCP* origMsg, SS7MsgSCCP::Type type, bool local = false);
/**
* Check if we can send LUDT messages
* @param label The SS7 routing label
* @return True if we can send LUDT messages
*/
bool canSendLUDT(const SS7Label& label);
int segmentMessage(SS7MsgSCCP* origMsg, const SS7Label& label, bool local);
/**
* Helper method to know if we use ITU or ANSI
@ -10282,6 +10357,18 @@ protected:
virtual bool isEndpoint()
{ return m_endpoint; }
private:
// Helper method to calculate sccp address length
unsigned int getAddressLength(const NamedList& params, const String& prefix);
// Helper method used to ajust HopCounter and Importance parameters
void ajustMessageParams(NamedList& params, SS7MsgSCCP::Type type);
// Obtain maximum data length for a UDT, XUDT and LUDT message
void getMaxDataLen(const SS7MsgSCCP* msg, const SS7Label& label,
unsigned int& udtLength, unsigned int& xudtLength, unsigned int& ludtLength);
// Helper method to obtain data segments
// NOTE The list must be destroyed
ObjList* getDataSegments(unsigned int dataLength, unsigned int maxSegmentSize);
// Helper method to print a SCCP message
void printMessage(const SS7MSU* msu, const SS7MsgSCCP* msg, const SS7Label& label);
// Helper method used to extract the pointcode from Calling/Called party address.
// Also will call GT translate if there is no pointcode in called party address
bool fillPointCode(SS7PointCode& pointcode, SS7MsgSCCP* msg, const String& prefix, const char* pCode, bool translate);
@ -10318,7 +10405,7 @@ private:
void printStatus(bool extended);
void setNetworkUp(bool operational);
SS7MSU* buildMSU(SS7MsgSCCP* msg, const SS7Label& label) const;
SS7MSU* buildMSU(SS7MsgSCCP* msg, const SS7Label& label, bool checkLength = true) const;
bool routeSCLCMessage(SS7MsgSCCP*& msg, const SS7Label& label);
// Member data
SS7PointCode::Type m_type; // Point code type of this SCCP
@ -10330,7 +10417,7 @@ private:
u_int32_t m_segTimeout; // Time in milliseconds for segmentation timeout
bool m_ignoreUnkDigits; // Check if GT digit parser of should ignore unknown digits encoding
bool m_layer3Up; // Flag used to verify if the network is operational
bool m_supportLongData; // Flag used to check if this sccp can send LUDT messages
unsigned int m_maxUdtLength; // The maximum length of data packet transported in a UDT message
u_int32_t m_totalSent; // Counter of the total number of SCCP messages sent
u_int32_t m_totalReceived; // The number of incoming sccp messages
u_int32_t m_errors; // Counter of the number of messages that failed to be delivered