yate/libs/ysig/router.cpp

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"),&params,"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",
&params,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 = &params;
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(&params,true);
case SS7Router::Resume:
if (m_started || m_restart.started())
return TelEngine::controlReturn(&params,true);
// fall through
case SS7Router::Restart:
return TelEngine::controlReturn(&params,restart());
case SS7Router::Traffic:
if (!m_trafficSent.started())
m_trafficSent.start();
sendRestart();
// fall through
case SS7Router::Status:
printRoutes();
printStats();
return TelEngine::controlReturn(&params,operational());
case SS7Router::Advertise:
if (!(m_transfer && (m_started || m_phase2)))
return TelEngine::controlReturn(&params,false);
notifyRoutes();
return TelEngine::controlReturn(&params,true);
case SS7MsgSNM::RST:
case SS7MsgSNM::RSR:
if (!m_started)
return TelEngine::controlReturn(&params,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(&params,false);
if (routeState(static_cast<SS7MsgSNM::Type>(cmd)) == state)
return TelEngine::controlReturn(&params,true);
// a route state changed, advertise to the adjacent node
if (!(m_transfer && m_started && m_mngmt))
return TelEngine::controlReturn(&params,false);
const char* oper = lookup(state,s_dict_states);
if (!oper)
return TelEngine::controlReturn(&params,false);
NamedList* ctl = m_mngmt->controlCreate(oper);
if (!ctl)
return TelEngine::controlReturn(&params,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(&params,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(&params,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(&params,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: */