5002 lines
165 KiB
C++
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,¶ms,"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,¶ms), SS7Layer4(SS7MSU::SCCP|SS7MSU::National,¶ms), 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",
|
|
¶ms,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,¶ms))
|
|
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(¶ms,true);
|
|
case FullStatus:
|
|
if (m_extendedMonitoring)
|
|
printStatus(true);
|
|
else
|
|
Output("Extended monitoring disabled!! Full Status unavailable!");
|
|
return TelEngine::controlReturn(¶ms,true);
|
|
case EnableExtendedMonitoring:
|
|
m_extendedMonitoring = true;
|
|
return TelEngine::controlReturn(¶ms,true);
|
|
case DisableExtendedMonitoring:
|
|
m_extendedMonitoring = false;
|
|
return TelEngine::controlReturn(¶ms,true);
|
|
case EnablePrintMsg:
|
|
m_printMsg = true;
|
|
return TelEngine::controlReturn(¶ms,true);
|
|
case DisablePrintMsg:
|
|
m_printMsg = false;
|
|
return TelEngine::controlReturn(¶ms,true);
|
|
}
|
|
return TelEngine::controlReturn(¶ms,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: */
|