yate/libs/ysig/sccp.cpp

5002 lines
165 KiB
C++

/**
* sccp.cpp
* This file is part of the YATE Project http://YATE.null.ro
*
* Yet Another Signalling Stack - implements the support for SS7, ISDN and PSTN
*
* 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 "yatesig.h"
#include <yatephone.h>
#include<stdlib.h>
#include <string.h>
using namespace TelEngine;
#define MAX_MANDATORY_PARAMS 16
// 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
#define MAX_DATA_ITU 3952
#define MAX_DATA_ANSI 3904
static const char* s_userMutexName = "SCCPUserTransport";
static const char* s_sccpMutexName = "SCCPUserList";
static const char* s_managementMutexName = "SCCPManagement";
static const char* s_sccpTranslatorMutex = "SCCPTranslator";
static const char* s_sccpSubsystems = "SccpSubsystems";
static const char* s_sccpRemote = "SccpRemote";
struct SCCPParam {
// numeric type of the parameter
SS7MsgSCCP::Parameters type;
// size in octets, zero for variable
unsigned char size;
// SS7 name of the parameter
const char* name;
// decoder callback function
bool (*decoder)(const SS7SCCP*,NamedList&,const SCCPParam*,
const unsigned char*,unsigned int,const String&);
// encoder callback function
unsigned char (*encoder)(const SS7SCCP*,SS7MSU&,unsigned char*,
const SCCPParam*,const NamedString*,const NamedList*,const String&);
// table data to be used by the callback
const void* data;
};
struct MsgParams {
// type of the message described
SS7MsgSCCP::Type type;
// does the message support optional part?
bool optional;
// parameters, fixed then variable, separated/terminated by EndOfParameters
// using an array is a (moderate) waste of space
const SS7MsgSCCP::Parameters params[MAX_MANDATORY_PARAMS];
};
static const TokenDict s_return_cause[] = {
{ "No translation for an address of such nature", SS7SCCP::NoTranslationAddressNature },
{ "No translation for this specific address", SS7SCCP::NoTranslationSpecificAddress },
{ "Subsystem congestion", SS7SCCP::SubsystemCongestion },
{ "Subsystem failure", SS7SCCP::SubsystemFailure },
{ "Unequipped user", SS7SCCP::UnequippedUser },
{ "MTP failure", SS7SCCP::MtpFailure },
{ "Network Congestion", SS7SCCP::NetworkCongestion },
{ "Unqualified", SS7SCCP::Unqualified },
{ "Error in message transport", SS7SCCP::ErrorInMessageTransport },
{ "Error in local processing", SS7SCCP::ErrorInLocalProcessing },
{ "Destination can not perform reassembly", SS7SCCP::DestinationCanNotPerformReassembly },
{ "SCCP failure", SS7SCCP::SccpFailure },
{ "Hop counter violation", SS7SCCP::HopCounterViolation },
{ "Segmentation not supported", SS7SCCP::SegmentationNotSupported },
{ "Segmentation failure", SS7SCCP::SegmentationFailure },
// ANSI only
{ "Message change failure", SS7SCCP::MessageChangeFailure },
{ "Invalid INS routing request", SS7SCCP::InvalidINSRoutingRequest },
{ "Invalid ISNI routing request", SS7SCCP::InvalidISNIRoutingRequest },
{ "Unauthorized message", SS7SCCP::UnauthorizedMessage },
{ "Message incompatibility", SS7SCCP::MessageIncompatibility },
{ "Can not perform ISNI constrained routing", SS7SCCP::NotSupportedISNIRouting },
{ "Redundant ISNI constrained routing information", SS7SCCP::RedundantISNIConstrainedRouting },
{ "Unable to perform ISNI identification", SS7SCCP::ISNIIdentificationFailed },
{ 0, 0 }
};
static const TokenDict s_managementMessages[] = {
{ "SSA", SCCPManagement::SSA }, // Subsystem-allowed
{ "SSP", SCCPManagement::SSP }, // Subsystem-prohibited
{ "SST", SCCPManagement::SST }, // Subsystem-status-test
{ "SOR", SCCPManagement::SOR }, // Subsystem-out-of-service-request
{ "SOG", SCCPManagement::SOG }, // Subsystem-out-of-service-grant
{ "SSC", SCCPManagement::SSC }, // SCCP/Subsystem-congested (ITU only)
{ "SBR", SCCPManagement::SBR }, // Subsystem-backup-routing (ANSI only)
{ "SNR", SCCPManagement::SNR }, // Subsystem-normal-routing (ANSI only)
{ "SRT", SCCPManagement::SRT }, // Subsystem-routing-status-test (ANSI only)
{ 0, 0 }
};
static const TokenDict s_dict_control[] = {
{ "status", SS7SCCP::Status },
{ "full-status", SS7SCCP::FullStatus },
{ "enable-extended-monitoring", SS7SCCP::EnableExtendedMonitoring },
{ "disable-extended-monitoring", SS7SCCP::DisableExtendedMonitoring },
{ "enable-print-messages", SS7SCCP::EnablePrintMsg },
{ "disable-print-messages", SS7SCCP::DisablePrintMsg },
{ 0, 0 }
};
const TokenDict SCCPManagement::s_broadcastType[] = {
{ "UserOutOfService", SCCPManagement::UserOutOfService },
{ "UserInService", SCCPManagement::UserInService },
{ "SignallingPointInaccessible", SCCPManagement::PCInaccessible },
{ "SignallingPointAccessible", SCCPManagement::PCAccessible },
{ "RemoteSCCPInaccessible", SCCPManagement::SccpRemoteInaccessible },
{ "RemoteSCCPAccessible", SCCPManagement::SccpRemoteAccessible },
{ "SignallingPointCongested", SCCPManagement::PCCongested },
};
static const TokenDict s_sccpNotif[] = {
{ "Coordinate Request", SCCP::CoordinateRequest }, // (User->SCCP)
{ "Coordinate Confirm", SCCP::CoordinateConfirm }, // (SCCP->User)
{ "Coordinate Indication", SCCP::CoordinateIndication }, // (SCCP->User)
{ "Coordinate Response", SCCP::CoordinateResponse }, // (User->SCCP)
{ "Status Indication", SCCP::StatusIndication }, // (SCCP->User)
{ "Status Request", SCCP::StatusRequest }, // (User->SCCP)
{ "PointCode Status Indication", SCCP::PointCodeStatusIndication }, // (SCCP->User)
{ "Trafic Indication", SCCP::TraficIndication },
{ "Subsystem Status", SCCP::SubsystemStatus }, // (SCCP->User)
{ 0, 0 }
};
static const TokenDict s_numberingPlan[] = {
{ "unknown", 0x00 },
{ "isdn", 0x01 },
{ "e164", 0x01 },
{ "generic", 0x02 },
{ "data", 0x03 },
{ "x121", 0x03 },
{ "telex", 0x04 },
{ "maritime-mobile", 0x05 },
{ "e210", 0x05 },
{ "e211", 0x05 },
{ "land-mobile", 0x06 },
{ "e212", 0x06 },
{ "isdn-mobile", 0x07 },
{ "e214", 0x07 },
{ "network-specific", 0x0e },
{ 0, 0 }
};
static const TokenDict s_nai[] = {
{ "unknown", 0x00 },
{ "subscriber", 0x01 },
{ "national-reserved", 0x02 },
{ "national-significant", 0x03 },
{ "international", 0x04 },
{ 0, 0 }
};
static const TokenDict s_encodingScheme[] = {
{ "unknown", 0x00 },
{ "bcd", 0x01 },
{ "bcd", 0x02 },
{ 0, 0 }
};
static const TokenDict s_ansiSmi[] = {
{ "unknown", 0x00 },
{ "solitary", 0x01 },
{ "duplicated", 0x02 },
{ 0 , 0 }
};
const TokenDict SCCPManagement::s_states[] = {
{ "allowed", SCCPManagement::Allowed },
{ "prohibited", SCCPManagement::Prohibited },
{ "wait-for-grant", SCCPManagement::WaitForGrant },
{ "ignore-tests", SCCPManagement::IgnoreTests },
{ "unknown", SCCPManagement::Unknown },
{ 0 , 0 }
};
const TokenDict s_messageReturn[] = {
{ "false", 0x00 },
{ "true", 0x08 },
{ "yes", 0x08 },
{ "on", 0x08 },
{ "enable",0x08 },
{ 0, 0 }
};
static bool compareLabel(const SS7Label& l1, const SS7Label& l2)
{
if (l1.opc() != l2.opc())
return false;
if (l1.dpc() != l2.dpc())
return false;
return true;
}
// Helper method increments a number stored in a string
static void incrementNS(NamedString* ns)
{
if (!ns)
return;
int counter = ns->toInteger();
counter++;
*ns = String(counter);
}
static bool compareNamedList(const NamedList& nl1, const NamedList& nl2)
{
if (nl1.length() != nl2.length())
return false;
NamedIterator iter(nl1);
while (const NamedString* pr = iter.get()) {
const NamedString* pr2 = nl2.getParam(pr->name());
if (!pr2 || (*pr2 != *pr))
return false;
}
return true;
}
static void getDictValue(NamedList& list, const char* paramName, int val, const TokenDict* dict)
{
NamedString* ns = new NamedString(paramName);
*ns = lookup(val,dict,0);
if (ns->null()) {
*ns = String(val).c_str();
}
list.setParam(ns);
}
static bool decodeRaw(const SS7SCCP* sccp, NamedList& list, const SCCPParam* param,
const unsigned char* buf, unsigned int len, const String& prefix)
{
if (len < 1)
return false;
String raw;
raw.hexify((void*)buf,len,' ');
DDebug(sccp,DebugInfo,"decodeRaw decoded %s=%s",param->name,raw.c_str());
list.addParam(prefix+param->name,raw);
return true;
}
// Raw decoder for unknown/failed parameter, dumps raw octets
static bool decodeRawParam(const SS7SCCP* sccp, NamedList& list, unsigned char value,
const unsigned char* buf, unsigned int len, const String& prefix)
{
String name("Param_");
name << value;
SCCPParam p;
p.type = (SS7MsgSCCP::Parameters)value;
p.size = len;
p.name = name;
p.decoder = 0;
p.encoder = 0;
p.data = 0;
return decodeRaw(sccp,list,&p,buf,len,prefix);
};
static bool decodeInt(const SS7SCCP* sccp, NamedList& list, const SCCPParam* param,
const unsigned char* buf, unsigned int len, const String& prefix)
{
unsigned int val = 0;
int shift = 0;
while (len--) {
val |= ((unsigned int)*buf++) << shift;
shift += 8;
}
DDebug(sccp,DebugAll,"decodeInt decoded %s=%s (%u)",param->name,lookup(val,(const TokenDict*)param->data),val);
SignallingUtils::addKeyword(list,prefix+param->name,(const TokenDict*)param->data,val);
return true;
}
static bool decodeProtocolClass(const SS7SCCP* sccp, NamedList& list, const SCCPParam* param,
const unsigned char* buffer, unsigned int len, const String& prefix)
{
unsigned char protocol = *buffer++;
unsigned int pClass = protocol & 0x0f;
if (pClass > 3) {
Debug(sccp,DebugWarn,"Received Invalid Protocol Class %d",pClass);
return false;
}
if (pClass < 2) // Protocol class 0 | 1 check return option
getDictValue(list,prefix + "MessageReturn",protocol >> 4,s_messageReturn);
list.setParam(prefix + param->name, String(pClass));
return true;
}
static bool decodeCause(const SS7SCCP* sccp, NamedList& list, const SCCPParam* param,
const unsigned char* buffer, unsigned int len, const String& prefix)
{
if (len < 1)
return false;
unsigned char cause = *buffer++;
list.setParam(prefix + param->name,String(cause));
return true;
}
static bool decodeImportance(const SS7SCCP* sccp, NamedList& list, const SCCPParam* param,
const unsigned char* buffer, unsigned int len, const String& prefix)
{
if (len < 1)
return false;
int importance = *buffer++ & 0x07;
list.setParam(prefix + "Importance",String(importance));
return true;
}
static void getDigits(String& num, bool oddNum, const unsigned char* buf, unsigned int len,
bool ignoreUnk)
{
static const char digits1[] = "0123456789\0BC\0\0.";
static const char digits2[] = "0123456789ABCDE.";
const char* digits = ignoreUnk ? digits1 : digits2;
for (unsigned int i = 0; i < len; i++) {
num += digits[buf[i] & 0x0f];
if (oddNum && ((i+1) == len))
break;
num += digits[buf[i] >> 4];
}
}
// Decode methods
static bool decodeItuAddress(const SS7SCCP* sccp, NamedList& params,const SCCPParam* param,
const unsigned char* buffer, unsigned int length, const String& prefix)
{
unsigned char addressIndicator = *buffer++;
length--;
String prName = prefix + param->name;
while (true) {
if ((addressIndicator & 0x01) == 0x01) { // Have Pointcode
if (length < 2)
break;
int pointcode = *buffer++;
pointcode |= (*buffer++ & 0x3f) << 8;
params.addParam(prName + ".pointcode",String(pointcode));
length -= 2;
}
if ((addressIndicator & 0x02) == 0x02) { // Have SSN
if (length < 1)
break;
unsigned char ssn = *buffer++;
params.addParam(prName + ".ssn",String(ssn));
length --;
}
params.addParam(prName + ".route", ((addressIndicator & 0x40) == 0x40)
? YSTRING("ssn") : YSTRING("gt"));
unsigned char gti = (addressIndicator >> 2) & 0x0f;
if (!gti) // No Global Title Present
return true;
bool odd = false;
String tmp;
String gtName = prName + ".gt";
if (gti == 0x01) { // GT includes Nature Of Address Indicator
if (length < 1) {
break;
}
unsigned char nai = *buffer++;
length--;
getDictValue(params,gtName + ".nature", nai & 0x7f,s_nai);
odd = (nai & 0x80) != 0;
} else if (gti == 0x02) { // GT includes Translation Type
if (length < 1)
break;
params.addParam(gtName + ".translation", String((int)*buffer++));
length--;
tmp.hexify((void*)buffer,length,' ');
} else if (gti == 0x03) { // GT includes tt, np & es
if (length < 2)
break;
params.addParam(gtName+ ".translation", String((int)*buffer++));
length--;
unsigned char npes = *buffer++;
length--;
getDictValue(params,gtName + ".plan", npes >> 4,s_numberingPlan);
unsigned int es = npes & 0x0f;
getDictValue(params,gtName + ".encoding", es,s_encodingScheme);
switch (es) {
case 1:
odd = true;
case 2:
break;
default:
tmp.hexify((void*)buffer,length,' ');
}
} else if (gti == 0x04) { // GT includes tt, np, es & nai
if (length < 3)
break;
params.addParam(gtName+ ".translation", String((int)*buffer++));
length--;
unsigned char npes = *buffer++;
unsigned char es = npes & 0x0f;
length--;
getDictValue(params,gtName + ".plan", npes >> 4,s_numberingPlan);
getDictValue(params,gtName + ".encoding", es,s_encodingScheme);
getDictValue(params,gtName + ".nature", *buffer++ & 0x7f,s_nai);
length--;
switch (es) {
case 1:
odd = true;
case 2:
break;
default:
tmp.hexify((void*)buffer,length,' ');
}
} else {
Debug(sccp,DebugMild, "Unable to decode ITU GT with GTI = %d",gti);
return false;
}
if (tmp.null())
getDigits(tmp,odd,buffer,length,sccp && sccp->ignoreUnknownAddrSignals());
params.addParam(gtName, tmp);
return true;
}
Debug(sccp,DebugWarn,"Failed to decode ITU address!!! short message length");
return false;
}
static bool decodeAnsiAddress(const SS7SCCP* sccp, NamedList& params,const SCCPParam* param,
const unsigned char* buffer, unsigned int length, const String& prefix)
{
unsigned char addressIndicator = *buffer++;
length--;
String prName = prefix + param->name;
while (true) {
if ((addressIndicator & 0x01) == 0x01) { // Have SSN
if (length < 1)
break;
params.addParam(prName + ".ssn",String(*buffer++));
length --;
}
if ((addressIndicator & 0x02) == 0x02) { // Have Pointcode
if (length < 3)
break;
unsigned int pointcode = *buffer++;
pointcode |= (*buffer++ << 8);
pointcode |= (*buffer++ << 16);
length -= 3;
params.addParam(prName + ".pointcode",String(pointcode));
}
params.addParam(prName + ".route", ((addressIndicator & 0x40) == 0x40) ?
YSTRING("ssn") : YSTRING("gt"));
unsigned char gti = (addressIndicator >> 2) & 0x0f;
if (!gti) // No Global Title Present
return true;
bool odd = false;
String tmp;
String gtName = prName + ".gt";
if (gti == 0x01) { // GT includes tt, np & es
if (length < 2)
break;
params.addParam(gtName + ".translation", String((int)*buffer++));
length--;
unsigned char npes = *buffer++;
unsigned char es = npes & 0x0f;
length--;
getDictValue(params,gtName + ".plan", npes >> 4, s_numberingPlan);
getDictValue(params,gtName + ".encoding", es,s_encodingScheme);
switch (es) {
case 1:
odd = true;
case 2:
break;
default:
tmp.hexify((void*)buffer,length,' ');
}
} else if (gti == 0x02) { // GT includes Translation Type
if (length < 1)
break;
params.addParam(gtName + ".translation", String((int)*buffer++));
length--;
tmp.hexify((void*)buffer,length,' ');
} else {
Debug(sccp,DebugMild, "Unable to decode ANSI GT with GTI = %d",gti);
return false;
}
if (tmp.null())
getDigits(tmp,odd,buffer,length,sccp && sccp->ignoreUnknownAddrSignals());
params.addParam(gtName, tmp);
return true;
}
Debug(sccp,DebugWarn,"Failed to decode ANSI address!!! short message length");
return false;
}
static bool decodeAddress(const SS7SCCP* sccp, NamedList& paramsList,const SCCPParam* param,
const unsigned char* buffer, unsigned int length, const String& prefix)
{
if (length < 1)
return false;
if (sccp->ITU())
return decodeItuAddress(sccp,paramsList,param,buffer,length,prefix);
else
return decodeAnsiAddress(sccp,paramsList,param,buffer,length,prefix);
}
static bool decodeData(const SS7SCCP* sccp, SS7MsgSCCP* msg, const unsigned char* buffer, unsigned int length)
{
DataBlock* data = new DataBlock((void*)buffer,length, false);
msg->setData(data);
buffer += length;
return true;
}
static bool decodeSegmentation(const SS7SCCP* sccp, NamedList& params,const SCCPParam* param,
const unsigned char* buffer, unsigned int length, const String& prefix)
{
if (length < 4) {
DDebug(sccp,DebugNote,"Failed to decode %s parameter! Reason length to short.",param->name);
return false;
}
unsigned char segInfo = *buffer++;
String prName = prefix + param->name;
params.addParam(prName + ".FirstSegment", String((segInfo & 0x80) == 0x80));
params.addParam(prName + ".ProtocolClass", String((segInfo & 0x40) >> 6));
params.addParam(prName + ".RemainingSegments", String(segInfo & 0x0f));
unsigned int segLocalReference = *buffer++;
segLocalReference |= *buffer++ << 8;
segLocalReference |= *buffer++ << 16;
params.addParam(prName + ".SegmentationLocalReference", String(segLocalReference));
params.addParam(prName ,"true");
return true;
}
// Encode methods
static unsigned char encodeRaw(const SS7SCCP* sccp, SS7MSU& msu,
unsigned char* buf, const SCCPParam* param, const NamedString* val,
const NamedList*, const String&)
{
if (!(param && val))
return 0;
DDebug(sccp,DebugInfo,"encodeRaw encoding %s=%s",param->name,val->c_str());
DataBlock raw;
if (!raw.unHexify(val->c_str(),val->length(),' ')) {
DDebug(sccp,DebugMild,"encodeRaw failed: invalid string");
return 0;
}
if (!raw.length() || raw.length() > 254 ||
(param->size && param->size != raw.length())) {
DDebug(sccp,DebugMild,"encodeRaw failed: param size=%u data length=%u",
param->size,raw.length());
return 0;
}
if (buf) {
::memcpy(buf,raw.data(),raw.length());
return raw.length();
}
unsigned char size = (unsigned char)raw.length();
msu.append(&size,1);
msu += raw;
return size;
}
// Encoder for fixed length little-endian integer values
static unsigned char encodeInt(const SS7SCCP* sccp, SS7MSU& msu, unsigned char* buf,
const SCCPParam* param, const NamedString* val, const NamedList*, const String&)
{
if (!param)
return 0;
unsigned int n = param->size;
if (!n)
return 0;
unsigned int v = 0;
if (val)
v = val->toInteger((const TokenDict*)param->data);
DDebug(sccp,DebugAll,"encodeInt encoding %s=%u on %u octets",param->name,v,n);
if (!buf) {
unsigned int l = msu.length();
DataBlock dummy(0,n+1);
msu += dummy;
buf = (unsigned char*)msu.getData(l,n+1);
*buf++ = n & 0xff;
}
while (n--) {
*buf++ = v & 0xff;
v >>= 8;
}
return param->size;
}
static unsigned char encodeProtocolClass(const SS7SCCP* sccp, SS7MSU& msu,
unsigned char* buf, const SCCPParam* param, const NamedString* val,
const NamedList* extra, const String& prefix)
{
if (!buf) {
Debug(sccp,DebugWarn,"Request to encode ProtocolClass in a null buffer!!!");
return 0;
}
unsigned char protocolClass = extra->getIntValue(prefix + "ProtocolClass");
if (protocolClass > 3) {
Debug(sccp,DebugWarn,"Invalid ProtocolClass value %d, for encoding",protocolClass);
return 0;
}
if (protocolClass < 2) {
int errorReturn = extra->getIntValue(prefix + "MessageReturn",s_messageReturn);
protocolClass |= errorReturn << 4;
}
*buf = protocolClass;
return 1;
}
static DataBlock* setDigits(const char* val)
{
if (!val)
return 0;
unsigned char buf[32];
unsigned int len = 0;
bool odd = false;
while (val && (len < sizeof(buf))) {
char c = *val++;
if (!c)
break;
unsigned char n = 0;
if (('0' <= c) && (c <= '9'))
n = c - '0';
else if ('.' == c)
n = 15;
else if ('A' == c)
n = 10;
else if ('B' == c)
n = 11;
else if ('C' == c)
n = 12;
else if ('D' == c)
n = 13;
else if ('E' == c)
n = 14;
else
continue;
odd = !odd;
if (odd)
buf[len] = n;
else
buf[len++] |= (n << 4);
}
if (odd)
len++;
DataBlock* tmp = new DataBlock(buf,len);
return tmp;
}
static unsigned char encodeItuAddress(const SS7SCCP* sccp, SS7MSU& msu,
unsigned char* buf, const SCCPParam* param, const NamedString* val,
const NamedList* extra, const String& prefix)
{
unsigned char length = 1;
unsigned char data[32];
unsigned char addressIndicator = 0;
String preName(prefix + param->name);
bool havePC = extra->getParam(preName + ".pointcode") != 0;
if (havePC) {
int pointcode = extra->getIntValue(preName + ".pointcode",0);
addressIndicator |= 0x01;
data[++length] = pointcode & 0xff;
data[++length] = (pointcode >> 8) & 0x3f;
}
bool haveSSN = extra->getParam(preName + ".ssn") != 0;
if (haveSSN) {
int ssn = extra->getIntValue(preName + ".ssn",0);
addressIndicator |= 0x02;
data[++length] = ssn;
}
NamedString* route = extra->getParam(preName + ".route");
if (route && *route == YSTRING("ssn")) { // Marck route on SSN
if (param->name == YSTRING("CalledPartyAddress") && !haveSSN)
Debug(sccp,DebugNote,"Request to route on SSN with no ssn present!");
addressIndicator |= 0x40;
}
NamedString* gtNr = YOBJECT(NamedString,extra->getParam(preName + ".gt"));
if (!gtNr) { // No Global Title present!!!
if ((addressIndicator & 0x40) == 0)
DDebug(sccp,DebugNote,"RouteIndicator set on global title. But no global title present!!!");
data[1] = addressIndicator;
data[0] = length;
DataBlock tmp(data,length + 1,false);
msu += tmp;
tmp.clear(false);
return data[0];
}
NamedString* nature = YOBJECT(NamedString,extra->getParam(preName + ".gt.nature"));
NamedString* translation = YOBJECT(NamedString,extra->getParam(preName + ".gt.translation"));
NamedString* plan = YOBJECT(NamedString,extra->getParam(preName + ".gt.plan"));
NamedString* encoding = YOBJECT(NamedString,extra->getParam(preName + ".gt.encoding"));
bool odd = false;
DataBlock* digits = 0;
if (nature && !translation) { // GT = 0x01
addressIndicator |= 0x04;
int nai = nature->toInteger(s_nai);
odd = (gtNr->length() % 2) ? false : true;
if (!odd)
nai |= 0x80;
data[++length] = nai & 0xff;
} else if (translation && !(plan && encoding) && !nature) { // GT = 0x02
addressIndicator |= 0x08;
int tt = translation->toInteger();
data[++length] = tt & 0xff;
digits = new DataBlock();
if (!digits->unHexify(*gtNr,gtNr->length(),' ')) {
Debug(sccp,DebugInfo,"Setting unknown odd/even number of digits!!");
TelEngine::destruct(digits);
}
} else if (translation && plan && encoding && !nature) { // GT = 0x03
addressIndicator |= 0x0c;
int tt = translation->toInteger();
data[++length] = tt & 0xff;
int np = plan->toInteger(s_numberingPlan);
int es = encoding->toInteger(s_encodingScheme);
switch (es) {
case 1:
case 2:
odd = (gtNr->length() % 2 == 1);
es = odd ? 1 : 2;
break;
default:
digits = new DataBlock();
if (!digits->unHexify(*gtNr,gtNr->length(),' ')) {
Debug(sccp,DebugInfo,"Setting unknown odd/even number of digits!!");
TelEngine::destruct(digits);
}
}
data[++length] = ((np & 0x0f) << 4) | (es & 0x0f);
} else if (translation && plan && encoding && nature) { // GT = 0x04
addressIndicator |= 0x10;
int tt = translation->toInteger();
data[++length] = tt & 0xff;
int np = plan->toInteger(s_numberingPlan);
int es = encoding->toInteger(s_encodingScheme);
switch (es) {
case 1:
case 2:
odd = (gtNr->length() % 2 == 1);
es = odd ? 1 : 2;
break;
default:
digits = new DataBlock();
if (!digits->unHexify(*gtNr,gtNr->length(),' ')) {
Debug(sccp,DebugInfo,"Setting unknown odd/even number of digits!!");
TelEngine::destruct(digits);
}
}
data[++length] = ((np & 0x0f) << 4) | (es & 0x0f);
int nai = nature->toInteger(s_nai);
data[++length] = nai & 0x7f;
} else {
Debug(sccp,DebugWarn,"Can not encode ITU GTI. Unknown GTI value for : nai= %s, Plan & Encoding = %s, TranslationType = %s",
nature? "present" : "missing",(plan && encoding)? "present" : "missing",translation ? "present" : "missing");
return 0;
}
data[1] = addressIndicator;
if (!digits && !(digits = setDigits(*gtNr))) {
Debug(DebugWarn,"Failed to encode digits!!");
return 0;
}
data[0] = length + digits->length();
DataBlock tmp(data,length + 1,false);
msu += tmp;
msu += *digits;
tmp.clear(false);
TelEngine::destruct(digits);
return data[0];
}
static unsigned char encodeAnsiAddress(const SS7SCCP* sccp, SS7MSU& msu,
unsigned char* buf, const SCCPParam* param, const NamedString* val,
const NamedList* extra, const String& prefix)
{
unsigned char length = 1;
unsigned char data[32];
unsigned char addressIndicator = 0;
String preName(prefix + param->name);
bool havePC = extra->getParam(preName + ".pointcode") != 0;
bool haveSSN = extra->getParam(preName + ".ssn") != 0;
if (haveSSN) {
int ssn = extra->getIntValue(preName + ".ssn",0);
addressIndicator |= 0x01;
data[++length] = ssn;
}
addressIndicator |= 0x80; // Mark the 8 bit from address indicator to national use
if (havePC) {
int pointcode = extra->getIntValue(preName + ".pointcode",0);
addressIndicator |= 0x02;
data[++length] = pointcode & 0xff;
data[++length] = (pointcode >> 8) & 0xff;
data[++length] = (pointcode >> 16) & 0xff;
}
NamedString* route = extra->getParam(preName + ".route");
if (route && *route == YSTRING("ssn")) { // Marck route on SSN
if (param->name == YSTRING("CalledPartyAddress") && !haveSSN)
Debug(sccp,DebugNote,"Request to route on SSN with no ssn present!");
addressIndicator |= 0x40;
}
NamedString* gtNr = YOBJECT(NamedString,extra->getParam(preName + ".gt"));
if (!gtNr) { // No Global Title present!!!
if ((addressIndicator & 0x40) == 0)
DDebug(sccp,DebugNote,"RouteIndicator set on global title. But no global title present!!!");
data[1] = addressIndicator;
data[0] = length & 0xff;
DataBlock tmp(data,length + 1,false);
msu += tmp;
tmp.clear(false);
return data[0];
}
NamedString* translation = YOBJECT(NamedString,extra->getParam(preName + ".gt.translation"));
NamedString* plan = YOBJECT(NamedString,extra->getParam(preName + ".gt.plan"));
NamedString* encoding = YOBJECT(NamedString,extra->getParam(preName + ".gt.encoding"));
DataBlock* digits = 0;
bool odd = false;
if (translation && !(plan && encoding)) { // GT = 0x02
addressIndicator |= 0x08;
int tt = translation->toInteger();
data[++length] = tt & 0xff;
digits = new DataBlock();
if (!digits->unHexify(*gtNr,gtNr->length(),' ')) {
Debug(sccp,DebugInfo,"Setting unknown odd/even number of digits!!");
TelEngine::destruct(digits);
}
} else if (translation && plan && encoding) { // GT = 0x01
addressIndicator |= 0x04;
int tt = translation->toInteger();
data[++length] = tt & 0xff;
int np = plan->toInteger(s_numberingPlan);
int es = encoding->toInteger(s_encodingScheme);
switch (es) {
case 1:
case 2:
odd = (gtNr->length() % 2 == 1);
es = odd ? 1 : 2;
break;
default:
digits = new DataBlock();
if (!digits->unHexify(*gtNr,gtNr->length(),' ')) {
Debug(sccp,DebugInfo,"Setting unknown odd/even number of digits!!");
TelEngine::destruct(digits);
}
}
data[++length] = ((np & 0x0f) << 4) | (es & 0x0f);
} else {
Debug(sccp,DebugWarn,"Can not encode ANSI GTI. Unknown GTI value for : Plan & Encoding = %s, TranslationType = %s",
(plan && encoding)? "present" : "missing",translation ? "present" : "missing");
return 0;
}
data[1] = addressIndicator;
if (!digits && !(digits = setDigits(*gtNr))) {
Debug(DebugWarn,"Failed to encode digits!!");
return 0;
}
data[0] = length + digits->length();
DataBlock tmp(data,length + 1,false);
msu += tmp;
msu += *digits;
tmp.clear(false);
TelEngine::destruct(digits);
return data[0];
}
static unsigned char encodeAddress(const SS7SCCP* sccp, SS7MSU& msu,
unsigned char* buf, const SCCPParam* param, const NamedString* val,
const NamedList* extra, const String& prefix)
{
if (!param || buf || param->size)
return 0;
if (sccp->ITU())
return encodeItuAddress(sccp,msu,buf,param,val,extra,prefix);
else
return encodeAnsiAddress(sccp,msu,buf,param,val,extra,prefix);
return 0;
}
static unsigned char encodeSegmentation(const SS7SCCP* sccp, SS7MSU& msu,
unsigned char* buf, const SCCPParam* param, const NamedString* val,
const NamedList* extra, const String& prefix)
{
String preName(prefix + param->name);
unsigned char length = 1;
unsigned char data[6];
unsigned char segInfo = 0;
int leftSgm = extra->getIntValue(preName + ".RemainingSegments",0);
segInfo |= leftSgm & 0x0f;
int protocolClass = extra->getIntValue(preName + ".ProtocolClass",0);
if (protocolClass)
segInfo |= 0x40;
bool firstSgm = extra->getBoolValue(preName + ".FirstSegment",false);
if (firstSgm)
segInfo |= 0x80;
data[1] = segInfo;
unsigned int sgmLocalReference = extra->getIntValue(preName + ".SegmentationLocalReference",0);
data[++length] = sgmLocalReference & 0xff;
data[++length] = sgmLocalReference >> 8 & 0xff;
data[++length] = sgmLocalReference >> 16 & 0xff;
data[0] = length & 0xff;
DataBlock tmp(data,length + 1,false);
msu += tmp;
tmp.clear(false);
return data[0];
}
static unsigned char encodeImportance(const SS7SCCP* sccp, SS7MSU& msu,
unsigned char* buf, const SCCPParam* param, const NamedString* val,
const NamedList* extra, const String& prefix)
{
unsigned char data[6];
data[0] = 1;
int importance = extra->getIntValue(prefix + param->name);
data[1] = importance & 0x07;
DataBlock tmp(data,2,false);
msu += tmp;
tmp.clear(false);
return data[0];
}
static unsigned int encodeData(const SS7SCCP* sccp, SS7MSU& msu, SS7MsgSCCP* msg)
{
if (!msg) {
DDebug(sccp,DebugNote,"Request to encode data for a null message");
return 0;
}
DataBlock* data = msg->getData();
if (!data) {
DDebug(sccp,DebugNote,"Request to encode message %s with null data",
SS7MsgSCCP::lookup(msg->type()));
return 0;
}
if (data->length() < 2) {
DDebug(sccp,DebugNote,"Request to encode message %s with short data",
SS7MsgSCCP::lookup(msg->type()));
return 0;
}
unsigned int length = data->length();
unsigned char header[2];
DataBlock tmp;
if (msg->isLongDataMessage()) {
header[0] = length & 0xff;
header[1] = length >> 8 & 0xff;
tmp.assign(header,2,false);
} else {
header[0] = length & 0xff;
tmp.assign(header,1,false);
}
msu += tmp;
msu += *data;
tmp.clear(false);
return length;
}
static unsigned char encodeCause(const SS7SCCP* sccp, SS7MSU& msu, unsigned char* buf,
const SCCPParam* param, const NamedString* val, const NamedList*, const String&)
{
if (!param)
return 0;
unsigned int n = param->size;
if (!n)
return 0;
unsigned int v = 0;
if (val)
v = val->toInteger();
DDebug(sccp,DebugAll,"encodeCause encoding %s=%u on %u octets",param->name,v,n);
if (!buf) {
unsigned int l = msu.length();
DataBlock dummy(0,n+1);
msu += dummy;
buf = (unsigned char*)msu.getData(l,n+1);
*buf++ = n & 0xff;
}
while (n--) {
*buf++ = v & 0xff;
v >>= 8;
}
return param->size;
}
#define MAKE_NAME(x) { #x, SS7MsgSCCP::x }
static const TokenDict s_names[] = {
// this list must be kept in synch with the header
MAKE_NAME(CR),
MAKE_NAME(CC),
MAKE_NAME(CREF),
MAKE_NAME(RLSD),
MAKE_NAME(RLC),
MAKE_NAME(DT1),
MAKE_NAME(DT2),
MAKE_NAME(AK),
MAKE_NAME(UDT),
MAKE_NAME(UDTS),
MAKE_NAME(ED),
MAKE_NAME(EA),
MAKE_NAME(RSR),
MAKE_NAME(RSC),
MAKE_NAME(ERR),
MAKE_NAME(IT),
MAKE_NAME(XUDT),
MAKE_NAME(XUDTS),
MAKE_NAME(LUDT),
MAKE_NAME(LUDTS),
{ 0, 0 }
};
#undef MAKE_NAME
#define MAKE_PARAM(p,s,a,d,t) { SS7MsgSCCP::p,s,#p,a,d,t }
static const SCCPParam s_paramDefs[] = {
// name len decoder encoder table References
// Standard parameters
MAKE_PARAM(DestinationLocalReference, 3,decodeInt, encodeInt, 0), // ITU:Q.713 3.2 | Ansi: 1000112.3 3.2
MAKE_PARAM(SourceLocalReference, 3,decodeInt, encodeInt, 0), // ITU:Q.713 3.3 | Ansi: 1000112.3 3.3
MAKE_PARAM(CalledPartyAddress, 0,decodeAddress, encodeAddress, 0), // ITU:Q.713 3.4 | Ansi: 1000112.3 3.4
MAKE_PARAM(CallingPartyAddress, 0,decodeAddress, encodeAddress, 0), // ITU:Q.713 3.5 | Ansi: 1000112.3 3.5
MAKE_PARAM(ProtocolClass, 1,decodeProtocolClass,encodeProtocolClass,0), // ITU:Q.713 3.6 | Ansi: 1000112.3 3.6
MAKE_PARAM(Segmenting, 0,0, 0, 0), // ITU:Q.713 3.7 | Ansi: 1000112.3 3.7
MAKE_PARAM(ReceiveSequenceNumber, 0,0, 0, 0), // ITU:Q.713 3.8 | Ansi: 1000112.3 3.8
MAKE_PARAM(Sequencing, 0,0, 0, 0), // ITU:Q.713 3.9 | Ansi: 1000112.3 3.9
MAKE_PARAM(Credit, 0,0, 0, 0), // ITU:Q.713 3.10 | Ansi: 1000112.3 3.10
MAKE_PARAM(ReleaseCause, 1,decodeCause, encodeCause, 0), // ITU:Q.713 3.11 | Ansi: 1000112.3 3.11
MAKE_PARAM(ReturnCause, 1,decodeCause, encodeCause, 0), // ITU:Q.713 3.12 | Ansi: 1000112.3 3.12
MAKE_PARAM(ResetCause, 1,decodeCause, encodeCause, 0), // ITU:Q.713 3.13 | Ansi: 1000112.3 3.13
MAKE_PARAM(ErrorCause, 1,decodeCause, encodeCause, 0), // ITU:Q.713 3.14 | Ansi: 1000112.3 3.14
MAKE_PARAM(RefusalCause, 1,decodeCause, encodeCause, 0), // ITU:Q.713 3.15 | Ansi: 1000112.3 3.15
MAKE_PARAM(Data, 0,0, 0, 0), // ITU:Q.713 3.16 | Ansi: 1000112.3 3.16
MAKE_PARAM(Segmentation, 4,decodeSegmentation, encodeSegmentation, 0), // ITU:Q.713 3.17 | Ansi: 1000112.3 3.18
MAKE_PARAM(HopCounter, 1,decodeInt, encodeInt, 0), // ITU:Q.713 3.18 | Ansi: 1000112.3 3.17
MAKE_PARAM(Importance, 0,decodeImportance, encodeImportance, 0), // ITU:Q.713 3.19
MAKE_PARAM(LongData, 0,0, 0, 0), // ITU:Q.713 3.20 | Ansi: 1000112.3 3.20
MAKE_PARAM(MessageTypeInterworking, 0,0, 0, 0), // Ansi: 1000112.3 3.22
MAKE_PARAM(INS, 0,0, 0, 0), // Ansi: 1000112.3 3.21
MAKE_PARAM(ISNI, 0,0, 0, 0), // Ansi: 1000112.3 3.19
{ SS7MsgSCCP::EndOfParameters, 0, 0, 0, 0, 0 }
};
#undef MAKE_PARAM
// Descriptor of SCCP message
static const MsgParams s_common_params[] = {
{ SS7MsgSCCP::CR, true,
{
SS7MsgSCCP::SourceLocalReference,
SS7MsgSCCP::ProtocolClass,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::CalledPartyAddress,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::CC, true,
{
SS7MsgSCCP::DestinationLocalReference,
SS7MsgSCCP::SourceLocalReference,
SS7MsgSCCP::ProtocolClass,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::CREF, true,
{
SS7MsgSCCP::DestinationLocalReference,
SS7MsgSCCP::RefusalCause,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::RLSD, true,
{
SS7MsgSCCP::DestinationLocalReference,
SS7MsgSCCP::SourceLocalReference,
SS7MsgSCCP::ReleaseCause,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::RLC, false,
{
SS7MsgSCCP::DestinationLocalReference,
SS7MsgSCCP::SourceLocalReference,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::DT1, false,
{
SS7MsgSCCP::DestinationLocalReference,
SS7MsgSCCP::Sequencing,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::Data,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::DT2, false,
{
SS7MsgSCCP::DestinationLocalReference,
SS7MsgSCCP::Sequencing,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::Data,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::AK, false,
{
SS7MsgSCCP::DestinationLocalReference,
SS7MsgSCCP::ReceiveSequenceNumber,
SS7MsgSCCP::Credit,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::UDT, false,
{
SS7MsgSCCP::ProtocolClass,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::CalledPartyAddress,
SS7MsgSCCP::CallingPartyAddress,
SS7MsgSCCP::Data,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::UDTS, false,
{
SS7MsgSCCP::ReturnCause,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::CalledPartyAddress,
SS7MsgSCCP::CallingPartyAddress,
SS7MsgSCCP::Data,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::ED, false,
{
SS7MsgSCCP::DestinationLocalReference,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::Data,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::EA, false,
{
SS7MsgSCCP::DestinationLocalReference,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::RSR, false,
{
SS7MsgSCCP::DestinationLocalReference,
SS7MsgSCCP::SourceLocalReference,
SS7MsgSCCP::ResetCause,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::RSC, false,
{
SS7MsgSCCP::DestinationLocalReference,
SS7MsgSCCP::SourceLocalReference,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::ERR, false,
{
SS7MsgSCCP::DestinationLocalReference,
SS7MsgSCCP::ErrorCause,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::IT, false,
{
SS7MsgSCCP::DestinationLocalReference,
SS7MsgSCCP::SourceLocalReference,
SS7MsgSCCP::ProtocolClass,
SS7MsgSCCP::Sequencing,
SS7MsgSCCP::Credit,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::XUDT, true,
{
SS7MsgSCCP::ProtocolClass,
SS7MsgSCCP::HopCounter,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::CalledPartyAddress,
SS7MsgSCCP::CallingPartyAddress,
SS7MsgSCCP::Data,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::LUDT, true,
{
SS7MsgSCCP::ProtocolClass,
SS7MsgSCCP::HopCounter,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::CalledPartyAddress,
SS7MsgSCCP::CallingPartyAddress,
SS7MsgSCCP::LongData,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::XUDTS, true,
{
SS7MsgSCCP::ReturnCause,
SS7MsgSCCP::HopCounter,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::CalledPartyAddress,
SS7MsgSCCP::CallingPartyAddress,
SS7MsgSCCP::Data,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::LUDTS, true,
{
SS7MsgSCCP::ReturnCause,
SS7MsgSCCP::HopCounter,
SS7MsgSCCP::EndOfParameters,
SS7MsgSCCP::CalledPartyAddress,
SS7MsgSCCP::CallingPartyAddress,
SS7MsgSCCP::LongData,
SS7MsgSCCP::EndOfParameters
}
},
{ SS7MsgSCCP::Unknown, false, { SS7MsgSCCP::EndOfParameters } }
};
static bool decodeParam(const SS7SCCP* sccp, NamedList& list, const SCCPParam* param,
const unsigned char* buf, unsigned int len, const String& prefix)
{
DDebug(sccp,DebugAll,"decodeParam(%p,%p,%p,%u) type=0x%02x, size=%u, name='%s'",
&list,param,buf,len,param->type,param->size,param->name);
if (param->size && (param->size != len))
return false;
if (param->decoder)
return param->decoder(sccp,list,param,buf,len,prefix);
return decodeRaw(sccp,list,param,buf,len,prefix);
}
// Generic encode helper function for a single mandatory parameter
static unsigned char encodeParam(const SS7SCCP* sccp, SS7MSU& msu,
const SCCPParam* param, const NamedList* params, ObjList& exclude,
const String& prefix, unsigned char* buf = 0)
{
DDebug(sccp,DebugAll,"encodeParam (mand) (%p,%p,%p,%p) type=0x%02x, size=%u, name='%s'",
&msu,param,params,buf,param->type,param->size,param->name);
// variable length must not receive fixed buffer
if (buf && !param->size)
return 0;
NamedString* val = params ? params->getParam(prefix+param->name) : 0;
if (val)
exclude.append(val)->setDelete(false);
if (param->encoder)
return param->encoder(sccp,msu,buf,param,val,params,prefix);
return encodeRaw(sccp,msu,buf,param,val,params,prefix);
}
// Generic encode helper for a single optional parameter
static unsigned char encodeParam(const SS7SCCP* sccp, SS7MSU& msu,
const SCCPParam* param, const NamedString* val,
const NamedList* extra, const String& prefix)
{
DDebug(sccp,DebugAll,"encodeParam (opt) (%p,%p,%p,%p) type=0x%02x, size=%u, name='%s'",
&msu,param,val,extra,param->type,param->size,param->name);
// add the parameter type now but remember the old length
unsigned int len = msu.length();
unsigned char tmp = param->type;
msu.append(&tmp,1);
unsigned char size = 0;
if (param->encoder)
size = param->encoder(sccp,msu,0,param,val,extra,prefix);
else
size = encodeRaw(sccp,msu,0,param,val,extra,prefix);
if (!size) {
Debug(sccp,DebugMild,"Unwinding type storage for failed parameter %s",param->name);
msu.truncate(len);
}
return size;
}
// Locate the description for a parameter by type
static const SCCPParam* getParamDesc(SS7MsgSCCP::Parameters type)
{
const SCCPParam* param = s_paramDefs;
for (; param->type != SS7MsgSCCP::EndOfParameters; param++) {
if (param->type == type)
return param;
}
return 0;
}
// Locate the description for a parameter by name
static const SCCPParam* getParamDesc(const String& name)
{
const SCCPParam* param = s_paramDefs;
for (; param->type != SS7MsgSCCP::EndOfParameters; param++) {
if (name == param->name)
return param;
}
return 0;
}
// Locate the description table for a message according to protocol type
static const MsgParams* getSccpParams(SS7MsgSCCP::Type msg)
{
const MsgParams* params = 0;
for (params = s_common_params; params->type != SS7MsgSCCP::Unknown; params++) {
if (params->type == msg)
return params;
}
return 0;
}
const TokenDict* SS7MsgSCCP::names()
{
return s_names;
}
SS7MsgSCCP::~SS7MsgSCCP()
{
if (m_data) {
m_data->clear(false);
TelEngine::destruct(m_data);
}
}
void SS7MsgSCCP::toString(String& dest, const SS7Label& label, bool params,
const void* raw, unsigned int rawLen) const
{
const char* enclose = "\r\n-----";
dest = enclose;
if (raw && rawLen) {
String tmp;
tmp.hexify((void*)raw,rawLen,' ');
dest << " " << tmp;
}
if (params) {
unsigned int n = m_params.length();
for (unsigned int i = 0; i < n; i++) {
NamedString* s = m_params.getParam(i);
if (s)
dest << "\r\n " << s->name() << "='" << *s << "'";
}
}
dest << enclose;
}
/**
* SS7MsgSccpReassemble
*/
SS7MsgSccpReassemble::SS7MsgSccpReassemble(SS7MsgSCCP* msg, const SS7Label& label,
unsigned int timeToLive)
: SS7MsgSCCP(msg->type()), m_label(label), m_callingPartyAddress(""),
m_segmentationLocalReference(0), m_timeout(0), m_remainingSegments(0),
m_firstSgmDataLen(0)
{
m_callingPartyAddress.copySubParams(msg->params(),
YSTRING("CallingPartyAddress."));
m_segmentationLocalReference = msg->params().getIntValue(
YSTRING("Segmentation.SegmentationLocalReference"));
m_timeout = Time::msecNow() + timeToLive;
m_remainingSegments = msg->params().getIntValue(
YSTRING("Segmentation.RemainingSegments"));
setData(new DataBlock(*msg->getData()));
params().copyParams(msg->params());
m_firstSgmDataLen = getData()->length();
// Update protocol class
if (msg->params().getIntValue(
YSTRING("Segmentation.ProtocolClass"), -1) > 0)
params().setParam("ProtocolClass",msg->params().getValue(
YSTRING("Segmentation.ProtocolClass")));
}
SS7MsgSccpReassemble::~SS7MsgSccpReassemble()
{
TelEngine::destruct(extractData());
}
bool SS7MsgSccpReassemble::canProcess(const SS7MsgSCCP* msg, const SS7Label& label)
{
if (!compareLabel(m_label,label))
return false;
if (m_segmentationLocalReference !=
(u_int32_t)msg->params().getIntValue(YSTRING("Segmentation.SegmentationLocalReference")))
return false;
NamedList address("");
address.copySubParams(msg->params(),YSTRING("CallingPartyAddress."));
return compareNamedList(address,m_callingPartyAddress);
}
SS7MsgSccpReassemble::Return SS7MsgSccpReassemble::appendSegment(SS7MsgSCCP* msg, const SS7Label& label)
{
if (!msg)
return Rejected;
if (!canProcess(msg,label))
return Rejected;
if ((m_remainingSegments - 1) != msg->params().getIntValue(YSTRING("Segmentation.RemainingSegments"),-1)) {
DDebug("SS7MsgSccpReassemble",DebugNote,"Received out of sequence segment %d : %d",
msg->params().getIntValue(YSTRING("Segmentation.RemainingSegments"),-1),m_remainingSegments);
return Error;
}
m_remainingSegments--;
if (m_firstSgmDataLen < msg->getData()->length()) {
DDebug("SS7MsgSccpReassemble",DebugNote,"Received data segment bigger than first data segment");
return Error;
}
getData()->append(*msg->getData());
return m_remainingSegments == 0 ? Finished : Accepted;
}
/**
* class SCCP
*/
SCCP::SCCP()
: m_translatorLocker(true,s_sccpTranslatorMutex), m_usersLocker(true,s_sccpMutexName), m_translator(0)
{
}
SCCP::~SCCP()
{
DDebug(this,DebugAll,"Destroying SCCP [%p]",this);
// If we have undetached users scream as hard as we can
if (m_users.skipNull())
Debug(this,DebugCrit,"Destroying SCCP with %d undetached users!!!",m_users.count());
if (m_translator)
Debug(this,DebugCrit,"Destroying SCCP with a valid translator!!!");
}
void SCCP::attach(SCCPUser* user)
{
if (!user)
return;
DDebug(this,DebugAll,"Attaching user (%p)",user);
Lock lock(m_usersLocker);
// Detach it if already exists
detach(user);
// Append the user
m_users.append(user)->setDelete(false);
}
void SCCP::detach(SCCPUser* user)
{
if (!user)
return;
Lock lock(m_usersLocker);
m_users.remove(user,false);
}
void SCCP::attachGTT(GTT* gtt)
{
Lock lock(m_translatorLocker);
if (gtt == m_translator)
return;
m_translator = gtt;
}
NamedList* SCCP::translateGT(const NamedList& params, const String& prefix, const String& nextPrefix)
{
Lock lock(m_translatorLocker);
if (!m_translator) {
Debug(this,isEndpoint() ? DebugInfo : DebugMild,
"Failed to translate Global Title! Reason: No GTT attached to sccp [%p]",this);
return 0;
}
RefPointer<GTT> translator = m_translator;
if (!translator)
return 0;
lock.drop();
return translator->routeGT(params,prefix,nextPrefix);
}
HandledMSU SCCP::pushMessage(DataBlock& data, NamedList& params, int ssn)
{
m_usersLocker.lock();
ListIterator iter(m_users);
SCCPUser* usr = 0;
params.setParam("ssn",String(ssn));
while ((usr = YOBJECT(SCCPUser,iter.get()))) {
RefPointer<SCCPUser> pointer = usr;
if (!pointer)
continue;
m_usersLocker.unlock();
HandledMSU handled = pointer->receivedData(data,params);
switch (handled) {
case HandledMSU::Accepted:
case HandledMSU::Failure:
return handled;
case HandledMSU::Rejected:
default:
break; // break switch
}
m_usersLocker.lock();
}
m_usersLocker.unlock();
DDebug(this,DebugInfo,"SCCP data message was not processed by any user!");
return HandledMSU::Unequipped;
}
HandledMSU SCCP::notifyMessage(DataBlock& data, NamedList& params, int ssn)
{
m_usersLocker.lock();
ListIterator iter(m_users);
SCCPUser* usr = 0;
params.setParam("ssn",String(ssn));
while ((usr = YOBJECT(SCCPUser,iter.get()))) {
RefPointer<SCCPUser> pointer = usr;
if (!pointer)
continue;
m_usersLocker.unlock();
HandledMSU handled = pointer->notifyData(data,params);
switch (handled) {
case HandledMSU::Accepted:
case HandledMSU::Failure:
return handled;
case HandledMSU::Rejected:
default:
break; // break switch
}
m_usersLocker.lock();
}
m_usersLocker.unlock();
DDebug(this,DebugAll,"SCCP notify message was not processed by any user!");
return HandledMSU::Unequipped;
}
bool SCCP::managementMessage(Type type, NamedList& params)
{
m_usersLocker.lock();
ListIterator iter(m_users);
bool ret = false;
SCCPUser* usr = 0;
while ((usr = YOBJECT(SCCPUser,iter.get()))) {
RefPointer<SCCPUser> pointer = usr;
if (!pointer)
continue;
m_usersLocker.unlock();
if (pointer->managementNotify(type,params))
ret = true;
m_usersLocker.lock();
}
m_usersLocker.unlock();
return ret;
}
int SCCP::sendMessage(DataBlock& data, const NamedList& params)
{
Debug(this,DebugStub,"Please implement SCCP sendMessage");
return false;
}
bool SCCP::managementStatus(Type type, NamedList& params)
{
DDebug(this,DebugStub,"Please implement SCCP::managementStatus()!!");
return false;
}
void SCCP::resolveGTParams(SS7MsgSCCP* msg, const NamedList* gtParams)
{
if (!msg || !gtParams)
return;
msg->params().clearParam(YSTRING("CalledPartyAddress"),'.');
for (unsigned int i = 0;i < gtParams->length();i++) {
NamedString* val = gtParams->getParam(i);
if (val && (val->name().startsWith("gt") || val->name() == YSTRING("pointcode") ||
val->name() == YSTRING("ssn") || val->name() == YSTRING("route")))
msg->params().setParam("CalledPartyAddress." + val->name(),*val);
}
NamedString* param = 0;
if ((param = gtParams->getParam(YSTRING("sccp"))))
msg->params().setParam(param->name(),*param);
if (!gtParams->hasSubParams(YSTRING("CallingPartyAddress.")))
return;
msg->params().clearParam(YSTRING("CallingPartyAddress"),'.');
msg->params().copySubParams(*gtParams,YSTRING("CallingPartyAddress."),false);
}
/**
* class SCCPUser
*/
SCCPUser::SCCPUser(const NamedList& config)
: SignallingComponent(config,&config),
m_sccp(0), m_sccpMutex(true,s_userMutexName), m_sls(-1)
{
String tmp;
config.dump(tmp,"\r\n ",'\'',true);
DDebug(DebugAll,"SCCPUser::SCCPUser(%s)",tmp.c_str());
}
SCCPUser::~SCCPUser()
{
DDebug(this,DebugAll,"Destroying SCCPUser [%p]",this);
}
void SCCPUser::destroyed()
{
Lock lock(m_sccpMutex);
if (m_sccp)
attach(0);
lock.drop();
SignallingComponent::destroyed();
}
void SCCPUser::attach(SCCP* sccp)
{
Lock lock(m_sccpMutex);
if (!sccp) {
if (!m_sccp) {
DDebug(this,DebugNote,"Request to attach null sccp!!! ");
return;
}
m_sccp->detach(this);
TelEngine::destruct(m_sccp);
return;
}
if (m_sccp == sccp) {
sccp->deref();
DDebug(this,DebugInfo,"Requesting to attach the same sccp (%p)",m_sccp);
return;
}
SCCP* temp = m_sccp;
m_sccp = sccp;
// Do not ref the sccp because we already have an reference
m_sccp->attach(this);
// Destruct the old sccp
if (temp) {
temp->detach(this);
TelEngine::destruct(temp);
temp = 0;
}
}
bool SCCPUser::initialize(const NamedList* config)
{
DDebug(this,DebugInfo,"SCCPUser::initialize(%p) [%p]",config,this);
if (engine()) {
NamedList params("sccp");
if (!resolveConfig(YSTRING("sccp"),params,config))
params.addParam("local-config","true");
// NOTE SS7SCCP is created on demand!!!
// engine ->build method will search for the requested sccc and
// if it was found will return it with the ref counter incremented
// if it wasn't found the refcounter will be 1
// For this behavior SCCPUser attach method will not reference the sccp
// pointer instead will use the reference of engine build
if (params.toBoolean(true))
attach(YOBJECT(SCCP,engine()->build("SCCP",params,true)));
} else
Debug(this,DebugWarn,"SccpUser::initialize() can not attach sccp; null SigEngine!");
return m_sccp != 0;
}
bool SCCPUser::sendData(DataBlock& data, NamedList& params)
{
if (!m_sccp) {
Debug(this,DebugMild,"Can not send data! No Sccp attached!");
return false;
}
bool sequenceControl = params.getBoolValue("sequenceControl",false);
params.addParam("ProtocolClass",(sequenceControl ? "1" : "0"));
int sls = params.getIntValue("sls",-1);
if (sls < 0) {
// Preserve the sls only if sequence control is requested
if (sequenceControl)
sls = m_sls;
if (sls < 0)
sls = Random::random() & 0xff;
}
else
sls &= 0xff;
params.setParam("sls", String(sls));
if (sccp()->sendMessage(data,params) < 0)
return false;
m_sls = sls; // Keep the last SLS sent
return true;
}
bool SCCPUser::sccpNotify(SCCP::Type type, NamedList& params)
{
if (!m_sccp) {
Debug(this,DebugMild,"Can not send data! No Sccp attached!");
return false;
}
return sccp()->managementStatus(type,params);
}
HandledMSU SCCPUser::receivedData(DataBlock& data, NamedList& params)
{
Debug(DebugStub,"Please implement SCCPUser::receivedData(DataBlock& data, const NamedList& params)");
return 0;
}
HandledMSU SCCPUser::notifyData(DataBlock& data, NamedList& params)
{
Debug(DebugStub,"Please implement SCCPUser::notifyData(DataBlock& data, const NamedList& params)");
return 0;
}
bool SCCPUser::managementNotify(SCCP::Type type, NamedList& params)
{
Debug(this,DebugStub,"Please implement SCCPUser::managementNotify()");
return false;
}
/**
* class GTT
*/
GTT::GTT(const NamedList& config)
: SignallingComponent(config.safe("GTT"),&config,"ss7-gtt"),
m_sccp(0)
{
}
GTT::~GTT()
{
if (m_sccp) {
m_sccp->attachGTT(0);
TelEngine::destruct(m_sccp);
m_sccp = 0;
}
}
bool GTT::initialize(const NamedList* config)
{
DDebug(this,DebugInfo,"GTT::initialize(%p) [%p]",config,this);
if (engine()) {
NamedList params("sccp");
if (!resolveConfig(YSTRING("sccp"),params,config))
params.addParam("local-config","true");
if (params.toBoolean(true))
attach(YOBJECT(SCCP,engine()->build("SCCP",params,true)));
} else
Debug(this,DebugWarn,"GTT::initialize() can not attach sccp; null SigEngine");
return m_sccp != 0;
}
NamedList* GTT::routeGT(const NamedList& gt, const String& prefix, const String& nextPrefix)
{
Debug(DebugStub,"Please implement NamedList* GTT::routeGT(%s,%s,%s)",
gt.c_str(),prefix.c_str(),nextPrefix.c_str());
return 0;
}
void GTT::attach(SCCP* sccp)
{
if (!sccp)
return;
if (m_sccp == sccp) {
sccp->deref();
return;
}
SCCP* tmp = m_sccp;
m_sccp = sccp;
m_sccp->attachGTT(this);
if (!tmp)
return;
TelEngine::destruct(tmp);
tmp = 0;
}
void GTT::destroyed()
{
if (m_sccp) {
m_sccp->attachGTT(0);
TelEngine::destruct(m_sccp);
m_sccp = 0;
}
SignallingComponent::destroyed();
}
/**
* SCCPManagement
*/
SCCPManagement::SCCPManagement(const NamedList& params, SS7PointCode::Type type)
: SignallingComponent(params,&params,"ss7-sccp-mgm"),
Mutex(true, s_managementMutexName), m_remoteSccp(),
m_statusTest(), m_localSubsystems(), m_concerned(), m_pcType(type), m_sccp(0), m_unknownSubsystems("ssn"),
m_subsystemFailure(0), m_routeFailure(0), m_autoAppend(false), m_printMessages(false)
{
DDebug(DebugAll,"Creating SCCP management (%p)",this);
// stat.info timer
m_testTimeout = params.getIntValue(YSTRING("test-timer"),5000);
if (m_testTimeout < 5000)
m_testTimeout = 5000;
else if (m_testTimeout > 10000)
m_testTimeout = 10000;
// coord.chg timer
m_coordTimeout = params.getIntValue(YSTRING("coord-timer"),1000);
if (m_coordTimeout < 1000)
m_coordTimeout = 1000;
if (m_coordTimeout > 2000)
m_coordTimeout = 2000;
m_ignoreStatusTestsInterval = params.getIntValue(YSTRING("ignore-tests"),1000);
m_printMessages = params.getBoolValue(YSTRING("print-messages"), false);
m_autoAppend = params.getBoolValue(YSTRING("auto-monitor"),false);
for (unsigned int i = 0;i < params.length();i++) {
NamedString* param = params.getParam(i);
if (!param)
continue;
XDebug(this,DebugAll,"Parsing param %s : %s",param->name().c_str(),param->c_str());
if (param->name() == YSTRING("remote")) {
SccpRemote* rem = new SccpRemote(m_pcType);
if (rem->initialize(*param))
m_remoteSccp.append(rem);
else {
Debug(this,DebugConf,"Failed to initialize remote sccp %s",param->c_str());
TelEngine::destruct(rem);
}
} else if (param->name() == YSTRING("concerned")) {
SccpRemote* rem = new SccpRemote(m_pcType);
if (rem->initialize(*param))
m_concerned.append(rem);
else {
Debug(this,DebugConf,"Failed to initialize concerned sccp %s",param->c_str());
TelEngine::destruct(rem);
}
}
}
NamedString* lsubs = params.getParam(YSTRING("local-subsystems"));
ObjList* list = lsubs ? lsubs->split(',') : 0;
if (!list)
return;
for (ObjList* o = list->skipNull();o;o = o->skipNext()) {
String* s = static_cast<String*>(o->get());
unsigned char ssn = s->toInteger();
if (ssn < 2)
continue;
m_localSubsystems.append(new SccpLocalSubsystem(ssn,getCoordTimeout(),
getIgnoreTestsInterval()));
}
TelEngine::destruct(list);
}
SCCPManagement::~SCCPManagement()
{
DDebug(this,DebugAll,"Destroing SCCPManagement %p",this);
m_sccp = 0;
}
void SCCPManagement::attach(SS7SCCP* sccp)
{
Lock lock(this);
if (!sccp || m_sccp)
return;
m_sccp = sccp;
}
bool SCCPManagement::initialize(const NamedList* config)
{
if (!config) {
DDebug(this,DebugNote,"Request to initialize sccp management from null conf");
return true;
}
Lock lock(this);
#ifdef DEBUG
String dst;
config->dump(dst,"\r\n");
Debug(this,DebugInfo,"Initializeing SCCPManagement(%p) %s",this,dst.c_str());
#endif
m_printMessages = config->getBoolValue(YSTRING("print-messages"), m_printMessages);
return true;
}
void SCCPManagement::pointcodeStatus(SS7Layer3* link, bool operational)
{
if (!sccp() || !operational) {
DDebug(this,DebugNote,"Can not process pointcode status sccp(%p) , is up : %s",
sccp(), String::boolText(operational));
return;
}
lock();
for (ObjList* o = m_remoteSccp.skipNull();o;o = o->skipNext()) {
SccpRemote* rsccp = static_cast<SccpRemote*>(o->get());
SS7Route::State state = sccp()->network()->getRouteState(m_pcType,rsccp->getPointCode());
XDebug(this,DebugAll,"Checking route status for remote sccp %s oldState: '%s' newState: '%s'",
rsccp->toString().c_str(),stateName(rsccp->getState()),SS7Route::stateName(state));
if ((int)state != (int)rsccp->getState()) {
unlock();
manageSccpRemoteStatus(rsccp,state);
lock();
}
}
unlock();
}
void SCCPManagement::routeStatus(SS7PointCode::Type type, const SS7PointCode& node, SS7Route::State state)
{
if (!sccp() || !sccp()->isLayer3Up()) {
DDebug(this,DebugNote,"Can not process pointcode status sccp(%p) , is up : %s",
sccp(), sccp() ? String::boolText(sccp()->isLayer3Up()) : "false");
return;
}
lock();
for (ObjList* o = m_remoteSccp.skipNull();o;o = o->skipNext()) {
SccpRemote* rsccp = static_cast<SccpRemote*>(o->get());
#ifdef XDEBUG
String dest;
dest << " Local: " << rsccp->getPointCode() << " remote : " << node;
XDebug (this,DebugNote,"Processing routeStatus %s oldState: '%s' newState: '%s'", dest.c_str(),
stateName(rsccp->getState()),SS7Route::stateName(state));
#endif
if (rsccp->getPointCode() != node)
continue;
if ((int)rsccp->getState() == (int)state)
break;
RefPointer<SccpRemote> ref = rsccp;
if (!ref)
continue;
unlock();
manageSccpRemoteStatus(rsccp,state);
return;
}
unlock();
}
bool SCCPManagement::handleMessage(int msgType, unsigned char ssn, unsigned char smi, NamedList& params)
{
int pointcode = params.getIntValue(YSTRING("pointcode"));
Lock lock(this);
bool sendMsg = false;
MsgType msg = SSA;
switch (msgType) {
case SSA:
case SSP:
{
SccpSubsystem* sccpSub = new SccpSubsystem(ssn);
SccpRemote* rsccp = new SccpRemote(pointcode,m_pcType);
lock.drop();
if (ssn == 1 && msgType == SSA)
manageSccpRemoteStatus(rsccp,SS7Route::Allowed);
else if (ssn > 1)
handleSubsystemStatus(sccpSub, msgType == SSA, rsccp, smi);
else
Debug(this,DebugWarn,"Received Invalid sccp message %s for ssn %d",
lookup(msgType,s_managementMessages), ssn);
TelEngine::destruct(sccpSub);
TelEngine::destruct(rsccp);
return true;
}
case SST: // Received sst
{
if (ssn == 1) { // SST is initiated for local sccp send ssa
sendMsg = true;
break;
}
SccpLocalSubsystem* sccps = getLocalSubsystem(ssn);
if (sccps) {
XDebug(this,DebugAll,"Received SST for %d state: %s ignoreTests %s",
ssn,stateName(sccps->getState()),String::boolText(sccps->ignoreTests()));
if (sccps->ignoreTests())
return true;
if (sccps->getState() == SCCPManagement::Allowed) {
sendMsg = true;
break;
}
lock.drop();
if (!managementMessage(SCCP::SubsystemStatus,params))
return true;
String* status = params.getParam(YSTRING("subsystem-status"));
if (status && *status == YSTRING("UserInService"))
sendMessage(msg,params);
return true;
}
if (!sendMsg)
Debug(this,DebugConf,"Received SST from: '%s' for missing local subsystem %d",
params.getValue(YSTRING("RemotePC")),ssn);
break;
}
case SOR:
{
lock.drop();
managementMessage(SCCP::CoordinateIndication,params);
return true;
}
case SOG:
handleSog(ssn,pointcode);
return true;
default:
Debug(sccp(),DebugNote,"Received unknown management Message '%s'",
lookup(msgType,s_managementMessages));
}
lock.drop();
if (sendMsg)
sendMessage(msg,params);
return true;
}
bool SCCPManagement::managementMessage(SCCP::Type type, NamedList& params)
{
if (!m_sccp)
return false;
return m_sccp->managementMessage(type,params);
}
void SCCPManagement::putValue(NamedList& params,int val,const char* name, bool dict)
{
if (val < 0)
return;
if (!dict)
params.setParam(name,String(val));
else
params.setParam(name,lookup(val,s_broadcastType));
}
void SCCPManagement::localBroadcast(SCCP::Type type, int pointcode, int sps,
int rss, int rl, int ssn, int ss)
{
if (!m_sccp)
return;
NamedList params("lb");
putValue(params,pointcode,"pointcode");
putValue(params,rl,"restriction-level");
putValue(params,ssn,"ssn");
putValue(params,sps,"signalling-point-status",true);
putValue(params,ss,"subsystem-status",true);
putValue(params,rss,"remote-sccp-status",true);
m_sccp->managementMessage(type,params);
}
SccpLocalSubsystem* SCCPManagement::getLocalSubsystem(unsigned char ssn)
{
Lock lock(this);
for (ObjList* o = m_localSubsystems.skipNull();o;o = o->skipNext()) {
SccpLocalSubsystem* ss = static_cast<SccpLocalSubsystem*>(o->get());
if (ss && ss->getSSN() == ssn)
return ss;
}
return 0;
}
bool SCCPManagement::processMessage(SS7MsgSCCP* message)
{
Debug(DebugStub,"Please implement management message decoder");
return true;
}
const TokenDict* SCCPManagement::broadcastType()
{
return s_broadcastType;
}
void SCCPManagement::notify(SCCP::Type type, NamedList& params)
{
if (!m_sccp)
return;
#ifdef DEBUG
String tmp;
params.dump(tmp,"\r\n");
Debug(this,DebugAll,"User notify %s : \r\n%s",lookup(type,s_sccpNotif),tmp.c_str());
#endif
unsigned char ssn = params.getIntValue(YSTRING("ssn"));
if (ssn < 2) {
Debug(this,DebugNote,"Received management notify with invalid ssn %d",ssn);
return;
}
unsigned char smi = params.getIntValue(YSTRING("smi")); // subsystem multiplicity indicator
if (smi > 3) {
Debug(this,DebugNote, "Received management notify message with unknown smi: %d , ssn: %d",
smi,ssn);
smi = 0;
}
switch (type) {
case SCCP::CoordinateRequest: // Affected subsystem, subsystem multiplicity indicator
handleCoordinateChanged(ssn,smi,params);
break;
case SCCP::CoordinateResponse:// Affected subsystem, subsystem multiplicity indicator
params.setParam(YSTRING("pointcode"),String(m_sccp->getPackedPointCode()));
sendMessage(SOG,params);
break;
case SCCP::StatusRequest: // Affected subsystem, subsystem multiplicity indicator, user status
{
const char* subsystemStatus = params.getValue(YSTRING("subsystem-status"));
int status = lookup(subsystemStatus,broadcastType());
if (status != UserOutOfService && status != UserInService) {
Debug(this,DebugNote,"Reveived subsystem status indication with wrong subsystem status: %s",
subsystemStatus);
return;
}
SccpSubsystem* sub = new SccpSubsystem(ssn);
handleSubsystemStatus(sub, status == UserInService, 0, smi);
TelEngine::destruct(sub);
break;
}
default:
Debug(this,DebugNote,"Unhandled message '%s' received from attached users!",
lookup(type,s_sccpNotif));
}
}
void SCCPManagement::handleSog(unsigned char ssn, int pointcode)
{
for (ObjList* ol = m_localSubsystems.skipNull();ol;ol = ol->skipNext()) {
SccpLocalSubsystem* sls = static_cast<SccpLocalSubsystem*>(ol->get());
if (sls->receivedSOG(ssn,pointcode))
break;
}
}
void SCCPManagement::handleCoordinateChanged(unsigned char ssn, int smi, const NamedList& params)
{
Lock lock(this);
SccpLocalSubsystem* sub = getLocalSubsystem(ssn);
if (!sub) {
Debug(this,DebugInfo,"Dinamicaly appending ssn %d to local subsystems list!",ssn);
sub = new SccpLocalSubsystem(ssn,m_coordTimeout,m_ignoreStatusTestsInterval,smi);
m_localSubsystems.append(sub);
}
sub->ref();
lock.drop();
if (sub->getState() == SCCPManagement::Prohibited)
Debug(this,DebugStub,"Subsystem %d wishes to go oos but is already oos! Logic Bug?",sub->getSSN());
sub->clearBackups();
int count = params.getIntValue(YSTRING("backups"));
for (int i = 0;i < count; i++) {
String name = "backup.";
name << i;
int subsys = params.getIntValue(name + ".ssn", -1);
int pointcode = params.getIntValue(name + ".pointcode",-1);
if (pointcode <= 0) {
Debug(this,DebugStub,"Coordinate change request to a local subsystem!");
continue;
}
if (subsys < 2 || pointcode < 0) {
Debug(this,DebugMild,"Invalid backup subsystem pc:%d, ssn:%d",pointcode,subsys);
continue;
}
RemoteBackupSubsystem* bs = new RemoteBackupSubsystem(subsys,pointcode,true);
sub->appendBackup(bs);
NamedList data("");
data.setParam("smi",String(smi));
data.setParam("ssn",String(subsys));
data.setParam("pointcode",String(pointcode));
data.setParam("RemotePC",String(pointcode));
sendMessage(SOR,data);
}
sub->startCoord();
sub->setState(WaitForGrant);
TelEngine::destruct(sub);
}
SccpRemote* SCCPManagement::getRemoteSccp(int pointcode)
{
for (ObjList* o = m_remoteSccp.skipNull();o;o = o->skipNext()) {
SccpRemote* rsccp = static_cast<SccpRemote*>(o->get());
if (rsccp->getPackedPointcode() == pointcode)
return rsccp;
}
return 0;
}
void SCCPManagement::routeFailure(SS7MsgSCCP* msg)
{
if (!m_sccp)
return;
Lock lock(this);
m_routeFailure++;
if (!msg || !msg->params().getParam(YSTRING("RemotePC"))) {
DDebug(this,DebugNote,"Route failure, with no pointcode present!");
return;
}
int pointcode = msg->params().getIntValue(YSTRING("RemotePC"));
if (pointcode < 1) {
Debug(this,DebugWarn,"Remote pointcode %d is invalid!",pointcode);
return;
}
if (pointcode == m_sccp->getPackedPointCode())
return;
SccpRemote* rsccp = getRemoteSccp(pointcode);
if (rsccp && rsccp->getState() == SCCPManagement::Prohibited) {
lock.drop();
updateTables(rsccp);
return;
}
if (!rsccp) {
if (m_autoAppend) {
Debug(this,DebugNote,"Dynamic appending remote sccp %d to state monitoring list",
pointcode);
rsccp = new SccpRemote(pointcode,m_pcType);
m_remoteSccp.append(rsccp);
} else
Debug(this,DebugMild,
"Remote sccp '%d' state is not monitored! Future message routing may not reach target!",
pointcode);
}
RefPointer<SccpRemote>ref = rsccp;
lock.drop();
if (!ref)
return;
manageSccpRemoteStatus(rsccp,SS7Route::Prohibited);
}
void SCCPManagement::subsystemFailure(SS7MsgSCCP* msg, const SS7Label& label)
{
if (!m_sccp) {
DDebug(this,DebugNote,"Request to process subsystem failure with no sccp attached!");
return;
}
if (!msg || !msg->params().getParam(YSTRING("CalledPartyAddress.ssn"))) {
DDebug(this,DebugNote,"Subsystem failure! no ssn");
return;
}
int ssn = msg->params().getIntValue(YSTRING("CalledPartyAddress.ssn"),0);
if (ssn <= 1) {
DDebug(this,DebugNote,"Subsystem failure, invalid ssn: '%d'",ssn);
return;
}
Lock lock(this);
// Find local subsystem and change status
SccpLocalSubsystem* ss = getLocalSubsystem(ssn);
if (ss)
ss->setState(SCCPManagement::Prohibited);
if (m_sccp->extendedMonitoring()) {
m_subsystemFailure++;
NamedString* sub = msg->params().getParam(YSTRING("CalledPartyAddress.ssn"));
if (sub) {
NamedString* ssnParam = m_unknownSubsystems.getParam(*sub);
if (ssnParam)
incrementNS(ssnParam);
else
m_unknownSubsystems.setParam(*sub,"1");
}
}
lock.drop();
notifyConcerned(SSP,ssn,0);
}
void SCCPManagement::subsystemsStatus(String& dest,bool extended)
{
Lock lock(this);
if (m_localSubsystems.skipNull()) {
dest << "Local subsystems state : count: " << m_localSubsystems.count() << "\r\n";
for (ObjList* o = m_localSubsystems.skipNull();o;o = o->skipNext()) {
SccpLocalSubsystem* ss = static_cast<SccpLocalSubsystem*>(o->get());
if (!ss)
continue;
ss->dump(dest);
dest << "\r\n";
}
}
if (m_subsystemFailure == 0) {
dest << "\r\nMissing Local Subsystem: " << m_subsystemFailure;
if (!extended)
return;
for (unsigned int i = 0;i < m_unknownSubsystems.length();i++) {
NamedString* ssn = m_unknownSubsystems.getParam(i);
if (!ssn)
continue;
dest << "\r\nReceived: " << *ssn << " packets for subsystem : " << ssn->name();
}
}
if (!m_remoteSccp.skipNull())
return;
dest << "\r\nRemoteSccp: count: " << m_remoteSccp.count();
for (ObjList* o = m_remoteSccp.skipNull(); o;o = o->skipNext()) {
SccpRemote* sr = static_cast<SccpRemote*>(o->get());
if (!sr)
continue;
sr->dump(dest,true);
}
}
void SCCPManagement::updateTables(SccpRemote* rsccp, SccpSubsystem* ssn)
{
if (!rsccp && !ssn) {
Debug(sccp(),DebugMild,"Request to update tables but no pointcode or ssn present!!");
return;
}
if (!sccp()) {
DDebug(this,DebugMild,"Request to update tables with no sccp attached");
return;
}
const SS7PointCode* local = rsccp ? &rsccp->getPointCode() : sccp()->getLocalPointCode();
if (!local) {
Debug(sccp(),DebugWarn,"Can not update tables, no pointcode present!");
return;
}
NamedList params("sccp.update");
params.setParam("pointcode",String(local->pack(m_pcType)));
params.setParam("pc-type",String((int)m_pcType));
if (rsccp)
params.setParam("pc-state",stateName(rsccp->getState()));
params.setParam("component",sccp()->toString());
if (ssn) {
params.setParam("subsystem",String(ssn->getSSN()));
params.setParam("subsystem-state",stateName(ssn->getState()));
}
sccp()->updateTables(params);
}
void SCCPManagement::routeStatus(String& dest,bool extended)
{
dest << "\r\nRouting Status:";
dest << "\r\nMessages Failed to be routed: " << m_routeFailure;
if (!extended)
return;
// TODO call gtt print unknown translations
}
void SCCPManagement::timerTick(const Time& when)
{
if (!lock(SignallingEngine::maxLockWait()))
return;
ObjList coordt;
for (ObjList* o = m_localSubsystems.skipNull();o;o = o->skipNext()) {
SccpLocalSubsystem* ss = static_cast<SccpLocalSubsystem*>(o->get());
if (!ss)
continue;
if (ss->timeout() && ss->ref())
coordt.append(ss);
}
// Use another list to append the sst's because the alternative is expensive for timer tick
// (ListIterator)
ObjList ssts;
for (ObjList* o = m_statusTest.skipNull();o;o = o->skipNext()) {
SubsystemStatusTest* sst = static_cast<SubsystemStatusTest*>(o->get());
if (!sst->timeout())
continue;
if (sst->ref())
ssts.append(sst);
}
unlock();
if (coordt.skipNull())
for (ObjList* o = coordt.skipNull();o;o = o->skipNext()) {
SccpLocalSubsystem* ss = static_cast<SccpLocalSubsystem*>(o->get());
ss->manageTimeout(this);
}
if (!ssts.skipNull())
return;
for (ObjList* o = ssts.skipNull();o;o = o->skipNext()) {
SubsystemStatusTest* sst = static_cast<SubsystemStatusTest*>(o->get());
if (!sst)
continue;
if (sst->markAllowed() && sst->getSubsystem()->getSSN() == 1) {
manageSccpRemoteStatus(sst->getRemote(),SS7Route::Allowed);
continue;
}
sst->restartTimer();
if (!sendSST(sst->getRemote(),sst->getSubsystem()))
sst->setAllowed(false);
}
}
void SCCPManagement::stopSst(SccpRemote* remoteSccp, SccpSubsystem* rSubsystem, SccpSubsystem* less)
{
if (!remoteSccp)
return;
Lock lock(this);
ListIterator iter(m_statusTest);
SubsystemStatusTest* sst = 0;
while ((sst = YOBJECT(SubsystemStatusTest,iter.get()))) {
if (sst->getRemote()->getPointCode() != remoteSccp->getPointCode())
continue;
if (sst->getSubsystem()) {
if (rSubsystem && rSubsystem->getSSN() != sst->getSubsystem()->getSSN())
continue;
if (less && less->getSSN() == sst->getSubsystem()->getSSN())
continue;
}
m_statusTest.remove(sst);
}
}
bool SCCPManagement::sendSST(SccpRemote* remote, SccpSubsystem* sub)
{
NamedList params("");
params.setParam("pointcode",String(remote->getPackedPointcode()));
params.setParam("RemotePC",String(remote->getPackedPointcode()));
params.setParam("smi",String(sub->getSmi()));
params.setParam("ssn",String(sub->getSSN()));
return sendMessage(SST,params);
}
void SCCPManagement::startSst(SccpRemote* remoteSccp, SccpSubsystem* rSubsystem)
{
if (!remoteSccp || !rSubsystem)
return;
DDebug(this,DebugNote,"Requested to start test for pc : %d ssn: %d",remoteSccp->getPackedPointcode(),rSubsystem->getSSN());
Lock lock(this);
for (ObjList* o = m_statusTest.skipNull();o;o = o->skipNext()) {
SubsystemStatusTest* sst = static_cast<SubsystemStatusTest*>(o->get());
if (sst->getRemote()->getPointCode() != remoteSccp->getPointCode())
continue;
if (sst->getSubsystem() && rSubsystem->getSSN() == sst->getSubsystem()->getSSN())
return; // We already have the test
}
SubsystemStatusTest* sst = new SubsystemStatusTest(m_testTimeout);
if (!sst->startTest(remoteSccp,rSubsystem)) {
TelEngine::destruct(sst);
return;
}
m_statusTest.append(sst);
lock.drop();
if (!sendSST(remoteSccp,rSubsystem))
sst->setAllowed(false);
}
void SCCPManagement::mtpEndRestart()
{
if (!m_sccp)
return;
lock();
ListIterator iter(m_concerned);
SccpRemote* sr = 0;
while ((sr = YOBJECT(SccpRemote,iter.get()))) {
SS7Route::State state = sccp()->network()->getRouteState(m_pcType,sr->getPointCode());
RefPointer<SccpRemote> ptr = sr;
unlock();
if (sr->getState() != (SccpStates)state)
manageSccpRemoteStatus(sr,state); // Update remote sccp state
if (state != SS7Route::Allowed) {
lock();
continue;
}
NamedList params("");
params.setParam("pointcode",String(m_sccp->getPackedPointCode()));
params.setParam("RemotePC",String(sr->getPackedPointcode()));
params.setParam("smi","0");
params.setParam("ssn","1");
sendMessage(SSA,params);
lock();
}
unlock();
}
void SCCPManagement::notifyConcerned(MsgType msg, unsigned char ssn, int smi)
{
DDebug(this,DebugAll,"Notify concerned: msg '%s' ssn: '%d', smi: %d",
lookup(msg,s_managementMessages),ssn,smi);
if (!sccp())
return;
Lock lock(this);
ObjList concerned;
for (ObjList* o = m_concerned.skipNull();o;o = o->skipNext()) {
SccpRemote* rsccp = static_cast<SccpRemote*>(o->get());
if (!rsccp || !rsccp->getSubsystem(ssn))
continue;
if (rsccp->ref())
concerned.append(rsccp);
}
if (!concerned.skipNull()) {
DDebug(this,DebugNote,"No Concerned pointcode for ssn %d",ssn);
return;
}
NamedList params("");
params.setParam("ssn",String((int)ssn));
params.setParam("pointcode",String(sccp()->getPackedPointCode()));
params.setParam("smi",String(smi));
lock.drop();
for (ObjList* o = concerned.skipNull();o; o = o->skipNext()) {
SccpRemote* rsccp = static_cast<SccpRemote*>(o->get());
if (!rsccp)
continue;
params.setParam("RemotePC",String(rsccp->getPackedPointcode()));
sendMessage(msg,params);
}
}
void SCCPManagement::sccpUnavailable(const SS7PointCode& pointcode, unsigned char cause)
{
#ifdef DEBUG
String dest;
dest << pointcode;
Debug(this,DebugInfo,"Received UPU %s cause : %d",dest.c_str(), cause);
#endif
Lock lock(this);
SccpRemote* rsccp = getRemoteSccp(pointcode.pack(m_pcType));
// Do not process UPU if we do not monitor the remote sccp state
if (!rsccp)
return;
rsccp->setState(SCCPManagement::Prohibited);
// Stop all subsystem status tests
ListIterator iter(m_statusTest);
SubsystemStatusTest* test = 0;
bool testStarted = false;
while ((test = YOBJECT(SubsystemStatusTest,iter.get()))) {
if (!test || !test->getRemote() || pointcode != test->getRemote()->getPointCode())
continue;
// Do not stop test for SSN = 1 if the cause is not Unequipped
SccpSubsystem* sub = test->getSubsystem();
if (sub->getSSN() == 1 && cause != HandledMSU::Unequipped) {
testStarted = true;
continue;
}
m_statusTest.remove(test);
}
if (!testStarted && cause != HandledMSU::Unequipped) {
SubsystemStatusTest* sst = new SubsystemStatusTest(m_testTimeout);
SccpSubsystem* sub = new SccpSubsystem(1);
if (!sst->startTest(rsccp,new SccpSubsystem(1))) {
TelEngine::destruct(sst);
TelEngine::destruct(sub);
return;
}
TelEngine::destruct(sub);
m_statusTest.append(sst);
sst->setAllowed(false);
}
lock.drop();
localBroadcast(SCCP::StatusIndication,rsccp->getPackedPointcode(),-1,SccpRemoteInaccessible);
}
void SCCPManagement::printMessage(String& dest, MsgType type, const NamedList& params)
{
const char* enclose = "\r\n-----";
dest = enclose;
dest << "\r\n " << lookup(type,s_managementMessages);
dest << " pc: " << params.getValue(YSTRING("pointcode")) << ", ";
dest << "ssn: " << params.getValue(YSTRING("ssn")) << ", ";
dest << "smi: " << params.getValue(YSTRING("smi"));
if (type == SSC) {
dest << ", cl: " << params.getValue(YSTRING("congestion-level"));
}
dest << enclose;
}
/**
* SccpLocalSubsystem
*/
SccpLocalSubsystem::SccpLocalSubsystem(unsigned char ssn, u_int64_t coordInterval, u_int64_t ignoreInterval,unsigned char smi)
: Mutex(true,s_sccpSubsystems), m_ssn(ssn), m_smi(smi), m_state(SCCPManagement::Allowed),
m_coordTimer(coordInterval), m_ignoreTestsTimer(ignoreInterval), m_backups(), m_receivedAll(true)
{
DDebug("SccpSubsystem", DebugAll,"Creating sccp subsystem [%p] with ssn '%d', smi '%d'",this,ssn,smi);
}
SccpLocalSubsystem::~SccpLocalSubsystem()
{
DDebug("SccpSubsystem", DebugAll,"Destroing sccp subsystem [%p] with ssn '%d'",this,m_ssn);
}
bool SccpLocalSubsystem::timeout()
{
Lock lock(this);
if (m_coordTimer.timeout()) {
m_coordTimer.stop();
m_receivedAll = true;
for (ObjList* o = m_backups.skipNull();o;o = o->skipNext()) {
RemoteBackupSubsystem* sbs = static_cast<RemoteBackupSubsystem*>(o->get());
if (sbs->waitingForGrant())
m_receivedAll = false;
}
if (m_receivedAll)
m_ignoreTestsTimer.start();
return true;
}
if (m_ignoreTestsTimer.timeout()) {
m_state = SCCPManagement::Prohibited;
m_ignoreTestsTimer.stop();
}
return false;
}
void SccpLocalSubsystem::manageTimeout(SCCPManagement* mgm)
{
if (!mgm)
return;
if (m_receivedAll) {
mgm->localBroadcast(SCCP::CoordinateConfirm,-1,-1,-1,-1,m_ssn,m_smi);
mgm->notifyConcerned(SCCPManagement::SSP,m_ssn,m_smi);
m_state = SCCPManagement::IgnoreTests;
return;
}
m_state = SCCPManagement::Allowed;
/// TODO send local broadcast with request denied!!!
}
void SccpLocalSubsystem::dump(String& dest)
{
dest << "Subsystem: " << m_ssn << " , smi: " << m_smi;
dest << ", state: " << SCCPManagement::stateName(m_state) << " ";
}
bool SccpLocalSubsystem::receivedSOG(unsigned char ssn, int pointcode)
{
Lock lock(this);
for (ObjList* o = m_backups.skipNull();o;o = o->skipNext()) {
RemoteBackupSubsystem* sbs = static_cast<RemoteBackupSubsystem*>(o->get());
if (!sbs->equals(ssn,pointcode))
continue;
sbs->permisionGranted();
return true;
}
return false;
}
void SccpLocalSubsystem::setIgnoreTests(bool ignore)
{
if (ignore)
m_ignoreTestsTimer.start();
else
m_ignoreTestsTimer.stop();
}
/**
* SccpRemote
*/
SccpRemote::SccpRemote(const SS7PointCode::Type type)
: Mutex(true, s_sccpRemote), m_pointcode(type,0), m_pointcodeType(type), m_state(SCCPManagement::Allowed)
{
DDebug("RemoteSccp",DebugAll,"Creating remote sccp [%p]",this);
}
SccpRemote::SccpRemote(unsigned int pointcode, SS7PointCode::Type pcType)
: m_pointcode(pcType,pointcode), m_pointcodeType(pcType), m_state(SCCPManagement::Allowed)
{
DDebug("RemoteSccp",DebugAll,"Creating remote sccp [%p] for pointcode %d",this,pointcode);
}
SccpRemote::~SccpRemote()
{
#ifdef XDEBUG
String tmp;
tmp << m_pointcode;
Debug("RemoteSccp",DebugAll,"Destroying remote sccp [%p], %s",this,tmp.c_str());
#endif
}
bool SccpRemote::initialize(const String& params)
{
ObjList* o = params.split(':',false);
if (!o)
return false;
String* pointcode = static_cast<String*>(o->get());
if (!pointcode) {
TelEngine::destruct(o);
return false;
}
bool pointcodeAssigned = false;
if (pointcode->find('-') > 0)
pointcodeAssigned = m_pointcode.assign(*pointcode,m_pointcodeType);
else
pointcodeAssigned = m_pointcode.unpack(m_pointcodeType,pointcode->toInteger());
if (!pointcodeAssigned) {
TelEngine::destruct(o);
return false;
}
ObjList* subsystems = o->skipNext();
while (subsystems) {
String* sub = static_cast<String*>(subsystems->get());
if (!sub)
break;
subsystems = sub->split(',',false);
if (!subsystems)
break;
for (ObjList* ob = subsystems->skipNull();ob;ob = ob->skipNext()) {
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 too big!",
ssn,m_pointcode.pack(m_pointcodeType));
continue;
}
m_subsystems.append(new SccpSubsystem(ssn));
}
TelEngine::destruct(subsystems);
break;
}
TelEngine::destruct(o);
return true;
}
SccpSubsystem* SccpRemote::getSubsystem(int ssn)
{
Lock lock(this);
for (ObjList* o = m_subsystems.skipNull();o;o = o->skipNext()) {
SccpSubsystem* sub = static_cast<SccpSubsystem*>(o->get());
if (sub && sub->getSSN() == ssn)
return sub;
}
return 0;
}
void SccpRemote::setState(SCCPManagement::SccpStates state)
{
if (m_state == state)
return;
Lock lock(this);
m_state = state;
for (ObjList* o = m_subsystems.skipNull();o;o = o->skipNext()) {
SccpSubsystem* sub = static_cast<SccpSubsystem*>(o->get());
sub->setState(state);
}
}
void SccpRemote::dump(String& dest, bool extended)
{
Lock lock(this);
dest << "\r\n----Sccp : " << m_pointcode;
dest << " (" << m_pointcode.pack(m_pointcodeType) << "," << SS7PointCode::lookup(m_pointcodeType) << ") ";
dest << "State : " << SCCPManagement::stateName(m_state) << "; ";
if (extended) {
dest << "Subsystems : " << m_subsystems.count() << "; ";
for (ObjList* o = m_subsystems.skipNull();o;o = o->skipNext()) {
SccpSubsystem* ss = static_cast<SccpSubsystem*>(o->get());
if (!ss)
continue;
ss->dump(dest);
dest << " | ";
}
}
dest << "----";
}
bool SccpRemote::changeSubsystemState(int ssn,SCCPManagement::SccpStates newState)
{
Lock lock(this);
SccpSubsystem* ss = getSubsystem(ssn);
if (!ss)
return true;
if (ss->getState() == newState)
return false;
ss->setState(newState);
return true;
}
/**
* SubsystemStatusTest
*/
SubsystemStatusTest::~SubsystemStatusTest()
{
DDebug("SST",DebugAll,"Stoping SST for pc: '%d' ssn: '%d'", m_remoteSccp ? m_remoteSccp->getPackedPointcode() : 0,
m_remoteSubsystem ? m_remoteSubsystem->getSSN() : 0);
if (m_remoteSccp)
TelEngine::destruct(m_remoteSccp);
if (m_remoteSubsystem)
TelEngine::destruct(m_remoteSubsystem);
}
bool SubsystemStatusTest::startTest(SccpRemote* remoteSccp, SccpSubsystem* rSubsystem)
{
if (!remoteSccp || !remoteSccp->ref())
return false;
m_remoteSccp = remoteSccp;
if (!rSubsystem || !rSubsystem->ref()) {
TelEngine::destruct(m_remoteSccp);
return false;
}
#ifdef DEBUG
String dump;
remoteSccp->dump(dump,false);
Debug("SST",DebugInfo,"Starting subsystem status test for '%s' ssn = '%d' subsystem state : %s",
dump.c_str(),rSubsystem->getSSN(),SCCPManagement::stateName(rSubsystem->getState()));
#endif
m_remoteSubsystem = rSubsystem;
m_statusInfo.start();
if (rSubsystem->getSSN() == 1)
m_markAllowed = true;
return true;
}
void SubsystemStatusTest::restartTimer()
{
m_interval *= 2;
if (m_interval > MAX_INFO_TIMER)
m_interval = MAX_INFO_TIMER;
m_statusInfo.fire(Time::msecNow() + m_interval);
}
/**
* class SS7SCCP
*/
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_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)
{
DDebug(this,DebugInfo,"Creating new SS7SCCP [%p]",this);
#ifdef DEBUG
if (debugAt(DebugAll)) {
String tmp;
params.dump(tmp,"\r\n ",'\'',true);
Debug(this,DebugAll,"SS7SCCP::SS7SCCP(%p) [%p]%s",
&params,this,tmp.c_str());
}
#endif
const char* stype = params.getValue(YSTRING("pointcodetype"));
m_type = SS7PointCode::lookup(stype);
if (m_type == SS7PointCode::Other) {
Debug(this,DebugConf,"Invalid point code type '%s'",c_safe(stype));
return;
}
String* lpc = params.getParam(YSTRING("localpointcode"));
m_localPointCode = new SS7PointCode(0,0,0);
bool pointcodeAssigned = false;
if (lpc) {
if (lpc->find('-') > 0)
pointcodeAssigned = m_localPointCode->assign(*lpc,m_type);
else
pointcodeAssigned = m_localPointCode->unpack(m_type,lpc->toInteger());
}
if (!pointcodeAssigned) {
Debug(this,DebugWarn,"Invalid localpointcode='%s'",lpc ? lpc->c_str() : "null");
Debug(this,DebugConf,"No local PointCode configured!! GT translations with no local PointCode may lead to undesired behavior");
TelEngine::destruct(m_localPointCode);
m_localPointCode = 0;
}
int hc = params.getIntValue("hopcounter",15);
if (hc < 1 || hc > 15)
hc = 15;
m_hopCounter = hc;
m_ignoreUnkDigits = params.getBoolValue(YSTRING("ignore-unknown-digits"),true);
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_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);
if (m_segTimeout < 5000)
m_segTimeout = 5000;
if (m_segTimeout > 20000)
m_segTimeout = 20000;
if ((m_type == SS7PointCode::ITU || m_type == SS7PointCode::ANSI) && m_localPointCode) {
NamedList mgmParams("sccp-mgm");
if (!resolveConfig(YSTRING("management"),mgmParams,&params))
mgmParams.addParam("local-config","true");
mgmParams.setParam("type",m_type == SS7PointCode::ITU ? "ss7-sccp-itu-mgm" : "ss7-sccp-ansi-mgm");
if (mgmParams.toBoolean(true)) {
if (m_type == SS7PointCode::ITU)
m_management = YOBJECT(SS7ItuSccpManagement,YSIGCREATE(SCCPManagement,&mgmParams));
else if (m_type == SS7PointCode::ANSI)
m_management = YOBJECT(SS7AnsiSccpManagement,YSIGCREATE(SCCPManagement,&mgmParams));
}
if (!m_management)
Debug(this,DebugWarn,"Failed to create sccp management!");
else if (m_management->initialize(&mgmParams))
m_management->attach(this);
} else
Debug(this,DebugConf,"Created SS7SCCP '%p' without management! No local pointcode pressent!",this);
}
SS7SCCP::~SS7SCCP()
{
if (m_localPointCode)
m_localPointCode->destruct();
DDebug(this,DebugAll,"Destroying SS7SCCP [%p]",this);
}
bool SS7SCCP::initialize(const NamedList* config)
{
#ifdef DEBUG
String tmp;
if (config && debugAt(DebugAll))
config->dump(tmp,"\r\n ",'\'',true);
Debug(this,DebugInfo,"SS7SCCP::initialize(%p) [%p]%s",config,this,tmp.c_str());
#endif
if (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)
hc = 15;
m_hopCounter = hc;
m_extendedMonitoring = config->getBoolValue(YSTRING("extended-monitoring"),m_extendedMonitoring);
}
if (m_management)
SignallingComponent::insert(m_management);
return SS7Layer4::initialize(config);
}
void SS7SCCP::destroyed()
{
if (m_management)
TelEngine::destruct(m_management);
SS7Layer4::destroyed();
}
void SS7SCCP::attach(SS7Layer3* network)
{
SS7Layer4::attach(network);
setNetworkUp(network && network->operational());
}
bool SS7SCCP::managementStatus(Type type, NamedList& params)
{
if (m_management)
m_management->notify(type,params);
return false;
}
void SS7SCCP::timerTick(const Time& when)
{
if (!lock(SignallingEngine::maxLockWait()))
return;
for (ObjList* o = m_reassembleList.skipNull();o;) {
SS7MsgSccpReassemble* usr = YOBJECT(SS7MsgSccpReassemble,o->get());
if (usr->timeout()) {
o->remove();
o = o->skipNull();
}
else
o = o->skipNext();
}
unlock();
}
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)
{
if (!sccpMsg || !sccpMsg->getData())
return -1;
if (unknownPointCodeType()) {
Debug(this,DebugConf,"SCCP unavailable!! Reason Unknown pointcode type %s",SS7PointCode::lookup(m_type));
return -1;
}
Lock lock(this);
if (!m_layer3Up) {
DDebug(this,DebugNote,"Can not send sccp message, L3 is down");
return -1;
}
int dpc = getPointCode(sccpMsg,"CalledPartyAddress","RemotePC",true);
if (dpc == -2) {
lock.drop();
return routeLocal(sccpMsg);
}
int opc = getPointCode(sccpMsg,"CallingPartyAddress","LocalPC",false);
lock.drop();
if (dpc < 0 || opc < 0) {
if (m_management)
m_management->routeFailure(sccpMsg);
return -1;
}
return sendSCCPMessage(sccpMsg,dpc,opc,local);
}
int SS7SCCP::sendSCCPMessage(SS7MsgSCCP* sccpMsg,int dpc,int opc, bool local)
{
Lock lock(this);
int sls = sccpMsg->params().getIntValue(YSTRING("sls"),-1);
SS7PointCode dest(m_type,dpc);
SS7PointCode orig(m_type,opc > 0 ? opc : m_localPointCode->pack(m_type));
// Build the routing label
SS7Label outLabel(m_type,dest,orig,sls);
if (sccpMsg->getData()->length() > m_maxUdtLength) {
lock.drop();
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
if (sccpMsg->params().getParam(YSTRING("RemotePC")) &&
sccpMsg->params().getIntValue(YSTRING("CalledPartyAddress.ssn"),0) != 0) {
sccpMsg->params().setParam("CalledPartyAddress.route","ssn");
} else
sccpMsg->params().setParam("CalledPartyAddress.route","gt");
}
// Build the msu
SS7MSU* msu = buildMSU(sccpMsg,outLabel);
lock.drop();
if (!msu)
return segmentMessage(sccpMsg,outLabel,local);
printMessage(msu,sccpMsg,outLabel);
sls = transmitMSU(*msu,outLabel,sls);
#ifdef DEBUG
if (sls < 0)
Debug(this,DebugNote,"Failed to transmit message %s. %d",SS7MsgSCCP::lookup(sccpMsg->type()),sls);
#endif
// CleanUp memory
TelEngine::destruct(msu);
return sls;
}
bool SS7SCCP::fillLabelAndReason(String& dest,const SS7Label& label,const SS7MsgSCCP* msg)
{
dest << " Routing label : " << label;
if (!isSCLCSMessage(msg->type()))
return false;
dest << " Reason: ";
dest << lookup(msg->params().getIntValue(YSTRING("ReturnCause")),s_return_cause,
"Unknown");
return true;
}
// Obtain a pointcode from called/calling party address
// Return: -1 On Error; -2 If the message should be routed to a local sccp; else the pointcode
int SS7SCCP::getPointCode(SS7MsgSCCP* msg, const String& prefix, const char* pCode, bool translate)
{
if (!msg)
return -1;
bool havePointCode = false;
NamedString* pcNs = msg->params().getParam(pCode);
if (pcNs && pcNs->toInteger(0) > 0)
havePointCode = true;
if (!havePointCode) {
pcNs = msg->params().getParam(prefix + ".pointcode");
if (pcNs && pcNs->toInteger(0) > 0) {
msg->params().setParam(new NamedString(pCode,*pcNs));
havePointCode = true;
}
}
if (!havePointCode && translate) { // CalledParyAddress with no pointcode. Check for Global Title
NamedList* route = translateGT(msg->params(),prefix,
YSTRING("CallingPartyAddress"));
m_totalGTTranslations++;
if (!route) {
m_gttFailed++;
return -1;
}
resolveGTParams(msg,route);
NamedString* localRouting = route->getParam(YSTRING("sccp"));
if (localRouting && *localRouting != toString()) {
msg->params().copyParam(*route,YSTRING("RemotePC"));
TelEngine::destruct(route);
return -2;
}
bool havePC = route->getParam(pCode) != 0;
NamedString* trpc = route->getParam(YSTRING("pointcode"));
if (!trpc && !havePC) {
Debug(this,DebugWarn,"The GT has not been translated to a pointcode!!");
TelEngine::destruct(route);
return -1;
}
if (!havePC)
msg->params().setParam(pCode,*trpc);
else
msg->params().setParam(pCode,route->getValue(pCode));
TelEngine::destruct(route);
} else if (!havePointCode && !translate) { // CallingPartyAddress with no pointcode. Assign sccp pointcode
if (!m_localPointCode) {
Debug(this,DebugWarn,"Can not build routing label. No local pointcode present and no pointcode present in CallingPartyAddress");
return -1;
}
return m_localPointCode->pack(m_type);
}
return msg->params().getIntValue(pCode);
}
int SS7SCCP::routeLocal(SS7MsgSCCP* msg)
{
if (!msg) {
Debug(this,DebugWarn,"Failed to route local! Null message!");
return -1;
}
NamedString* sccp = msg->params().getParam(YSTRING("sccp"));
if (!sccp || *sccp == toString()) {
Debug(this,DebugStub,
"Requested to local route sccp message without sccp component!");
return -1;
}
int dpc = msg->params().getIntValue("RemotePC",-1);
if (dpc < 0)
dpc = msg->params().getIntValue("CalledPartyAddress.pointcode",-1);
if (dpc < 0) {
Debug(this,DebugNote,
"Unable to route local sccp message! No pointcode present.");
return -1;
}
if (!engine()) {
Debug(this,DebugMild,
"Unable to route local sccp message! No engine attached!");
return -1;
}
RefPointer<SS7SCCP> sccpCmp = YOBJECT(SS7SCCP,
engine()->find(*sccp,YSTRING("SS7SCCP")));
if (!sccpCmp) {
Debug(this,DebugNote,
"Unable to route local sccp message! SCCP component %s not found!",
sccp->c_str());
return -1;
}
msg->params().clearParam(YSTRING("LocalPC"));
msg->params().clearParam(YSTRING("CallingPartyAddress.pointcode"));
return sccpCmp->sendSCCPMessage(msg,dpc,-1,false);
}
int SS7SCCP::checkImportanceLevel(int msgType, int initialImportance)
{
if ((isSCLCMessage(msgType) && isSCLCSMessage(msgType))) {
Debug(this,DebugStub,"Check Importance level for a SCOC message!");
return 0;
}
if (isSCLCMessage(msgType)) // Max importance level is 6 and default is 4 for UDT,XUDT and LUDT
return (initialImportance >= 0 && initialImportance <= 6) ? initialImportance : 4;
if (isSCLCSMessage(msgType)) // Max importance level is 3 and default is 3 for UDTS,XUDTS and LUDTS
return (initialImportance >= 0 && initialImportance <= 3) ? initialImportance : 3;
return initialImportance;
}
void SS7SCCP::checkSCLCOptParams(SS7MsgSCCP* msg)
{
if (!msg || msg->type() == SS7MsgSCCP::UDT || !isSCLCMessage(msg->type())) // UDT does not have optional parameters
return;
if (!ITU()) {
msg->params().clearParam(YSTRING("Importance"));
return;
}
msg->params().clearParam(YSTRING("ISNI"));
msg->params().clearParam(YSTRING("INS"));
msg->params().clearParam(YSTRING("MessageTypeInterworking"));
}
// This method is called to send connectionless data
int SS7SCCP::sendMessage(DataBlock& data, const NamedList& params)
{
if (unknownPointCodeType()) {
Debug(this,DebugConf,"SCCP unavailable!! Reason Unknown pointcode type %s",SS7PointCode::lookup(m_type));
return -1;
}
#ifdef XDEBUG
String tmp;
params.dump(tmp,"\r\n ",'\'',true);
Debug(this,DebugAll,"SS7SCCP::sendMessage() [%p]%s",this,tmp.c_str());
#endif
Lock lock1(this);
SS7MsgSCCP* sccpMsg = 0;
// 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);
} else if ((params.getParam(YSTRING("ISNI")) || params.getParam(YSTRING("INS"))) &&
m_type == SS7PointCode::ANSI) {
// XUDT message ANSI only
sccpMsg = new SS7MsgSCCP(SS7MsgSCCP::XUDT);
} else if (params.getParam(YSTRING("HopCounter"))) {
sccpMsg = new SS7MsgSCCP(SS7MsgSCCP::XUDT);
} else // In rest send Unit Data Messages
sccpMsg = new SS7MsgSCCP(SS7MsgSCCP::UDT);
if (!sccpMsg) {
Debug(this,DebugWarn,"Failed to create SCCP message!");
m_errors++;
return -1;
}
sccpMsg->params().copyParams(params); // Copy the parameters to message
sccpMsg->params().setParam("generated","local");
if (m_localPointCode)
sccpMsg->params().setParam("LocalPC",String(getPackedPointCode()));
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
if (sccpMsg->type() == SS7MsgSCCP::XUDT || sccpMsg->type() == SS7MsgSCCP::LUDT)
checkSCLCOptParams(sccpMsg);
// Append data to message
sccpMsg->setData(&data);
lock1.drop();
int ret = transmitMessage(sccpMsg,true);
sccpMsg->removeData();
TelEngine::destruct(sccpMsg);
lock();
if (ret >= 0)
m_totalSent++;
else
m_errors++;
unlock();
return ret;
}
// 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.translation"));
const NamedString* plan = YOBJECT(NamedString,params.getParam(prefix + ".gt.plan"));
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,DebugConf,"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 pointersStart = headerLength;
maxLen -= headerLength;
// We have 3 mandatory variable parameters CallingAddress, CalledAddress,
// and Data and the pointer to optional parameters + 1 data length
headerLength += 5;
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
sccpParamsSize += MAX_OPT_LEN;
if (ludtSupport) {
unsigned int maxSupported = ITU() ? MAX_DATA_ITU : MAX_DATA_ANSI;
if (maxLen < maxSupported) {
ludt = maxLen - sccpParamsSize;
ludt -= 5; // The pointers and data length are on 2 octets
} else
ludt = maxSupported;
}
// 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)
{
if (!origMsg)
return -1;
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;
// Verify if we should bother to send the message
if (data->length() > (ITU() ? MAX_DATA_ITU : MAX_DATA_ANSI)) {
Debug(this,DebugNote,
"Unable to send SCCP message! Data length (%d) is too long",
data->length());
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,DebugWarn,"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)) && !isSCLCSMessage(msgType)) {
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());
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 = listSegments->count();
msgData.setParam("Segmentation.ProtocolClass",msgData.getValue(YSTRING("ProtocolClass")));
if (isSCLCMessage(msgType))
msgData.setParam("ProtocolClass","1"); // Segmentation is using in sequence delivery option
bool msgReturn = msgData.getBoolValue(YSTRING("MessageReturn"),false);
sls = msgData.getIntValue(YSTRING("sls"),-1);
// Transmit first segment
SS7MsgSCCP* msg = new SS7MsgSCCP(msgType);
msg->params().copyParams(msgData);
DataBlock temp;
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(msgType) ? --segments : 0));
msg->params().setParam("Segmentation.FirstSegment","true");
msg->setData(&temp);
SS7MSU* msu = buildMSU(msg,label,false);
msg->removeData();
temp.clear(false);
if (!msu) {
Debug(this,DebugWarn,"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(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 ((sg = getAndRemoveDataSegment(listSegments))) {
msg = new SS7MsgSCCP(msgType);
msg->params().copyParams(msgData);
sg->fillSegment(temp,*data);
TelEngine::destruct(sg);
msg->params().setParam("Segmentation.RemainingSegments",String(--segments));
msg->setData(&temp);
SS7MSU* msu = buildMSU(msg,label,false);
msg->removeData();
temp.clear(false);
if (!msu) {
Debug(this,DebugWarn,"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(msgType),segments);
return sls;
}
lock();
}
if (segments != 0)
Debug(this,DebugStub,"Bug in segment message!! RemainingSegments %d",segments);
TelEngine::destruct(listSegments);
unlock();
return sls;
}
SS7MsgSccpReassemble::Return SS7SCCP::reassembleSegment(SS7MsgSCCP* segment,
const SS7Label& label, SS7MsgSCCP*& msg)
{
if (segment->params().getBoolValue(YSTRING("Segmentation.FirstSegment"))) {
for (ObjList* o = m_reassembleList.skipNull(); o; o = o->skipNext()) {
SS7MsgSccpReassemble* reass = static_cast <SS7MsgSccpReassemble*>(o->get());
if (!reass || !reass->canProcess(segment,label))
continue;
m_reassembleList.remove(reass);
DDebug(this,DebugNote,"Duplicate first segment received!");
return SS7MsgSccpReassemble::Error;
}
SS7MsgSccpReassemble* reass = new SS7MsgSccpReassemble(segment,label,m_segTimeout);
m_reassembleList.append(reass);
return SS7MsgSccpReassemble::Accepted;
}
SS7MsgSccpReassemble::Return ret = SS7MsgSccpReassemble::Rejected;
for (ObjList* o = m_reassembleList.skipNull(); o; o = o->skipNext()) {
SS7MsgSccpReassemble* reass = static_cast <SS7MsgSccpReassemble*>(o->get());
if (!reass)
continue;
ret = reass->appendSegment(segment,label);
if (ret == SS7MsgSccpReassemble::Rejected)
continue;
if (ret == SS7MsgSccpReassemble::Error) {
m_reassembleList.remove(reass,false);
msg = reass;
return ret;
}
if (ret == SS7MsgSccpReassemble::Finished) {
m_reassembleList.remove(reass,false);
msg = reass;
}
return ret;
}
return ret;
}
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());
if (!msgParams) {
const char* name = SS7MsgSCCP::lookup(msg->type());
if (name)
Debug(this,DebugWarn,"No parameter table for SCCP MSU type %s [%p]",name,this);
else
Debug(this,DebugWarn,"Cannot create SCCP MSU type 0x%02x [%p]",msg->type(),this);
return 0;
}
unsigned int len = 1;
const SS7MsgSCCP::Parameters* plist = msgParams->params;
SS7MsgSCCP::Parameters ptype;
// first add the length of mandatory fixed parameters
while ((ptype = *plist++) != SS7MsgSCCP::EndOfParameters) {
const SCCPParam* param = getParamDesc(ptype);
if (!param) {
// this is fatal as we don't know the length
Debug(this,DebugCrit,"Missing description of fixed SCCP parameter 0x%02x [%p]",ptype,this);
return 0;
}
if (!param->size) {
Debug(this,DebugCrit,"Invalid (variable) description of fixed SCCP parameter 0x%02x [%p]",ptype,this);
return 0;
}
len += param->size;
}
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;
// then add one pointer octet to each mandatory variable parameter
while ((ptype = *plist++) != SS7MsgSCCP::EndOfParameters) {
const SCCPParam* param = getParamDesc(ptype);
if (!param) {
// this is fatal as we won't be able to populate later
Debug(this,DebugCrit,"Missing description of variable SCCP parameter 0x%02x [%p]",ptype,this);
return 0;
}
if (param->size)
Debug(this,DebugMild,"Invalid (fixed) description of variable SCCP parameter 0x%02x [%p]",ptype,this);
len += pointerLen;
}
// finally add a pointer to the optional part only if supported by type
if (msgParams->optional)
len += pointerLen;
SS7MSU* msu = new SS7MSU(sio(),label,0,len);
unsigned char* d = msu->getData(label.length()+1,len);
*d++ = msg->type();
ObjList exclude;
plist = msgParams->params;
String prefix = msg->params().getValue(YSTRING("message-prefix"));
// first populate with mandatory fixed parameters
while ((ptype = *plist++) != SS7MsgSCCP::EndOfParameters) {
const SCCPParam* param = getParamDesc(ptype);
if (!param) {
Debug(this,DebugFail,"Stage 2: no description of fixed SCCP parameter 0x%02x [%p]",ptype,this);
continue;
}
if (!param->size) {
Debug(this,DebugFail,"Stage 2: Invalid (variable) description of fixed SCCP parameter %s [%p]",param->name,this);
continue;
}
if (!encodeParam(this,*msu,param,&msg->params(),exclude,prefix,d))
Debug(this,DebugCrit,"Could not encode fixed SCCP parameter %s [%p]",param->name,this);
d += param->size;
}
// now populate with mandatory variable parameters
for (; (ptype = *plist++) != SS7MsgSCCP::EndOfParameters; ptr += pointerLen) {
const SCCPParam* param = getParamDesc(ptype);
if (!param) {
Debug(this,DebugFail,"Stage 2: no description of variable SCCP parameter 0x%02x [%p]",ptype,this);
continue;
}
if (param->size) {
Debug(this,DebugFail,"Stage 2: Invalid (fixed) description of variable SCCP parameter %s [%p]",param->name,this);
continue;
}
// remember the offset this parameter will actually get stored
len = msu->length();
unsigned int size = 0;
if (ptype == SS7MsgSCCP::Data || ptype == SS7MsgSCCP::LongData) {
size = encodeData(this,*msu,msg);
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)) {
Debug(this,DebugCrit,"Could not encode variable SCCP parameter %s [%p]",param->name,this);
continue;
}
if (ptype != SS7MsgSCCP::LongData && ((d[len] != size) || (msu->length() != (len + size + 1)))) {
Debug(this,DebugCrit,"Invalid encoding variable SCCP parameter %s (len=%u size=%u stor=%u msuLength = %u) [%p]",
param->name,len,size,d[len],msu->length(),this);
continue;
}
// store pointer to parameter
unsigned int storedLength = len - ptr;
if (!ludt) {
d[ptr] = storedLength;
continue;
}
storedLength --;
d[ptr] = storedLength & 0xff;
d[ptr+1] = storedLength >> 8;
}
if (msgParams->optional) {
// remember the offset past last mandatory == first optional parameter
len = msu->length();
// optional parameters are possible - try to set anything left in the message
unsigned int n = msg->params().length();
for (unsigned int i = 0; i < n; i++) {
NamedString* ns = msg->params().getParam(i);
if (!ns || exclude.find(ns))
continue;
if (prefix && !ns->name().startsWith(prefix))
continue;
String tmp(ns->name());
tmp >> prefix.c_str();
const SCCPParam* param = getParamDesc(tmp);
unsigned char size = 0;
if (param)
size = encodeParam(this,*msu,param,ns,&msg->params(),prefix);
else if (tmp.startSkip("Param_",false)) {
int val = tmp.toInteger(-1);
if (val >= 0 && val <= 255) {
SCCPParam p;
p.name = tmp;
p.type = (SS7MsgSCCP::Parameters)val;
p.size = 0;
p.encoder = 0;
size = encodeParam(this,*msu,&p,ns,&msg->params(),prefix);
}
}
if (!size)
continue;
if (len) {
d = msu->getData(0,len+1);
unsigned int storedLength = len - ptr;
if (ludt) {
storedLength --;
d[ptr] = storedLength & 0xff;
d[ptr+1] = storedLength >> 8;
} 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;
}
}
if (!len) {
// we stored some optional parameters so we need to put the terminator
DataBlock tmp(0,1);
*msu += tmp;
}
}
return msu;
}
HandledMSU SS7SCCP::receivedMSU(const SS7MSU& msu, const SS7Label& label, SS7Layer3* network, int sls)
{
if (msu.getSIF() != sif()) // SCCP message?
return HandledMSU::Rejected;
Lock lock(this);
if (unknownPointCodeType()) {
DDebug(this,DebugNote,"Rejecting MSU! Reason Unknown pointcode type");
lock.drop();
return HandledMSU::Rejected;
}
if (m_localPointCode && *m_localPointCode != label.dpc()) { // Is the msu for us?
lock.drop();
return HandledMSU::Rejected;
}
lock.drop();
const unsigned char* s = msu.getData(label.length() + 1,1);
if (!s) {
Debug(this,DebugNote,"Got short MSU");
return false;
}
unsigned int len = msu.length()-label.length()-1;
SS7MsgSCCP::Type type = (SS7MsgSCCP::Type)s[0];
String name = SS7MsgSCCP::lookup(type);
if (!name) {
String tmp;
tmp.hexify((void*)s,len,' ');
DDebug(this,DebugMild,"Received unknown SCCP type 0x%02x, length %u: %s",
type,len,tmp.c_str());
return false;
}
bool ok = processMSU(type,s+1,len-1,label,network,sls);
if (!ok && debugAt(DebugMild)) {
String tmp;
tmp.hexify((void*)s,len,' ');
Debug(this,DebugMild,"Unhandled SCCP message %s, length %u: %s",
name.c_str(),len,tmp.c_str());
}
return ok;
}
bool SS7SCCP::processMSU(SS7MsgSCCP::Type type, const unsigned char* paramPtr,
unsigned int paramLen, const SS7Label& label, SS7Layer3* network, int sls)
{
XDebug(this,DebugAll,"SS7SCCP::processMSU(%u,%p,%u,%p,%p,%d) [%p]",
type,paramPtr,paramLen,&label,network,sls,this);
Lock lock(this);
SS7MsgSCCP* msg = new SS7MsgSCCP(type);
if (!decodeMessage(msg,label.type(),paramPtr,paramLen)) {
DDebug(this,DebugNote,"Failed to decode SCCP message!");
m_errors++;
TelEngine::destruct(msg);
return false;
}
msg->params().setParam("LocalPC",String(label.dpc().pack(m_type)));
msg->params().setParam("RemotePC",String(label.opc().pack(m_type)));
msg->params().setParam("generated","remote");
// Set the sls in case of STP routing for sequence control
msg->params().setParam("sls",String(label.sls()));
if (m_printMsg && debugAt(DebugInfo)) {
String tmp;
msg->toString(tmp,label,debugAt(DebugAll),
m_extendedDebug ? paramPtr : 0,paramLen);
String tmp1;
fillLabelAndReason(tmp1,label,msg);
Debug(this,DebugInfo,"Received message (%p) '%s' %s %s",
msg,SS7MsgSCCP::lookup(msg->type()),tmp1.c_str(),tmp.c_str());
} else if (debugAt(DebugAll)) {
String tmp;
bool debug = fillLabelAndReason(tmp,label,msg);
Debug(this,debug ? DebugInfo : DebugAll,"Received message '%s' %s",
msg->name(),tmp.c_str());
}
// Form here something will happened with the message!
// return true
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()));
TelEngine::destruct(msg);
return true;
}
// Send Connection Refused
SS7MsgSCCP* ref = new SS7MsgSCCP(SS7MsgSCCP::CREF);
ref->params().setParam("DestinationLocalReference",msg->params().getValue(YSTRING("SourceLocalReference")));
ref->params().setParam("RefusalCause",String(0x13)); // Unequipped user
SS7Label outLabel(label.type(),label.opc(),label.dpc(),label.sls());
SS7MSU* msu = buildMSU(ref,outLabel);
if (!msu)
Debug(this,DebugWarn,"Failed to build msu from sccpMessage %s",
SS7MsgSCCP::lookup(ref->type()));
lock.drop();
transmitMSU(*msu,outLabel,outLabel.sls());
TelEngine::destruct(msu);
TelEngine::destruct(ref);
TelEngine::destruct(msg);
return true;
}
// If the Calling party address does not contain route information
// Set OPC as Calling Party Address pointcode
if (((protocolClass == 0 || protocolClass == 1) &&
isSCLCMessage(msg->type())) || isSCLCSMessage(msg->type())) { // ConnectionLess message
lock.drop();
routeSCLCMessage(msg,label);
} else {
Debug(this,DebugMild,"Received bad message! Inconsistence between msg type %s and protocol class %d",
SS7MsgSCCP::lookup(msg->type()),protocolClass);
}
TelEngine::destruct(msg);
return true;
}
// Process a SCCP message! return false if an error was detected
bool SS7SCCP::routeSCLCMessage(SS7MsgSCCP*& msg, const SS7Label& label)
{
Lock lock(this);
if (!msg) {
Debug(this,DebugWarn,"Request to route null sccp message");
m_errors++;
return false;
}
if (msg->params().getParam(YSTRING("Segmentation"))) {
// Verify if we had received Segmentation parameter with only one segment
// and let it pass trough
// The reassamblation of XUTDS and LUDTS is optional but, for code flow
// purpose, we are manageing it.
if (msg->params().getIntValue(YSTRING("Segmentation.RemainingSegments"),0) != 0 ||
!msg->params().getBoolValue(YSTRING("Segmentation.FirstSegment"),true)) {
// We have segmentation parameter with multiple segments
SS7MsgSCCP* finishead = 0;
int ret = reassembleSegment(msg,label,finishead);
if (ret == SS7MsgSccpReassemble::Accepted ||
ret == SS7MsgSccpReassemble::Rejected)
return true;
if (ret == SS7MsgSccpReassemble::Error) {
// For XUDTS and LUDTS messages the message return should allways
// be false
if (finishead && finishead->params().getBoolValue(YSTRING("MessageReturn"),false))
returnMessage(finishead,SegmentationFailure);
if (finishead)
TelEngine::destruct(finishead);
m_errors++;
return true;
}
if (!finishead) {
Debug(this,DebugStub,"Sccp Message finishead to reassemble but the message was not returned");
return true;
}
TelEngine::destruct(msg);
msg = finishead;
}
}
int errorCode = -1;
NamedString* route = msg->params().getParam(YSTRING("CalledPartyAddress.route"));
bool msgReturn = msg->params().getBoolValue(YSTRING("MessageReturn"),false);
bool informManagement = false;
while (route && *route != YSTRING("ssn")) {
if (!msg->params().getParam(YSTRING("CalledPartyAddress.gt"))) {
if (m_endpoint && msg->params().getParam(YSTRING("CalledPartyAddress.ssn")))
break; // If we are endpoint and we have a ssn try to process the message
Debug(this,DebugInfo,"Message requested to be routed on gt but no gt present!");
break;
}
NamedList* gtRoute = translateGT(msg->params(),"CalledPartyAddress",
"CallingPartyAddress");
m_totalGTTranslations++;
if (!gtRoute) {
if (m_endpoint && msg->params().getParam(YSTRING("CalledPartyAddress.ssn")))
break; // If we are endpoint and we have a ssn try to process the message
m_gttFailed++;
errorCode = NoTranslationSpecificAddress;
Debug(this,DebugInfo,"No Gt Found for : %s, or all routes are down!",
msg->params().getValue(YSTRING("CalledPartyAddress.gt")));
break;
}
resolveGTParams(msg,gtRoute);
NamedString* localRouting = gtRoute->getParam(YSTRING("sccp"));
if (localRouting && *localRouting != toString()) {
msg->params().copyParam(*gtRoute,YSTRING("RemotePC"));
TelEngine::destruct(gtRoute);
lock.drop();
return routeLocal(msg) >= 0;
}
bool haveRemotePC = gtRoute->getParam(YSTRING("RemotePC")) != 0;
if (!gtRoute->getParam(YSTRING("pointcode")) && !haveRemotePC) {
if (m_endpoint) {
// If we have an ssn try to process the message
if (msg->params().getParam(YSTRING("CalledPartyAddress.ssn")))
break;
if (gtRoute->getParam(YSTRING("ssn"))) {
msg->params().setParam("CalledPartyAddress.ssn",gtRoute->getValue(YSTRING("ssn")));
break;
}
}
Debug(this,DebugWarn,"The GT has not been translated to a pointcode!!");
TelEngine::destruct(gtRoute);
errorCode = NoTranslationAddressNature;
break;
}
msg->params().clearParam(YSTRING("CalledPartyAddress"),'.');
for (unsigned int i = 0;i < gtRoute->length();i++) {
NamedString* val = gtRoute->getParam(i);
if (val && (val->name().startsWith("gt") || val->name() == YSTRING("pointcode") ||
val->name() == YSTRING("ssn") || val->name() == YSTRING("route")))
msg->params().setParam("CalledPartyAddress." + val->name(),*val);
}
int pointcode = haveRemotePC ? gtRoute->getIntValue(YSTRING("RemotePC")) :
msg->params().getIntValue(YSTRING("CalledPartyAddress.pointcode"));
TelEngine::destruct(gtRoute);
if (msg->params().getIntValue(YSTRING("CalledPartyAddress.ssn"),-1) == 1) {
Debug(this,DebugNote,"GT Routing Warn!! Message %s global title translated for management!",
SS7MsgSCCP::lookup(msg->type()));
m_errors++;
return false; // Management message with global title translation
}
if (!m_localPointCode)
Debug(this,DebugConf,
"No local PointCode configured!! GT translations with no local PointCode may lead to undesired behavior");
if (msg->params().getParam(YSTRING("HopCounter"))) {
int hopcounter = msg->params().getIntValue(YSTRING("HopCounter"));
hopcounter --;
if (hopcounter <= 0) {
errorCode = HopCounterViolation;
break;
}
msg->params().setParam("HopCounter",String(hopcounter));
}
// If from the translated gt resulted a pointcode other then ours forward the message
if (pointcode > 0 && m_localPointCode && (unsigned int)pointcode != m_localPointCode->pack(m_type)) {
msg->params().setParam("RemotePC",String(pointcode));
lock.drop();
if (transmitMessage(msg) >= 0)
return true;
informManagement = true;
errorCode = MtpFailure;
}
break;
}
if (errorCode >= 0) {
m_errors++;
lock.drop();
if (informManagement && m_management)
m_management->routeFailure(msg);
if (msgReturn)
returnMessage(msg,errorCode);
else
Debug(this,DebugInfo,"Dropping message %s. Reason: %s",
SS7MsgSCCP::lookup(msg->type()),lookup(errorCode,s_return_cause));
return false;
}
int ssn = msg->params().getIntValue(YSTRING("CalledPartyAddress.ssn"),-1);
errorCode = SccpFailure;
while (ssn > 0) {
if (ssn == 0) {
Debug(this,DebugNote,"Requested user with ssn 0!");
errorCode = UnequippedUser;
break;
}
if (ssn == 1) { // Local Management message ?
while (true) {
int protocolClass = msg->params().getIntValue(YSTRING("ProtocolClass"));
// SCCP management messages need to have protocol class 0 with no special options
if (protocolClass != 0 || msgReturn)
break;
// Remote SSN must be management SSN (1)
if (msg->params().getIntValue(YSTRING("CallingPartyAddress.ssn"),-1) != 1)
break;
if (m_management) {
lock.drop();
return m_management->processMessage(msg);
}
break;
}
#ifdef DEBUG
String tmp;
msg->params().dump(tmp,"\r\n ",'\'',true);
Debug(this,DebugNote,"Received invalid SCCPManagement message! %s",tmp.c_str());
#endif
m_errors++;
return false;
}
// If we are here that means that the message is for local processing!
switch (msg->type()) {
case SS7MsgSCCP::XUDT:
case SS7MsgSCCP::LUDT:
case SS7MsgSCCP::UDT:
lock.drop();
{
int ret = pushMessage(*msg->getData(),msg->params(),ssn);
if (ret == HandledMSU::Accepted)
return true;
if (m_management)
m_management->subsystemFailure(msg,label);
errorCode = (ret == HandledMSU::Unequipped) ? UnequippedUser : SubsystemFailure;
}
break;
case SS7MsgSCCP::XUDTS:
case SS7MsgSCCP::LUDTS:
case SS7MsgSCCP::UDTS:
if (m_extendedMonitoring)
archiveMessage(msg);
DDebug(this,DebugAll,"Received service message %s. Reason: %s",
SS7MsgSCCP::lookup(msg->type()),lookup(msg->params().getIntValue(YSTRING("ReturnCause")),s_return_cause));
msg->params().setParam("location","remote");
lock.drop();
notifyMessage(*msg->getData(),msg->params(),ssn);
// Do not bother to verify the return code, because there is nothing that we can do for service messages
return true;
default:
Debug(this,DebugWarn,"Received unknown SCLC msg type %d",msg->type());
errorCode = ErrorInLocalProcessing;
break;
}
break;
}
m_errors++;
lock.drop();
if (msgReturn)
returnMessage(msg,errorCode);
else
Debug(this,DebugInfo,"Dropping message %s. Reason: %s",
SS7MsgSCCP::lookup(msg->type()),lookup(errorCode,s_return_cause));
return false;
}
void SS7SCCP::returnMessage(SS7MsgSCCP* message, int error)
{
DDebug(this,DebugInfo,"Returning message %s! reason : %s",SS7MsgSCCP::lookup(message->type()),lookup(error,s_return_cause));
if (!message) {
DDebug(this,DebugNote,"Message return method called for a null message!!");
return;
}
if (!message->getData()) {
DDebug(this,DebugWarn,"Message Return initiated with no data parameter");
return;
}
SS7MsgSCCP* msg = 0;
switch (message->type()) {
case SS7MsgSCCP::UDT:
msg = new SS7MsgSCCP(SS7MsgSCCP::UDTS);
break;
case SS7MsgSCCP::XUDT:
msg = new SS7MsgSCCP(SS7MsgSCCP::XUDTS);
break;
case SS7MsgSCCP::LUDT:
msg = new SS7MsgSCCP(SS7MsgSCCP::LUDTS);
break;
default:
DDebug(this,DebugInfo,"Message return procedure initiated for wrong message type %s",
SS7MsgSCCP::lookup(msg->type()));
return;
}
if (!msg) {
Debug(this,DebugStub,"Implementation bug!! null SCCP message");
return;
}
msg->params().copyParams(message->params());
switchAddresses(message->params(),msg->params());
msg->params().setParam("ReturnCause",String(error));
msg->setData(message->getData());
msg->params().clearParam(YSTRING("ProtocolClass"),'.');
msg->params().clearParam(YSTRING("Segmentation"),'.');
msg->params().clearParam(YSTRING("MessageReturn"),'.');
if (msg->params().getParam(YSTRING("Importance")))
msg->params().setParam("Importance","3"); // Default value for service messages
if (msg->params().getParam(YSTRING("HopCounter")))
msg->params().setParam("HopCounter",String(m_hopCounter));
transmitMessage(msg,true);
msg->removeData();
TelEngine::destruct(msg);
}
void SS7SCCP::switchAddresses(const NamedList& source, NamedList& dest)
{
// First remove the called and calling party address from dest
dest.clearParam(YSTRING("CalledPartyAddress"),'.');
dest.clearParam(YSTRING("CallingPartyAddress"),'.');
dest.clearParam(YSTRING("LocalPC"));
dest.clearParam(YSTRING("RemotePC"));
if (source.getParam(YSTRING("LocalPC")))
dest.setParam("LocalPC",source.getValue(YSTRING("LocalPC")));
// Do not set RemotePC because the message can fail after a gt was performed
// and than RemotePC represents message destination pc rather then
// originating pc. Obtain return address from CallingPartyAddress
// Copy the params
for (unsigned int i = 0;i < source.length();i++) {
NamedString* param = source.getParam(i);
if (!param || !param->name().startsWith("Call"))
continue;
String name = param->name();
if (name.startSkip(YSTRING("CalledPartyAddress"),false))
dest.setParam(new NamedString("CallingPartyAddress" + name,*param));
if (name.startSkip(YSTRING("CallingPartyAddress"),false))
dest.setParam(new NamedString("CalledPartyAddress" + name,*param));
}
}
bool SS7SCCP::decodeMessage(SS7MsgSCCP* msg, SS7PointCode::Type pcType,
const unsigned char* paramPtr, unsigned int paramLen)
{
if (!msg)
return false;
String msgTypeName((int)msg->type());
const char* msgName = SS7MsgSCCP::lookup(msg->type(),msgTypeName);
#ifdef XDEBUG
String tmp;
tmp.hexify((void*)paramPtr,paramLen,' ');
Debug(this,DebugAll,"Decoding msg=%s len=%u: %s [%p]",
msgName,paramLen,tmp.c_str(),this);
#else
DDebug(this,DebugAll,"Decoding msg=%s len=%u [%p]",
msgName,paramLen,this);
#endif
// see what parameters we expect for this message
const MsgParams* params = getSccpParams(msg->type());
if (!params) {
Debug(this,DebugWarn,"Parameters list could not be found for message %s [%p]",msgName,this);
return false;
}
// Get parameter prefix
String prefix = msg->params().getValue(YSTRING("message-prefix"));
// Add protocol and message type
switch (pcType) {
case SS7PointCode::ITU:
msg->params().addParam(prefix+"protocol-type","itu-t");
break;
case SS7PointCode::ANSI:
case SS7PointCode::ANSI8:
msg->params().addParam(prefix+"protocol-type","ansi");
break;
default: ;
}
msg->params().addParam(prefix+"message-type",msgName);
String unsupported;
const SS7MsgSCCP::Parameters* plist = params->params;
SS7MsgSCCP::Parameters ptype;
// first decode any mandatory fixed parameters the message should have
while ((ptype = *plist++) != SS7MsgSCCP::EndOfParameters) {
const SCCPParam* param = getParamDesc(ptype);
if (!param) {
// this is fatal as we don't know the length
Debug(this,DebugCrit,"Missing description of fixed SCCP parameter 0x%02x [%p]",ptype,this);
return false;
}
if (!param->size) {
Debug(this,DebugCrit,"Invalid (variable) description of fixed SCCP parameter %s [%p]",param->name,this);
return false;
}
if (paramLen < param->size) {
Debug(this,DebugWarn,"Truncated SCCP message! [%p]",this);
return false;
}
DDebug(this,DebugAll,"Decoding fixed SCCP Param %s",param->name);
if (!decodeParam(this,msg->params(),param,paramPtr,param->size,prefix)) {
Debug(this,DebugWarn,"Could not decode fixed SCCP parameter %s [%p]",param->name,this);
decodeRaw(this,msg->params(),param,paramPtr,param->size,prefix);
unsupported.append(param->name,",");
}
paramPtr += param->size;
paramLen -= param->size;
} // while ((ptype = *plist++)...
bool mustWarn = true;
bool ludt = msg->isLongDataMessage();
// next decode any mandatory variable parameters the message should have
while ((ptype = *plist++) != SS7MsgSCCP::EndOfParameters) {
mustWarn = false;
const SCCPParam* param = getParamDesc(ptype);
if (!param) {
// we could skip over unknown mandatory variable length but it's still bad
Debug(this,DebugCrit,"Missing description of variable SCCP parameter 0x%02x [%p]",ptype,this);
return false;
}
if (param->size)
Debug(this,DebugMild,"Invalid (fixed) description of variable SCCP parameter %s [%p]",param->name,this);
if (!paramPtr || paramLen <= 0) {
Debug(this,DebugWarn,
"Unexpected end of stream!! Expecting to decode variabile parameter %s but there is no data left!!!",
param->name);
return false;
}
unsigned int offs = paramPtr[0];
if (ludt) {
offs |= (paramPtr[1] << 8);
paramPtr++;
paramLen--;
}
if ((offs < 1) || (offs >= paramLen)) {
Debug(this,DebugWarn,"Invalid offset %u (len=%u) SCCP parameter %s [%p]",
offs,paramLen,param->name,this);
return false;
}
unsigned int size = paramPtr[offs];
if (ptype == SS7MsgSCCP::LongData) {
size |= (paramPtr[++offs] << 8);
size --;
}
if ((size < 1) || (offs+size >= paramLen)) {
Debug(this,DebugWarn,"Invalid size %u (ofs=%u, len=%u) SCCP parameter %s [%p]",
size,offs,paramLen,param->name,this);
return false;
}
bool decoded = false;
if (ptype == SS7MsgSCCP::Data || ptype == SS7MsgSCCP::LongData) {
if (!decodeData(this,msg,paramPtr+offs+1,size)) {
Debug(this,DebugWarn,"Could not decode data SCCP parameter %s (size=%u) [%p]",
param->name,size,this);
decodeRaw(this,msg->params(),param,paramPtr+offs+1,size,prefix);
}
decoded = true;
}
if (!decoded && !decodeParam(this,msg->params(),param,paramPtr+offs+1,size,prefix)) {
Debug(this,DebugWarn,"Could not decode variable SCCP parameter %s (size=%u) [%p]",
param->name,size,this);
decodeRaw(this,msg->params(),param,paramPtr+offs+1,size,prefix);
unsupported.append(param->name,",");
}
paramPtr++;
paramLen--;
} // while ((ptype = *plist++)...
// now decode the optional parameters if the message supports them
if (params->optional) {
unsigned int offs = 0;
if (paramLen) {
if (ludt && paramLen > 1) {
offs = paramPtr[0] | (paramPtr[1] << 8);
paramPtr++;
paramLen--;
} else if (!ludt)
offs = paramPtr[0];
}
if (offs >= paramLen) {
if (paramLen) {
Debug(this,DebugWarn,"Invalid SCCP optional offset %u (len=%u) [%p]",
offs,paramLen,this);
return false;
}
Debug(this,DebugMild,"SCCP message %s lacking optional parameters [%p]",
msgName,this);
}
else if (offs) {
mustWarn = true;
// advance pointer past mandatory parameters
paramPtr += offs;
paramLen -= offs;
while (paramLen) {
ptype = (SS7MsgSCCP::Parameters)(*paramPtr++);
paramLen--;
if (ptype == SS7MsgSCCP::EndOfParameters)
break;
if (paramLen < 2) {
Debug(this,DebugWarn,"Only %u octets while decoding optional SCCP parameter 0x%02x [%p]",
paramLen,ptype,this);
return false;
}
unsigned int size = *paramPtr++;
paramLen--;
if ((size < 1) || (size >= paramLen)) {
Debug(this,DebugWarn,"Invalid size %u (len=%u) SCCP optional parameter 0x%02x [%p]",
size,paramLen,ptype,this);
return false;
}
const SCCPParam* param = getParamDesc(ptype);
if (!param) {
Debug(this,DebugMild,"Unknown optional SCCP parameter 0x%02x (size=%u) [%p]",ptype,size,this);
decodeRawParam(this,msg->params(),ptype,paramPtr,size,prefix);
unsupported.append(String((unsigned int)ptype),",");
}
else if (!decodeParam(this,msg->params(),param,paramPtr,size,prefix)) {
Debug(this,DebugWarn,"Could not decode optional SCCP parameter %s (size=%u) [%p]",param->name,size,this);
decodeRaw(this,msg->params(),param,paramPtr,size,prefix);
unsupported.append(param->name,",");
}
paramPtr += size;
paramLen -= size;
} // while (paramLen)
} // else if (offs)
else
paramLen = 0;
}
if (unsupported)
msg->params().addParam(prefix + "parameters-unsupported",unsupported);
if (paramLen && mustWarn)
Debug(this,DebugWarn,"Got %u garbage octets after message type 0x%02x [%p]",
paramLen,msg->type(),this);
return true;
}
void SS7SCCP::receivedUPU(SS7PointCode::Type type, const SS7PointCode node,
SS7MSU::Services part, unsigned char cause, const SS7Label& label, int sls)
{
if (part != sif() || !m_management) // not SCCP
return;
m_management->sccpUnavailable(node,cause);
}
bool SS7SCCP::control(NamedList& params)
{
String* ret = params.getParam(YSTRING("completion"));
const String* oper = params.getParam(YSTRING("operation"));
const char* cmp = params.getValue(YSTRING("component"));
int cmd = oper ? oper->toInteger(s_dict_control,-1) : -1;
if (ret) {
if (oper && (cmd < 0))
return false;
String part = params.getValue(YSTRING("partword"));
if (cmp) {
if (toString() != cmp)
return false;
for (const TokenDict* d = s_dict_control; d->token; d++)
Module::itemComplete(*ret,d->token,part);
return true;
}
return Module::itemComplete(*ret,toString(),part);
}
if (toString() != cmp)
return false;
Lock lock(this);
switch (cmd) {
case Status:
printStatus(false);
return TelEngine::controlReturn(&params,true);
case FullStatus:
if (m_extendedMonitoring)
printStatus(true);
else
Output("Extended monitoring disabled!! Full Status unavailable!");
return TelEngine::controlReturn(&params,true);
case EnableExtendedMonitoring:
m_extendedMonitoring = true;
return TelEngine::controlReturn(&params,true);
case DisableExtendedMonitoring:
m_extendedMonitoring = false;
return TelEngine::controlReturn(&params,true);
case EnablePrintMsg:
m_printMsg = true;
return TelEngine::controlReturn(&params,true);
case DisablePrintMsg:
m_printMsg = false;
return TelEngine::controlReturn(&params,true);
}
return TelEngine::controlReturn(&params,false);
}
void SS7SCCP::printStatus(bool extended)
{
String dest = "";
dumpArchive(dest,extended);
if (!m_management)
return;
m_management->subsystemsStatus(dest);
m_management->routeStatus(dest);
Output("SCCP '%s' [%p] Time: " FMT64 " Status:%s",debugName(),this,Time::msecNow(),dest.c_str());
}
void SS7SCCP::notify(SS7Layer3* link, int sls)
{
if (!(link && network()))
return;
setNetworkUp(network()->operational());
if (m_management)
m_management->pointcodeStatus(link,network()->operational());
}
void SS7SCCP::setNetworkUp(bool operational)
{
if (m_layer3Up == operational)
return;
m_layer3Up = operational;
if (!m_management)
return;
DDebug(this,DebugInfo,"L3 is %s %p",operational ? "operational" : "down", m_management);
if (m_layer3Up)
m_management->mtpEndRestart();
else
m_management->stopSSTs();
}
void SS7SCCP::routeStatusChanged(SS7PointCode::Type type, const SS7PointCode& node, SS7Route::State state)
{
#ifdef DEBUG
String dump;
dump << node;
DDebug(this,DebugAll,"Route status changed %s %s %p",dump.c_str(),SS7Route::stateName(state),m_management);
#endif
state = network()->getRouteState(type,node);
if (m_management)
m_management->routeStatus(type,node,state);
}
void SS7SCCP::archiveMessage(SS7MsgSCCP* msg)
{
if (!msg)
return;
const char* type = SS7MsgSCCP::lookup(msg->type());
NamedString* msgType = m_msgReturnStatus.getParam(type);
if (msgType)
incrementNS(msgType);
else
m_msgReturnStatus.addParam(type,"1");
const char* code = msg->params().getValue(YSTRING("ReturnCode"));
NamedString* retCode = m_msgReturnStatus.getParam(code);
if (retCode)
incrementNS(retCode);
else
m_msgReturnStatus.addParam(code,"1");
}
void SS7SCCP::dumpArchive(String& msg, bool extended)
{
msg << "\r\nMessages Sent : " << m_totalSent;
msg << "\r\nMessages Received : " << m_totalReceived;
msg << "\r\nGT Translations : " << m_totalGTTranslations;
msg << "\r\nErrors : " << m_errors;
msg << "\r\nGT Translations failed : " << m_gttFailed;
NamedString* udts = m_msgReturnStatus.getParam(SS7MsgSCCP::lookup(SS7MsgSCCP::UDTS));
if (udts)
msg << "\r\n" << udts->name() << " : " << *udts;
NamedString* xudts = m_msgReturnStatus.getParam(SS7MsgSCCP::lookup(SS7MsgSCCP::XUDTS));
if (xudts)
msg << "\r\n" << xudts->name() << " : " << *xudts;
NamedString* ludts = m_msgReturnStatus.getParam(SS7MsgSCCP::lookup(SS7MsgSCCP::LUDTS));
if (ludts)
msg << "\r\n" << ludts->name() << " : " << *ludts;
if (!extended)
return;
msg << "\r\n Error Causes:";
for (unsigned int i = 0;i < m_msgReturnStatus.length();i++) {
NamedString* param = m_msgReturnStatus.getParam(i);
if (!param || param == udts || param == xudts || param == ludts)
continue;
const char* error = lookup(param->name().toInteger(),s_return_cause);
if (!error)
continue;
msg << "\r\nCount: " << *param << " Error: " << error;
}
}
bool SS7SCCP::isSCOCMsg(int msgType)
{
switch (msgType) {
case SS7MsgSCCP::CR:
case SS7MsgSCCP::CC:
case SS7MsgSCCP::CREF:
case SS7MsgSCCP::RLSD:
case SS7MsgSCCP::RLC:
case SS7MsgSCCP::DT1:
case SS7MsgSCCP::DT2:
case SS7MsgSCCP::AK:
case SS7MsgSCCP::ED:
case SS7MsgSCCP::EA:
case SS7MsgSCCP::RSR:
case SS7MsgSCCP::RSC:
case SS7MsgSCCP::ERR:
case SS7MsgSCCP::IT:
return true;
default:
return false;
}
return false;
}
/**
* SS7ItuSCCPManagement
*/
SS7ItuSccpManagement::SS7ItuSccpManagement(const NamedList& params)
: SCCPManagement(params,SS7PointCode::ITU)
{
DDebug(this,DebugAll,"Creating SS7ItuSccpManagement(%s) %p",params.c_str(),this);
}
bool SS7ItuSccpManagement::processMessage(SS7MsgSCCP* message)
{
if (!sccp())
return false;
DataBlock* data = message->getData();
if (!data) {
Debug(sccp(),DebugNote,"Request to process Itu management message with no data!");
return false;
}
if (data->length() < 5) {
Debug(sccp(),DebugNote,"Received short management message!");
return false;
}
const unsigned char* paramsPtr = (const unsigned char*)data->data();
unsigned char msg = *paramsPtr++;
const char* msgType = lookup(msg,s_managementMessages);
if (!msgType) {
Debug(sccp(),DebugNote,"Received unknown management message! 0x%x",msg);
return false;
}
if (msg > SSC) {
Debug(sccp(),DebugNote,"Received unknown ITU management message! 0x%x",msg);
return false;
}
// After msg type is SSN
message->params().setParam("ssn",String((int)*paramsPtr++));
// Pointcode 2 o
int pointcode = *paramsPtr++;
pointcode |= (*paramsPtr++ & 0x3f) << 8;
message->params().setParam("pointcode",String(pointcode));
// Subsystem Multiplicity Indicator
message->params().setParam("smi",String(*paramsPtr++ & 0x03));
// If message type is SSC decode congestion level
if (msg == SSC) {
if (!paramsPtr) {
Debug(sccp(),DebugNote,"Failed to decode SSC congestion level parameter! Reason short message.");
return false;
}
message->params().setParam("congestion-level",String(*paramsPtr & 0x0f));
}
if (printMessagess()) {
String dest;
printMessage(dest, (MsgType)msg,message->params());
Debug(this,DebugInfo,"Received message %s",dest.c_str());
}
return handleMessage(msg,message->params());
}
bool SS7ItuSccpManagement::sendMessage(SCCPManagement::MsgType msgType, const NamedList& params)
{
if (!sccp())
return false;
if (printMessagess()) {
String dest;
printMessage(dest, msgType,params);
Debug(this,DebugInfo,"Sending message %s",dest.c_str());
}
unsigned char ssn = params.getIntValue(YSTRING("ssn"));
int pointcode = params.getIntValue(YSTRING("pointcode"));
int smi = params.getIntValue(YSTRING("smi"));
int dataLen = msgType == SSC ? 6 : 5;
DataBlock data(0,dataLen);
unsigned char * d = (unsigned char*)data.data();
d[0] = msgType;
d[1] = ssn;
d[2] = pointcode & 0xff;
d[3] = (pointcode >> 8) & 0x3f;
d[4] = smi & 0x03;
if (msgType == SSC)
d[5] = params.getIntValue(YSTRING("congestion-level"),0) & 0x0f;
int localPC = sccp()->getPackedPointCode();
SS7MsgSCCP* msg = new SS7MsgSCCP(SS7MsgSCCP::UDT);
const char* remotePC = params.getValue(YSTRING("RemotePC"));
msg->params().setParam("ProtocolClass","0");
msg->params().setParam("CalledPartyAddress.ssn","1");
msg->params().setParam("CalledPartyAddress.pointcode",remotePC);
msg->params().setParam("CalledPartyAddress.route","ssn");
msg->params().setParam("CallingPartyAddress.ssn","1");
msg->params().setParam("CallingPartyAddress.route","ssn");
msg->params().setParam("CallingPartyAddress.pointcode",String(localPC));
msg->params().setParam("LocalPC",String(localPC));
msg->params().setParam("RemotePC",remotePC);
msg->setData(&data);
bool ret = sccp()->transmitMessage(msg) >= 0;
if (!ret)
Debug(this,DebugNote,"Failed to send management message %s to remote %s",
lookup(msgType,s_managementMessages),params.getValue(YSTRING("RemotePC")));
msg->extractData();
TelEngine::destruct(msg);
return ret;
}
void SS7ItuSccpManagement::manageSccpRemoteStatus(SccpRemote* rsccp, SS7Route::State newState)
{
if (!rsccp)
return;
#ifdef XDEBUG
String pc;
rsccp->dump(pc,false);
XDebug(this,DebugInfo,"Remote sccp '%s' status changed, new state: %s",pc.c_str(),SS7Route::stateName(newState));
#endif
switch (newState) {
case SS7Route::Congestion:
Debug(sccp(),DebugStub,"Please implement SCCPManagement Congestion");
break;
case SS7Route::Allowed:
{
// Set state should set the state to all subsystems
rsccp->setState(SCCPManagement::Allowed);
updateTables(rsccp);
rsccp->resetCongestion();
// Discontinue the Subsystem Status Test for SSN = 1
SccpSubsystem* ss = new SccpSubsystem(1);
stopSst(rsccp,ss);
TelEngine::destruct(ss);
localBroadcast(SCCP::PointCodeStatusIndication,rsccp->getPackedPointcode(),PCAccessible,-1,0);
localBroadcast(SCCP::PointCodeStatusIndication,rsccp->getPackedPointcode(),-1,SccpRemoteAccessible,0);
break;
}
case SS7Route::Prohibited:
{
rsccp->setState(SCCPManagement::Prohibited);
updateTables(rsccp);
// Discontinue all tests for the remote sccp
SccpSubsystem* ss = new SccpSubsystem(1);
stopSst(rsccp,0,ss); // Stop all sst except management
// Do not start SST if the route is down the message will fail to be
// sent. The status will be changed to allowed when the route is up
TelEngine::destruct(ss);
localBroadcast(SCCP::PointCodeStatusIndication,rsccp->getPackedPointcode(),PCInaccessible,-1,0);
localBroadcast(SCCP::PointCodeStatusIndication,rsccp->getPackedPointcode(),-1,SccpRemoteInaccessible,0);
break;
}
case SS7Route::Unknown:
rsccp->setState(SCCPManagement::Unknown);
break;
default:
DDebug(this,DebugNote,"Unhandled remote sccp status '%s'",SS7Route::stateName(newState));
}
}
bool SS7ItuSccpManagement::handleMessage(int msgType, NamedList& params)
{
unsigned char ssn = params.getIntValue(YSTRING("ssn"));
if (ssn == 0) {
Debug(this,DebugNote,"Received management message '%s' with invalid ssn '%d'",
lookup(msgType,s_managementMessages),ssn);
return false;
}
unsigned char smi = params.getIntValue(YSTRING("smi")); // subsystem multiplicity indicator
if (smi != 0) {
if (smi > 3) {
Debug(this,DebugWarn, "Received management message '%s' with unknown smi: '%d' , ssn: '%d'",
lookup(msgType,s_managementMessages),smi,ssn);
smi = 0;
} else
DDebug(this,DebugNote,"Received management message '%s' with national smi: %d",
lookup(msgType,s_managementMessages),smi);
}
switch (msgType) {
case SSC:
Debug(this,DebugStub,"Please implement subsystem congested!");
break;
default:
return SCCPManagement::handleMessage(msgType,ssn,smi,params);
}
return true;
}
void SS7ItuSccpManagement::handleSubsystemStatus(SccpSubsystem* subsystem, bool allowed, SccpRemote* remote, int smi)
{
if (!subsystem) {
Debug(sccp(),DebugWarn,"Request to handle subsystem status with no subsystem!");
return;
}
SCCPManagement::SccpStates ssnState = allowed ? SCCPManagement::Allowed : SCCPManagement::Prohibited;
subsystem->setState(ssnState);
DDebug(this,DebugInfo,"Handle subsystem status for pc: '%d' ssn: '%d' status %s", remote ? remote->getPackedPointcode() : 0,
subsystem ? subsystem->getSSN() : 0, stateName(ssnState));
Lock lock(this);
bool localSubsystem = false;
// Change the status of the subsystem
if (!remote || remote->getPointCode() == *sccp()->getLocalPointCode()) { // LocalSubsystem
SccpLocalSubsystem* subs = getLocalSubsystem(subsystem->getSSN());
if (subs) {
if (subs->getState() == ssnState) // Same state? do nothing
return;
subs->resetTimers();
subs->setState(ssnState);
} else // Append dynamically
m_localSubsystems.append(new SccpLocalSubsystem(subsystem->getSSN(),getCoordTimeout(),
getIgnoreTestsInterval()));
localSubsystem = true;
} else {
SccpRemote* rsccp = getRemoteSccp(remote->getPackedPointcode());
if (rsccp && !rsccp->changeSubsystemState(subsystem->getSSN(),ssnState))
return;
}
// Stop all subsystem status tests
if (!localSubsystem && allowed)
stopSst(remote,subsystem);
else if (!localSubsystem) // Initiate subsystem status test
startSst(remote,subsystem);
lock.drop();
// update translation tables
if (!localSubsystem)
updateTables(remote,subsystem);
// Local Broadcast user in/out of service
NamedList params("");
if (!localSubsystem)
params.setParam("pointcode",String(remote->getPackedPointcode()));
params.setParam("ssn",String(subsystem->getSSN()));
params.setParam("subsystem-status",lookup(allowed ? UserInService : UserOutOfService,broadcastType()));
managementMessage(SCCP::StatusIndication,params);
// Send broadcast for all concerned signalling points
///TODO for now we send only for local interested subsystems
if (!localSubsystem)
return;
notifyConcerned(allowed ? SSA : SSP, subsystem->getSSN(),smi);
}
/**
* SS7AnsiSCCPManagement
*/
SS7AnsiSccpManagement::~SS7AnsiSccpManagement()
{
DDebug(this,DebugAll,"Destroing Ansi Sccp Management(%p)",this);
}
bool SS7AnsiSccpManagement::processMessage(SS7MsgSCCP* message)
{
if (!sccp()) {
return false;
}
DataBlock* data = message->getData();
if (!data) {
DDebug(sccp(),DebugNote,"Request to process Ansi management message with no data!");
return false;
}
if (data->length() < 6) {
DDebug(sccp(),DebugNote,"Received short Ansi management message! %d",data->length());
return false;
}
const unsigned char* paramsPtr = (const unsigned char*)data->data();
unsigned char msg = *paramsPtr++;
const char* msgType = lookup(msg,s_managementMessages);
if (!msgType) {
DDebug(sccp(),DebugNote,"Received unknown management message! 0x%x",msg);
return false;
}
if (msg > 0x05 && msg < 0xfd) {
DDebug(sccp(),DebugNote,"Received unknown Ansi management message! 0x%x",msg);
return false;
}
// After msg type is SSN
message->params().setParam("ssn",String((int)*paramsPtr++));
// Pointcode 2 o
unsigned int pointcode = *paramsPtr++;
pointcode |= (*paramsPtr++ << 8);
pointcode |= (*paramsPtr++ << 16);
message->params().setParam("pointcode",String(pointcode));
// Subsystem Multiplicity Indicator
message->params().setParam("SMI",String(*paramsPtr++ & 0x03));
if (printMessagess()) {
String dest;
printMessage(dest, (MsgType)msg,message->params());
Debug(this,DebugInfo,"Received message %s",dest.c_str());
}
return handleMessage(msg,message->params());
}
bool SS7AnsiSccpManagement::sendMessage(SCCPManagement::MsgType msgType, const NamedList& params)
{
if (!sccp())
return false;
if (printMessagess()) {
String dest;
printMessage(dest, msgType,params);
Debug(this,DebugInfo,"Sending message %s",dest.c_str());
}
unsigned char ssn = params.getIntValue(YSTRING("ssn"));
int pointcode = params.getIntValue(YSTRING("pointcode"));
int smi = params.getIntValue(YSTRING("smi"));
DataBlock data(0,6);
unsigned char * d = (unsigned char*)data.data();
d[0] = msgType;
d[1] = ssn;
d[2] = pointcode & 0xff;
d[3] = (pointcode >> 8) & 0xff;
d[4] = (pointcode >> 16) & 0xff;
d[5] = smi & 0x03;
int localPC = sccp()->getPackedPointCode();
SS7MsgSCCP* msg = new SS7MsgSCCP(SS7MsgSCCP::UDT);
const char* remotePC = params.getValue(YSTRING("RemotePC"));
msg->params().setParam("ProtocolClass","0");
msg->params().setParam("CalledPartyAddress.ssn","1");
msg->params().setParam("CalledPartyAddress.pointcode",remotePC);
msg->params().setParam("CalledPartyAddress.route","ssn");
msg->params().setParam("CallingPartyAddress.ssn","1");
msg->params().setParam("CallingPartyAddress.route","ssn");
msg->params().setParam("CallingPartyAddress.pointcode",String(localPC));
msg->params().setParam("LocalPC",String(localPC));
msg->params().setParam("RemotePC",remotePC);
msg->setData(&data);
bool ret = sccp()->transmitMessage(msg) >= 0;
if (!ret)
Debug(this,DebugNote,"Failed to send management message %s to remote %s",
lookup(msgType,s_managementMessages),params.getValue(YSTRING("RemotePC")));
msg->extractData();
TelEngine::destruct(msg);
return ret;
}
bool SS7AnsiSccpManagement::handleMessage(int msgType, NamedList& params)
{
unsigned char ssn = params.getIntValue(YSTRING("ssn"));
if (ssn == 0) {
Debug(this,DebugNote,"Received management message '%s' with invalid ssn '%d'",
lookup(msgType,s_managementMessages),ssn);
return false;
}
unsigned char smi = params.getIntValue(YSTRING("smi")); // subsystem multiplicity indicator
if (!lookup(smi,s_ansiSmi)) {
Debug(this,DebugWarn, "Received management message '%s' with invalid smi: '%d' , ssn: '%d'",
lookup(msgType,s_managementMessages),smi,ssn);
smi = 0;
}
switch (msgType) {
case SBR:
case SNR:
case SRT:
Debug(this,DebugStub,"Please implement %s message handling!",lookup(msgType,s_managementMessages));
break;
default:
return SCCPManagement::handleMessage(msgType,ssn,smi,params);
}
return true;
}
void SS7AnsiSccpManagement::manageSccpRemoteStatus(SccpRemote* rsccp, SS7Route::State newState)
{
if (!rsccp)
return;
#ifdef XDEBUG
String pc;
rsccp->dump(pc,false);
XDebug(this,DebugInfo,"Remote sccp '%s' status changed, new state: %s",pc.c_str(),SS7Route::stateName(newState));
#endif
switch (newState) {
case SS7Route::Congestion:
Debug(sccp(),DebugStub,"Please implement SCCPManagement Congestion");
break;
case SS7Route::Allowed:
{
// Set state should set the state to all subsystems
rsccp->setState(SCCPManagement::Allowed);
rsccp->resetCongestion();
localBroadcast(SCCP::PointCodeStatusIndication,rsccp->getPackedPointcode(),
PCAccessible,-1,0);
// Discontinue all subsystem status tests
stopSst(rsccp);
localBroadcast(SCCP::PointCodeStatusIndication,rsccp->getPackedPointcode(),
-1,SccpRemoteAccessible,0);
updateTables(rsccp);
rsccp->lock();
ListIterator ssns(rsccp->getSubsystems());
rsccp->unlock();
SccpSubsystem* ss = 0;
while ((ss = YOBJECT(SccpSubsystem,ssns.get())))
localBroadcast(SCCP::StatusIndication,-1,-1,-1,-1,ss->getSSN(),UserInService);
break;
}
case SS7Route::Prohibited:
{
rsccp->setState(SCCPManagement::Prohibited);
localBroadcast(SCCP::PointCodeStatusIndication,rsccp->getPackedPointcode(),
PCInaccessible,-1,0);
SccpSubsystem* ss = new SccpSubsystem(1);
stopSst(rsccp,ss);
TelEngine::destruct(ss);
updateTables(rsccp);
localBroadcast(SCCP::PointCodeStatusIndication,rsccp->getPackedPointcode(),
-1,SccpRemoteInaccessible,0);
rsccp->lock();
ListIterator ssns(rsccp->getSubsystems());
rsccp->unlock();
SccpSubsystem* ss1 = 0;
while ((ss1 = YOBJECT(SccpSubsystem,ssns.get())))
localBroadcast(SCCP::StatusIndication,-1,-1,-1,-1,ss1->getSSN(),UserOutOfService);
break;
}
case SS7Route::Unknown:
rsccp->setState(SCCPManagement::Unknown);
break;
default:
DDebug(this,DebugNote,"Unhandled remote sccp status '%s'",SS7Route::stateName(newState));
}
}
void SS7AnsiSccpManagement::handleSubsystemStatus(SccpSubsystem* subsystem, bool allowed, SccpRemote* remote, int smi)
{
if (!subsystem || subsystem->getSSN() <= 0) {
Debug(sccp(),DebugWarn,"Request to handle subsystem status with no subsystem!");
return;
}
SCCPManagement::SccpStates ssnState = allowed ? SCCPManagement::Allowed : SCCPManagement::Prohibited;
subsystem->setState(ssnState);
DDebug(this,DebugInfo,"Handle subsystem status for pc: '%d' ssn: '%d' status %s", remote ? remote->getPackedPointcode() : 0,
subsystem ? subsystem->getSSN() : 0, stateName(ssnState));
Lock lock(this);
bool localSubsystem = false;
// Change the status of the subsystem
if (!remote || remote->getPointCode() == *sccp()->getLocalPointCode()) { // LocalSubsystem
SccpLocalSubsystem* subs = getLocalSubsystem(subsystem->getSSN());
if (subs) {
if (subs->getState() == ssnState) // Same state? do nothing
return;
subs->resetTimers();
subs->setState(ssnState);
} else // Append dynamically
m_localSubsystems.append(new SccpLocalSubsystem(subsystem->getSSN(),getCoordTimeout(),
getIgnoreTestsInterval()));
localSubsystem = true;
} else {
SccpRemote* rsccp = getRemoteSccp(remote->getPackedPointcode());
if (rsccp && !rsccp->changeSubsystemState(subsystem->getSSN(),ssnState))
return;
}
// Stop all subsystem status tests
if (!localSubsystem && allowed)
stopSst(remote,subsystem);
else if (!localSubsystem) // Initiate subsystem status test
startSst(remote,subsystem);
lock.drop();
// update translation tables
if (!localSubsystem)
updateTables(remote,subsystem);
// Local Broadcast user in/out of service
localBroadcast(SCCP::StatusIndication,localSubsystem ? -1 :remote->getPackedPointcode(),
-1,-1,-1,subsystem->getSSN(), allowed ? UserInService : UserOutOfService);
// Send broadcast for all concerned signalling points
///TODO for now we send only for local interested subsystems
if (!localSubsystem)
return;
notifyConcerned(allowed ? SSA : SSP, subsystem->getSSN(),smi);
}
/* vi: set ts=8 sw=4 sts=4 noet: */