/** * 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-2013 Null Team * * This software is distributed under multiple licenses; * see the COPYING file in the main directory for licensing * information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include "yatesig.h" #include using namespace TelEngine; typedef GenPointer L3Pointer; typedef GenPointer 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(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(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(o->get()); if (!p) continue; RefPointer l3 = static_cast(*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(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(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(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(iter.get())) { RefPointer l3 = static_cast(*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(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(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(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(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(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(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(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(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(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(o->get()); if (!*p) continue; for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) { SS7PointCode::Type type = static_cast(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(o->get()); if (!*p || ((*p) == network)) continue; for (ObjList* r = (*p)->getRoutes(type); r; r = r->next()) { const SS7Route* route = static_cast(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(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(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 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(l->get()); if (!p) continue; RefPointer l4 = static_cast(*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(iter.get()); if (!route) break; if ((route->state() & states) == 0) continue; routeChanged(route,static_cast(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(i+1)); for (; l; l = l->next()) { const SS7Route* r = static_cast(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(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(iter.get()); if (!route) break; if (!route->detach(network)) { SS7PointCode::Type type = static_cast(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(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(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(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(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(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(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(o->get()); if (!*p || ((*p) != network)) continue; for (unsigned int i = 0; i < YSS7_PCTYPE_COUNT; i++) { SS7PointCode::Type type = static_cast(i+1); for (ObjList* v = p->view(type).skipNull(); v; v = v->skipNext()) { SS7Route* r = static_cast(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(nl->get()); if (!l3) continue; SS7Route* r = l3->findRoute(type,packedPC); if (!r) { Debug(this,DebugGoOn,"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(iter.get())) { if (p && *p) { RefPointer l4 = static_cast(*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(i+1); const ObjList* l = getRoutes(type); if (l) l = l->skipNull(); for (; l; l = l->skipNext()) { const SS7Route* r = static_cast(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(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(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(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(i+1); unsigned int adjacent = 0; for (ObjList* l = l3->getRoutes(type); l; l = l->next()) { SS7Route* r = static_cast(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(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(i+1); const ObjList* l = getRoutes(type); if (l) l = l->skipNull(); for (; l; l = l->skipNext()) { const SS7Route* r = static_cast(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(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(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(i+1); const ObjList* l = getRoutes(type); if (l) l = l->skipNull(); for (; l; l = l->skipNext()) { SS7Route* r = static_cast(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(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(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(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(i+1); const ObjList* l = getRoutes(type); if (l) l = l->skipNull(); for (; l; l = l->skipNext()) { SS7Route* r = static_cast(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(i+1); const ObjList* l = getRoutes(type); if (l) l = l->skipNull(); for (; l; l = l->skipNext()) static_cast(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(i+1); const ObjList* l = getRoutes(type); if (l) l = l->skipNull(); for (; l; l = l->skipNext()) static_cast(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(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(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(o->get()); if (!*p || (*p)->getRoutePriority(link.type(),remote)) continue; RefPointer net = static_cast(*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(o->get()); if (!*p || (*p)->getRoutePriority(link.type(),remote)) continue; RefPointer net = static_cast(*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(o->get()); if (!*p || (*p)->getRoutePriority(link.type(),remote)) continue; RefPointer net = static_cast(*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(o->get()); if (!*p || (*p)->getRoutePriority(link.type(),remote)) continue; RefPointer net = static_cast(*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(iter.get())) { if (p && *p) { RefPointer l4 = static_cast(*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(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(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(cmd)))) { if (!params.getBoolValue(YSTRING("automatic"))) err << "no such route: " << *dest << " from: " << src; break; } } else if (!setRouteState(type,pc,routeState(static_cast(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: */