2026 lines
61 KiB
C++
2026 lines
61 KiB
C++
/**
|
|
* router.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>
|
|
|
|
using namespace TelEngine;
|
|
|
|
typedef GenPointer<SS7Layer3> L3Pointer;
|
|
typedef GenPointer<SS7Layer4> L4Pointer;
|
|
|
|
namespace { //anonymous
|
|
|
|
class L3ViewPtr : public L3Pointer
|
|
{
|
|
public:
|
|
inline L3ViewPtr(SS7Layer3* l3)
|
|
: L3Pointer(l3)
|
|
{ }
|
|
inline ObjList& view(SS7PointCode::Type type)
|
|
{ return m_views[type-1]; }
|
|
private:
|
|
ObjList m_views[YSS7_PCTYPE_COUNT];
|
|
};
|
|
|
|
class HeldMSU : public SS7MSU
|
|
{
|
|
friend class TelEngine::SS7Route;
|
|
private:
|
|
inline HeldMSU(const SS7Router* router, const SS7MSU& msu,
|
|
const SS7Label& label, int sls, SS7Route::State states, const SS7Layer3* source)
|
|
: SS7MSU(msu),
|
|
m_router(router), m_label(label), m_sls(sls),
|
|
m_states(states), m_source(source)
|
|
{ }
|
|
|
|
const SS7Router* m_router;
|
|
const SS7Label m_label;
|
|
int m_sls;
|
|
SS7Route::State m_states;
|
|
const SS7Layer3* m_source;
|
|
};
|
|
|
|
}; // anonymous namespace
|
|
|
|
// Control operations
|
|
static const TokenDict s_dict_control[] = {
|
|
{ "show", SS7Router::Status },
|
|
{ "pause", SS7Router::Pause },
|
|
{ "resume", SS7Router::Resume },
|
|
{ "restart", SS7Router::Restart },
|
|
{ "traffic", SS7Router::Traffic },
|
|
{ "advertise", SS7Router::Advertise },
|
|
{ "prohibit", SS7MsgSNM::TFP },
|
|
{ "restrict", SS7MsgSNM::TFR },
|
|
{ "congest", SS7MsgSNM::TFC },
|
|
{ "allow", SS7MsgSNM::TFA },
|
|
{ "allowed", SS7MsgSNM::TRA },
|
|
{ "test-prohibited", SS7MsgSNM::RST },
|
|
{ "test-restricted", SS7MsgSNM::RSR },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
static const TokenDict s_dict_states[] = {
|
|
{ "prohibit", SS7Route::Prohibited },
|
|
{ "unknown", SS7Route::Unknown },
|
|
{ "restrict", SS7Route::Restricted },
|
|
{ "congest", SS7Route::Congestion },
|
|
{ "allow", SS7Route::Allowed },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
static SS7Route::State routeState(SS7MsgSNM::Type cmd)
|
|
{
|
|
switch (cmd) {
|
|
case SS7MsgSNM::TFP:
|
|
case SS7MsgSNM::RST:
|
|
return SS7Route::Prohibited;
|
|
case SS7MsgSNM::TFR:
|
|
case SS7MsgSNM::RSR:
|
|
return SS7Route::Restricted;
|
|
case SS7MsgSNM::TFC:
|
|
return SS7Route::Congestion;
|
|
case SS7MsgSNM::TFA:
|
|
case SS7MsgSNM::TRA:
|
|
return SS7Route::Allowed;
|
|
default:
|
|
return SS7Route::Unknown;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SS7Route
|
|
*/
|
|
// Get the state to name token table
|
|
const TokenDict* SS7Route::stateNames()
|
|
{
|
|
return s_dict_states;
|
|
}
|
|
|
|
// Attach a network to use for this destination or change its priority
|
|
void SS7Route::attach(SS7Layer3* network, SS7PointCode::Type type)
|
|
{
|
|
if (!network)
|
|
return;
|
|
unsigned int priority = network->getRoutePriority(type,m_packed);
|
|
// No route to point code ?
|
|
if (priority == (unsigned int)-1)
|
|
return;
|
|
Lock lock(this);
|
|
// Remove from list if already there
|
|
detach(network);
|
|
SS7Route* route = network->findRoute(m_type,m_packed);
|
|
if (route) {
|
|
if (m_maxDataLength > route->getMaxDataLength() || m_maxDataLength == 0)
|
|
m_maxDataLength = route->getMaxDataLength();
|
|
}
|
|
// Insert
|
|
if (priority == 0) {
|
|
m_networks.insert(new L3Pointer(network));
|
|
return;
|
|
}
|
|
for (ObjList* o = m_networks.skipNull(); o; o = o->skipNext()) {
|
|
L3Pointer* p = static_cast<L3Pointer*>(o->get());
|
|
if (!*p)
|
|
continue;
|
|
if (priority <= (*p)->getRoutePriority(type,m_packed)) {
|
|
o->insert(new L3Pointer(network));
|
|
return;
|
|
}
|
|
}
|
|
m_networks.append(new L3Pointer(network));
|
|
}
|
|
|
|
// Remove a network from the list without deleting it
|
|
bool SS7Route::detach(SS7Layer3* network)
|
|
{
|
|
Lock lock(this);
|
|
ObjList* o = m_networks.skipNull();
|
|
if (!network)
|
|
return o != 0;
|
|
for (; o; o = o->skipNext()) {
|
|
L3Pointer* p = static_cast<L3Pointer*>(o->get());
|
|
if (*p && *p == network) {
|
|
m_networks.remove(p);
|
|
break;
|
|
}
|
|
}
|
|
m_maxDataLength = 0;
|
|
for (o = m_networks.skipNull(); o; o = o->skipNext()) {
|
|
L3Pointer* p = static_cast<L3Pointer*>(o->get());
|
|
if (!p)
|
|
continue;
|
|
RefPointer<SS7Layer3> l3 = static_cast<SS7Layer3*>(*p);
|
|
if (!l3)
|
|
continue;
|
|
SS7Route* route = l3->findRoute(m_type,m_packed);
|
|
if (route) {
|
|
if (m_maxDataLength > route->getMaxDataLength() ||
|
|
m_maxDataLength == 0)
|
|
m_maxDataLength = route->getMaxDataLength();
|
|
}
|
|
}
|
|
return 0 != m_networks.skipNull();
|
|
}
|
|
|
|
// Check if a network is in the list (thread safe)
|
|
bool SS7Route::hasNetwork(const SS7Layer3* network)
|
|
{
|
|
if (!network)
|
|
return false;
|
|
Lock lock(this);
|
|
for (ObjList* o = m_networks.skipNull(); o; o = o->skipNext()) {
|
|
L3Pointer* p = static_cast<L3Pointer*>(o->get());
|
|
if (*p && network == *p)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Check if a network is in the list (const but unsafe)
|
|
bool SS7Route::hasNetwork(const SS7Layer3* network) const
|
|
{
|
|
if (!network)
|
|
return false;
|
|
for (ObjList* o = m_networks.skipNull(); o; o = o->skipNext()) {
|
|
L3Pointer* p = static_cast<L3Pointer*>(o->get());
|
|
if (*p && network == *p)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Check if at least one network is operational
|
|
bool SS7Route::operational(int sls)
|
|
{
|
|
Lock lock(this);
|
|
for (ObjList* o = m_networks.skipNull(); o; o = o->skipNext()) {
|
|
L3Pointer* p = static_cast<L3Pointer*>(o->get());
|
|
if (*p && (*p)->operational(sls))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Check and reset congestion status
|
|
bool SS7Route::congested()
|
|
{
|
|
if (m_congCount >= 8 || m_congBytes >= 256) {
|
|
m_congCount = 0;
|
|
m_congBytes = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Try to transmit a MSU through one of the attached networks
|
|
int SS7Route::transmitMSU(const SS7Router* router, const SS7MSU& msu,
|
|
const SS7Label& label, int sls, State states, const SS7Layer3* source)
|
|
{
|
|
lock();
|
|
if (msu.getSIF() > SS7MSU::MTNS && m_buffering) {
|
|
if (m_state & states) {
|
|
// Store User Part messages in the controlled rerouting buffer
|
|
DDebug(router,DebugInfo,"Storing %s MSU in reroute buffer of %u",
|
|
msu.getServiceName(),packed());
|
|
m_reroute.append(new HeldMSU(router,msu,label,sls,states,source));
|
|
sls = 0;
|
|
}
|
|
else
|
|
sls = -1;
|
|
}
|
|
else
|
|
sls = transmitInternal(router,msu,label,sls,states,source);
|
|
unlock();
|
|
return sls;
|
|
}
|
|
|
|
// Transmit the MSU, called with the route locked
|
|
int SS7Route::transmitInternal(const SS7Router* router, const SS7MSU& msu,
|
|
const SS7Label& label, int sls, State states, const SS7Layer3* source)
|
|
{
|
|
#ifdef DEBUG
|
|
bool info = true;
|
|
#else
|
|
bool info = false;
|
|
#endif
|
|
int offs = 0;
|
|
bool userPart = (msu.getSIF() > SS7MSU::MTNS);
|
|
if (userPart)
|
|
offs = sls >> shift();
|
|
ListIterator iter(m_networks,offs);
|
|
while (L3Pointer* p = static_cast<L3Pointer*>(iter.get())) {
|
|
RefPointer<SS7Layer3> l3 = static_cast<SS7Layer3*>(*p);
|
|
if (!l3 || (l3 == source) ||
|
|
!(l3->getRouteState(label.type(),label.dpc(),userPart) & states))
|
|
continue;
|
|
unlock();
|
|
XDebug(router,DebugAll,"Attempting transmitMSU %s on L3=%p '%s' [%p]",
|
|
msu.getServiceName(),(void*)l3,l3->toString().c_str(),router);
|
|
int res = l3->transmitMSU(msu,label,sls);
|
|
lock();
|
|
if (res != -1) {
|
|
unsigned int cong = l3->congestion(res);
|
|
if (cong) {
|
|
m_congCount++;
|
|
m_congBytes += msu.length();
|
|
}
|
|
if (info) {
|
|
String addr;
|
|
addr << label;
|
|
Debug(router,DebugInfo,"MSU %s size %u sent on %s:%d%s",
|
|
addr.c_str(),msu.length(),l3->toString().c_str(),res,
|
|
(cong ? " (congested)" : ""));
|
|
}
|
|
return res;
|
|
}
|
|
info = true;
|
|
}
|
|
Debug(router,DebugMild,"Could not send %s MSU size %u on any linkset",
|
|
msu.getServiceName(),msu.length());
|
|
return -1;
|
|
}
|
|
|
|
void SS7Route::rerouteCheck(u_int64_t when)
|
|
{
|
|
lock();
|
|
if (m_buffering && m_buffering <= when) {
|
|
if (m_state & Prohibited)
|
|
rerouteFlush();
|
|
unsigned int c = 0;
|
|
while (HeldMSU* msu = static_cast<HeldMSU*>(m_reroute.remove(false))) {
|
|
transmitInternal(msu->m_router,*msu,msu->m_label,msu->m_sls,
|
|
msu->m_states,msu->m_source);
|
|
TelEngine::destruct(msu);
|
|
c++;
|
|
}
|
|
if (c)
|
|
Debug(DebugNote,"Released %u MSUs from reroute buffer of %u",c,packed());
|
|
m_buffering = 0;
|
|
}
|
|
unlock();
|
|
}
|
|
|
|
void SS7Route::rerouteFlush()
|
|
{
|
|
if (!m_buffering)
|
|
return;
|
|
lock();
|
|
unsigned int c = m_reroute.count();
|
|
if (c)
|
|
Debug(DebugMild,"Flushed %u MSUs from reroute buffer of %u",c,packed());
|
|
m_reroute.clear();
|
|
m_buffering = 0;
|
|
unlock();
|
|
}
|
|
|
|
void SS7Route::reroute()
|
|
{
|
|
XDebug(DebugAll,"Initiating controlled rerouting to %u",packed());
|
|
lock();
|
|
m_buffering = Time::now() + 800000;
|
|
unlock();
|
|
}
|
|
|
|
|
|
/**
|
|
* SS7Router
|
|
*/
|
|
SS7Router::SS7Router(const NamedList& params)
|
|
: SignallingComponent(params.safe("SS7Router"),¶ms,"ss7-router"),
|
|
Mutex(true,"SS7Router"),
|
|
m_changes(0), m_transfer(false), m_phase2(false), m_started(false),
|
|
m_restart(0), m_isolate(0), m_statsMutex(false,"SS7RouterStats"),
|
|
m_trafficOk(0), m_trafficSent(0), m_routeTest(0), m_testRestricted(false),
|
|
m_transferSilent(false), m_checkRoutes(false), m_autoAllowed(false),
|
|
m_sendUnavail(true), m_sendProhibited(true),
|
|
m_rxMsu(0), m_txMsu(0), m_fwdMsu(0), m_failMsu(0), m_congestions(0),
|
|
m_mngmt(0)
|
|
{
|
|
#ifdef DEBUG
|
|
if (debugAt(DebugAll)) {
|
|
String tmp;
|
|
params.dump(tmp,"\r\n ",'\'',true);
|
|
Debug(this,DebugAll,"SS7Router::SS7Router(%p) [%p]%s",
|
|
¶ms,this,tmp.c_str());
|
|
}
|
|
#endif
|
|
const String* tr = params.getParam(YSTRING("transfer"));
|
|
if (!TelEngine::null(tr)) {
|
|
m_transferSilent = (*tr == YSTRING("silent"));
|
|
m_transfer = !m_transferSilent && tr->toBoolean();
|
|
}
|
|
setNI(SS7MSU::getNetIndicator(params.getValue(YSTRING("netindicator")),SS7MSU::National));
|
|
m_autoAllowed = params.getBoolValue(YSTRING("autoallow"),m_autoAllowed);
|
|
m_sendUnavail = params.getBoolValue(YSTRING("sendupu"),m_sendUnavail);
|
|
m_sendProhibited = params.getBoolValue(YSTRING("sendtfp"),m_sendProhibited);
|
|
m_restart.interval(params,"starttime",5000,(m_transfer ? 60000 : 10000),false);
|
|
m_isolate.interval(params,"isolation",500,1000,true);
|
|
m_routeTest.interval(params,"testroutes",10000,50000,true),
|
|
m_trafficOk.interval(m_restart.interval() + 4000);
|
|
m_trafficSent.interval(m_restart.interval() + 8000);
|
|
m_testRestricted = params.getBoolValue(YSTRING("testrestricted"),m_testRestricted);
|
|
loadLocalPC(params);
|
|
const String* param = params.getParam(YSTRING("management"));
|
|
const char* name = "ss7snm";
|
|
if (param) {
|
|
if (*param && !param->toBoolean(false))
|
|
name = param->c_str();
|
|
}
|
|
else
|
|
param = ¶ms;
|
|
if (param->toBoolean(true)) {
|
|
NamedPointer* ptr = YOBJECT(NamedPointer,param);
|
|
NamedList* mConfig = ptr ? YOBJECT(NamedList,ptr->userData()) : 0;
|
|
NamedList mParams(name);
|
|
mParams.addParam("basename",name);
|
|
if (mConfig)
|
|
mParams.copyParams(*mConfig);
|
|
else {
|
|
if (params.hasSubParams(mParams + "."))
|
|
mParams.copySubParams(params,mParams + ".");
|
|
else
|
|
mParams.addParam("local-config","true");
|
|
mConfig = &mParams;
|
|
}
|
|
attach(m_mngmt = YSIGCREATE(SS7Management,&mParams));
|
|
}
|
|
}
|
|
|
|
SS7Router::~SS7Router()
|
|
{
|
|
Debug(this,DebugInfo,"SS7Router destroyed, rx=%lu, tx=%lu, fwd=%lu, fail=%lu, cong=%lu",
|
|
m_rxMsu,m_txMsu,m_fwdMsu,m_failMsu,m_congestions);
|
|
}
|
|
|
|
bool SS7Router::initialize(const NamedList* config)
|
|
{
|
|
#ifdef DEBUG
|
|
String tmp;
|
|
if (config && debugAt(DebugAll))
|
|
config->dump(tmp,"\r\n ",'\'',true);
|
|
Debug(this,DebugInfo,"SS7Router::initialize(%p) [%p]%s",config,this,tmp.c_str());
|
|
#endif
|
|
if (config) {
|
|
debugLevel(config->getIntValue(YSTRING("debuglevel_router"),
|
|
config->getIntValue(YSTRING("debuglevel"),-1)));
|
|
const String* tr = config->getParam(YSTRING("transfer"));
|
|
if (!TelEngine::null(tr)) {
|
|
m_transferSilent = (*tr == YSTRING("silent"));
|
|
m_transfer = !m_transferSilent && tr->toBoolean(m_transfer);
|
|
}
|
|
m_autoAllowed = config->getBoolValue(YSTRING("autoallow"),m_autoAllowed);
|
|
m_sendUnavail = config->getBoolValue(YSTRING("sendupu"),m_sendUnavail);
|
|
m_sendProhibited = config->getBoolValue(YSTRING("sendtfp"),m_sendProhibited);
|
|
}
|
|
if (m_mngmt)
|
|
SignallingComponent::insert(m_mngmt);
|
|
return m_started || (config && !config->getBoolValue(YSTRING("autostart"))) || restart();
|
|
}
|
|
|
|
void SS7Router::loadLocalPC(const NamedList& params)
|
|
{
|
|
Lock lock(m_routeMutex);
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++)
|
|
m_local[i] = 0;
|
|
unsigned int n = params.length();
|
|
for (unsigned int i= 0; i < n; i++) {
|
|
NamedString* ns = params.getParam(i);
|
|
if (!ns)
|
|
continue;
|
|
if (ns->name() != "local")
|
|
continue;
|
|
ObjList* route = ns->split(',',true);
|
|
ObjList* obj = route->skipNull();
|
|
SS7PointCode pc;
|
|
SS7PointCode::Type type = SS7PointCode::Other;
|
|
do {
|
|
if (!obj)
|
|
break;
|
|
type = SS7PointCode::lookup(obj->get()->toString());
|
|
obj = obj->skipNext();
|
|
if (obj)
|
|
pc.assign(obj->get()->toString(),type);
|
|
} while (false);
|
|
TelEngine::destruct(route);
|
|
unsigned int packed = pc.pack(type);
|
|
if ((unsigned int)type > YSS7_PCTYPE_COUNT || !packed) {
|
|
Debug(this,DebugNote,"Invalid %s='%s' (invalid point code%s) [%p]",
|
|
ns->name().c_str(),ns->safe(),type == SS7PointCode::Other ? " type" : "",this);
|
|
continue;
|
|
}
|
|
m_local[type - 1] = packed;
|
|
}
|
|
}
|
|
|
|
unsigned char SS7Router::getNI(SS7PointCode::Type pcType, unsigned char defNI) const
|
|
{
|
|
if ((defNI & 0xc0) == 0)
|
|
defNI <<= 6;
|
|
if (SS7Layer3::hasType(pcType))
|
|
return SS7Layer3::getNI(pcType,defNI);
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* p = static_cast<L3ViewPtr*>(o->get());
|
|
if ((*p)->hasType(pcType))
|
|
return (*p)->getNI(pcType,defNI);
|
|
}
|
|
return defNI;
|
|
}
|
|
|
|
unsigned int SS7Router::getDefaultLocal(SS7PointCode::Type type) const
|
|
{
|
|
unsigned int local = getLocal(type);
|
|
if (!local) {
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* p = static_cast<L3ViewPtr*>(o->get());
|
|
unsigned int l = (*p)->getLocal(type);
|
|
if (l && local && (l != local))
|
|
return 0;
|
|
local = l;
|
|
}
|
|
}
|
|
return local;
|
|
}
|
|
|
|
bool SS7Router::operational(int sls) const
|
|
{
|
|
if (!m_started || m_isolate.started())
|
|
return false;
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* p = static_cast<L3ViewPtr*>(o->get());
|
|
if ((*p)->operational(sls))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SS7Router::restart()
|
|
{
|
|
Debug(this,DebugNote,"Restart of %s initiated [%p]",
|
|
(m_transfer ? "STP" : "SN"),this);
|
|
lock();
|
|
m_phase2 = false;
|
|
m_started = false;
|
|
m_isolate.stop();
|
|
m_routeTest.stop();
|
|
m_trafficOk.stop();
|
|
m_trafficSent.stop();
|
|
m_restart.stop();
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* p = static_cast<L3ViewPtr*>(o->get());
|
|
if (!(*p)->operational()) {
|
|
clearView(*p);
|
|
clearRoutes(*p,false);
|
|
}
|
|
}
|
|
checkRoutes();
|
|
m_checkRoutes = true;
|
|
m_restart.start();
|
|
m_trafficOk.start();
|
|
unlock();
|
|
rerouteFlush();
|
|
return true;
|
|
}
|
|
|
|
void SS7Router::disable()
|
|
{
|
|
Debug(this,DebugNote,"MTP operation is disabled [%p]",this);
|
|
lock();
|
|
m_phase2 = false;
|
|
m_started = false;
|
|
m_checkRoutes = false;
|
|
m_isolate.stop();
|
|
m_restart.stop();
|
|
m_routeTest.stop();
|
|
m_trafficOk.stop();
|
|
m_trafficSent.stop();
|
|
unlock();
|
|
rerouteFlush();
|
|
}
|
|
|
|
// Attach a SS7 Layer 3 (network) to the router
|
|
void SS7Router::attach(SS7Layer3* network)
|
|
{
|
|
if (!network || network == this)
|
|
return;
|
|
SignallingComponent::insert(network);
|
|
lock();
|
|
bool add = true;
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* p = static_cast<L3ViewPtr*>(o->get());
|
|
if (*p == network) {
|
|
add = false;
|
|
break;
|
|
}
|
|
}
|
|
if (add) {
|
|
m_changes++;
|
|
m_layer3.append(new L3ViewPtr(network));
|
|
Debug(this,DebugAll,"Attached network (%p,'%s') [%p]",
|
|
network,network->toString().safe(),this);
|
|
}
|
|
updateRoutes(network);
|
|
buildViews();
|
|
unlock();
|
|
network->attach(this);
|
|
}
|
|
|
|
// Detach a SS7 Layer 3 (network) from the router
|
|
void SS7Router::detach(SS7Layer3* network)
|
|
{
|
|
if (!network)
|
|
return;
|
|
Lock lock(this);
|
|
const char* name = 0;
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* p = static_cast<L3ViewPtr*>(o->get());
|
|
if (*p != network)
|
|
continue;
|
|
m_changes++;
|
|
m_layer3.remove(p);
|
|
removeRoutes(network);
|
|
if (engine() && engine()->find(network)) {
|
|
name = network->toString().safe();
|
|
lock.drop();
|
|
network->attach(0);
|
|
}
|
|
Debug(this,DebugAll,"Detached network (%p,'%s') [%p]",network,name,this);
|
|
break;
|
|
}
|
|
buildViews();
|
|
}
|
|
|
|
// Attach a SS7 Layer 4 (service) to the router. Attach itself to the service
|
|
void SS7Router::attach(SS7Layer4* service)
|
|
{
|
|
if (!service)
|
|
return;
|
|
SignallingComponent::insert(service);
|
|
lock();
|
|
bool add = true;
|
|
for (ObjList* o = m_layer4.skipNull(); o; o = o->skipNext()) {
|
|
L4Pointer* p = static_cast<L4Pointer*>(o->get());
|
|
if (*p == service) {
|
|
add = false;
|
|
break;
|
|
}
|
|
}
|
|
if (add) {
|
|
m_changes++;
|
|
m_layer4.append(new L4Pointer(service));
|
|
Debug(this,DebugAll,"Attached service (%p,'%s') [%p]",
|
|
service,service->toString().safe(),this);
|
|
}
|
|
unlock();
|
|
service->attach(this);
|
|
}
|
|
|
|
// Detach a SS7 Layer 4 (service) from the router. Detach itself from the service
|
|
void SS7Router::detach(SS7Layer4* service)
|
|
{
|
|
if (!service)
|
|
return;
|
|
Lock lock(this);
|
|
for (ObjList* o = m_layer4.skipNull(); o; o = o->skipNext()) {
|
|
L4Pointer* p = static_cast<L4Pointer*>(o->get());
|
|
if (*p != service)
|
|
continue;
|
|
m_changes++;
|
|
m_layer4.remove(p);
|
|
if (service == (SS7Layer4*)m_mngmt)
|
|
m_mngmt = 0;
|
|
const char* name = 0;
|
|
if (engine() && engine()->find(service)) {
|
|
name = service->toString().safe();
|
|
lock.drop();
|
|
service->attach(0);
|
|
}
|
|
Debug(this,DebugAll,"Detached service (%p,'%s') [%p]",service,name,this);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SS7Router::buildViews()
|
|
{
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* p = static_cast<L3ViewPtr*>(o->get());
|
|
if (!*p)
|
|
continue;
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
SS7PointCode::Type type = static_cast<SS7PointCode::Type>(i+1);
|
|
buildView(type,p->view(type),*p);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SS7Router::buildView(SS7PointCode::Type type, ObjList& view, SS7Layer3* network)
|
|
{
|
|
view.clear();
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* p = static_cast<L3ViewPtr*>(o->get());
|
|
if (!*p || ((*p) == network))
|
|
continue;
|
|
for (ObjList* r = (*p)->getRoutes(type); r; r = r->next()) {
|
|
const SS7Route* route = static_cast<const SS7Route*>(r->get());
|
|
if (!route)
|
|
continue;
|
|
if (!network->getRoutePriority(type,route->packed()))
|
|
continue;
|
|
ObjList* v;
|
|
for (v = view.skipNull(); v; v = v->skipNext()) {
|
|
const SS7Route* r = static_cast<const SS7Route*>(v->get());
|
|
if (r->packed() == route->packed())
|
|
break;
|
|
}
|
|
if (!v) {
|
|
DDebug(this,DebugAll,"Creating route to %u from %s in view of %s",
|
|
route->packed(),(*p)->toString().c_str(),network->toString().c_str());
|
|
view.append(new SS7Route(route->packed(),type));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SS7Router::timerTick(const Time& when)
|
|
{
|
|
Lock mylock(this,SignallingEngine::maxLockWait());
|
|
if (!mylock.locked())
|
|
return;
|
|
if (m_isolate.timeout(when.msec())) {
|
|
Debug(this,DebugWarn,"Node is isolated and down! [%p]",this);
|
|
m_phase2 = false;
|
|
m_started = false;
|
|
m_isolate.stop();
|
|
m_restart.stop();
|
|
m_trafficOk.stop();
|
|
m_trafficSent.stop();
|
|
mylock.drop();
|
|
rerouteFlush();
|
|
return;
|
|
}
|
|
if (m_started) {
|
|
if (m_routeTest.timeout(when.msec())) {
|
|
m_routeTest.start(when.msec());
|
|
mylock.drop();
|
|
sendRouteTest();
|
|
}
|
|
else if (m_trafficOk.timeout(when.msec())) {
|
|
m_trafficOk.stop();
|
|
silentAllow();
|
|
}
|
|
else if (m_trafficSent.timeout(when.msec()))
|
|
m_trafficSent.stop();
|
|
mylock.drop();
|
|
rerouteCheck(when);
|
|
return;
|
|
}
|
|
// MTP restart actions
|
|
if (m_transfer && !m_phase2) {
|
|
if (m_restart.timeout(when.msec() + 5000))
|
|
restart2();
|
|
}
|
|
else if (m_restart.timeout(when.msecNow())) {
|
|
Debug(this,DebugNote,"Restart of %s complete [%p]",
|
|
(m_transfer ? "STP" : "SN"),this);
|
|
m_restart.stop();
|
|
m_started = true;
|
|
m_phase2 = false;
|
|
// send TRA to all operational adjacent nodes
|
|
sendRestart();
|
|
if (!m_trafficSent.started())
|
|
m_trafficSent.start();
|
|
if (m_checkRoutes)
|
|
checkRoutes();
|
|
// advertise all non-Prohibited routes we learned about
|
|
if (m_transfer)
|
|
notifyRoutes(SS7Route::NotProhibited);
|
|
// iterate and notify all user parts
|
|
ObjList* l = &m_layer4;
|
|
for (; l; l = l->next()) {
|
|
L4Pointer* p = static_cast<L4Pointer*>(l->get());
|
|
if (p && *p)
|
|
(*p)->notify(this,-1);
|
|
}
|
|
if (m_routeTest.interval())
|
|
m_routeTest.start(when.msec());
|
|
}
|
|
}
|
|
|
|
void SS7Router::restart2()
|
|
{
|
|
Lock mylock(this);
|
|
if (m_phase2 || !m_transfer)
|
|
return;
|
|
Debug(this,DebugNote,"Restart of STP entering second phase [%p]",this);
|
|
m_phase2 = true;
|
|
mylock.drop();
|
|
// advertise Prohibited routes we learned until now
|
|
notifyRoutes(SS7Route::Prohibited);
|
|
}
|
|
|
|
int SS7Router::routeMSU(const SS7MSU& msu, const SS7Label& label, SS7Layer3* network, int sls, SS7Route::State states)
|
|
{
|
|
XDebug(this,DebugStub,"Possibly incomplete SS7Router::routeMSU(%p,%p,%p,%d) states=0x%X",
|
|
&msu,&label,network,sls,states);
|
|
m_routeMutex.lock();
|
|
RefPointer<SS7Route> route = findRoute(label.type(),label.dpc().pack(label.type()));
|
|
m_routeMutex.unlock();
|
|
int slsTx = route ? route->transmitMSU(this,msu,label,sls,states,network) : -1;
|
|
if (slsTx >= 0) {
|
|
bool cong = route->congested();
|
|
if (cong) {
|
|
Debug(this,DebugMild,"Route to %u reports congestion",route->packed());
|
|
while (m_mngmt) {
|
|
unsigned int local = getLocal(label.type());
|
|
if (!local)
|
|
break;
|
|
NamedList* ctl = m_mngmt->controlCreate("congest");
|
|
if (!ctl)
|
|
break;
|
|
String addr;
|
|
addr << SS7PointCode::lookup(label.type()) << ",";
|
|
addr << SS7PointCode(label.type(),local) << "," << label.opc();
|
|
String dest;
|
|
dest << SS7PointCode(label.type(),route->packed());
|
|
ctl->addParam("address",addr);
|
|
ctl->addParam("destination",dest);
|
|
ctl->setParam("automatic",String::boolText(true));
|
|
m_mngmt->controlExecute(ctl);
|
|
break;
|
|
}
|
|
}
|
|
m_statsMutex.lock();
|
|
m_txMsu++;
|
|
if (network)
|
|
m_fwdMsu++;
|
|
if (cong)
|
|
m_congestions++;
|
|
m_statsMutex.unlock();
|
|
}
|
|
else {
|
|
m_statsMutex.lock();
|
|
m_failMsu++;
|
|
m_statsMutex.unlock();
|
|
if (!route) {
|
|
String tmp;
|
|
tmp << label.dpc();
|
|
Debug(this,DebugMild,"No route to %s was found for %s MSU size %u",
|
|
tmp.c_str(),msu.getServiceName(),msu.length());
|
|
}
|
|
else
|
|
Debug(this,DebugAll,"Failed to send %s MSU size %u on %s route %u",
|
|
msu.getServiceName(),msu.length(),route->stateName(),route->packed());
|
|
}
|
|
return slsTx;
|
|
}
|
|
|
|
int SS7Router::transmitMSU(const SS7MSU& msu, const SS7Label& label, int sls)
|
|
{
|
|
SS7Route::State states = SS7Route::NotProhibited;
|
|
switch (msu.getSIF()) {
|
|
case SS7MSU::SNM:
|
|
if ((msu.at(label.length()+1) & 0x0f) == SS7MsgSNM::MIM) {
|
|
int res = routeMSU(msu,label,0,sls,SS7Route::AnyState);
|
|
if (res >= 0)
|
|
return res;
|
|
// now we are desperate to send a link management packet
|
|
sls = -2;
|
|
}
|
|
case SS7MSU::MTN:
|
|
case SS7MSU::MTNS:
|
|
// Management and Maintenance can be sent even on prohibited routes
|
|
states = SS7Route::AnyState;
|
|
break;
|
|
default:
|
|
if (!m_started)
|
|
return -1;
|
|
}
|
|
return routeMSU(msu,label,0,sls,states);
|
|
}
|
|
|
|
HandledMSU SS7Router::receivedMSU(const SS7MSU& msu, const SS7Label& label, SS7Layer3* network, int sls)
|
|
{
|
|
if (m_autoAllowed && network && (msu.getSIF() > SS7MSU::MTNS)) {
|
|
unsigned int src = label.opc().pack(label.type());
|
|
Lock mylock(m_routeMutex);
|
|
SS7Route* route = findRoute(label.type(),src);
|
|
if (route && !route->priority() && (route->state() & (SS7Route::Unknown|SS7Route::Prohibited))) {
|
|
Debug(this,DebugNote,"Auto activating adjacent route %u on '%s' [%p]",
|
|
src,network->toString().c_str(),this);
|
|
setRouteSpecificState(label.type(),src,src,SS7Route::Allowed,network);
|
|
if (m_transfer && m_started)
|
|
notifyRoutes(SS7Route::KnownState,src);
|
|
}
|
|
}
|
|
if ((msu.getSIF() > SS7MSU::MTNS) && !m_started)
|
|
return HandledMSU::Failure;
|
|
bool maint = (msu.getSIF() == SS7MSU::MTN) || (msu.getSIF() == SS7MSU::MTNS);
|
|
if (!maint) {
|
|
m_statsMutex.lock();
|
|
m_rxMsu++;
|
|
m_statsMutex.unlock();
|
|
}
|
|
lock();
|
|
ObjList* l;
|
|
HandledMSU ret;
|
|
do {
|
|
for (l = &m_layer4; l; l = l->next()) {
|
|
L4Pointer* p = static_cast<L4Pointer*>(l->get());
|
|
if (!p)
|
|
continue;
|
|
RefPointer<SS7Layer4> l4 = static_cast<SS7Layer4*>(*p);
|
|
if (!l4)
|
|
continue;
|
|
XDebug(this,DebugAll,"Attempting receivedMSU %s to L4=%p '%s' [%p]",
|
|
msu.getServiceName(),(void*)l4,l4->toString().c_str(),this);
|
|
int chg = m_changes;
|
|
unlock();
|
|
HandledMSU handled = l4->receivedMSU(msu,label,network,sls);
|
|
XDebug(this,DebugAll,"L4=%p '%s' returned %u [%p]",
|
|
(void*)l4,l4->toString().c_str(),(unsigned int)handled,this);
|
|
switch (handled) {
|
|
case HandledMSU::Accepted:
|
|
case HandledMSU::Failure:
|
|
return handled;
|
|
case HandledMSU::Rejected:
|
|
break;
|
|
default:
|
|
ret = handled;
|
|
}
|
|
lock();
|
|
// if list has changed break with l not null so repeat the scan
|
|
if (chg != m_changes)
|
|
break;
|
|
}
|
|
} while (l); // loop until the list was scanned to end
|
|
unlock();
|
|
switch (ret) {
|
|
// these cases are explicitely set by the user parts
|
|
case HandledMSU::Unequipped:
|
|
case HandledMSU::Inaccessible:
|
|
if (m_sendUnavail)
|
|
return ret;
|
|
return HandledMSU::Failure;
|
|
default:
|
|
break;
|
|
}
|
|
// maintenance must stop here, others may be transferred out
|
|
if (maint)
|
|
return HandledMSU::Rejected;
|
|
unsigned int dpc = label.dpc().pack(label.type());
|
|
// if packet was for this node as set in router don't process any further
|
|
if (getLocal(label.type()) == dpc)
|
|
return m_sendUnavail ? HandledMSU::Unequipped : HandledMSU::Failure;
|
|
bool local = network && (network->getLocal(label.type()) == dpc);
|
|
if (m_transfer || m_transferSilent) {
|
|
if (routeMSU(msu,label,network,label.sls(),SS7Route::NotProhibited) >= 0)
|
|
return HandledMSU::Accepted;
|
|
// not routed and not local - send TFP or just drop it silently
|
|
if (!local)
|
|
return m_sendProhibited ? HandledMSU::NoAddress : HandledMSU::Failure;
|
|
}
|
|
if (HandledMSU::NoCircuit == ret)
|
|
return HandledMSU::NoCircuit;
|
|
return (local && m_sendUnavail) ? HandledMSU::Unequipped : HandledMSU::Failure;
|
|
}
|
|
|
|
// Call the route changed notification for all known routes
|
|
void SS7Router::notifyRoutes(SS7Route::State states, unsigned int onlyPC)
|
|
{
|
|
if (SS7Route::Unknown == states)
|
|
return;
|
|
DDebug(this,DebugAll,"Notifying routes with states 0x%02X only to %u [%p]",
|
|
states,onlyPC,this);
|
|
Lock lock(m_routeMutex);
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
ListIterator iter(m_route[i]);
|
|
while (true) {
|
|
SS7Route* route = static_cast<SS7Route*>(iter.get());
|
|
if (!route)
|
|
break;
|
|
if ((route->state() & states) == 0)
|
|
continue;
|
|
routeChanged(route,static_cast<SS7PointCode::Type>(i+1),0,0,onlyPC,true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Call the route changed notification for all known routes on a network
|
|
void SS7Router::notifyRoutes(SS7Route::State states, const SS7Layer3* network)
|
|
{
|
|
if (SS7Route::Unknown == states || !network)
|
|
return;
|
|
DDebug(this,DebugAll,"Notifying routes with states 0x%02X only to '%s' [%p]",
|
|
states,network->toString().c_str(),this);
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
const ObjList* l = network->getRoutes(static_cast<SS7PointCode::Type>(i+1));
|
|
for (; l; l = l->next()) {
|
|
const SS7Route* r = static_cast<const SS7Route*>(l->get());
|
|
if (r && !r->priority())
|
|
notifyRoutes(states,r->packed());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add a network to the routing table. Clear all its routes before appending it to the table
|
|
void SS7Router::updateRoutes(SS7Layer3* network)
|
|
{
|
|
if (!network)
|
|
return;
|
|
Lock lock(m_routeMutex);
|
|
removeRoutes(network);
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
SS7PointCode::Type type = (SS7PointCode::Type)(i + 1);
|
|
for (ObjList* o = network->m_route[i].skipNull(); o; o = o->skipNext()) {
|
|
SS7Route* src = static_cast<SS7Route*>(o->get());
|
|
SS7Route* dest = findRoute(type,src->packed());
|
|
if (dest) {
|
|
if (dest->priority() > src->priority())
|
|
dest->m_priority = src->priority();
|
|
if (dest->shift() < src->shift())
|
|
dest->m_shift = src->shift();
|
|
}
|
|
else {
|
|
dest = new SS7Route(*src);
|
|
m_route[i].append(dest);
|
|
}
|
|
DDebug(this,DebugAll,"Add route type=%s packed=%u for network (%p,'%s') [%p]",
|
|
SS7PointCode::lookup(type),src->m_packed,network,network->toString().safe(),this);
|
|
dest->attach(network,type);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove the given network from all destinations in the routing table.
|
|
// Remove the entry in the routing table if empty (no more routes to the point code).
|
|
void SS7Router::removeRoutes(SS7Layer3* network)
|
|
{
|
|
if (!network)
|
|
return;
|
|
Lock lock(m_routeMutex);
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
ListIterator iter(m_route[i]);
|
|
while (true) {
|
|
SS7Route* route = static_cast<SS7Route*>(iter.get());
|
|
if (!route)
|
|
break;
|
|
if (!route->detach(network)) {
|
|
SS7PointCode::Type type = static_cast<SS7PointCode::Type>(i+1);
|
|
DDebug(this,DebugAll,"Removing empty route type=%s packed=%u [%p]",
|
|
SS7PointCode::lookup(type),route->m_packed,this);
|
|
switch (route->state()) {
|
|
case SS7Route::Unknown:
|
|
case SS7Route::Prohibited:
|
|
break;
|
|
default:
|
|
// if an active route is removed broadcast it prohibited
|
|
route->m_state = SS7Route::Prohibited;
|
|
routeChanged(route,type,0,network);
|
|
}
|
|
m_route[i].remove(route,true);
|
|
}
|
|
}
|
|
}
|
|
DDebug(this,DebugAll,"Removed network (%p,'%s') from routing table [%p]",
|
|
network,network->toString().safe(),this);
|
|
}
|
|
|
|
// Route changed notification, if we are STP advertise routes to concerned neighbours
|
|
void SS7Router::routeChanged(const SS7Route* route, SS7PointCode::Type type,
|
|
unsigned int remotePC, const SS7Layer3* network, unsigned int onlyPC, bool forced)
|
|
{
|
|
if (!route)
|
|
return;
|
|
const char* pct = SS7PointCode::lookup(type);
|
|
String dest;
|
|
dest << SS7PointCode(type,route->packed());
|
|
if (dest.null())
|
|
return;
|
|
DDebug(this,DebugAll,"Destination %s:%u state: %s set by %u only to %u [%p]",
|
|
pct,route->packed(),route->stateName(),remotePC,onlyPC,this);
|
|
// only forward TRx if we are a STP and not in Restart Phase 1
|
|
if (!(m_transfer && (m_started || m_phase2)))
|
|
return;
|
|
// and during MTP restart only advertise Route Prohibited
|
|
if (route->state() != SS7Route::Prohibited && !m_started)
|
|
return;
|
|
if (m_mngmt && (route->state() != SS7Route::Unknown)) {
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* l3p = static_cast<L3ViewPtr*>(o->get());
|
|
if (!l3p || ((*l3p) == network))
|
|
continue;
|
|
if (!((forced && onlyPC) || (*l3p)->operational()))
|
|
continue;
|
|
for (ObjList* v = l3p->view(type).skipNull(); v; v = v->skipNext()) {
|
|
SS7Route* r = static_cast<SS7Route*>(v->get());
|
|
if (r->packed() != route->packed())
|
|
continue;
|
|
SS7Route::State state = getRouteView(type,r->packed(),0,*l3p);
|
|
if ((r->state() == state) && !forced)
|
|
break;
|
|
DDebug(this,DebugAll,"Route %u of view '%s' changed: %s -> %s",
|
|
r->packed(),(*l3p)->toString().c_str(),
|
|
SS7Route::stateName(r->state()),SS7Route::stateName(state));
|
|
r->m_state = state;
|
|
unsigned int local = (*l3p)->getLocal(type);
|
|
if (!local)
|
|
local = getLocal(type);
|
|
if (!local)
|
|
break;
|
|
// never advertise a local point code from itself
|
|
if (r->packed() == local)
|
|
break;
|
|
const char* cmd = SS7Route::stateName(state);
|
|
v = (*l3p)->getRoutes(type);
|
|
if (v)
|
|
v = v->skipNull();
|
|
for (; v; v = v->skipNext()) {
|
|
r = static_cast<SS7Route*>(v->get());
|
|
if (r->priority() || (r->state() == SS7Route::Prohibited))
|
|
continue;
|
|
if (onlyPC && (r->packed() != onlyPC))
|
|
continue;
|
|
NamedList* ctl = m_mngmt->controlCreate(cmd);
|
|
if (!ctl)
|
|
break;
|
|
String addr;
|
|
addr << pct << "," << SS7PointCode(type,local) << ","
|
|
<< SS7PointCode(type,r->packed());
|
|
Debug(this,DebugInfo,"Advertising Route %s %s %s [%p]",
|
|
dest.c_str(),cmd,addr.c_str(),this);
|
|
ctl->addParam("address",addr);
|
|
ctl->addParam("destination",dest);
|
|
ctl->setParam("automatic",String::boolText(true));
|
|
m_mngmt->controlExecute(ctl);
|
|
}
|
|
break;
|
|
} // route search in view
|
|
} // network iteration
|
|
}
|
|
}
|
|
|
|
// Get the view of a route from a specific outside network
|
|
SS7Route::State SS7Router::getRouteView(SS7PointCode::Type type, unsigned int packedPC,
|
|
unsigned int remotePC, const SS7Layer3* network)
|
|
{
|
|
if (type == SS7PointCode::Other || (unsigned int)type > YSS7_PCTYPE_COUNT || !packedPC)
|
|
return SS7Route::Unknown;
|
|
if (remotePC && !network) {
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
SS7Layer3* l3 = *static_cast<L3ViewPtr*>(o->get());
|
|
if (l3 && !l3->getRoutePriority(type,remotePC)) {
|
|
network = l3;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (network && !network->allowedTo(type,packedPC)) {
|
|
DDebug(this,DebugInfo,"View of %u from %u on %s is Prohibited",
|
|
packedPC,remotePC,network->toString().c_str());
|
|
return SS7Route::Prohibited;
|
|
}
|
|
SS7Route* route = 0;
|
|
if (network)
|
|
route = const_cast<SS7Layer3*>(network)->findRoute(type,packedPC);
|
|
SS7Route::State routeState = route ? route->state() : SS7Route::Unknown;
|
|
unsigned int routePrio = route ? route->priority() : (unsigned int)-1;
|
|
// combine all matching routes not on current network
|
|
SS7Route::State best = SS7Route::Unknown;
|
|
bool thisIsCurrent = (routeState & (SS7Route::NotProhibited|SS7Route::Unknown)) != 0;
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
SS7Layer3* l3 = *static_cast<L3ViewPtr*>(o->get());
|
|
if (!l3 || (l3 == network))
|
|
continue;
|
|
SS7Route::State state;
|
|
if (l3->operational()) {
|
|
SS7Route* r = l3->findRoute(type,packedPC);
|
|
if (!r)
|
|
continue;
|
|
if (r->priority() == routePrio) {
|
|
// sharing - neither is allowed to send through us to the route
|
|
DDebug(this,DebugAll,"Operational '%s' is load sharing with '%s'",
|
|
l3->toString().c_str(),network->toString().c_str());
|
|
best = SS7Route::Prohibited;
|
|
thisIsCurrent = false;
|
|
break;
|
|
}
|
|
state = r->state();
|
|
if ((r->priority() < routePrio || SS7Route::Unknown == routeState) && (state & SS7Route::NotProhibited))
|
|
thisIsCurrent = false;
|
|
DDebug(this,DebugAll,"Operational '%s' contributed state %s",
|
|
l3->toString().c_str(),SS7Route::stateName(state));
|
|
}
|
|
else {
|
|
state = SS7Route::Prohibited;
|
|
DDebug(this,DebugAll,"Non-operational '%s' contributed state %s",
|
|
l3->toString().c_str(),SS7Route::stateName(state));
|
|
}
|
|
if ((state & SS7Route::KnownState) > (best & SS7Route::KnownState))
|
|
best = state;
|
|
}
|
|
if (thisIsCurrent && (routePrio != (unsigned int)-1)) {
|
|
DDebug(this,DebugAll,"Route is current in an alternative set");
|
|
best = SS7Route::Prohibited;
|
|
}
|
|
DDebug(this,DebugInfo,"Route view of %u from %u%s%s: %s",
|
|
packedPC,remotePC,(network ? " on " : ""),
|
|
(network ? network->toString().c_str() : ""),SS7Route::stateName(best));
|
|
return best;
|
|
}
|
|
|
|
void SS7Router::clearView(const SS7Layer3* network)
|
|
{
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* p = static_cast<L3ViewPtr*>(o->get());
|
|
if (!*p || ((*p) != network))
|
|
continue;
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
SS7PointCode::Type type = static_cast<SS7PointCode::Type>(i+1);
|
|
for (ObjList* v = p->view(type).skipNull(); v; v = v->skipNext()) {
|
|
SS7Route* r = static_cast<SS7Route*>(v->get());
|
|
DDebug(this,DebugAll,"Route %u of view '%s' cleared: %s -> Unknown",
|
|
r->packed(),network->toString().c_str(),
|
|
SS7Route::stateName(r->state()));
|
|
r->m_state = SS7Route::Unknown;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Set the state of a route.
|
|
bool SS7Router::setRouteState(SS7PointCode::Type type, unsigned int packedPC, SS7Route::State state,
|
|
unsigned int remotePC, const SS7Layer3* network)
|
|
{
|
|
if (type == SS7PointCode::Other || (unsigned int)type > YSS7_PCTYPE_COUNT || !packedPC)
|
|
return false;
|
|
Lock lock(m_routeMutex);
|
|
SS7Route* route = findRoute(type,packedPC);
|
|
if (!route)
|
|
return false;
|
|
if (state != route->m_state) {
|
|
DDebug(this,DebugAll,"Local route %u/%u changed by %u: %s -> %s",
|
|
packedPC,route->priority(),remotePC,
|
|
SS7Route::stateName(route->state()),SS7Route::stateName(state));
|
|
route->reroute();
|
|
route->m_state = state;
|
|
if (state != SS7Route::Unknown)
|
|
routeChanged(route,type,remotePC,network);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Set the state of a route per source.
|
|
bool SS7Router::setRouteSpecificState(SS7PointCode::Type type, unsigned int packedPC,
|
|
unsigned int srcPC, SS7Route::State state, const SS7Layer3* changer)
|
|
{
|
|
if (type == SS7PointCode::Other || (unsigned int)type > YSS7_PCTYPE_COUNT || !packedPC)
|
|
return false;
|
|
Lock myLock(m_routeMutex);
|
|
SS7Route* route = findRoute(type,packedPC);
|
|
if (!route) {
|
|
Debug(this,DebugNote,"Route to %u advertised by %u not found",packedPC,srcPC);
|
|
return false;
|
|
}
|
|
SS7Route::State best = state;
|
|
bool ok = false;
|
|
for (ObjList* nl = route->m_networks.skipNull(); nl; nl = nl->skipNext()) {
|
|
SS7Layer3* l3 = *static_cast<L3Pointer*>(nl->get());
|
|
if (!l3)
|
|
continue;
|
|
SS7Route* r = l3->findRoute(type,packedPC);
|
|
if (!r) {
|
|
Debug(this,DebugWarn,"Route to %u not found in network '%s'",packedPC,l3->toString().c_str());
|
|
continue;
|
|
}
|
|
ok = true;
|
|
if (l3->getRoutePriority(type,srcPC)) {
|
|
DDebug(this,DebugAll,"Route %u/%u of network '%s' is: %s",
|
|
r->packed(),r->priority(),l3->toString().c_str(),SS7Route::stateName(r->state()));
|
|
if (((r->state() & SS7Route::KnownState) > (best & SS7Route::KnownState)) &&
|
|
l3->operational())
|
|
best = r->state();
|
|
}
|
|
else {
|
|
// srcPC is adjacent STP on this network
|
|
DDebug(this,DebugAll,"Route %u/%u of network '%s' changed: %s -> %s",
|
|
r->packed(),r->priority(),l3->toString().c_str(),
|
|
SS7Route::stateName(r->state()),SS7Route::stateName(state));
|
|
if (r->m_state != state) {
|
|
// controlled reroute for the entire linkset if node is adjacent
|
|
if (!r->priority())
|
|
reroute(l3);
|
|
else
|
|
route->reroute();
|
|
r->m_state = state;
|
|
}
|
|
}
|
|
}
|
|
if (srcPC && !ok) {
|
|
Debug(this,DebugWarn,"Route to %u advertised by %u not found in any network",packedPC,srcPC);
|
|
return false;
|
|
}
|
|
DDebug(this,DebugAll,"Local best route %u/%u changed by %u: %s -> %s",packedPC,
|
|
route->priority(),srcPC,SS7Route::stateName(route->state()),SS7Route::stateName(best));
|
|
// check if an adjacent node has been seen restarting elsewhere
|
|
bool restartElsewhere = srcPC && (srcPC != packedPC) && !route->priority() &&
|
|
(route->state() == SS7Route::Prohibited) && (best & SS7Route::NotProhibited);
|
|
route->m_state = best;
|
|
routeChanged(route,type,srcPC,changer);
|
|
if (restartElsewhere && m_transfer && m_started) {
|
|
DDebug(this,DebugInfo,"Adjacent node %u seen started by %u, sending TFPs",packedPC,srcPC);
|
|
notifyRoutes(SS7Route::Prohibited,packedPC);
|
|
}
|
|
myLock.drop();
|
|
SS7PointCode pc(type);
|
|
if (!pc.unpack(type,packedPC))
|
|
return true;
|
|
lock();
|
|
ListIterator iter(m_layer4);
|
|
while (L4Pointer* p = static_cast<L4Pointer*>(iter.get())) {
|
|
if (p && *p) {
|
|
RefPointer<SS7Layer4> l4 = static_cast<SS7Layer4*>(*p);
|
|
if (!l4)
|
|
continue;
|
|
unlock();
|
|
l4->routeStatusChanged(type,pc,state);
|
|
l4 = 0;
|
|
lock();
|
|
}
|
|
}
|
|
unlock();
|
|
return true;
|
|
}
|
|
|
|
// Send TRA to all or just one network
|
|
void SS7Router::sendRestart(const SS7Layer3* network)
|
|
{
|
|
if (!m_mngmt)
|
|
return;
|
|
DDebug(this,DebugAll,"sendRestart(%p) [%p]",network,this);
|
|
Lock lock(m_routeMutex);
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
SS7PointCode::Type type = static_cast<SS7PointCode::Type>(i+1);
|
|
const ObjList* l = getRoutes(type);
|
|
if (l)
|
|
l = l->skipNull();
|
|
for (; l; l = l->skipNext()) {
|
|
const SS7Route* r = static_cast<const SS7Route*>(l->get());
|
|
// send only to adjacent nodes
|
|
if (r->priority())
|
|
continue;
|
|
unsigned int adjacent = r->packed();
|
|
unsigned int local = getLocal(type);
|
|
for (ObjList* nl = r->m_networks.skipNull(); nl; nl = nl->skipNext()) {
|
|
SS7Layer3* l3 = *static_cast<L3Pointer*>(nl->get());
|
|
if (network && (network != l3))
|
|
continue;
|
|
if (l3->getRoutePriority(type,adjacent))
|
|
continue;
|
|
if (!l3->operational())
|
|
continue;
|
|
unsigned int netLocal = l3->getLocal(type);
|
|
if (!netLocal)
|
|
netLocal = local;
|
|
if (!netLocal)
|
|
continue;
|
|
// use the router's local address at most once
|
|
if (local == netLocal)
|
|
local = 0;
|
|
NamedList* ctl = m_mngmt->controlCreate("restart");
|
|
if (!ctl)
|
|
break;
|
|
String addr;
|
|
addr << SS7PointCode::lookup(type) <<
|
|
"," << SS7PointCode(type,netLocal) <<
|
|
"," << SS7PointCode(type,adjacent);
|
|
DDebug(this,DebugAll,"Sending Restart Allowed %s [%p]",addr.c_str(),this);
|
|
ctl->addParam("address",addr);
|
|
ctl->setParam("automatic",String::boolText(true));
|
|
m_mngmt->controlExecute(ctl);
|
|
if (network)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Send TRA by point code
|
|
void SS7Router::sendRestart(SS7PointCode::Type type, unsigned int packedPC)
|
|
{
|
|
if (!packedPC)
|
|
return;
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
SS7Layer3* l3 = *static_cast<L3ViewPtr*>(o->get());
|
|
if (!l3)
|
|
continue;
|
|
if (!l3->getRoutePriority(type,packedPC)) {
|
|
sendRestart(l3);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mark Allowed routes from which we didn't receive even a TRA
|
|
void SS7Router::silentAllow(const SS7Layer3* network)
|
|
{
|
|
DDebug(this,DebugInfo,"Trying to silently allow %s%s%s [%p]",
|
|
(network ? "'" : "all linksets"),
|
|
(network ? network->toString().c_str() : ""),(network ? "'" : ""),this);
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
SS7Layer3* l3 = *static_cast<L3ViewPtr*>(o->get());
|
|
if (!l3)
|
|
continue;
|
|
if (network && (network != l3))
|
|
continue;
|
|
if (!l3->operational())
|
|
continue;
|
|
SS7MTP3* mtp3 = YOBJECT(SS7MTP3,l3);
|
|
if (mtp3 && !mtp3->linksChecked())
|
|
continue;
|
|
bool noisy = true;
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
SS7PointCode::Type type = static_cast<SS7PointCode::Type>(i+1);
|
|
unsigned int adjacent = 0;
|
|
for (ObjList* l = l3->getRoutes(type); l; l = l->next()) {
|
|
SS7Route* r = static_cast<SS7Route*>(l->get());
|
|
if (!r)
|
|
continue;
|
|
if (!r->priority())
|
|
adjacent = r->packed();
|
|
if (r->state() != SS7Route::Unknown)
|
|
continue;
|
|
if (noisy) {
|
|
Debug(this,DebugNote,"Allowing unknown state routes of '%s' from %u [%p]",
|
|
l3->toString().c_str(),adjacent,this);
|
|
noisy = false;
|
|
}
|
|
setRouteSpecificState(type,r->packed(),adjacent,SS7Route::Allowed,l3);
|
|
if (!r->priority()) {
|
|
notifyRoutes(SS7Route::NotProhibited,r->packed());
|
|
sendRestart(l3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mark Allowed routes by point code
|
|
void SS7Router::silentAllow(SS7PointCode::Type type, unsigned int packedPC)
|
|
{
|
|
if (!packedPC)
|
|
return;
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
SS7Layer3* l3 = *static_cast<L3ViewPtr*>(o->get());
|
|
if (!l3)
|
|
continue;
|
|
if (!l3->getRoutePriority(type,packedPC)) {
|
|
silentAllow(l3);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Send RST and/or RSR to probe for routes left prohibited/restricted
|
|
void SS7Router::sendRouteTest()
|
|
{
|
|
if (!m_mngmt)
|
|
return;
|
|
int cnt = 0;
|
|
Lock lock(m_routeMutex);
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
SS7PointCode::Type type = static_cast<SS7PointCode::Type>(i+1);
|
|
const ObjList* l = getRoutes(type);
|
|
if (l)
|
|
l = l->skipNull();
|
|
for (; l; l = l->skipNext()) {
|
|
const SS7Route* r = static_cast<const SS7Route*>(l->get());
|
|
// adjacent routes are not tested this way
|
|
if (!r->priority())
|
|
continue;
|
|
const char* oper = 0;
|
|
switch (r->state()) {
|
|
case SS7Route::Unknown:
|
|
case SS7Route::Prohibited:
|
|
oper = "test-prohibited";
|
|
break;
|
|
case SS7Route::Restricted:
|
|
if (!m_testRestricted)
|
|
continue;
|
|
oper = "test-restricted";
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
unsigned int local = getLocal(type);
|
|
for (ObjList* nl = r->m_networks.skipNull(); nl; nl = nl->skipNext()) {
|
|
L3Pointer* n = static_cast<L3Pointer*>(nl->get());
|
|
if (!(*n)->operational())
|
|
continue;
|
|
if ((*n)->getRoutePriority(type,r->packed()) == (unsigned int)-1)
|
|
continue;
|
|
unsigned int netLocal = (*n)->getLocal(type);
|
|
if (!netLocal)
|
|
netLocal = local;
|
|
if (!netLocal)
|
|
continue;
|
|
unsigned int remote = 0;
|
|
for (ObjList* l2 = (*n)->getRoutes(type); l2; l2 = l2->next()) {
|
|
const SS7Route* r2 = static_cast<const SS7Route*>(l2->get());
|
|
if (!r2)
|
|
continue;
|
|
if (r2->priority() || (r2->state() != SS7Route::Allowed))
|
|
continue;
|
|
remote = r2->packed();
|
|
break;
|
|
}
|
|
if (!remote)
|
|
continue;
|
|
// use the router's local address at most once
|
|
if (local == netLocal)
|
|
local = 0;
|
|
NamedList* ctl = m_mngmt->controlCreate(oper);
|
|
if (!ctl)
|
|
break;
|
|
String addr;
|
|
addr << SS7PointCode::lookup(type) <<
|
|
"," << SS7PointCode(type,netLocal) <<
|
|
"," << SS7PointCode(type,remote);
|
|
String dest;
|
|
dest << SS7PointCode(type,r->packed());
|
|
DDebug(this,DebugAll,"Sending %s %s %s [%p]",
|
|
oper,dest.c_str(),addr.c_str(),this);
|
|
ctl->addParam("address",addr);
|
|
ctl->addParam("destination",dest);
|
|
ctl->setParam("automatic",String::boolText(true));
|
|
if (m_mngmt->controlExecute(ctl))
|
|
cnt++;
|
|
}
|
|
}
|
|
}
|
|
if (cnt)
|
|
Debug(this,DebugInfo,"Sent %d Route Test messages [%p]",cnt,this);
|
|
}
|
|
|
|
// Check if at least one adjacent route is available, start isolation if not
|
|
void SS7Router::checkRoutes(const SS7Layer3* noResume)
|
|
{
|
|
if (m_isolate.started() || !m_isolate.interval())
|
|
return;
|
|
bool isolated = true;
|
|
Lock lock(m_routeMutex);
|
|
m_checkRoutes = false;
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
SS7PointCode::Type type = static_cast<SS7PointCode::Type>(i+1);
|
|
const ObjList* l = getRoutes(type);
|
|
if (l)
|
|
l = l->skipNull();
|
|
for (; l; l = l->skipNext()) {
|
|
SS7Route* r = static_cast<SS7Route*>(l->get());
|
|
SS7Route::State state = getRouteView(type,r->packed());
|
|
if ((state & (SS7Route::NotProhibited|SS7Route::Unknown)) && !r->priority())
|
|
isolated = false;
|
|
if (r->state() != state) {
|
|
DDebug(this,DebugAll,"Local route %u/%u changed during check: %s -> %s",
|
|
r->packed(),r->priority(),
|
|
SS7Route::stateName(r->state()),SS7Route::stateName(state));
|
|
r->m_state = state;
|
|
routeChanged(r,type,0);
|
|
}
|
|
}
|
|
}
|
|
if (isolated && noResume && (m_started || m_restart.started())) {
|
|
Debug(this,DebugMild,"Node has become isolated! [%p]",this);
|
|
m_isolate.start();
|
|
m_trafficSent.stop();
|
|
// we are in an emergency - uninhibit any possible link
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* p = static_cast<L3ViewPtr*>(o->get());
|
|
SS7Layer3* l3 = *p;
|
|
if ((l3 == noResume) || !l3)
|
|
continue;
|
|
NamedList* ctl = l3->controlCreate("resume");
|
|
if (ctl) {
|
|
ctl->setParam("automatic",String::boolText(true));
|
|
ctl->setParam("emergency",String::boolText(true));
|
|
l3->controlExecute(ctl);
|
|
}
|
|
if (!m_isolate.started())
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear the routes of a linkset that's not in service
|
|
void SS7Router::clearRoutes(SS7Layer3* network, bool ok)
|
|
{
|
|
if (!network)
|
|
return;
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
SS7PointCode::Type type = static_cast<SS7PointCode::Type>(i+1);
|
|
const ObjList* l = network->getRoutes(type);
|
|
if (l)
|
|
l = l->skipNull();
|
|
unsigned int adjacent = 0;
|
|
for (; l; l = l->skipNext()) {
|
|
SS7Route* r = static_cast<SS7Route*>(l->get());
|
|
if (!r->priority())
|
|
adjacent = r->packed();
|
|
if (ok && (r->state() != SS7Route::Prohibited))
|
|
continue;
|
|
// if an adjacent node is operational but not in service we may have a chance
|
|
SS7Route::State state = (ok || !r->priority()) ? SS7Route::Unknown : SS7Route::Prohibited;
|
|
DDebug(DebugInfo,"Clearing route %u/%u of %s by %u to %s",
|
|
r->packed(),r->priority(),network->toString().c_str(),
|
|
adjacent,SS7Route::stateName(state));
|
|
setRouteSpecificState(type,r->packed(),adjacent,state,network);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initiate controlled rerouting on all routes including a linkset
|
|
void SS7Router::reroute(const SS7Layer3* network)
|
|
{
|
|
Lock lock(m_routeMutex);
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
SS7PointCode::Type type = static_cast<SS7PointCode::Type>(i+1);
|
|
const ObjList* l = getRoutes(type);
|
|
if (l)
|
|
l = l->skipNull();
|
|
for (; l; l = l->skipNext()) {
|
|
SS7Route* r = static_cast<SS7Route*>(l->get());
|
|
if (r->hasNetwork(network))
|
|
r->reroute();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if routes have finished controlled rerouting
|
|
void SS7Router::rerouteCheck(const Time& when)
|
|
{
|
|
Lock lock(m_routeMutex);
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
SS7PointCode::Type type = static_cast<SS7PointCode::Type>(i+1);
|
|
const ObjList* l = getRoutes(type);
|
|
if (l)
|
|
l = l->skipNull();
|
|
for (; l; l = l->skipNext())
|
|
static_cast<SS7Route*>(l->get())->rerouteCheck(when);
|
|
}
|
|
}
|
|
|
|
// Flush the controlled rerouting buffer of all routes
|
|
void SS7Router::rerouteFlush()
|
|
{
|
|
Lock lock(m_routeMutex);
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
SS7PointCode::Type type = static_cast<SS7PointCode::Type>(i+1);
|
|
const ObjList* l = getRoutes(type);
|
|
if (l)
|
|
l = l->skipNull();
|
|
for (; l; l = l->skipNext())
|
|
static_cast<SS7Route*>(l->get())->rerouteFlush();
|
|
}
|
|
}
|
|
|
|
bool SS7Router::uninhibit(SS7Layer3* network, int sls, bool remote)
|
|
{
|
|
if (!(network && m_mngmt))
|
|
return false;
|
|
bool ok = false;
|
|
const char* cmd = remote ? "link-force-uninhibit" : "link-uninhibit";
|
|
for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) {
|
|
SS7PointCode::Type type = static_cast<SS7PointCode::Type>(i+1);
|
|
unsigned int local = network->getLocal(type);
|
|
if (!local)
|
|
local = getLocal(type);
|
|
if (!local)
|
|
continue;
|
|
for (const ObjList* o = network->getRoutes(type); o; o = o->next()) {
|
|
const SS7Route* r = static_cast<const SS7Route*>(o->get());
|
|
if (!r || r->priority())
|
|
continue;
|
|
NamedList* ctl = m_mngmt->controlCreate(cmd);
|
|
if (!ctl)
|
|
return false;
|
|
String addr;
|
|
addr << SS7PointCode::lookup(type) <<
|
|
"," << SS7PointCode(type,local) <<
|
|
"," << SS7PointCode(type,r->packed()) <<
|
|
"," << sls;
|
|
DDebug(this,DebugInfo,"Requesting %s %s [%p]",cmd,addr.c_str(),this);
|
|
ctl->addParam("address",addr);
|
|
ctl->setParam("automatic",String::boolText(true));
|
|
m_mngmt->controlExecute(ctl);
|
|
ok = true;
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
bool SS7Router::inhibit(const SS7Label& link, int setFlags, int clrFlags, bool notLast)
|
|
{
|
|
int remote = link.dpc().pack(link.type());
|
|
if (!remote)
|
|
return false;
|
|
Lock mylock(this);
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* p = static_cast<L3ViewPtr*>(o->get());
|
|
if (!*p || (*p)->getRoutePriority(link.type(),remote))
|
|
continue;
|
|
RefPointer<SS7Layer3> net = static_cast<SS7Layer3*>(*p);
|
|
mylock.drop();
|
|
if (notLast && setFlags) {
|
|
const SS7MTP3* mtp3 = YOBJECT(SS7MTP3,net);
|
|
if (mtp3 && (mtp3->linksActive() == 1) && !mtp3->inhibited(link.sls()))
|
|
return false;
|
|
}
|
|
return net->inhibit(link.sls(),setFlags,clrFlags);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SS7Router::inhibited(const SS7Label& link, int flags)
|
|
{
|
|
int remote = link.dpc().pack(link.type());
|
|
if (!remote)
|
|
return false;
|
|
Lock mylock(this);
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* p = static_cast<L3ViewPtr*>(o->get());
|
|
if (!*p || (*p)->getRoutePriority(link.type(),remote))
|
|
continue;
|
|
RefPointer<SS7Layer3> net = static_cast<SS7Layer3*>(*p);
|
|
mylock.drop();
|
|
return net->inhibited(link.sls(),flags);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int SS7Router::getSequence(const SS7Label& link)
|
|
{
|
|
int remote = link.dpc().pack(link.type());
|
|
if (!remote)
|
|
return false;
|
|
Lock mylock(this);
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* p = static_cast<L3ViewPtr*>(o->get());
|
|
if (!*p || (*p)->getRoutePriority(link.type(),remote))
|
|
continue;
|
|
RefPointer<SS7Layer3> net = static_cast<SS7Layer3*>(*p);
|
|
mylock.drop();
|
|
return net->getSequence(link.sls());
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void SS7Router::recoverMSU(const SS7Label& link, int sequence)
|
|
{
|
|
int remote = link.dpc().pack(link.type());
|
|
if (!remote)
|
|
return;
|
|
Lock mylock(this);
|
|
for (ObjList* o = m_layer3.skipNull(); o; o = o->skipNext()) {
|
|
L3ViewPtr* p = static_cast<L3ViewPtr*>(o->get());
|
|
if (!*p || (*p)->getRoutePriority(link.type(),remote))
|
|
continue;
|
|
RefPointer<SS7Layer3> net = static_cast<SS7Layer3*>(*p);
|
|
mylock.drop();
|
|
net->recoverMSU(link.sls(),sequence);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SS7Router::receivedUPU(SS7PointCode::Type type, const SS7PointCode node,
|
|
SS7MSU::Services part, unsigned char cause, const SS7Label& label, int sls)
|
|
{
|
|
// Iterate and notify all User Parts
|
|
lock();
|
|
ListIterator iter(m_layer4);
|
|
while (L4Pointer* p = static_cast<L4Pointer*>(iter.get())) {
|
|
if (p && *p) {
|
|
RefPointer<SS7Layer4> l4 = static_cast<SS7Layer4*>(*p);
|
|
if (!l4)
|
|
continue;
|
|
unlock();
|
|
l4->receivedUPU(type,node,part,cause,label,sls);
|
|
l4 = 0;
|
|
lock();
|
|
}
|
|
}
|
|
unlock();
|
|
}
|
|
|
|
|
|
void SS7Router::notify(SS7Layer3* network, int sls)
|
|
{
|
|
DDebug(this,DebugInfo,"Notified %s on %p sls %d [%p]",
|
|
(network ? (network->operational() ? "net-up" : "net-down") : "no-net"),
|
|
network,sls,this);
|
|
bool useMe = false;
|
|
Lock lock(this);
|
|
if (network) {
|
|
if (network->inService(sls)) {
|
|
if (m_isolate.started()) {
|
|
Debug(this,DebugNote,"Isolation ended before shutting down [%p]",this);
|
|
m_isolate.stop();
|
|
}
|
|
bool tra = true;
|
|
// send TRA only if a link become operational
|
|
if (sls >= 0)
|
|
tra = network->operational(sls);
|
|
if (m_started) {
|
|
if (tra) {
|
|
// send TRA only for the first activated link
|
|
const SS7MTP3* mtp3 = YOBJECT(SS7MTP3,network);
|
|
if (!mtp3 || (mtp3->linksActive() <= 1)) {
|
|
// adjacent point restart
|
|
clearRoutes(network,true);
|
|
if (m_transfer)
|
|
notifyRoutes(SS7Route::Prohibited,network);
|
|
sendRestart(network);
|
|
m_trafficOk.start();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (!m_restart.started())
|
|
restart();
|
|
else if (tra)
|
|
clearRoutes(network,true);
|
|
useMe = true;
|
|
}
|
|
}
|
|
else {
|
|
clearView(network);
|
|
bool oper = network->operational(sls);
|
|
if (sls >= 0)
|
|
oper = oper || network->operational();
|
|
clearRoutes(network,oper);
|
|
checkRoutes(network);
|
|
}
|
|
reroute(network);
|
|
}
|
|
// iterate and notify all user parts
|
|
ObjList* l = &m_layer4;
|
|
for (; l; l = l->next()) {
|
|
L4Pointer* p = static_cast<L4Pointer*>(l->get());
|
|
if (p && *p) {
|
|
SS7Layer4* l4 = *p;
|
|
if (useMe && (l4 != m_mngmt))
|
|
l4->notify(this,-1);
|
|
else
|
|
l4->notify(network,sls);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SS7Router::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 (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_autoAllowed = params.getBoolValue(YSTRING("autoallow"),m_autoAllowed);
|
|
m_sendUnavail = params.getBoolValue(YSTRING("sendupu"),m_sendUnavail);
|
|
m_sendProhibited = params.getBoolValue(YSTRING("sendtfp"),m_sendProhibited);
|
|
if (!m_transfer)
|
|
m_transferSilent = params.getBoolValue(YSTRING("transfersilent"),m_transferSilent);
|
|
String err;
|
|
switch (cmd) {
|
|
case SS7Router::Pause:
|
|
disable();
|
|
return TelEngine::controlReturn(¶ms,true);
|
|
case SS7Router::Resume:
|
|
if (m_started || m_restart.started())
|
|
return TelEngine::controlReturn(¶ms,true);
|
|
// fall through
|
|
case SS7Router::Restart:
|
|
return TelEngine::controlReturn(¶ms,restart());
|
|
case SS7Router::Traffic:
|
|
if (!m_trafficSent.started())
|
|
m_trafficSent.start();
|
|
sendRestart();
|
|
// fall through
|
|
case SS7Router::Status:
|
|
printRoutes();
|
|
printStats();
|
|
return TelEngine::controlReturn(¶ms,operational());
|
|
case SS7Router::Advertise:
|
|
if (!(m_transfer && (m_started || m_phase2)))
|
|
return TelEngine::controlReturn(¶ms,false);
|
|
notifyRoutes();
|
|
return TelEngine::controlReturn(¶ms,true);
|
|
case SS7MsgSNM::RST:
|
|
case SS7MsgSNM::RSR:
|
|
if (!m_started)
|
|
return TelEngine::controlReturn(¶ms,false);
|
|
// fall through
|
|
case SS7MsgSNM::TRA:
|
|
case SS7MsgSNM::TFP:
|
|
case SS7MsgSNM::TFR:
|
|
case SS7MsgSNM::TFA:
|
|
{
|
|
SS7PointCode::Type type = SS7PointCode::lookup(params.getValue(YSTRING("pointcodetype")));
|
|
if (SS7PointCode::length(type) == 0) {
|
|
err << "missing 'pointcodetype'";
|
|
break;
|
|
}
|
|
const String* dest = params.getParam(YSTRING("destination"));
|
|
if (TelEngine::null(dest)) {
|
|
err << "missing 'destination'";
|
|
break;
|
|
}
|
|
SS7PointCode pc;
|
|
if (!pc.assign(*dest,type)) {
|
|
err << "invalid destination: " << *dest ;
|
|
break;
|
|
}
|
|
if (SS7MsgSNM::RST == cmd || SS7MsgSNM::RSR == cmd) {
|
|
const String* addr = params.getParam(YSTRING("back-address"));
|
|
if (TelEngine::null(addr))
|
|
addr = params.getParam(YSTRING("address"));
|
|
if (TelEngine::null(addr)) {
|
|
err = "missing 'address'";
|
|
break;
|
|
}
|
|
SS7PointCode opc;
|
|
ObjList* l = addr->split(',');
|
|
if (l->at(2))
|
|
opc.assign(l->at(2)->toString(),type);
|
|
TelEngine::destruct(l);
|
|
SS7Route::State state = getRouteView(type,pc.pack(type),opc.pack(type));
|
|
if (SS7Route::Unknown == state)
|
|
return TelEngine::controlReturn(¶ms,false);
|
|
if (routeState(static_cast<SS7MsgSNM::Type>(cmd)) == state)
|
|
return TelEngine::controlReturn(¶ms,true);
|
|
// a route state changed, advertise to the adjacent node
|
|
if (!(m_transfer && m_started && m_mngmt))
|
|
return TelEngine::controlReturn(¶ms,false);
|
|
const char* oper = lookup(state,s_dict_states);
|
|
if (!oper)
|
|
return TelEngine::controlReturn(¶ms,false);
|
|
NamedList* ctl = m_mngmt->controlCreate(oper);
|
|
if (!ctl)
|
|
return TelEngine::controlReturn(¶ms,false);
|
|
Debug(this,DebugInfo,"Requesting %s %s to %s [%p]",
|
|
dest->c_str(),oper,addr->c_str(),this);
|
|
ctl->addParam("address",addr->c_str());
|
|
ctl->addParam("destination",*dest);
|
|
ctl->setParam("automatic",String::boolText(true));
|
|
m_mngmt->controlExecute(ctl);
|
|
return TelEngine::controlReturn(¶ms,true);
|
|
}
|
|
String src = params.getParam(YSTRING("source"));
|
|
if (src.null()) {
|
|
const String* addr = params.getParam(YSTRING("address"));
|
|
if (addr) {
|
|
ObjList* l = addr->split(',');
|
|
if (l && l->at(1))
|
|
src = l->at(1)->toString();
|
|
TelEngine::destruct(l);
|
|
}
|
|
}
|
|
if (src) {
|
|
SS7PointCode opc;
|
|
if (!opc.assign(src,type)) {
|
|
if (!params.getBoolValue(YSTRING("automatic")))
|
|
err << "invalid source: " << src ;
|
|
break;
|
|
}
|
|
if (!setRouteSpecificState(type,pc,opc,routeState(static_cast<SS7MsgSNM::Type>(cmd)))) {
|
|
if (!params.getBoolValue(YSTRING("automatic")))
|
|
err << "no such route: " << *dest << " from: " << src;
|
|
break;
|
|
}
|
|
}
|
|
else if (!setRouteState(type,pc,routeState(static_cast<SS7MsgSNM::Type>(cmd)))) {
|
|
if (!params.getBoolValue(YSTRING("automatic")))
|
|
err << "no such route: " << *dest;
|
|
break;
|
|
}
|
|
if (m_started && (SS7MsgSNM::TRA == cmd)) {
|
|
// allow all routes for which TFx was not received before TRA
|
|
silentAllow(type,pc.pack(type));
|
|
// advertise routes and availability to just restarted node
|
|
if (!m_trafficSent.started()) {
|
|
m_trafficSent.start();
|
|
if (m_transfer)
|
|
notifyRoutes(SS7Route::KnownState,pc.pack(type));
|
|
sendRestart(type,pc.pack(type));
|
|
}
|
|
}
|
|
return TelEngine::controlReturn(¶ms,true);
|
|
}
|
|
break;
|
|
case -1:
|
|
break;
|
|
default:
|
|
Debug(this,DebugStub,"Unimplemented control '%s' (%0x02X) [%p]",
|
|
oper->c_str(),cmd,this);
|
|
}
|
|
if (err)
|
|
Debug(this,DebugWarn,"Control error: %s [%p]",err.c_str(),this);
|
|
return TelEngine::controlReturn(¶ms,false);
|
|
}
|
|
|
|
void SS7Router::printStats()
|
|
{
|
|
String tmp;
|
|
m_statsMutex.lock();
|
|
tmp << "Rx=" << (unsigned int)m_rxMsu << ", Tx=" << (unsigned int)m_txMsu;
|
|
tmp << ", Fwd=" << (unsigned int)m_fwdMsu << ", Fail=" << (unsigned int)m_failMsu;
|
|
tmp << ", Cong=" << (unsigned int)m_congestions;
|
|
m_statsMutex.unlock();
|
|
Output("Statistics for '%s': %s",debugName(),tmp.c_str());
|
|
}
|
|
|
|
// Detach management. Call SignallingComponent::detach()
|
|
void SS7Router::destroyed()
|
|
{
|
|
if (m_mngmt)
|
|
detach(m_mngmt);
|
|
SS7Layer3::destroyed();
|
|
}
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|