1211 lines
37 KiB
C++
1211 lines
37 KiB
C++
/**
|
|
* management.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-2013 Null Team
|
|
*
|
|
* This software is distributed under multiple licenses;
|
|
* see the COPYING file in the main directory for licensing
|
|
* information for this specific distribution.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*/
|
|
|
|
#include "yatesig.h"
|
|
#include <yatephone.h>
|
|
|
|
#include <string.h>
|
|
|
|
using namespace TelEngine;
|
|
|
|
#define MAKE_NAME(x) { #x, SS7MsgSNM::x }
|
|
static const TokenDict s_snm_names[] = {
|
|
// this list must be kept in synch with the header
|
|
MAKE_NAME(COO),
|
|
MAKE_NAME(ECO),
|
|
MAKE_NAME(RCT),
|
|
MAKE_NAME(TFP),
|
|
MAKE_NAME(RST),
|
|
MAKE_NAME(RSP), // alias
|
|
MAKE_NAME(LIN),
|
|
MAKE_NAME(TRA),
|
|
MAKE_NAME(DLC),
|
|
MAKE_NAME(UPU),
|
|
MAKE_NAME(COA),
|
|
MAKE_NAME(ECA),
|
|
MAKE_NAME(TFC),
|
|
MAKE_NAME(TCP),
|
|
MAKE_NAME(TFPA), // alias
|
|
MAKE_NAME(RSR),
|
|
MAKE_NAME(LUN),
|
|
MAKE_NAME(TRW),
|
|
MAKE_NAME(CSS),
|
|
MAKE_NAME(XCO),
|
|
MAKE_NAME(TFR),
|
|
MAKE_NAME(RCP),
|
|
MAKE_NAME(LIA),
|
|
MAKE_NAME(CNS),
|
|
MAKE_NAME(XCA),
|
|
MAKE_NAME(TCR),
|
|
MAKE_NAME(RCR),
|
|
MAKE_NAME(LUA),
|
|
MAKE_NAME(CNP),
|
|
MAKE_NAME(CBD),
|
|
MAKE_NAME(TFA),
|
|
MAKE_NAME(LID),
|
|
MAKE_NAME(CBA),
|
|
MAKE_NAME(TCA),
|
|
MAKE_NAME(TFAA), // alias
|
|
MAKE_NAME(LFU),
|
|
MAKE_NAME(LLT),
|
|
MAKE_NAME(LLI), // alias
|
|
MAKE_NAME(LRT),
|
|
MAKE_NAME(LRI), // alias
|
|
{ 0, 0 }
|
|
};
|
|
|
|
static const TokenDict s_snm_group[] = {
|
|
// this list must be kept in synch with the header
|
|
MAKE_NAME(CHM),
|
|
MAKE_NAME(ECM),
|
|
MAKE_NAME(FCM),
|
|
MAKE_NAME(TFM),
|
|
MAKE_NAME(RSM),
|
|
MAKE_NAME(MIM),
|
|
MAKE_NAME(TRM),
|
|
MAKE_NAME(DLM),
|
|
MAKE_NAME(UFC),
|
|
{ 0, 0 }
|
|
};
|
|
#undef MAKE_NAME
|
|
|
|
#define TIMER5M 300000
|
|
|
|
namespace { //anonymous
|
|
|
|
class SnmPending : public SignallingMessageTimer, public SS7Label
|
|
{
|
|
public:
|
|
inline SnmPending(SS7MSU* msg, const SS7Label& label, int txSls, u_int64_t interval, u_int64_t global = 0)
|
|
: SignallingMessageTimer(interval,global), SS7Label(label),
|
|
m_msu(msg), m_txSls(txSls)
|
|
{ }
|
|
|
|
inline ~SnmPending()
|
|
{ TelEngine::destruct(m_msu); }
|
|
|
|
inline SS7MSU& msu() const
|
|
{ return *m_msu; }
|
|
|
|
inline int txSls() const
|
|
{ return m_txSls; }
|
|
|
|
inline SS7MsgSNM::Type snmType() const
|
|
{ return (SS7MsgSNM::Type)m_msu->at(length()+1,0); }
|
|
|
|
inline const char* snmName() const
|
|
{ return SS7MsgSNM::lookup(snmType(),"Unknown"); }
|
|
|
|
inline bool matches(const SS7Label& lbl) const
|
|
{ return opc() == lbl.dpc() && dpc() == lbl.opc() && sls() == lbl.sls(); }
|
|
|
|
private:
|
|
SS7MSU* m_msu;
|
|
int m_txSls;
|
|
};
|
|
|
|
}; // anonymous namespace
|
|
|
|
// Constructor
|
|
SS7MsgSNM::SS7MsgSNM(unsigned char type)
|
|
: SignallingMessage(lookup((Type)type,"Unknown")),
|
|
m_type(type)
|
|
{
|
|
}
|
|
|
|
void SS7MsgSNM::toString(String& dest, const SS7Label& label, bool params) const
|
|
{
|
|
const char* enclose = "\r\n-----";
|
|
dest = enclose;
|
|
dest << "\r\n" << name() << " [label=" << label << ']';
|
|
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;
|
|
}
|
|
|
|
// Parse a received buffer and build a message from it
|
|
SS7MsgSNM* SS7MsgSNM::parse(SS7Management* receiver, unsigned char type,
|
|
SS7PointCode::Type pcType, const unsigned char* buf, unsigned int len)
|
|
{
|
|
const char* pct = SS7PointCode::lookup(pcType);
|
|
if (!pct)
|
|
return 0;
|
|
SS7MsgSNM* msg = new SS7MsgSNM(type);
|
|
msg->params().addParam("pointcodetype",pct);
|
|
#ifdef XDEBUG
|
|
String tmp;
|
|
tmp.hexify((void*)buf,len,' ');
|
|
Debug(receiver,DebugAll,"Decoding msg=%s pctype=%s buf: %s [%p]",
|
|
msg->name(),pct,tmp.c_str(),receiver);
|
|
#endif
|
|
// TODO: parse the rest of the message. Check extra bytes (message specific)
|
|
if (!(buf && len))
|
|
return msg;
|
|
do {
|
|
// TFP,TFR,TFA: Q.704 15.7, RST,RSR: Q.704 15.10
|
|
// There must be at least 2 bytes in buffer
|
|
if (type == TFP || type == TFR || type == TFA || type == TFC ||
|
|
type == RST || type == RSR) {
|
|
// 2 bytes destination
|
|
SS7PointCode pc;
|
|
unsigned char spare;
|
|
if (pc.assign(pcType,buf,len,&spare)) {
|
|
String tmp;
|
|
tmp << pc;
|
|
msg->params().addParam("destination",tmp);
|
|
if (spare) {
|
|
tmp.hexify(&spare,1);
|
|
msg->params().addParam("spare",tmp);
|
|
}
|
|
}
|
|
else
|
|
Debug(receiver,DebugNote,
|
|
"Failed to decode destination for msg=%s len=%u [%p]",
|
|
msg->name(),len,receiver);
|
|
break;
|
|
}
|
|
// COO,COA,XCO,XCA: changeover sequence, slc
|
|
else if (type == COO || type == COA || type == XCO || type == XCA) {
|
|
int seq = -1;
|
|
int slc = -1;
|
|
switch (pcType) {
|
|
case SS7PointCode::ITU:
|
|
if (len >= 1)
|
|
seq = buf[0];
|
|
if ((type == XCO || type == XCA) && (len >= 3))
|
|
seq |= (((unsigned int)buf[1]) << 8) | (((unsigned int)buf[2]) << 16);
|
|
break;
|
|
case SS7PointCode::ANSI:
|
|
if (len >= 2) {
|
|
slc = buf[0] & 0x0f;
|
|
seq = (buf[0] >> 4) | (((unsigned int)buf[1]) << 4);
|
|
if ((type == XCO || type == XCA) && (len >= 4))
|
|
seq |= (((unsigned int)buf[2]) << 12) | (((unsigned int)buf[3]) << 20);
|
|
}
|
|
break;
|
|
default:
|
|
Debug(DebugStub,"Please implement COO decoding for type %u",pcType);
|
|
}
|
|
if (seq >= 0)
|
|
msg->params().addParam("sequence",String(seq));
|
|
if (slc >= 0)
|
|
msg->params().addParam("slc",String(slc));
|
|
}
|
|
// CBD,CBA: changeback code, slc
|
|
else if (type == CBD || type == CBA) {
|
|
int code = -1;
|
|
int slc = -1;
|
|
switch (pcType) {
|
|
case SS7PointCode::ITU:
|
|
if (len >= 1)
|
|
code = buf[0];
|
|
break;
|
|
case SS7PointCode::ANSI:
|
|
if (len >= 2) {
|
|
slc = buf[0] & 0x0f;
|
|
code = (buf[0] >> 4) | (((unsigned int)buf[1]) << 4);
|
|
}
|
|
break;
|
|
default:
|
|
Debug(DebugStub,"Please implement CBD decoding for type %u",pcType);
|
|
}
|
|
if (code >= 0)
|
|
msg->params().addParam("code",String(code));
|
|
if (slc >= 0)
|
|
msg->params().addParam("slc",String(slc));
|
|
}
|
|
// UPU: user part ID, unavailability cause
|
|
else if (type == UPU) {
|
|
SS7PointCode pc;
|
|
unsigned char spare;
|
|
if (pc.assign(pcType,buf,len,&spare)) {
|
|
String tmp;
|
|
tmp << pc;
|
|
msg->params().addParam("destination",tmp);
|
|
if (spare) {
|
|
tmp.hexify(&spare,1);
|
|
msg->params().addParam("spare",tmp);
|
|
}
|
|
unsigned int dlen = SS7PointCode::length(pcType);
|
|
if (dlen < len) {
|
|
msg->params().addParam("part",String((unsigned int)buf[dlen] & 0x0f));
|
|
msg->params().addParam("cause",String((unsigned int)buf[dlen] >> 4));
|
|
}
|
|
}
|
|
else
|
|
Debug(receiver,DebugNote,
|
|
"Failed to decode destination for msg=%s len=%u [%p]",
|
|
msg->name(),len,receiver);
|
|
}
|
|
} while (false);
|
|
return msg;
|
|
}
|
|
|
|
const TokenDict* SS7MsgSNM::names()
|
|
{
|
|
return s_snm_names;
|
|
}
|
|
|
|
|
|
#define MAKE_NAME(x) { #x, SS7MsgMTN::x }
|
|
static const TokenDict s_mtn_names[] = {
|
|
// this list must be kept in synch with the header
|
|
MAKE_NAME(SLTM),
|
|
MAKE_NAME(SLTA),
|
|
{ 0, 0 }
|
|
};
|
|
#undef MAKE_NAME
|
|
|
|
const TokenDict* SS7MsgMTN::names()
|
|
{
|
|
return s_mtn_names;
|
|
}
|
|
|
|
|
|
// Control operations
|
|
static const TokenDict s_dict_control[] = {
|
|
{ "prohibit", SS7MsgSNM::TFP },
|
|
{ "restrict", SS7MsgSNM::TFR },
|
|
{ "congest", SS7MsgSNM::TFC },
|
|
{ "allow", SS7MsgSNM::TFA },
|
|
{ "restart", SS7MsgSNM::TRA },
|
|
{ "changeover", SS7MsgSNM::COO },
|
|
{ "changeback", SS7MsgSNM::CBD },
|
|
{ "link-inhibit", SS7MsgSNM::LIN },
|
|
{ "link-uninhibit", SS7MsgSNM::LUN },
|
|
{ "link-force-uninhibit", SS7MsgSNM::LFU },
|
|
{ "test-congestion", SS7MsgSNM::RCT },
|
|
{ "test-prohibited", SS7MsgSNM::RST },
|
|
{ "test-restricted", SS7MsgSNM::RSR },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
SS7Management::SS7Management(const NamedList& params, unsigned char sio)
|
|
: SignallingComponent(params.safe("SS7Management"),¶ms,"ss7-snm"),
|
|
SS7Layer4(sio,¶ms),
|
|
m_changeMsgs(true), m_changeSets(false), m_neighbours(true)
|
|
{
|
|
m_changeMsgs = params.getBoolValue(YSTRING("changemsgs"),m_changeMsgs);
|
|
m_changeSets = params.getBoolValue(YSTRING("changesets"),m_changeSets);
|
|
m_neighbours = params.getBoolValue(YSTRING("neighbours"),m_neighbours);
|
|
}
|
|
|
|
|
|
HandledMSU SS7Management::receivedMSU(const SS7MSU& msu, const SS7Label& label, SS7Layer3* network, int sls)
|
|
{
|
|
if (msu.getSIF() != sif())
|
|
return HandledMSU::Rejected;
|
|
if (network) {
|
|
unsigned int local = network->getLocal(label.type());
|
|
if (local && label.dpc().pack(label.type()) != local)
|
|
return HandledMSU::Rejected;
|
|
}
|
|
SS7Router* router = YOBJECT(SS7Router,SS7Layer4::network());
|
|
if (router && (router != network)) {
|
|
unsigned int local = router->getLocal(label.type());
|
|
if (local && label.dpc().pack(label.type()) != local)
|
|
return HandledMSU::Rejected;
|
|
}
|
|
|
|
unsigned int len = msu.length() - label.length() - 1;
|
|
// according to Q.704 there should be at least the heading codes (8 bit)
|
|
const unsigned char* buf = msu.getData(label.length()+1,1);
|
|
if (!buf)
|
|
return false;
|
|
RefPointer<SS7MsgSNM> msg = SS7MsgSNM::parse(this,buf[0],label.type(),buf+1,len-1);
|
|
if (!msg)
|
|
return false;
|
|
msg->deref();
|
|
|
|
if (debugAt(DebugInfo)) {
|
|
String tmp;
|
|
msg->toString(tmp,label,debugAt(DebugAll));
|
|
const char* name = network ? network->toString().c_str() : 0;
|
|
Debug(this,DebugInfo,"Received %u bytes message (%p) on %s:%d%s",
|
|
len,static_cast<void*>(msg),name,sls,tmp.c_str());
|
|
}
|
|
|
|
String addr;
|
|
addr << label;
|
|
if (m_neighbours && (msg->type() != SS7MsgSNM::UPU)) {
|
|
int prio = -1;
|
|
if (router)
|
|
prio = (int)router->getRoutePriority(label.type(),label.opc());
|
|
else if (network)
|
|
prio = (int)network->getRoutePriority(label.type(),label.opc());
|
|
if (prio) {
|
|
Debug(this,DebugMild,"Refusing %s message from %s node %s",
|
|
msg->name(),(prio > 0 ? "non-neighboor" : "unknown"),addr.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
SS7Label lbl(label,label.sls(),0);
|
|
{
|
|
String tmp;
|
|
tmp << SS7PointCode::lookup(label.type()) << "," << addr;
|
|
// put the addresses with commas as we need them
|
|
char* fix = const_cast<char*>(tmp.c_str());
|
|
for (; *fix; fix++)
|
|
if (':' == *fix)
|
|
*fix = ',';
|
|
msg->params().addParam("address",tmp);
|
|
tmp.clear();
|
|
tmp << SS7PointCode::lookup(label.type()) << "," << lbl;
|
|
fix = const_cast<char*>(tmp.c_str());
|
|
for (; *fix; fix++)
|
|
if (':' == *fix)
|
|
*fix = ',';
|
|
msg->params().addParam("back-address",tmp);
|
|
}
|
|
switch (msg->group()) {
|
|
case SS7MsgSNM::CHM:
|
|
case SS7MsgSNM::ECM:
|
|
case SS7MsgSNM::MIM:
|
|
{
|
|
// for ANSI the SLC is not stored in SLS but in a separate field
|
|
int slc = msg->params().getIntValue(YSTRING("slc"),-1);
|
|
if (slc >= 0 && slc <= 255)
|
|
lbl.setSls((unsigned char)slc);
|
|
}
|
|
// check if the addressed link exists
|
|
if (router && !router->inhibit(lbl,0,0)) {
|
|
Debug(this,DebugMild,"Received %s for inexistent %s on SLS %d [%p]",
|
|
msg->name(),addr.c_str(),sls,this);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (msg->type() == SS7MsgSNM::TFP ||
|
|
msg->type() == SS7MsgSNM::TFR ||
|
|
msg->type() == SS7MsgSNM::TFA ||
|
|
msg->type() == SS7MsgSNM::TFC ||
|
|
msg->type() == SS7MsgSNM::RST ||
|
|
msg->type() == SS7MsgSNM::RSR) {
|
|
String dest = msg->params().getValue(YSTRING("destination"));
|
|
if (!dest.null()) {
|
|
const char* oper = lookup(msg->type(),s_dict_control);
|
|
Debug(this,DebugInfo,"%s (label=%s): Traffic %s to dest=%s [%p]",
|
|
msg->name(),addr.c_str(),oper,dest.c_str(),this);
|
|
if (router && oper) {
|
|
NamedList* ctrl = router->controlCreate(oper);
|
|
if (ctrl) {
|
|
ctrl->copyParams(msg->params());
|
|
ctrl->setParam("automatic",String::boolText(true));
|
|
router->controlExecute(ctrl);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
Debug(this,DebugNote,"Received %s (label=%s) without destination [%p]",
|
|
msg->name(),addr.c_str(),this);
|
|
}
|
|
else if (msg->type() == SS7MsgSNM::TRA) {
|
|
String dest;
|
|
dest << label.opc();
|
|
Debug(this,DebugInfo,"%s (label=%s): Traffic can restart to dest=%s [%p]",
|
|
msg->name(),addr.c_str(),dest.c_str(),this);
|
|
if (router) {
|
|
NamedList* ctrl = router->controlCreate("allowed");
|
|
if (ctrl) {
|
|
ctrl->copyParams(msg->params());
|
|
ctrl->setParam("destination",dest);
|
|
ctrl->setParam("automatic",String::boolText(true));
|
|
router->controlExecute(ctrl);
|
|
}
|
|
}
|
|
}
|
|
else if (msg->type() == SS7MsgSNM::COO ||
|
|
msg->type() == SS7MsgSNM::XCO ||
|
|
msg->type() == SS7MsgSNM::ECO) {
|
|
if (!len--)
|
|
return false;
|
|
const unsigned char* s = msu.getData(label.length()+2,len);
|
|
if (!s)
|
|
return false;
|
|
Debug(this,DebugAll,"%s (code len=%u) [%p]",msg->name(),len,this);
|
|
if (!m_changeMsgs)
|
|
return true;
|
|
if (inhibit(lbl,SS7Layer2::Inactive)) {
|
|
String link;
|
|
link << msg->params().getValue(YSTRING("pointcodetype")) << "," << lbl;
|
|
Debug(this,DebugNote,"Changeover order on %s",link.c_str());
|
|
int seq = msg->params().getIntValue(YSTRING("sequence"),-1);
|
|
if (seq >= 0)
|
|
recover(lbl,seq);
|
|
seq = router ? router->getSequence(lbl) : -1;
|
|
if (seq >= 0) {
|
|
int len = 2;
|
|
unsigned char data[5];
|
|
data[0] = SS7MsgSNM::COA;
|
|
if (seq & 0xff000000) {
|
|
seq &= 0x00ffffff;
|
|
if (msg->type() != SS7MsgSNM::COO || (seq & 0x00ffff80)) {
|
|
data[0] = SS7MsgSNM::XCA;
|
|
len += 2;
|
|
}
|
|
}
|
|
switch (label.type()) {
|
|
case SS7PointCode::ITU:
|
|
data[1] = (unsigned char)seq;
|
|
if (len >= 4) {
|
|
data[2] = (unsigned char)(seq >> 8);
|
|
data[3] = (unsigned char)(seq >> 16);
|
|
}
|
|
break;
|
|
case SS7PointCode::ANSI:
|
|
data[1] = (unsigned char)((msg->params().getIntValue(YSTRING("slc"),sls) & 0x0f) | (seq << 4));
|
|
data[2] = (unsigned char)(seq >> 4);
|
|
len++;
|
|
if (len >= 5) {
|
|
data[3] = (unsigned char)(seq >> 12);
|
|
data[4] = (unsigned char)(seq >> 20);
|
|
}
|
|
break;
|
|
default:
|
|
Debug(DebugStub,"Please implement COO for type %u",label.type());
|
|
return false;
|
|
}
|
|
return transmitMSU(SS7MSU(msu.getSIO(),lbl,&data,len),lbl,sls) >= 0;
|
|
}
|
|
else {
|
|
// postpone an ECA in case we are unable to send a COA/XCA
|
|
static unsigned char data = SS7MsgSNM::ECA;
|
|
return postpone(new SS7MSU(msu.getSIO(),lbl,&data,1),lbl,sls,0,200);
|
|
}
|
|
}
|
|
else
|
|
Debug(this,DebugMild,"Unexpected %s %s [%p]",msg->name(),addr.c_str(),this);
|
|
}
|
|
else if (msg->type() == SS7MsgSNM::COA ||
|
|
msg->type() == SS7MsgSNM::XCA ||
|
|
msg->type() == SS7MsgSNM::ECA) {
|
|
if (!len--)
|
|
return false;
|
|
Debug(this,DebugAll,"%s (code len=%u) [%p]",msg->name(),len,this);
|
|
if (!m_changeMsgs)
|
|
return true;
|
|
lock();
|
|
SnmPending* pend = 0;
|
|
for (ObjList* l = m_pending.skipNull(); l; l = l->skipNext()) {
|
|
SnmPending* p = static_cast<SnmPending*>(l->get());
|
|
const unsigned char* ptr = p->msu().getData(p->length()+1,1);
|
|
if (!(ptr && p->matches(label)))
|
|
continue;
|
|
switch (ptr[0]) {
|
|
case SS7MsgSNM::COO:
|
|
case SS7MsgSNM::XCO:
|
|
case SS7MsgSNM::ECO:
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
pend = static_cast<SnmPending*>(m_pending.remove(p,false));
|
|
break;
|
|
}
|
|
unlock();
|
|
if (pend) {
|
|
String link;
|
|
link << msg->params().getValue(YSTRING("pointcodetype")) << "," << *pend;
|
|
Debug(this,DebugNote,"Changeover acknowledged on %s",link.c_str());
|
|
inhibit(*pend,SS7Layer2::Inactive);
|
|
int seq = msg->params().getIntValue(YSTRING("sequence"),-1);
|
|
if (seq >= 0)
|
|
recover(*pend,seq);
|
|
}
|
|
else
|
|
Debug(this,DebugMild,"Unexpected %s %s [%p]",msg->name(),addr.c_str(),this);
|
|
TelEngine::destruct(pend);
|
|
}
|
|
else if (msg->type() == SS7MsgSNM::CBD) {
|
|
if (!len--)
|
|
return false;
|
|
const unsigned char* s = msu.getData(label.length()+2,len);
|
|
if (!s)
|
|
return false;
|
|
Debug(this,DebugAll,"%s (code len=%u) [%p]",msg->name(),len,this);
|
|
if (!m_changeMsgs)
|
|
return true;
|
|
if (inhibit(lbl,0,SS7Layer2::Inactive)) {
|
|
String link;
|
|
link << msg->params().getValue(YSTRING("pointcodetype")) << "," << lbl;
|
|
Debug(this,DebugNote,"Changeback declaration on %s",link.c_str());
|
|
SS7MSU answer(msu.getSIO(),lbl,0,len+1);
|
|
unsigned char* d = answer.getData(lbl.length()+1,len+1);
|
|
if (!d)
|
|
return false;
|
|
*d++ = SS7MsgSNM::CBA;
|
|
while (len--)
|
|
*d++ = *s++;
|
|
return transmitMSU(answer,lbl,sls) >= 0;
|
|
}
|
|
else
|
|
Debug(this,DebugMild,"Unexpected %s %s [%p]",msg->name(),addr.c_str(),this);
|
|
}
|
|
else if (msg->type() == SS7MsgSNM::CBA) {
|
|
if (!len--)
|
|
return false;
|
|
Debug(this,DebugAll,"%s (code len=%u) [%p]",msg->name(),len,this);
|
|
if (!m_changeMsgs)
|
|
return true;
|
|
lock();
|
|
SnmPending* pend = 0;
|
|
for (ObjList* l = m_pending.skipNull(); l; l = l->skipNext()) {
|
|
SnmPending* p = static_cast<SnmPending*>(l->get());
|
|
if (p->msu().length() != msu.length())
|
|
continue;
|
|
const unsigned char* ptr = p->msu().getData(p->length()+1,len+1);
|
|
if (!ptr || (ptr[0] != SS7MsgSNM::CBD))
|
|
continue;
|
|
if (::memcmp(ptr+1,buf+1,len) || !p->matches(label))
|
|
continue;
|
|
pend = static_cast<SnmPending*>(m_pending.remove(p,false));
|
|
break;
|
|
}
|
|
unlock();
|
|
if (pend) {
|
|
String link;
|
|
link << msg->params().getValue(YSTRING("pointcodetype")) << "," << *pend;
|
|
Debug(this,DebugNote,"Changeback acknowledged on %s",link.c_str());
|
|
inhibit(*pend,0,SS7Layer2::Inactive);
|
|
}
|
|
else
|
|
Debug(this,DebugMild,"Unexpected %s %s [%p]",msg->name(),addr.c_str(),this);
|
|
TelEngine::destruct(pend);
|
|
}
|
|
else if (msg->type() == SS7MsgSNM::LIN) {
|
|
Debug(this,DebugAll,"%s (code len=%u) [%p]",msg->name(),len,this);
|
|
if (router) {
|
|
bool ok = router->inhibit(lbl,SS7Layer2::Remote,0,true);
|
|
unsigned char data = ok ? SS7MsgSNM::LIA : SS7MsgSNM::LID;
|
|
if (ok) {
|
|
static unsigned char lrt = SS7MsgSNM::LRT;
|
|
postpone(new SS7MSU(msu.getSIO(),lbl,&lrt,1),lbl,sls,0,TIMER5M);
|
|
}
|
|
return transmitMSU(SS7MSU(msu.getSIO(),lbl,&data,1),lbl,sls) >= 0;
|
|
}
|
|
}
|
|
else if (msg->type() == SS7MsgSNM::LIA || msg->type() == SS7MsgSNM::LUA) {
|
|
Debug(this,DebugAll,"%s (code len=%u) [%p]",msg->name(),len,this);
|
|
unsigned char test = (msg->type() == SS7MsgSNM::LIA) ? SS7MsgSNM::LIN : SS7MsgSNM::LUN;
|
|
lock();
|
|
SnmPending* pend = 0;
|
|
for (ObjList* l = m_pending.skipNull(); l; l = l->skipNext()) {
|
|
SnmPending* p = static_cast<SnmPending*>(l->get());
|
|
const unsigned char* ptr = p->msu().getData(p->length()+1,1);
|
|
if (!(ptr && p->matches(label)))
|
|
continue;
|
|
if (ptr[0] != test)
|
|
continue;
|
|
pend = static_cast<SnmPending*>(m_pending.remove(p,false));
|
|
break;
|
|
}
|
|
unlock();
|
|
if (pend) {
|
|
if (test == SS7MsgSNM::LIN) {
|
|
inhibit(*pend,SS7Layer2::Local);
|
|
static unsigned char llt = SS7MsgSNM::LLT;
|
|
postpone(new SS7MSU(msu.getSIO(),*pend,&llt,1),*pend,sls,0,TIMER5M);
|
|
}
|
|
else {
|
|
inhibit(*pend,0,SS7Layer2::Local);
|
|
lock();
|
|
for (ObjList* l = m_pending.skipNull(); l; l = l->skipNext()) {
|
|
SnmPending* p = static_cast<SnmPending*>(l->get());
|
|
const unsigned char* ptr = p->msu().getData(p->length()+1,1);
|
|
if (!ptr || (ptr[0] != SS7MsgSNM::LLT))
|
|
continue;
|
|
if (!p->matches(label))
|
|
continue;
|
|
m_pending.remove(p);
|
|
break;
|
|
}
|
|
unlock();
|
|
}
|
|
}
|
|
else
|
|
Debug(this,DebugMild,"Unexpected %s %s [%p]",msg->name(),addr.c_str(),this);
|
|
}
|
|
else if (msg->type() == SS7MsgSNM::LUN) {
|
|
Debug(this,DebugAll,"%s (code len=%u) [%p]",msg->name(),len,this);
|
|
if (router && router->inhibit(lbl,0,SS7Layer2::Remote)) {
|
|
lock();
|
|
for (ObjList* l = m_pending.skipNull(); l; ) {
|
|
SnmPending* p = static_cast<SnmPending*>(l->get());
|
|
const unsigned char* ptr = p->msu().getData(p->length()+1,1);
|
|
if (ptr && ((ptr[0] == SS7MsgSNM::LRT) || (ptr[0] == SS7MsgSNM::LFU)) && p->matches(label)) {
|
|
m_pending.remove(p);
|
|
l = m_pending.skipNull();
|
|
}
|
|
else
|
|
l = l->skipNext();
|
|
}
|
|
unlock();
|
|
static unsigned char lua = SS7MsgSNM::LUA;
|
|
return transmitMSU(SS7MSU(msu.getSIO(),lbl,&lua,1),lbl,sls) >= 0;
|
|
}
|
|
}
|
|
else if (msg->type() == SS7MsgSNM::LID) {
|
|
Debug(this,DebugAll,"%s (code len=%u) [%p]",msg->name(),len,this);
|
|
bool found = false;
|
|
lock();
|
|
for (ObjList* l = m_pending.skipNull(); l; l = l->skipNext()) {
|
|
SnmPending* p = static_cast<SnmPending*>(l->get());
|
|
const unsigned char* ptr = p->msu().getData(p->length()+1,1);
|
|
if (!ptr || (ptr[0] != SS7MsgSNM::LIN))
|
|
continue;
|
|
if (!p->matches(label))
|
|
continue;
|
|
m_pending.remove(p);
|
|
found = true;
|
|
break;
|
|
}
|
|
unlock();
|
|
if (found)
|
|
Debug(this,DebugWarn,"Remote refused to inhibit link %d",label.sls());
|
|
else
|
|
Debug(this,DebugMild,"Unexpected %s %s [%p]",msg->name(),addr.c_str(),this);
|
|
}
|
|
else if (msg->type() == SS7MsgSNM::LFU) {
|
|
Debug(this,DebugAll,"%s (code len=%u) [%p]",msg->name(),len,this);
|
|
static unsigned char data = SS7MsgSNM::LUN;
|
|
int global = 0;
|
|
// if link is locally inhibited execute the complete procedure
|
|
if (router && router->inhibited(lbl,SS7Layer2::Local))
|
|
global = 2400;
|
|
return postpone(new SS7MSU(msu.getSIO(),lbl,&data,1),lbl,sls,1200,global);
|
|
}
|
|
else if (msg->type() == SS7MsgSNM::LRT) {
|
|
Debug(this,DebugAll,"%s (code len=%u) [%p]",msg->name(),len,this);
|
|
if (router && router->inhibited(lbl,SS7Layer2::Local))
|
|
return true;
|
|
static unsigned char data = SS7MsgSNM::LUN;
|
|
return postpone(new SS7MSU(msu.getSIO(),lbl,&data,1),lbl,sls,1200,2400);
|
|
}
|
|
else if (msg->type() == SS7MsgSNM::LLT) {
|
|
Debug(this,DebugAll,"%s (code len=%u) [%p]",msg->name(),len,this);
|
|
if (router && router->inhibited(lbl,SS7Layer2::Remote))
|
|
return true;
|
|
static unsigned char data = SS7MsgSNM::LFU;
|
|
return postpone(new SS7MSU(msu.getSIO(),lbl,&data,1),lbl,sls,1200,2400);
|
|
}
|
|
else if (msg->type() == SS7MsgSNM::UPU) {
|
|
Debug(this,DebugNote,"Unavailable part %s at %s, cause %s",
|
|
msg->params().getValue(YSTRING("part"),"?"),
|
|
msg->params().getValue(YSTRING("destination"),"?"),
|
|
msg->params().getValue(YSTRING("cause"),"?"));
|
|
if (router) {
|
|
unsigned char part = msg->params().getIntValue(YSTRING("part"),-1);
|
|
unsigned char cause = msg->params().getIntValue(YSTRING("cause"),-1);
|
|
SS7PointCode pc;
|
|
if (part > SS7MSU::MTNS && part <= 0x0f && cause <= 0x0f &&
|
|
pc.assign(msg->params().getValue(YSTRING("destination")),label.type()))
|
|
router->receivedUPU(label.type(),pc,(SS7MSU::Services)part,
|
|
cause,label,sls);
|
|
}
|
|
}
|
|
else {
|
|
String tmp;
|
|
tmp.hexify((void*)buf,len,' ');
|
|
String params;
|
|
unsigned int n = msg->params().count();
|
|
if (n)
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedString* ns = static_cast<NamedString*>(msg->params().getParam(i));
|
|
if (ns)
|
|
params.append(String(ns->name()) + "=" + *ns,",");
|
|
}
|
|
Debug(this,DebugMild,
|
|
"Unhandled SNM type=%s group=%s label=%s params:%s len=%u: %s ",
|
|
msg->name(),lookup(msg->group(),s_snm_group,"Spare"),
|
|
addr.c_str(),params.c_str(),len,tmp.c_str());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SS7Management::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 = -1;
|
|
if (!TelEngine::null(oper)) {
|
|
cmd = oper->toInteger(s_dict_control,cmd);
|
|
if (cmd < 0)
|
|
cmd = oper->toInteger(s_snm_names,cmd);
|
|
}
|
|
|
|
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 (!(cmp && toString() == cmp))
|
|
return false;
|
|
|
|
m_changeMsgs = params.getBoolValue(YSTRING("changemsgs"),m_changeMsgs);
|
|
m_changeSets = params.getBoolValue(YSTRING("changesets"),m_changeSets);
|
|
m_neighbours = params.getBoolValue(YSTRING("neighbours"),m_neighbours);
|
|
const String* addr = params.getParam(YSTRING("address"));
|
|
if (cmd < 0 || TelEngine::null(addr))
|
|
return SignallingComponent::control(params);
|
|
// TYPE,opc,dpc,sls,spare
|
|
SS7PointCode::Type t = SS7PointCode::Other;
|
|
ObjList* l = addr->split(',');
|
|
if (l->at(0) && (t = SS7PointCode::lookup(l->at(0)->toString())) != SS7PointCode::Other) {
|
|
unsigned char netInd = ni();
|
|
if (network())
|
|
netInd = network()->getNI(t,netInd);
|
|
unsigned char txSio = getSIO(params,ssf(),prio(),netInd);
|
|
SS7PointCode opc,dpc;
|
|
int sls = -1;
|
|
int spare = 0;
|
|
if (l->at(1) && opc.assign(l->at(1)->toString(),t) &&
|
|
l->at(2) && dpc.assign(l->at(2)->toString(),t)) {
|
|
if (l->at(3))
|
|
sls = l->at(3)->toString().toInteger(sls);
|
|
if (l->at(4))
|
|
spare = l->at(4)->toString().toInteger(spare);
|
|
TelEngine::destruct(l);
|
|
SS7Label lbl(t,dpc,opc,sls,spare);
|
|
int txSls = sls;
|
|
switch (cmd) {
|
|
case SS7MsgSNM::COO:
|
|
case SS7MsgSNM::COA:
|
|
case SS7MsgSNM::XCO:
|
|
case SS7MsgSNM::XCA:
|
|
case SS7MsgSNM::CBD:
|
|
case SS7MsgSNM::CBA:
|
|
case SS7MsgSNM::LIN:
|
|
case SS7MsgSNM::LIA:
|
|
case SS7MsgSNM::LID:
|
|
case SS7MsgSNM::LUN:
|
|
case SS7MsgSNM::LUA:
|
|
case SS7MsgSNM::LFU:
|
|
txSls = (txSls + 1) & 0xff;
|
|
}
|
|
txSls = params.getIntValue(YSTRING("linksel"),txSls) & 0xff;
|
|
String tmp;
|
|
tmp << SS7PointCode::lookup(lbl.type()) << "," << lbl;
|
|
Debug(this,DebugAll,"Sending %s to %s on %d [%p]",
|
|
SS7MsgSNM::lookup((SS7MsgSNM::Type)cmd),tmp.c_str(),txSls,this);
|
|
switch (cmd) {
|
|
// Messages containing a destination point code
|
|
case SS7MsgSNM::TFP:
|
|
case SS7MsgSNM::TFA:
|
|
case SS7MsgSNM::TFR:
|
|
case SS7MsgSNM::TFC:
|
|
case SS7MsgSNM::RST:
|
|
case SS7MsgSNM::RSR:
|
|
{
|
|
addr = params.getParam(YSTRING("destination"));
|
|
SS7PointCode dest(opc);
|
|
if (TelEngine::null(addr) || dest.assign(*addr,t)) {
|
|
unsigned char data[5];
|
|
int len = SS7PointCode::length(t)+1;
|
|
data[0] = cmd;
|
|
return TelEngine::controlReturn(¶ms,dest.store(t,data+1,spare) &&
|
|
((cmd == SS7MsgSNM::TFP) ?
|
|
postpone(new SS7MSU(txSio,lbl,data,len),lbl,txSls,1000) :
|
|
(transmitMSU(SS7MSU(txSio,lbl,data,len),lbl,txSls) >= 0)));
|
|
}
|
|
}
|
|
return TelEngine::controlReturn(¶ms,false);
|
|
// Messages sent with just the code
|
|
case SS7MsgSNM::ECO:
|
|
case SS7MsgSNM::TRA:
|
|
case SS7MsgSNM::LIA:
|
|
case SS7MsgSNM::LUA:
|
|
case SS7MsgSNM::LID:
|
|
case SS7MsgSNM::LLT:
|
|
case SS7MsgSNM::LRT:
|
|
case SS7MsgSNM::RCT:
|
|
case SS7MsgSNM::CSS:
|
|
case SS7MsgSNM::CNS:
|
|
case SS7MsgSNM::CNP:
|
|
{
|
|
unsigned char data = cmd;
|
|
return TelEngine::controlReturn(¶ms,transmitMSU(SS7MSU(txSio,lbl,&data,1),lbl,txSls) >= 0);
|
|
}
|
|
// Messages postponed with just the code
|
|
case SS7MsgSNM::LIN:
|
|
{
|
|
unsigned char data = cmd;
|
|
return TelEngine::controlReturn(¶ms,postpone(new SS7MSU(txSio,lbl,&data,1),lbl,txSls,2500,5000));
|
|
}
|
|
case SS7MsgSNM::LUN:
|
|
case SS7MsgSNM::LFU:
|
|
{
|
|
unsigned char data = cmd;
|
|
return TelEngine::controlReturn(¶ms,postpone(new SS7MSU(txSio,lbl,&data,1),lbl,txSls,1200,2400));
|
|
}
|
|
// Changeover messages
|
|
case SS7MsgSNM::COO:
|
|
case SS7MsgSNM::COA:
|
|
case SS7MsgSNM::XCO:
|
|
case SS7MsgSNM::XCA:
|
|
if (params.getBoolValue(YSTRING("emergency"),false)) {
|
|
unsigned char data = (SS7MsgSNM::COO == cmd) ? SS7MsgSNM::ECO : SS7MsgSNM::ECA;
|
|
return TelEngine::controlReturn(¶ms,transmitMSU(SS7MSU(txSio,lbl,&data,1),lbl,txSls) >= 0);
|
|
}
|
|
else {
|
|
int seq = params.getIntValue(YSTRING("sequence"),0) & 0x00ffffff;
|
|
if (SS7MsgSNM::COO == cmd || SS7MsgSNM::COA == cmd)
|
|
seq &= 0x7f;
|
|
int len = 2;
|
|
unsigned char data[5];
|
|
data[0] = cmd;
|
|
switch (t) {
|
|
case SS7PointCode::ITU:
|
|
data[1] = (unsigned char)seq;
|
|
if (SS7MsgSNM::XCO == cmd || SS7MsgSNM::XCA == cmd) {
|
|
data[2] = (unsigned char)(seq >> 8);
|
|
data[3] = (unsigned char)(seq >> 16);
|
|
len += 2;
|
|
}
|
|
break;
|
|
case SS7PointCode::ANSI:
|
|
data[1] = (unsigned char)((params.getIntValue(YSTRING("slc"),sls) & 0x0f) | (seq << 4));
|
|
data[2] = (unsigned char)(seq >> 4);
|
|
len = 3;
|
|
if (SS7MsgSNM::XCO == cmd || SS7MsgSNM::XCA == cmd) {
|
|
data[3] = (unsigned char)(seq >> 12);
|
|
data[4] = (unsigned char)(seq >> 20);
|
|
len += 2;
|
|
}
|
|
break;
|
|
default:
|
|
Debug(DebugStub,"Please implement COO for type %u",t);
|
|
return TelEngine::controlReturn(¶ms,false);
|
|
}
|
|
return TelEngine::controlReturn(¶ms,(cmd == SS7MsgSNM::COA)
|
|
? transmitMSU(SS7MSU(txSio,lbl,&data,len),lbl,txSls) >= 0
|
|
: postpone(new SS7MSU(txSio,lbl,&data,len),lbl,txSls,1800,0,true));
|
|
}
|
|
// Changeback messages
|
|
case SS7MsgSNM::CBD:
|
|
case SS7MsgSNM::CBA:
|
|
{
|
|
int code = params.getIntValue(YSTRING("code"),0);
|
|
int len = 2;
|
|
unsigned char data[3];
|
|
data[0] = cmd;
|
|
switch (t) {
|
|
case SS7PointCode::ITU:
|
|
data[1] = (unsigned char)code;
|
|
break;
|
|
case SS7PointCode::ANSI:
|
|
data[1] = (unsigned char)((params.getIntValue(YSTRING("slc"),sls) & 0x0f) | (code << 4));
|
|
data[2] = (unsigned char)(code >> 4);
|
|
len = 3;
|
|
break;
|
|
default:
|
|
Debug(DebugStub,"Please implement CBD for type %u",t);
|
|
return TelEngine::controlReturn(¶ms,false);
|
|
}
|
|
return TelEngine::controlReturn(¶ms,(cmd == SS7MsgSNM::CBA)
|
|
? transmitMSU(SS7MSU(txSio,lbl,&data,len),lbl,txSls) >= 0
|
|
: postpone(new SS7MSU(txSio,lbl,&data,len),lbl,txSls,1000,2000,true));
|
|
}
|
|
default:
|
|
if (cmd >= 0)
|
|
Debug(this,DebugStub,"Unimplemented control %s (%d) [%p]",
|
|
lookup(cmd,s_snm_names,"???"),cmd,this);
|
|
}
|
|
}
|
|
}
|
|
TelEngine::destruct(l);
|
|
return TelEngine::controlReturn(¶ms,false);
|
|
}
|
|
|
|
void SS7Management::notify(SS7Layer3* network, int sls)
|
|
{
|
|
Debug(this,DebugAll,"SS7Management::notify(%p,%d) [%p]",network,sls,this);
|
|
if (network && (sls >= 0)) {
|
|
DDebug(this,DebugInfo,"Link %d inhibitions: 0x%02X [%p]",
|
|
sls,network->inhibited(sls),this);
|
|
bool linkUp = network->operational(sls);
|
|
if (linkUp && !network->inhibited(sls,SS7Layer2::Inactive))
|
|
return;
|
|
bool linkAvail[257];
|
|
bool force = true;
|
|
int txSls;
|
|
bool localLink = false;
|
|
for (txSls = 0; m_changeMsgs && (txSls < 256); txSls++)
|
|
localLink = (linkAvail[txSls] = (txSls != sls) && network->inService(txSls)) || localLink;
|
|
// if no link is available in linkset rely on another linkset
|
|
linkAvail[256] = m_changeSets && !localLink;
|
|
for (unsigned int i = 0; m_changeMsgs && (i < YSS7_PCTYPE_COUNT); i++) {
|
|
SS7PointCode::Type type = static_cast<SS7PointCode::Type>(i+1);
|
|
unsigned int local = network->getLocal(type);
|
|
if (!local && SS7Layer4::network())
|
|
local = SS7Layer4::network()->getLocal(type);
|
|
if (!local)
|
|
continue;
|
|
String addr;
|
|
addr << SS7PointCode::lookup(type) << "," << SS7PointCode(type,local);
|
|
Debug(this,DebugNote,"Link %s:%d is %s [%p]",addr.c_str(),sls,
|
|
(linkUp ? "up" : "down"),this);
|
|
const char* oper = linkUp ? "changeback" : "changeover";
|
|
ObjList* routes = getNetRoutes(network,type);
|
|
if (routes)
|
|
routes = routes->skipNull();
|
|
for (; routes; routes = routes->skipNext()) {
|
|
const SS7Route* r = static_cast<const SS7Route*>(routes->get());
|
|
if (r && !r->priority()) {
|
|
// found adjacent node, emit change orders to it
|
|
int seq = -1;
|
|
txSls = 0;
|
|
if (!linkUp && network->inhibited(sls,SS7Layer2::Inactive)) {
|
|
// already inactive, fix sequences if possible
|
|
seq = network->getSequence(sls);
|
|
DDebug(this,DebugAll,"Got sequence %d for link %s:%d [%p]",
|
|
seq,addr.c_str(),sls,this);
|
|
if (seq < 0)
|
|
return;
|
|
txSls = 256;
|
|
}
|
|
String tmp = addr;
|
|
tmp << "," << SS7PointCode(type,r->packed()) << "," << sls;
|
|
String slc(sls);
|
|
for (; txSls <= 256; txSls++) {
|
|
if (!linkAvail[txSls])
|
|
continue;
|
|
NamedList* ctl = controlCreate(oper);
|
|
if (!ctl)
|
|
continue;
|
|
Debug(this,DebugAll,"Sending Link %d %s %s on %d [%p]",
|
|
sls,oper,tmp.c_str(),txSls,this);
|
|
ctl->setParam("address",tmp);
|
|
ctl->setParam("slc",slc);
|
|
ctl->setParam("linksel",String(txSls & 0xff));
|
|
if (linkUp)
|
|
ctl->setParam("code",String((txSls + sls) & 0xff));
|
|
else {
|
|
if (seq < 0)
|
|
seq = network->getSequence(sls);
|
|
DDebug(this,DebugAll,"Got sequence number %d [%p]",seq,this);
|
|
if (seq >= 0)
|
|
ctl->setParam("sequence",String(seq));
|
|
else
|
|
ctl->setParam("emergency",String::boolText(true));
|
|
}
|
|
ctl->setParam("automatic",String::boolText(true));
|
|
controlExecute(ctl);
|
|
force = false;
|
|
}
|
|
while (seq >= 0) {
|
|
// scan pending list for matching ECA, turn them into COA/XCA
|
|
SS7Label label(type,local,r->packed(),sls);
|
|
lock();
|
|
SnmPending* pend = 0;
|
|
for (ObjList* l = m_pending.skipNull(); l; l = l->skipNext()) {
|
|
SnmPending* p = static_cast<SnmPending*>(l->get());
|
|
const unsigned char* ptr = p->msu().getData(p->length()+1,1);
|
|
if (!(ptr && p->matches(label)))
|
|
continue;
|
|
if (ptr[0] != SS7MsgSNM::ECA)
|
|
continue;
|
|
pend = static_cast<SnmPending*>(m_pending.remove(p,false));
|
|
break;
|
|
}
|
|
unlock();
|
|
if (pend) {
|
|
const char* cmd = "COA";
|
|
if (seq & 0xff000000) {
|
|
seq &= 0x00ffffff;
|
|
cmd = "XCA";
|
|
}
|
|
Debug(this,DebugInfo,"Turning pending ECA into %s with sequence %d [%p]",
|
|
cmd,seq,this);
|
|
NamedList* ctl = controlCreate(cmd);
|
|
if (ctl) {
|
|
ctl->setParam("address",tmp);
|
|
ctl->setParam("slc",slc);
|
|
ctl->setParam("linksel",String(pend->txSls()));
|
|
ctl->setParam("sequence",String(seq));
|
|
ctl->setParam("automatic",String::boolText(true));
|
|
controlExecute(ctl);
|
|
force = false;
|
|
}
|
|
TelEngine::destruct(pend);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (force) {
|
|
if (linkUp) {
|
|
Debug(this,DebugMild,"Could not changeback link %d, activating anyway [%p]",sls,this);
|
|
network->inhibit(sls,0,SS7Layer2::Inactive);
|
|
}
|
|
else {
|
|
Debug(this,DebugMild,"Could not changeover link %d, deactivating anyway [%p]",sls,this);
|
|
network->inhibit(sls,SS7Layer2::Inactive,0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SS7Management::postpone(SS7MSU* msu, const SS7Label& label, int txSls,
|
|
u_int64_t interval, u_int64_t global, bool force, const Time& when)
|
|
{
|
|
lock();
|
|
unsigned int len = msu->length();
|
|
for (ObjList* l = m_pending.skipNull(); l; l = l->skipNext()) {
|
|
SnmPending* p = static_cast<SnmPending*>(l->get());
|
|
if (p->txSls() != txSls || p->msu().length() != len)
|
|
continue;
|
|
if (::memcmp(msu->data(),p->msu().data(),len))
|
|
continue;
|
|
const unsigned char* buf = msu->getData(label.length()+1,1);
|
|
Debug(this,DebugAll,"Refusing to postpone duplicate %s on %d",
|
|
SS7MsgSNM::lookup((SS7MsgSNM::Type)buf[0],"???"),txSls);
|
|
TelEngine::destruct(msu);
|
|
break;
|
|
}
|
|
unlock();
|
|
if (msu && ((interval == 0) || (transmitMSU(*msu,label,txSls) >= 0) || force)) {
|
|
lock();
|
|
m_pending.add(new SnmPending(msu,label,txSls,interval,global),when);
|
|
unlock();
|
|
return true;
|
|
}
|
|
TelEngine::destruct(msu);
|
|
return false;
|
|
}
|
|
|
|
bool SS7Management::timeout(const SS7MSU& msu, const SS7Label& label, int txSls, bool final)
|
|
{
|
|
DDebug(this,DebugAll,"Timeout %u%s [%p]",txSls,(final ? " final" : ""),this);
|
|
if (!final)
|
|
return true;
|
|
const unsigned char* buf = msu.getData(label.length()+1,1);
|
|
if (!buf)
|
|
return false;
|
|
String link;
|
|
link << SS7PointCode::lookup(label.type()) << "," << label;
|
|
switch (buf[0]) {
|
|
case SS7MsgSNM::COO:
|
|
case SS7MsgSNM::XCO:
|
|
case SS7MsgSNM::ECO:
|
|
Debug(this,DebugNote,"Changeover timed out on %s",link.c_str());
|
|
inhibit(label,SS7Layer2::Inactive);
|
|
break;
|
|
case SS7MsgSNM::ECA:
|
|
Debug(this,DebugNote,"Emergency changeover acknowledge on %s",link.c_str());
|
|
transmitMSU(msu,label,txSls);
|
|
break;
|
|
case SS7MsgSNM::CBD:
|
|
Debug(this,DebugNote,"Changeback timed out on %s",link.c_str());
|
|
inhibit(label,0,SS7Layer2::Inactive);
|
|
break;
|
|
case SS7MsgSNM::LIN:
|
|
Debug(this,DebugWarn,"Link inhibit timed out on %s",link.c_str());
|
|
break;
|
|
case SS7MsgSNM::LUN:
|
|
Debug(this,DebugWarn,"Link uninhibit timed out on %s",link.c_str());
|
|
break;
|
|
case SS7MsgSNM::LRT:
|
|
if (inhibited(label,SS7Layer2::Remote))
|
|
postpone(new SS7MSU(msu),label,txSls,TIMER5M);
|
|
break;
|
|
case SS7MsgSNM::LLT:
|
|
if (inhibited(label,SS7Layer2::Local))
|
|
postpone(new SS7MSU(msu),label,txSls,TIMER5M);
|
|
break;
|
|
case SS7MsgSNM::TFP:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SS7Management::timeout(SignallingMessageTimer& timer, bool final)
|
|
{
|
|
SnmPending& msg = static_cast<SnmPending&>(timer);
|
|
if (final) {
|
|
String addr;
|
|
addr << msg;
|
|
Debug(this,DebugInfo,"Expired %s control sequence to %s [%p]",
|
|
msg.snmName(),addr.c_str(),this);
|
|
}
|
|
return timeout(msg.msu(),msg,msg.txSls(),final);
|
|
}
|
|
|
|
void SS7Management::timerTick(const Time& when)
|
|
{
|
|
for (;;) {
|
|
if (!lock(SignallingEngine::maxLockWait()))
|
|
break;
|
|
SnmPending* msg = static_cast<SnmPending*>(m_pending.timeout(when));
|
|
unlock();
|
|
if (!msg)
|
|
break;
|
|
if (!msg->global().started() || msg->global().timeout(when.msec()))
|
|
timeout(*msg,true);
|
|
else if (timeout(*msg,false)) {
|
|
transmitMSU(msg->msu(),*msg,msg->txSls());
|
|
m_pending.add(msg,when);
|
|
msg = 0;
|
|
}
|
|
TelEngine::destruct(msg);
|
|
}
|
|
}
|
|
|
|
bool SS7Management::inhibit(const SS7Label& link, int setFlags, int clrFlags)
|
|
{
|
|
SS7Router* router = YOBJECT(SS7Router,SS7Layer4::network());
|
|
return router && router->inhibit(link,setFlags,clrFlags);
|
|
}
|
|
|
|
bool SS7Management::inhibited(const SS7Label& link, int flags)
|
|
{
|
|
SS7Router* router = YOBJECT(SS7Router,SS7Layer4::network());
|
|
return router && router->inhibited(link,flags);
|
|
}
|
|
|
|
void SS7Management::recover(const SS7Label& link, int sequence)
|
|
{
|
|
SS7Router* router = YOBJECT(SS7Router,SS7Layer4::network());
|
|
if (router)
|
|
router->recoverMSU(link,sequence);
|
|
}
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|