2006-04-18 22:31:16 +00:00
|
|
|
/**
|
|
|
|
* layer2.cpp
|
2014-02-05 11:42:17 +00:00
|
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
2006-04-18 22:31:16 +00:00
|
|
|
*
|
2007-11-26 13:01:04 +00:00
|
|
|
* Yet Another Signalling Stack - implements the support for SS7, ISDN and PSTN
|
|
|
|
*
|
2006-04-18 22:31:16 +00:00
|
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
2014-02-05 11:42:17 +00:00
|
|
|
* Copyright (C) 2004-2014 Null Team
|
2006-04-18 22:31:16 +00:00
|
|
|
*
|
2013-08-06 13:38:10 +00:00
|
|
|
* 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.
|
2006-04-18 22:31:16 +00:00
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2013-08-06 13:38:10 +00:00
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
2006-04-18 22:31:16 +00:00
|
|
|
*/
|
|
|
|
|
2007-11-26 13:01:04 +00:00
|
|
|
#include "yatesig.h"
|
2009-07-28 10:51:50 +00:00
|
|
|
#include <yatephone.h>
|
2007-11-26 13:50:52 +00:00
|
|
|
#include <string.h>
|
2006-04-18 22:31:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
using namespace TelEngine;
|
|
|
|
|
2009-07-28 10:51:50 +00:00
|
|
|
static const TokenDict s_dict_prio[] = {
|
|
|
|
{ "regular", SS7MSU::Regular },
|
|
|
|
{ "special", SS7MSU::Special },
|
|
|
|
{ "circuit", SS7MSU::Circuit },
|
|
|
|
{ "facility", SS7MSU::Facility },
|
|
|
|
{ 0, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const TokenDict s_dict_netind[] = {
|
|
|
|
{ "international", SS7MSU::International },
|
|
|
|
{ "spareinternational", SS7MSU::SpareInternational },
|
|
|
|
{ "national", SS7MSU::National },
|
|
|
|
{ "reservednational", SS7MSU::ReservedNational },
|
|
|
|
{ 0, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const TokenDict s_dict_control[] = {
|
|
|
|
{ "pause", SS7Layer2::Pause },
|
|
|
|
{ "resume", SS7Layer2::Resume },
|
|
|
|
{ "align", SS7Layer2::Align },
|
|
|
|
{ 0, 0 }
|
|
|
|
};
|
2007-11-26 13:50:52 +00:00
|
|
|
|
|
|
|
SS7MSU::SS7MSU(unsigned char sio, const SS7Label label, void* value, unsigned int len)
|
|
|
|
{
|
|
|
|
DataBlock::assign(0,1 + label.length() + len);
|
|
|
|
unsigned char* d = (unsigned char*)data();
|
|
|
|
*d++ = sio;
|
|
|
|
label.store(d);
|
|
|
|
d += label.length();
|
|
|
|
if (value && len)
|
|
|
|
::memcpy(d,value,len);
|
|
|
|
}
|
|
|
|
|
|
|
|
SS7MSU::SS7MSU(unsigned char sif, unsigned char ssf, const SS7Label label, void* value, unsigned int len)
|
|
|
|
{
|
|
|
|
DataBlock::assign(0,1 + label.length() + len);
|
|
|
|
unsigned char* d = (unsigned char*)data();
|
|
|
|
*d++ = (sif & 0x0f) | (ssf & 0xf0);
|
|
|
|
label.store(d);
|
|
|
|
d += label.length();
|
|
|
|
if (value && len)
|
|
|
|
::memcpy(d,value,len);
|
|
|
|
}
|
|
|
|
|
2006-05-02 15:50:45 +00:00
|
|
|
SS7MSU::~SS7MSU()
|
2006-04-29 11:51:39 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2006-05-02 15:50:45 +00:00
|
|
|
bool SS7MSU::valid() const
|
|
|
|
{
|
|
|
|
return (3 < length()) && (length() < 273);
|
|
|
|
}
|
|
|
|
|
2006-06-03 16:10:54 +00:00
|
|
|
#define CASE_STR(x) case x: return #x
|
|
|
|
const char* SS7MSU::getServiceName() const
|
|
|
|
{
|
|
|
|
switch (getSIF()) {
|
|
|
|
CASE_STR(SNM);
|
|
|
|
CASE_STR(MTN);
|
|
|
|
CASE_STR(MTNS);
|
|
|
|
CASE_STR(SCCP);
|
|
|
|
CASE_STR(TUP);
|
|
|
|
CASE_STR(ISUP);
|
|
|
|
CASE_STR(DUP_C);
|
|
|
|
CASE_STR(DUP_F);
|
|
|
|
CASE_STR(MTP_T);
|
|
|
|
CASE_STR(BISUP);
|
|
|
|
CASE_STR(SISUP);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* SS7MSU::getPriorityName() const
|
|
|
|
{
|
|
|
|
switch (getPrio()) {
|
|
|
|
CASE_STR(Regular);
|
|
|
|
CASE_STR(Special);
|
|
|
|
CASE_STR(Circuit);
|
|
|
|
CASE_STR(Facility);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* SS7MSU::getIndicatorName() const
|
|
|
|
{
|
|
|
|
switch (getNI()) {
|
|
|
|
CASE_STR(International);
|
|
|
|
CASE_STR(SpareInternational);
|
|
|
|
CASE_STR(National);
|
|
|
|
CASE_STR(ReservedNational);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#undef CASE_STR
|
|
|
|
|
2007-11-26 13:50:52 +00:00
|
|
|
unsigned char SS7MSU::getPriority(const char* name, unsigned char defVal)
|
|
|
|
{
|
|
|
|
return (unsigned char)lookup(name,s_dict_prio,defVal);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char SS7MSU::getNetIndicator(const char* name, unsigned char defVal)
|
|
|
|
{
|
|
|
|
return (unsigned char)lookup(name,s_dict_netind,defVal);
|
|
|
|
}
|
|
|
|
|
2006-05-02 15:50:45 +00:00
|
|
|
|
2006-06-04 18:05:02 +00:00
|
|
|
void SS7Layer2::attach(SS7L2User* l2user)
|
|
|
|
{
|
2007-11-26 13:50:52 +00:00
|
|
|
Lock lock(m_l2userMutex);
|
2006-06-04 18:05:02 +00:00
|
|
|
if (m_l2user == l2user)
|
|
|
|
return;
|
2007-11-26 13:50:52 +00:00
|
|
|
SS7L2User* tmp = m_l2user;
|
2006-06-04 18:05:02 +00:00
|
|
|
m_l2user = l2user;
|
2007-11-26 13:50:52 +00:00
|
|
|
lock.drop();
|
|
|
|
if (tmp) {
|
|
|
|
const char* name = 0;
|
|
|
|
if (engine() && engine()->find(tmp)) {
|
|
|
|
name = tmp->toString().safe();
|
|
|
|
tmp->detach(this);
|
|
|
|
}
|
|
|
|
Debug(this,DebugAll,"Detached L2 user (%p,'%s') [%p]",tmp,name,this);
|
|
|
|
}
|
2006-06-04 18:05:02 +00:00
|
|
|
if (!l2user)
|
|
|
|
return;
|
2007-11-26 13:50:52 +00:00
|
|
|
Debug(this,DebugAll,"Attached L2 user (%p,'%s') [%p]",
|
|
|
|
l2user,l2user->toString().safe(),this);
|
2006-06-04 18:05:02 +00:00
|
|
|
insert(l2user);
|
|
|
|
l2user->attach(this);
|
|
|
|
}
|
|
|
|
|
2011-07-12 09:17:08 +00:00
|
|
|
void SS7Layer2::timerTick(const Time& when)
|
|
|
|
{
|
|
|
|
SignallingComponent::timerTick(when);
|
2012-03-02 16:01:45 +00:00
|
|
|
if (!m_l2userMutex.lock(SignallingEngine::maxLockWait()))
|
|
|
|
return;
|
2011-07-12 09:17:08 +00:00
|
|
|
RefPointer<SS7L2User> tmp = m_notify ? m_l2user : 0;
|
|
|
|
m_notify = false;
|
|
|
|
m_l2userMutex.unlock();
|
|
|
|
if (tmp) {
|
|
|
|
XDebug(this,DebugAll,"SS7Layer2 notifying user [%p]",this);
|
|
|
|
tmp->notify(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-28 16:39:34 +00:00
|
|
|
void SS7Layer2::notify()
|
|
|
|
{
|
2012-03-15 16:03:22 +00:00
|
|
|
unsigned int wasUp = 0;
|
|
|
|
bool doNotify = false;
|
|
|
|
if (!operational()) {
|
|
|
|
wasUp = upTime();
|
2011-03-28 16:39:34 +00:00
|
|
|
m_lastUp = 0;
|
2012-03-15 16:03:22 +00:00
|
|
|
doNotify = (wasUp != 0);
|
|
|
|
}
|
|
|
|
else if (!m_lastUp) {
|
2011-03-28 16:39:34 +00:00
|
|
|
m_lastUp = Time::secNow();
|
2012-03-15 16:03:22 +00:00
|
|
|
doNotify = true;
|
|
|
|
}
|
2011-03-28 16:39:34 +00:00
|
|
|
m_l2userMutex.lock();
|
2011-07-12 09:17:08 +00:00
|
|
|
m_notify = true;
|
2011-03-28 16:39:34 +00:00
|
|
|
m_l2userMutex.unlock();
|
2012-03-15 16:03:22 +00:00
|
|
|
if (doNotify && engine()) {
|
2012-06-07 08:28:59 +00:00
|
|
|
String text(statusName());
|
|
|
|
if (wasUp)
|
|
|
|
text << ", was up " << wasUp;
|
2012-03-15 16:03:22 +00:00
|
|
|
NamedList params("");
|
|
|
|
params.addParam("from",toString());
|
|
|
|
params.addParam("type","ss7-layer2");
|
|
|
|
params.addParam("operational",String::boolText(operational()));
|
2012-06-07 08:28:59 +00:00
|
|
|
params.addParam("text",text);
|
2012-03-15 16:03:22 +00:00
|
|
|
engine()->notify(this,params);
|
|
|
|
}
|
2011-03-28 16:39:34 +00:00
|
|
|
}
|
|
|
|
|
2006-05-02 15:50:45 +00:00
|
|
|
unsigned int SS7Layer2::status() const
|
|
|
|
{
|
|
|
|
return ProcessorOutage;
|
|
|
|
}
|
|
|
|
|
2006-06-03 16:10:54 +00:00
|
|
|
const char* SS7Layer2::statusName(unsigned int status, bool brief) const
|
|
|
|
{
|
|
|
|
switch (status) {
|
|
|
|
case OutOfAlignment:
|
|
|
|
return brief ? "O" : "Out Of Alignment";
|
|
|
|
case NormalAlignment:
|
|
|
|
return brief ? "N" : "Normal Alignment";
|
|
|
|
case EmergencyAlignment:
|
|
|
|
return brief ? "E" : "Emergency Alignment";
|
|
|
|
case OutOfService:
|
|
|
|
return brief ? "OS" : "Out Of Service";
|
|
|
|
case ProcessorOutage:
|
|
|
|
return brief ? "PO" : "Processor Outage";
|
|
|
|
case Busy:
|
|
|
|
return brief ? "B" : "Busy";
|
|
|
|
default:
|
|
|
|
return brief ? "?" : "Unknown Status";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-02 15:50:45 +00:00
|
|
|
bool SS7Layer2::control(Operation oper, NamedList* params)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-07-28 10:51:50 +00:00
|
|
|
bool SS7Layer2::control(NamedList& params)
|
|
|
|
{
|
2011-06-03 14:25:17 +00:00
|
|
|
String* ret = params.getParam(YSTRING("completion"));
|
|
|
|
const String* oper = params.getParam(YSTRING("operation"));
|
|
|
|
const char* cmp = params.getValue(YSTRING("component"));
|
2009-07-28 10:51:50 +00:00
|
|
|
int cmd = oper ? oper->toInteger(s_dict_control,-1) : -1;
|
|
|
|
if (ret) {
|
|
|
|
if (oper && (cmd < 0))
|
|
|
|
return false;
|
2011-06-03 14:25:17 +00:00
|
|
|
String part = params.getValue(YSTRING("partword"));
|
2009-07-28 10:51:50 +00:00
|
|
|
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);
|
|
|
|
}
|
2009-08-25 21:07:34 +00:00
|
|
|
if (!(cmp && toString() == cmp))
|
|
|
|
return false;
|
2013-02-08 15:23:06 +00:00
|
|
|
return TelEngine::controlReturn(¶ms,(cmd >= 0) && control((Operation)cmd,¶ms));
|
2009-07-28 10:51:50 +00:00
|
|
|
}
|
|
|
|
|
2010-08-24 05:47:12 +00:00
|
|
|
bool SS7Layer2::getEmergency(NamedList* params, bool emg) const
|
|
|
|
{
|
2010-08-24 10:35:31 +00:00
|
|
|
if (m_autoEmergency && !emg) {
|
2010-08-24 05:47:12 +00:00
|
|
|
const SS7MTP3* mtp3 = YOBJECT(SS7MTP3,m_l2user);
|
|
|
|
if (mtp3 && !mtp3->linksActive())
|
|
|
|
emg = true;
|
|
|
|
}
|
|
|
|
if (params)
|
2011-06-03 14:25:17 +00:00
|
|
|
emg = params->getBoolValue(YSTRING("emergency"),emg);
|
2010-08-24 05:47:12 +00:00
|
|
|
return emg;
|
|
|
|
}
|
|
|
|
|
2010-08-27 09:58:46 +00:00
|
|
|
bool SS7Layer2::inhibit(int setFlags, int clrFlags)
|
|
|
|
{
|
|
|
|
int old = m_inhibited;
|
|
|
|
m_inhibited = (m_inhibited | setFlags) & ~clrFlags;
|
2010-10-19 04:06:18 +00:00
|
|
|
if (old != m_inhibited || (setFlags & clrFlags)) {
|
2010-08-30 07:04:21 +00:00
|
|
|
bool cycle = (setFlags & Inactive) && operational();
|
|
|
|
if (cycle)
|
|
|
|
control(Pause);
|
2010-08-27 12:22:41 +00:00
|
|
|
Debug(this,DebugNote,"Link inhibition changed 0x%02X -> 0x%02X [%p]",
|
|
|
|
old,m_inhibited,this);
|
2010-08-31 09:42:55 +00:00
|
|
|
if (operational())
|
2010-08-27 12:22:41 +00:00
|
|
|
notify();
|
2010-08-30 07:04:21 +00:00
|
|
|
if (cycle)
|
|
|
|
control(Resume);
|
2010-08-27 12:22:41 +00:00
|
|
|
}
|
2010-08-27 09:58:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-05-02 15:50:45 +00:00
|
|
|
|
2007-11-26 13:50:52 +00:00
|
|
|
SS7MTP2::SS7MTP2(const NamedList& params, unsigned int status)
|
2012-06-07 10:27:45 +00:00
|
|
|
: SignallingComponent(params.safe("SS7MTP2"),¶ms,"ss7-mtp2"),
|
2009-05-26 13:43:24 +00:00
|
|
|
SignallingDumpable(SignallingDumper::Mtp2),
|
|
|
|
Mutex(true,"SS7MTP2"),
|
2006-06-03 16:10:54 +00:00
|
|
|
m_status(status), m_lStatus(OutOfService), m_rStatus(OutOfAlignment),
|
2008-10-29 13:45:50 +00:00
|
|
|
m_interval(0), m_resend(0), m_abort(0), m_fillTime(0), m_congestion(false),
|
2007-11-26 13:50:52 +00:00
|
|
|
m_bsn(127), m_fsn(127), m_bib(true), m_fib(true),
|
2011-04-29 15:27:27 +00:00
|
|
|
m_lastFsn(128), m_lastBsn(127), m_lastBib(true), m_errors(0), m_maxErrors(64),
|
2011-02-04 11:43:43 +00:00
|
|
|
m_resendMs(250), m_abortMs(5000), m_fillIntervalMs(20), m_fillLink(true),
|
2011-02-04 14:18:56 +00:00
|
|
|
m_autostart(false), m_flushMsus(true)
|
2007-11-26 13:50:52 +00:00
|
|
|
{
|
2009-05-26 13:43:24 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (debugAt(DebugAll)) {
|
|
|
|
String tmp;
|
|
|
|
params.dump(tmp,"\r\n ",'\'',true);
|
|
|
|
Debug(this,DebugAll,"SS7MTP2::SS7MTP2(%p,%s) [%p]%s",
|
|
|
|
¶ms,statusName(true),this,tmp.c_str());
|
|
|
|
}
|
|
|
|
#endif
|
2011-06-03 14:25:17 +00:00
|
|
|
m_fillLink = params.getBoolValue(YSTRING("filllink"),m_fillLink);
|
|
|
|
m_maxErrors = params.getIntValue(YSTRING("maxerrors"),64);
|
2011-04-29 15:27:27 +00:00
|
|
|
if (m_maxErrors < 8)
|
|
|
|
m_maxErrors = 8;
|
|
|
|
else if (m_maxErrors > 256)
|
|
|
|
m_maxErrors = 256;
|
2011-06-03 14:25:17 +00:00
|
|
|
setDumper(params.getValue(YSTRING("layer2dump")));
|
2007-11-26 13:50:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SS7MTP2::~SS7MTP2()
|
2006-05-02 15:50:45 +00:00
|
|
|
{
|
2007-11-26 13:50:52 +00:00
|
|
|
setDumper();
|
2006-05-02 15:50:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int SS7MTP2::status() const
|
|
|
|
{
|
2006-06-03 16:10:54 +00:00
|
|
|
return m_lStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SS7MTP2::setLocalStatus(unsigned int status)
|
|
|
|
{
|
|
|
|
if (status == m_lStatus)
|
|
|
|
return;
|
2007-11-26 13:50:52 +00:00
|
|
|
DDebug(this,DebugInfo,"Local status change: %s -> %s [%p]",
|
2006-06-03 16:10:54 +00:00
|
|
|
statusName(m_lStatus,true),statusName(status,true),this);
|
|
|
|
m_lStatus = status;
|
2008-10-29 13:45:50 +00:00
|
|
|
m_fillTime = 0;
|
2006-06-03 16:10:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SS7MTP2::setRemoteStatus(unsigned int status)
|
|
|
|
{
|
|
|
|
if (status == m_rStatus)
|
|
|
|
return;
|
2007-11-26 13:50:52 +00:00
|
|
|
DDebug(this,DebugInfo,"Remote status change: %s -> %s [%p]",
|
2006-06-03 16:10:54 +00:00
|
|
|
statusName(m_rStatus,true),statusName(status,true),this);
|
|
|
|
m_rStatus = status;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SS7MTP2::aligned() const
|
|
|
|
{
|
|
|
|
return ((m_lStatus == NormalAlignment) || (m_lStatus == EmergencyAlignment)) &&
|
|
|
|
((m_rStatus == NormalAlignment) || (m_rStatus == EmergencyAlignment));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SS7MTP2::operational() const
|
|
|
|
{
|
|
|
|
return aligned() && !m_interval;
|
2006-05-02 15:50:45 +00:00
|
|
|
}
|
|
|
|
|
2009-05-26 13:43:24 +00:00
|
|
|
bool SS7MTP2::initialize(const NamedList* config)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
String tmp;
|
|
|
|
if (config && debugAt(DebugAll))
|
|
|
|
config->dump(tmp,"\r\n ",'\'',true);
|
|
|
|
Debug(this,DebugInfo,"SS7MTP2::initialize(%p) [%p]%s",config,this,tmp.c_str());
|
|
|
|
#endif
|
2010-08-24 10:35:31 +00:00
|
|
|
if (config) {
|
2011-06-03 14:25:17 +00:00
|
|
|
debugLevel(config->getIntValue(YSTRING("debuglevel_mtp2"),
|
|
|
|
config->getIntValue(YSTRING("debuglevel"),-1)));
|
|
|
|
m_autoEmergency = config->getBoolValue(YSTRING("autoemergency"),true);
|
|
|
|
unsigned int maxErrors = config->getIntValue(YSTRING("maxerrors"),m_maxErrors);
|
2011-04-29 15:27:27 +00:00
|
|
|
if (maxErrors < 8)
|
|
|
|
m_maxErrors = 8;
|
|
|
|
else if (maxErrors > 256)
|
|
|
|
m_maxErrors = 256;
|
|
|
|
else
|
|
|
|
m_maxErrors = maxErrors;
|
2010-08-24 10:35:31 +00:00
|
|
|
}
|
2011-06-03 14:25:17 +00:00
|
|
|
m_autostart = !config || config->getBoolValue(YSTRING("autostart"),true);
|
|
|
|
m_flushMsus = !config || config->getBoolValue(YSTRING("flushmsus"),true);
|
2009-05-26 13:43:24 +00:00
|
|
|
if (config && !iface()) {
|
2012-11-22 08:50:51 +00:00
|
|
|
NamedList params("");
|
|
|
|
if (resolveConfig(YSTRING("sig"),params,config) ||
|
|
|
|
resolveConfig(YSTRING("basename"),params,config)) {
|
|
|
|
params.addParam("basename",params);
|
2009-05-26 13:43:24 +00:00
|
|
|
params.addParam("protocol","ss7");
|
2012-11-22 08:50:51 +00:00
|
|
|
int rx = params.getIntValue(YSTRING("rxunderrun"));
|
|
|
|
if ((rx > 0) && (rx < 25))
|
|
|
|
params.setParam("rxunderrun","25");
|
2009-05-26 13:43:24 +00:00
|
|
|
SignallingInterface* ifc = YSIGCREATE(SignallingInterface,¶ms);
|
|
|
|
if (!ifc)
|
|
|
|
return false;
|
|
|
|
SignallingReceiver::attach(ifc);
|
2012-11-22 08:50:51 +00:00
|
|
|
if (!(ifc->initialize(¶ms) && control((Operation)SignallingInterface::Enable,¶ms)))
|
2009-05-26 13:43:24 +00:00
|
|
|
TelEngine::destruct(SignallingReceiver::attach(0));
|
|
|
|
}
|
|
|
|
}
|
2011-02-04 11:43:43 +00:00
|
|
|
return iface() && control(Resume,const_cast<NamedList*>(config));
|
2009-05-26 13:43:24 +00:00
|
|
|
}
|
|
|
|
|
2006-05-02 15:50:45 +00:00
|
|
|
bool SS7MTP2::control(Operation oper, NamedList* params)
|
|
|
|
{
|
2009-08-11 21:57:56 +00:00
|
|
|
if (params) {
|
|
|
|
lock();
|
2011-06-03 14:25:17 +00:00
|
|
|
m_fillLink = params->getBoolValue(YSTRING("filllink"),m_fillLink);
|
|
|
|
m_autoEmergency = params->getBoolValue(YSTRING("autoemergency"),m_autoEmergency);
|
|
|
|
m_autostart = params->getBoolValue(YSTRING("autostart"),m_autostart);
|
|
|
|
m_flushMsus = params->getBoolValue(YSTRING("flushmsus"),m_flushMsus);
|
|
|
|
unsigned int maxErrors = params->getIntValue(YSTRING("maxerrors"),m_maxErrors);
|
2011-04-29 15:27:27 +00:00
|
|
|
if (maxErrors < 8)
|
|
|
|
m_maxErrors = 8;
|
|
|
|
else if (maxErrors > 256)
|
|
|
|
m_maxErrors = 256;
|
|
|
|
else
|
|
|
|
m_maxErrors = maxErrors;
|
2009-08-11 21:57:56 +00:00
|
|
|
// The following are for test purposes
|
2011-06-03 14:25:17 +00:00
|
|
|
if (params->getBoolValue(YSTRING("toggle-bib")))
|
2009-08-11 21:57:56 +00:00
|
|
|
m_bib = !m_bib;
|
2011-06-03 14:25:17 +00:00
|
|
|
if (params->getBoolValue(YSTRING("toggle-fib")))
|
2009-08-11 21:57:56 +00:00
|
|
|
m_fib = !m_fib;
|
2011-06-03 14:25:17 +00:00
|
|
|
int tmp = params->getIntValue(YSTRING("change-fsn"));
|
2009-08-11 21:57:56 +00:00
|
|
|
if (tmp)
|
|
|
|
m_fsn = (m_fsn + tmp) & 0x7f;
|
|
|
|
unlock();
|
2011-06-03 14:25:17 +00:00
|
|
|
tmp = params->getIntValue(YSTRING("send-lssu"),-1);
|
2009-08-11 21:57:56 +00:00
|
|
|
if (tmp >= 0)
|
|
|
|
transmitLSSU(tmp);
|
2011-06-03 14:25:17 +00:00
|
|
|
if (params->getBoolValue(YSTRING("send-fisu")))
|
2009-08-11 21:57:56 +00:00
|
|
|
transmitFISU();
|
2011-06-03 14:25:17 +00:00
|
|
|
if (params->getBoolValue(YSTRING("simulate-error")))
|
2011-04-29 15:27:27 +00:00
|
|
|
notify(SignallingInterface::HardwareError);
|
2009-08-11 21:57:56 +00:00
|
|
|
}
|
2006-05-02 15:50:45 +00:00
|
|
|
switch (oper) {
|
|
|
|
case Pause:
|
2011-02-04 11:43:43 +00:00
|
|
|
abortAlignment(false);
|
2013-02-08 15:23:06 +00:00
|
|
|
return TelEngine::controlReturn(params,true);
|
2006-05-02 15:50:45 +00:00
|
|
|
case Resume:
|
2011-02-04 11:43:43 +00:00
|
|
|
if (aligned() || !m_autostart)
|
2013-02-08 15:23:06 +00:00
|
|
|
return TelEngine::controlReturn(params,true);
|
2006-05-11 15:39:33 +00:00
|
|
|
// fall-through
|
2006-05-02 15:50:45 +00:00
|
|
|
case Align:
|
2010-08-24 05:47:12 +00:00
|
|
|
startAlignment(getEmergency(params));
|
2013-02-08 15:23:06 +00:00
|
|
|
return TelEngine::controlReturn(params,true);
|
2006-05-02 15:50:45 +00:00
|
|
|
case Status:
|
2013-02-08 15:23:06 +00:00
|
|
|
return TelEngine::controlReturn(params,operational());
|
2006-05-02 15:50:45 +00:00
|
|
|
default:
|
|
|
|
return SignallingReceiver::control((SignallingInterface::Operation)oper,params);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-26 13:50:52 +00:00
|
|
|
bool SS7MTP2::notify(SignallingInterface::Notification event)
|
|
|
|
{
|
|
|
|
switch (event) {
|
|
|
|
case SignallingInterface::LinkDown:
|
|
|
|
Debug(this,DebugWarn,"Interface is down - realigning [%p]",this);
|
2011-02-04 11:43:43 +00:00
|
|
|
abortAlignment(m_autostart);
|
2007-11-26 13:50:52 +00:00
|
|
|
break;
|
|
|
|
case SignallingInterface::LinkUp:
|
|
|
|
Debug(this,DebugInfo,"Interface is up [%p]",this);
|
2011-02-04 14:18:56 +00:00
|
|
|
control(Resume);
|
2007-11-26 13:50:52 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
XDebug(this,DebugMild,"Got error %u: %s [%p]",
|
|
|
|
event,lookup(event,SignallingInterface::s_notifName),this);
|
2011-04-29 15:27:27 +00:00
|
|
|
{
|
|
|
|
unsigned int err = (m_errors += 256) >> 8;
|
|
|
|
if (err >= (operational() ? m_maxErrors :
|
|
|
|
((m_rStatus == EmergencyAlignment) ? 1 : 4))) {
|
|
|
|
Debug(this,DebugWarn,"Got %u errors - realigning [%p]",err,this);
|
|
|
|
abortAlignment(m_autostart);
|
|
|
|
}
|
2007-11-26 13:50:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-06-02 18:16:03 +00:00
|
|
|
void SS7MTP2::timerTick(const Time& when)
|
|
|
|
{
|
2011-07-12 09:17:08 +00:00
|
|
|
SS7Layer2::timerTick(when);
|
2012-03-02 16:01:45 +00:00
|
|
|
if (!lock(SignallingEngine::maxLockWait()))
|
|
|
|
return;
|
2006-06-03 16:10:54 +00:00
|
|
|
bool tout = m_interval && (when >= m_interval);
|
|
|
|
if (tout)
|
|
|
|
m_interval = 0;
|
2007-11-26 13:50:52 +00:00
|
|
|
bool aborting = m_abort && (when >= m_abort);
|
|
|
|
if (aborting)
|
|
|
|
m_abort = m_resend = 0;
|
|
|
|
bool resend = m_resend && (when >= m_resend);
|
|
|
|
if (resend)
|
|
|
|
m_resend = 0;
|
2006-06-03 16:10:54 +00:00
|
|
|
unlock();
|
2007-11-26 13:50:52 +00:00
|
|
|
if (aborting) {
|
|
|
|
Debug(this,DebugWarn,"Timeout for MSU acknowledgement, realigning [%p]",this);
|
2011-02-04 11:43:43 +00:00
|
|
|
abortAlignment(m_autostart);
|
2007-11-26 13:50:52 +00:00
|
|
|
return;
|
|
|
|
}
|
2006-06-03 16:10:54 +00:00
|
|
|
if (operational()) {
|
2006-06-08 18:31:00 +00:00
|
|
|
if (tout) {
|
2007-11-26 13:50:52 +00:00
|
|
|
Debug(this,DebugInfo,"Proving period ended, link operational [%p]",this);
|
|
|
|
lock();
|
2010-08-21 18:21:49 +00:00
|
|
|
m_lastSeqRx = -1;
|
2007-11-26 13:50:52 +00:00
|
|
|
unsigned int q = m_queue.count();
|
2011-02-04 14:18:56 +00:00
|
|
|
if (!q)
|
|
|
|
;
|
|
|
|
else if (m_flushMsus || q >= 64) {
|
2007-11-26 13:50:52 +00:00
|
|
|
// there shouldn't have been that many queued MSUs
|
|
|
|
Debug(this,DebugWarn,"Cleaning %u queued MSUs from proved link! [%p]",q,this);
|
|
|
|
m_queue.clear();
|
|
|
|
}
|
2011-02-04 14:18:56 +00:00
|
|
|
else {
|
2008-11-07 11:38:21 +00:00
|
|
|
Debug(this,DebugNote,"Changing FSN of %u MSUs queued in proved link! [%p]",q,this);
|
2007-11-26 13:50:52 +00:00
|
|
|
// transmit a FISU just before the bunch of MSUs
|
|
|
|
transmitFISU();
|
|
|
|
resend = true;
|
|
|
|
// reset the FSN of packets still waiting in queue
|
2008-11-07 11:38:21 +00:00
|
|
|
m_lastBsn = m_fsn;
|
2007-11-26 13:50:52 +00:00
|
|
|
ObjList* l = m_queue.skipNull();
|
|
|
|
for (; l; l = l->skipNext()) {
|
|
|
|
DataBlock* packet = static_cast<DataBlock*>(l->get());
|
|
|
|
unsigned char* buf = (unsigned char*)packet->data();
|
|
|
|
// update the FSN/FIB in packet, BSN/BIB will be updated later
|
|
|
|
m_fsn = (m_fsn + 1) & 0x7f;
|
|
|
|
buf[1] = m_fib ? m_fsn | 0x80 : m_fsn;
|
|
|
|
}
|
|
|
|
Debug(this,DebugNote,"Renumbered %u packets, last FSN=%u [%p]",
|
|
|
|
q,m_fsn,this);
|
|
|
|
}
|
|
|
|
unlock();
|
2006-06-08 18:31:00 +00:00
|
|
|
SS7Layer2::notify();
|
|
|
|
}
|
2007-11-26 13:50:52 +00:00
|
|
|
if (resend) {
|
|
|
|
int c = 0;
|
|
|
|
lock();
|
2009-08-11 21:57:56 +00:00
|
|
|
m_fib = m_lastBib;
|
2007-11-26 13:50:52 +00:00
|
|
|
ObjList* l = m_queue.skipNull();
|
|
|
|
for (; l; l = l->skipNext()) {
|
|
|
|
DataBlock* packet = static_cast<DataBlock*>(l->get());
|
|
|
|
unsigned char* buf = (unsigned char*)packet->data();
|
|
|
|
// update the BSN/BIB in packet
|
|
|
|
buf[0] = m_bib ? m_bsn | 0x80 : m_bsn;
|
2009-08-11 21:57:56 +00:00
|
|
|
// also adjust the FIB but not FSN
|
|
|
|
if (m_fib)
|
|
|
|
buf[1] |= 0x80;
|
|
|
|
else
|
|
|
|
buf[1] &= 0x7f;
|
2008-11-07 11:38:21 +00:00
|
|
|
Debug(this,DebugInfo,"Resending packet %p with FSN=%u [%p]",
|
|
|
|
packet,buf[1] & 0x7f,this);
|
2009-06-01 13:31:29 +00:00
|
|
|
txPacket(*packet,false,SignallingInterface::SS7Msu);
|
2007-11-26 13:50:52 +00:00
|
|
|
c++;
|
|
|
|
}
|
2008-10-29 13:45:50 +00:00
|
|
|
if (c) {
|
|
|
|
m_resend = Time::now() + (1000 * m_resendMs);
|
|
|
|
m_fillTime = 0;
|
2008-11-07 11:38:21 +00:00
|
|
|
Debug(this,DebugInfo,"Resent %d packets, last bsn=%u/%u [%p]",
|
2008-10-29 13:45:50 +00:00
|
|
|
c,m_lastBsn,m_lastBib,this);
|
|
|
|
}
|
2007-11-26 13:50:52 +00:00
|
|
|
unlock();
|
|
|
|
}
|
2006-06-03 16:10:54 +00:00
|
|
|
}
|
2009-08-11 21:57:56 +00:00
|
|
|
else if (tout) {
|
2011-02-04 14:18:56 +00:00
|
|
|
switch (m_lStatus) {
|
|
|
|
case OutOfService:
|
|
|
|
if (m_status != OutOfService)
|
2006-06-03 16:10:54 +00:00
|
|
|
setLocalStatus(OutOfAlignment);
|
2011-02-04 14:18:56 +00:00
|
|
|
break;
|
|
|
|
case OutOfAlignment:
|
|
|
|
Debug(this,DebugMild,"Initial alignment timed out, retrying");
|
|
|
|
break;
|
2006-06-03 16:10:54 +00:00
|
|
|
}
|
2008-10-29 13:45:50 +00:00
|
|
|
}
|
|
|
|
if (when >= m_fillTime) {
|
|
|
|
if (operational())
|
|
|
|
transmitFISU();
|
|
|
|
else
|
|
|
|
transmitLSSU();
|
2006-06-03 16:10:54 +00:00
|
|
|
}
|
2006-06-02 18:16:03 +00:00
|
|
|
}
|
|
|
|
|
2006-05-02 07:12:18 +00:00
|
|
|
// Transmit a MSU retaining a copy for retransmissions
|
2006-05-02 15:50:45 +00:00
|
|
|
bool SS7MTP2::transmitMSU(const SS7MSU& msu)
|
2006-04-29 11:51:39 +00:00
|
|
|
{
|
2006-05-02 07:12:18 +00:00
|
|
|
if (msu.length() < 3) {
|
2007-11-26 13:50:52 +00:00
|
|
|
Debug(this,DebugWarn,"Asked to send too short MSU of length %u [%p]",
|
2006-05-02 07:12:18 +00:00
|
|
|
msu.length(),this);
|
|
|
|
return false;
|
|
|
|
}
|
2006-06-03 16:10:54 +00:00
|
|
|
if (!operational()) {
|
2007-11-26 13:50:52 +00:00
|
|
|
DDebug(this,DebugInfo,"Asked to send MSU while not operational [%p]",this);
|
2006-05-05 17:57:34 +00:00
|
|
|
return false;
|
|
|
|
}
|
2008-03-13 08:26:08 +00:00
|
|
|
#ifdef XDEBUG
|
|
|
|
String tmp;
|
|
|
|
tmp.hexify((void*)msu.data(),msu.length(),' ');
|
|
|
|
XDebug(this,DebugAll,"SS7MTP2::transmitMSU(%p) len=%u: %s [%p]",
|
|
|
|
&msu,msu.length(),tmp.c_str(),this);
|
|
|
|
#endif
|
2006-05-02 07:12:18 +00:00
|
|
|
// if we don't have an attached interface don't bother
|
|
|
|
if (!iface())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
DataBlock* packet = new DataBlock(0,3);
|
|
|
|
*packet += msu;
|
|
|
|
|
|
|
|
// set BSN+BIB, FSN+FIB, LENGTH in the 3 extra bytes
|
|
|
|
unsigned char* buf = (unsigned char*)packet->data();
|
|
|
|
buf[2] = (msu.length() > 0x3f) ? 0x3f : msu.length() & 0x3f;
|
|
|
|
// lock the object so we can safely use member variables
|
|
|
|
Lock lock(this);
|
2007-11-26 13:50:52 +00:00
|
|
|
m_fsn = (m_fsn + 1) & 0x7f;
|
2008-10-29 13:45:50 +00:00
|
|
|
m_fillTime = 0;
|
2006-05-02 07:12:18 +00:00
|
|
|
buf[0] = m_bib ? m_bsn | 0x80 : m_bsn;
|
|
|
|
buf[1] = m_fib ? m_fsn | 0x80 : m_fsn;
|
2007-11-26 13:50:52 +00:00
|
|
|
DDebug(this,DebugInfo,"New local bsn=%u/%d fsn=%u/%d [%p]",
|
|
|
|
m_bsn,m_bib,m_fsn,m_fib,this);
|
2006-05-02 07:12:18 +00:00
|
|
|
m_queue.append(packet);
|
2007-11-26 13:50:52 +00:00
|
|
|
DDebug(this,DebugInfo,"There are %u packets in queue [%p]",
|
|
|
|
m_queue.count(),this);
|
|
|
|
bool ok = false;
|
|
|
|
if (operational()) {
|
2009-06-01 13:31:29 +00:00
|
|
|
ok = txPacket(*packet,false,SignallingInterface::SS7Msu);
|
2007-11-26 13:50:52 +00:00
|
|
|
transmitFISU();
|
|
|
|
}
|
|
|
|
if (!m_abort)
|
|
|
|
m_abort = Time::now() + (1000 * m_abortMs);
|
|
|
|
if (!m_resend)
|
|
|
|
m_resend = Time::now() + (1000 * m_resendMs);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove the MSUs in the queue, the upper layer will move them to another link
|
2010-08-29 18:14:52 +00:00
|
|
|
void SS7MTP2::recoverMSU(int sequence)
|
2007-11-26 13:50:52 +00:00
|
|
|
{
|
2011-02-04 14:18:56 +00:00
|
|
|
Debug(this,DebugInfo,"Recovering MSUs from sequence %d",sequence);
|
2007-11-26 13:50:52 +00:00
|
|
|
for (;;) {
|
2010-08-29 18:14:52 +00:00
|
|
|
lock();
|
2008-11-07 11:38:21 +00:00
|
|
|
DataBlock* pkt = static_cast<DataBlock*>(m_queue.remove(false));
|
2010-08-29 18:14:52 +00:00
|
|
|
unlock();
|
2007-11-26 13:50:52 +00:00
|
|
|
if (!pkt)
|
|
|
|
break;
|
2010-08-29 18:14:52 +00:00
|
|
|
unsigned char* head = pkt->data(0,4);
|
|
|
|
if (head) {
|
|
|
|
int seq = head[1] & 0x7f;
|
|
|
|
if (sequence < 0 || ((seq - sequence) & 0x7f) < 0x3f) {
|
|
|
|
sequence = -1;
|
|
|
|
SS7MSU msu(head + 3,pkt->length() - 3);
|
|
|
|
recoveredMSU(msu);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Debug(this,DebugAll,"Not recovering MSU with seq=%d, requested %d",
|
|
|
|
seq,sequence);
|
2008-11-07 11:38:21 +00:00
|
|
|
}
|
|
|
|
TelEngine::destruct(pkt);
|
2007-11-26 13:50:52 +00:00
|
|
|
}
|
2006-04-29 11:51:39 +00:00
|
|
|
}
|
|
|
|
|
2006-05-02 07:12:18 +00:00
|
|
|
// Decode a received packet into signalling units
|
|
|
|
bool SS7MTP2::receivedPacket(const DataBlock& packet)
|
2006-04-29 11:51:39 +00:00
|
|
|
{
|
2008-09-18 14:05:26 +00:00
|
|
|
dump(packet,false,sls());
|
2006-05-02 07:12:18 +00:00
|
|
|
if (packet.length() < 3) {
|
2007-11-26 13:50:52 +00:00
|
|
|
XDebug(this,DebugMild,"Received short packet of length %u [%p]",
|
2006-05-02 20:49:40 +00:00
|
|
|
packet.length(),this);
|
2006-05-02 07:12:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const unsigned char* buf = (const unsigned char*)packet.data();
|
|
|
|
unsigned int len = buf[2] & 0x3f;
|
|
|
|
if ((len == 0x3f) && (packet.length() > 0x42))
|
|
|
|
len = packet.length() - 3;
|
|
|
|
else if (len != (packet.length() - 3)) {
|
2007-11-26 13:50:52 +00:00
|
|
|
XDebug(this,DebugMild,"Received packet with length indicator %u but length %u [%p]",
|
2006-05-02 07:12:18 +00:00
|
|
|
len,packet.length(),this);
|
|
|
|
return false;
|
|
|
|
}
|
2008-11-07 11:38:21 +00:00
|
|
|
|
2011-04-29 15:27:27 +00:00
|
|
|
// adjust error counter
|
|
|
|
if (m_errors && operational())
|
|
|
|
m_errors--;
|
2008-11-07 11:38:21 +00:00
|
|
|
// process LSSU and FISU to detect link status changes
|
|
|
|
switch (len) {
|
|
|
|
case 2:
|
|
|
|
processLSSU(buf[3] + (buf[4] << 8));
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
processLSSU(buf[3]);
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
processFISU();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check sequence numbers
|
2006-05-02 07:12:18 +00:00
|
|
|
unsigned char bsn = buf[0] & 0x7f;
|
|
|
|
unsigned char fsn = buf[1] & 0x7f;
|
2006-05-11 15:39:33 +00:00
|
|
|
bool bib = (buf[0] & 0x80) != 0;
|
2006-05-02 07:12:18 +00:00
|
|
|
bool fib = (buf[1] & 0x80) != 0;
|
2006-05-11 15:39:33 +00:00
|
|
|
// lock the object as we modify members
|
|
|
|
lock();
|
2008-11-18 18:55:48 +00:00
|
|
|
// sequence control as explained by Q.703 5.2.2
|
|
|
|
unsigned char diff = (fsn - m_bsn) & 0x7f;
|
|
|
|
XDebug(this,DebugAll,"got bsn=%u/%d fsn=%u/%d local bsn=%u/%d fsn=%u/%d diff=%u len=%u [%p]",
|
|
|
|
bsn,bib,fsn,fib,m_bsn,m_bib,m_fsn,m_fib,diff,len,this);
|
2008-11-07 11:38:21 +00:00
|
|
|
if (aligned()) {
|
|
|
|
// received FSN should be only 1 ahead of last we handled
|
|
|
|
if (diff > 1) {
|
|
|
|
if (diff < 64)
|
|
|
|
Debug(this,DebugMild,"We lost %u packets, remote fsn=%u local bsn=%u [%p]",
|
|
|
|
(diff - 1),fsn,m_bsn,this);
|
|
|
|
if (fsn != m_lastFsn) {
|
|
|
|
m_lastFsn = fsn;
|
|
|
|
// toggle BIB to request immediate retransmission
|
|
|
|
m_bib = !m_bib;
|
|
|
|
DDebug(this,DebugInfo,"New local bsn=%u/%d fsn=%u/%d [%p]",
|
|
|
|
m_bsn,m_bib,m_fsn,m_fib,this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_lastFsn = 128;
|
2006-06-02 18:16:03 +00:00
|
|
|
|
2008-11-07 11:38:21 +00:00
|
|
|
if (m_lastBib != bib) {
|
|
|
|
Debug(this,DebugNote,"Remote requested resend remote bsn=%u local fsn=%u [%p]",
|
|
|
|
bsn,m_fsn,this);
|
|
|
|
m_lastBib = bib;
|
|
|
|
m_resend = Time::now();
|
|
|
|
}
|
|
|
|
unqueueAck(bsn);
|
2011-02-04 14:18:56 +00:00
|
|
|
// end proving now if received MSU with correct sequence
|
|
|
|
if (m_interval && (diff == 1))
|
|
|
|
m_interval = Time::now();
|
2008-11-07 11:38:21 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// keep sequence numbers in sync with the remote
|
2006-06-02 18:16:03 +00:00
|
|
|
m_bsn = fsn;
|
2006-06-03 16:10:54 +00:00
|
|
|
m_bib = fib;
|
2007-11-26 13:50:52 +00:00
|
|
|
m_lastBsn = bsn;
|
|
|
|
m_lastBib = bib;
|
2008-10-29 13:45:50 +00:00
|
|
|
m_fillTime = 0;
|
2006-06-03 16:10:54 +00:00
|
|
|
}
|
2006-05-11 15:39:33 +00:00
|
|
|
unlock();
|
2006-05-02 07:12:18 +00:00
|
|
|
|
2008-11-07 11:38:21 +00:00
|
|
|
if (len < 3)
|
|
|
|
return true;
|
|
|
|
// just drop MSUs if not operational or out of sequence
|
|
|
|
if (!((diff == 1) && operational()))
|
2006-06-02 18:16:03 +00:00
|
|
|
return false;
|
2010-08-21 18:21:49 +00:00
|
|
|
m_lastSeqRx = m_bsn = fsn;
|
2008-10-29 13:45:50 +00:00
|
|
|
m_fillTime = 0;
|
2007-11-26 13:50:52 +00:00
|
|
|
DDebug(this,DebugInfo,"New local bsn=%u/%d fsn=%u/%d [%p]",
|
|
|
|
m_bsn,m_bib,m_fsn,m_fib,this);
|
2006-05-02 15:50:45 +00:00
|
|
|
SS7MSU msu((void*)(buf+3),len,false);
|
2006-06-03 16:10:54 +00:00
|
|
|
bool ok = receivedMSU(msu);
|
|
|
|
if (!ok) {
|
|
|
|
String s;
|
|
|
|
s.hexify(msu.data(),msu.length(),' ');
|
2007-11-26 13:50:52 +00:00
|
|
|
Debug(this,DebugMild,"Unhandled MSU len=%u Serv: %s, Prio: %s, Net: %s, Data: %s",
|
2006-06-03 16:10:54 +00:00
|
|
|
msu.length(),msu.getServiceName(),msu.getPriorityName(),
|
|
|
|
msu.getIndicatorName(),s.c_str());
|
|
|
|
}
|
2006-05-02 07:12:18 +00:00
|
|
|
msu.clear(false);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2008-11-07 11:38:21 +00:00
|
|
|
// Remove from send queue confirmed packets up to received BSN
|
|
|
|
void SS7MTP2::unqueueAck(unsigned char bsn)
|
|
|
|
{
|
|
|
|
if (m_lastBsn == bsn)
|
|
|
|
return;
|
|
|
|
// positive acknowledgement - Q.703 6.3.1
|
|
|
|
DDebug(this,DebugNote,"Unqueueing packets in range %u - %u [%p]",
|
|
|
|
m_lastBsn,bsn,this);
|
|
|
|
int c = 0;
|
|
|
|
for (;;) {
|
|
|
|
unsigned char efsn = (m_lastBsn + 1) & 0x7f;
|
|
|
|
DataBlock* packet = static_cast<DataBlock*>(m_queue.get());
|
|
|
|
if (!packet) {
|
|
|
|
Debug(this,DebugMild,"Queue empty while expecting packet with FSN=%u [%p]",
|
|
|
|
efsn,this);
|
|
|
|
m_lastBsn = bsn;
|
|
|
|
// all packets confirmed - stop resending
|
|
|
|
m_resend = 0;
|
|
|
|
m_abort = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
unsigned char pfsn = ((const unsigned char*)packet->data())[1] & 0x7f;
|
|
|
|
if (pfsn != efsn)
|
|
|
|
Debug(this,DebugMild,"Found in queue packet with FSN=%u expected %u [%p]",
|
|
|
|
pfsn,efsn,this);
|
|
|
|
c++;
|
|
|
|
XDebug(this,DebugInfo,"Unqueueing packet %p with FSN=%u [%p]",
|
|
|
|
packet,pfsn,this);
|
|
|
|
m_queue.remove(packet);
|
|
|
|
m_lastBsn = pfsn;
|
|
|
|
if (pfsn == bsn) {
|
|
|
|
if (m_queue.count() == 0) {
|
|
|
|
// all packets confirmed - stop resending
|
|
|
|
m_resend = 0;
|
|
|
|
m_abort = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (c) {
|
|
|
|
DDebug(this,DebugNote,"Unqueued %d packets up to FSN=%u [%p]",c,bsn,this);
|
|
|
|
m_abort = m_resend ? Time::now() + (1000 * m_abortMs) : 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transmit packet to interface, dump it if successfull
|
2007-11-26 13:50:52 +00:00
|
|
|
bool SS7MTP2::txPacket(const DataBlock& packet, bool repeat, SignallingInterface::PacketType type)
|
|
|
|
{
|
|
|
|
if (transmitPacket(packet,repeat,type)) {
|
2008-09-18 14:05:26 +00:00
|
|
|
dump(packet,true,sls());
|
2007-11-26 13:50:52 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2006-05-02 07:12:18 +00:00
|
|
|
// Process incoming FISU
|
|
|
|
void SS7MTP2::processFISU()
|
|
|
|
{
|
2009-05-28 17:52:27 +00:00
|
|
|
if (m_fillLink && !aligned())
|
|
|
|
m_fillTime = 0;
|
2006-05-02 07:12:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Process incoming LSSU
|
|
|
|
void SS7MTP2::processLSSU(unsigned int status)
|
|
|
|
{
|
2006-06-03 16:10:54 +00:00
|
|
|
status &= 0x07;
|
2007-11-26 13:50:52 +00:00
|
|
|
XDebug(this,DebugAll,"Process LSSU with status %s (L:%s R:%s)",
|
|
|
|
statusName(status,true),statusName(m_lStatus,true),statusName(m_rStatus,true));
|
2009-08-11 21:57:56 +00:00
|
|
|
bool unaligned = !aligned();
|
2008-11-07 11:38:21 +00:00
|
|
|
setRemoteStatus(status);
|
2006-06-03 16:10:54 +00:00
|
|
|
if (status == Busy) {
|
|
|
|
if (unaligned)
|
2011-02-04 11:43:43 +00:00
|
|
|
abortAlignment(m_autostart);
|
2006-06-03 16:10:54 +00:00
|
|
|
else
|
|
|
|
m_congestion = true;
|
|
|
|
return;
|
|
|
|
}
|
2009-08-11 21:57:56 +00:00
|
|
|
// cancel any timer except aborted or initial alignment
|
2006-06-02 18:16:03 +00:00
|
|
|
switch (status) {
|
2006-06-03 16:10:54 +00:00
|
|
|
case OutOfAlignment:
|
2006-06-02 18:16:03 +00:00
|
|
|
case NormalAlignment:
|
|
|
|
case EmergencyAlignment:
|
2011-02-04 14:18:56 +00:00
|
|
|
if (m_lStatus == OutOfService) {
|
|
|
|
if (m_status != OutOfService)
|
|
|
|
setLocalStatus(OutOfAlignment);
|
|
|
|
break;
|
|
|
|
}
|
2006-06-03 16:10:54 +00:00
|
|
|
if (!(unaligned && startProving()))
|
|
|
|
setLocalStatus(m_status);
|
2006-06-02 18:16:03 +00:00
|
|
|
break;
|
|
|
|
default:
|
2011-02-04 14:18:56 +00:00
|
|
|
if (!m_interval) {
|
|
|
|
if (m_status != OutOfService)
|
|
|
|
abortAlignment(m_autostart);
|
|
|
|
}
|
2009-08-11 21:57:56 +00:00
|
|
|
else if (m_lStatus != OutOfService && m_lStatus != OutOfAlignment)
|
2006-06-03 16:10:54 +00:00
|
|
|
m_interval = 0;
|
2006-06-02 18:16:03 +00:00
|
|
|
}
|
2006-04-29 11:51:39 +00:00
|
|
|
}
|
|
|
|
|
2006-05-02 15:50:45 +00:00
|
|
|
// Emit a locally generated LSSU
|
|
|
|
bool SS7MTP2::transmitLSSU(unsigned int status)
|
|
|
|
{
|
|
|
|
unsigned char buf[5];
|
|
|
|
buf[2] = 1;
|
|
|
|
buf[3] = status & 0xff;
|
|
|
|
status = (status >> 8) & 0xff;
|
|
|
|
if (status) {
|
|
|
|
// we need 2-byte LSSU to fit
|
|
|
|
buf[2] = 2;
|
|
|
|
buf[4] = status;
|
|
|
|
}
|
|
|
|
// lock the object so we can safely use member variables
|
|
|
|
lock();
|
2008-11-18 18:55:48 +00:00
|
|
|
bool repeat = m_fillLink && (m_status != OutOfService);
|
2006-05-02 15:50:45 +00:00
|
|
|
buf[0] = m_bib ? m_bsn | 0x80 : m_bsn;
|
|
|
|
buf[1] = m_fib ? m_fsn | 0x80 : m_fsn;
|
|
|
|
DataBlock packet(buf,buf[2]+3,false);
|
2007-11-26 13:50:52 +00:00
|
|
|
XDebug(this,DebugAll,"Transmit LSSU with status %s",statusName(buf[3],true));
|
2009-06-01 13:31:29 +00:00
|
|
|
bool ok = txPacket(packet,repeat,SignallingInterface::SS7Lssu);
|
2008-10-29 13:45:50 +00:00
|
|
|
m_fillTime = Time::now() + (1000 * m_fillIntervalMs);
|
2006-05-02 15:50:45 +00:00
|
|
|
unlock();
|
|
|
|
packet.clear(false);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Emit a locally generated FISU
|
|
|
|
bool SS7MTP2::transmitFISU()
|
|
|
|
{
|
|
|
|
unsigned char buf[3];
|
|
|
|
buf[2] = 0;
|
|
|
|
// lock the object so we can safely use member variables
|
|
|
|
lock();
|
|
|
|
buf[0] = m_bib ? m_bsn | 0x80 : m_bsn;
|
|
|
|
buf[1] = m_fib ? m_fsn | 0x80 : m_fsn;
|
|
|
|
DataBlock packet(buf,3,false);
|
2009-06-01 13:31:29 +00:00
|
|
|
bool ok = txPacket(packet,m_fillLink,SignallingInterface::SS7Fisu);
|
2008-10-29 13:45:50 +00:00
|
|
|
m_fillTime = Time::now() + (1000 * m_fillIntervalMs);
|
2006-05-02 15:50:45 +00:00
|
|
|
unlock();
|
|
|
|
packet.clear(false);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2006-06-03 16:10:54 +00:00
|
|
|
void SS7MTP2::startAlignment(bool emergency)
|
2006-05-02 15:50:45 +00:00
|
|
|
{
|
2006-06-02 18:16:03 +00:00
|
|
|
lock();
|
2007-11-26 13:50:52 +00:00
|
|
|
unsigned int q = m_queue.count();
|
|
|
|
if (q)
|
|
|
|
Debug(this,DebugWarn,"Starting alignment with %u queued MSUs! [%p]",q,this);
|
|
|
|
else
|
2009-08-11 21:57:56 +00:00
|
|
|
Debug(this,DebugInfo,"Starting %s alignment [%p]",
|
|
|
|
(emergency ? "emergency" : "normal"),this);
|
2011-02-04 14:18:56 +00:00
|
|
|
m_bsn = m_fsn = 127;
|
|
|
|
m_bib = m_fib = true;
|
|
|
|
if (m_lStatus != OutOfService) {
|
|
|
|
setLocalStatus(OutOfService);
|
|
|
|
unlock();
|
|
|
|
transmitLSSU();
|
|
|
|
lock();
|
|
|
|
}
|
2006-06-03 16:10:54 +00:00
|
|
|
m_status = emergency ? EmergencyAlignment : NormalAlignment;
|
2009-08-11 21:57:56 +00:00
|
|
|
m_abort = m_resend = 0;
|
2006-06-03 16:10:54 +00:00
|
|
|
setLocalStatus(OutOfAlignment);
|
2009-08-11 21:57:56 +00:00
|
|
|
m_interval = Time::now() + 5000000;
|
2006-06-02 18:16:03 +00:00
|
|
|
unlock();
|
2006-06-03 16:10:54 +00:00
|
|
|
transmitLSSU();
|
2009-08-11 21:57:56 +00:00
|
|
|
SS7Layer2::notify();
|
2006-05-02 15:50:45 +00:00
|
|
|
}
|
|
|
|
|
2011-02-04 11:43:43 +00:00
|
|
|
void SS7MTP2::abortAlignment(bool retry)
|
2006-05-02 15:50:45 +00:00
|
|
|
{
|
2006-06-02 18:16:03 +00:00
|
|
|
lock();
|
2009-08-11 21:57:56 +00:00
|
|
|
DDebug(this,DebugNote,"Aborting alignment [%p]",this);
|
2011-02-04 11:43:43 +00:00
|
|
|
if (!retry)
|
|
|
|
m_status = OutOfService;
|
2006-06-03 16:10:54 +00:00
|
|
|
setLocalStatus(OutOfService);
|
|
|
|
m_interval = Time::now() + 1000000;
|
2007-11-26 13:50:52 +00:00
|
|
|
m_abort = m_resend = 0;
|
|
|
|
m_errors = 0;
|
2011-02-04 14:18:56 +00:00
|
|
|
m_bsn = m_fsn = 127;
|
|
|
|
m_bib = m_fib = true;
|
2008-11-07 11:38:21 +00:00
|
|
|
m_fillTime = 0;
|
2006-06-02 18:16:03 +00:00
|
|
|
unlock();
|
2011-02-04 14:18:56 +00:00
|
|
|
transmitLSSU();
|
2006-06-08 18:31:00 +00:00
|
|
|
SS7Layer2::notify();
|
2006-05-02 15:50:45 +00:00
|
|
|
}
|
|
|
|
|
2006-06-03 16:10:54 +00:00
|
|
|
bool SS7MTP2::startProving()
|
|
|
|
{
|
2009-08-11 21:57:56 +00:00
|
|
|
if (!aligned())
|
2006-06-03 16:10:54 +00:00
|
|
|
return false;
|
|
|
|
lock();
|
2006-06-04 18:05:02 +00:00
|
|
|
bool emg = (m_rStatus == EmergencyAlignment);
|
2007-11-26 13:50:52 +00:00
|
|
|
Debug(this,DebugInfo,"Starting %s proving interval [%p]",
|
2006-06-04 18:05:02 +00:00
|
|
|
emg ? "emergency" : "normal",this);
|
2006-06-03 16:10:54 +00:00
|
|
|
// proving interval is defined in octet transmission times
|
2006-06-04 18:05:02 +00:00
|
|
|
u_int64_t interval = emg ? 4096 : 65536;
|
2006-06-03 16:10:54 +00:00
|
|
|
// FIXME: assuming 64 kbit/s, 125 usec/octet
|
|
|
|
m_interval = Time::now() + (125 * interval);
|
|
|
|
unlock();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-04-18 22:31:16 +00:00
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|