SS7 work-in-proggress

git-svn-id: http://yate.null.ro/svn/yate/trunk@834 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2006-06-03 16:10:54 +00:00
parent 5f3116ebc9
commit ef287a29ca
5 changed files with 329 additions and 70 deletions

View File

@ -35,12 +35,74 @@ bool SS7MSU::valid() const
return (3 < length()) && (length() < 273);
}
#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
unsigned int SS7Layer2::status() const
{
return ProcessorOutage;
}
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";
}
}
bool SS7Layer2::control(Operation oper, NamedList* params)
{
return false;
@ -48,20 +110,53 @@ bool SS7Layer2::control(Operation oper, NamedList* params)
SS7MTP2::SS7MTP2(unsigned int status)
: Mutex(false), m_status(status),
: Mutex(false),
m_status(status), m_lStatus(OutOfService), m_rStatus(OutOfAlignment),
m_interval(0), m_congestion(false),
m_bsn(127), m_fsn(127), m_bib(true), m_fib(true)
{
setName("mtp2");
}
unsigned int SS7MTP2::status() const
{
return m_status;
return m_lStatus;
}
void SS7MTP2::setLocalStatus(unsigned int status)
{
if (status == m_lStatus)
return;
DDebug(engine(),DebugInfo,"Local status change: %s -> %s [%p]",
statusName(m_lStatus,true),statusName(status,true),this);
m_lStatus = status;
}
void SS7MTP2::setRemoteStatus(unsigned int status)
{
if (status == m_rStatus)
return;
DDebug(engine(),DebugInfo,"Remote status change: %s -> %s [%p]",
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;
}
bool SS7MTP2::control(Operation oper, NamedList* params)
{
switch (oper) {
case Pause:
m_status = OutOfService;
abortAlignment();
return true;
case Resume:
@ -73,7 +168,7 @@ bool SS7MTP2::control(Operation oper, NamedList* params)
return true;
break;
case Status:
return aligned();
return operational();
default:
return SignallingReceiver::control((SignallingInterface::Operation)oper,params);
}
@ -81,10 +176,29 @@ bool SS7MTP2::control(Operation oper, NamedList* params)
void SS7MTP2::timerTick(const Time& when)
{
if (aligned())
lock();
bool tout = m_interval && (when >= m_interval);
if (tout)
m_interval = 0;
unlock();
if (operational()) {
if (tout)
DDebug(engine(),DebugInfo,"Proving period ended, link operational [%p]",this);
transmitFISU();
else
}
else {
if (tout && (m_lStatus == OutOfService)) {
switch (m_status) {
case NormalAlignment:
case EmergencyAlignment:
setLocalStatus(OutOfAlignment);
break;
default:
setLocalStatus(m_status);
}
}
transmitLSSU();
}
}
// Transmit a MSU retaining a copy for retransmissions
@ -95,8 +209,8 @@ bool SS7MTP2::transmitMSU(const SS7MSU& msu)
msu.length(),this);
return false;
}
if (!aligned()) {
DDebug(engine(),DebugInfo,"Asked to send MSU while unaligned [%p]",this);
if (!operational()) {
DDebug(engine(),DebugInfo,"Asked to send MSU while not operational [%p]",this);
return false;
}
XDebug(engine(),DebugAll,"SS7MTP2::transmitMSU(%p) len=%u [%p]",
@ -137,7 +251,6 @@ bool SS7MTP2::receivedPacket(const DataBlock& packet)
len,packet.length(),this);
return false;
}
bool ok = true;
// packet length is valid, check sequence numbers
unsigned char bsn = buf[0] & 0x7f;
unsigned char fsn = buf[1] & 0x7f;
@ -148,10 +261,26 @@ bool SS7MTP2::receivedPacket(const DataBlock& packet)
XDebug(engine(),DebugInfo,"got bsn=%u/%d fsn=%u/%d local bsn=%u/%d fsn=%u/%d [%p]",
bsn,bib,fsn,fib,m_bsn,m_bib,m_fsn,m_fib,this);
ok = (fsn == ((m_bsn + 1) & 0x7f));
if (ok)
if ((m_rStatus == OutOfAlignment) || (m_rStatus == OutOfService)) {
// sync sequence
m_bsn = fsn;
m_bib = fib;
}
// sequence control as explained by Q.703 5.2.2
bool same = (fsn == m_bsn);
bool next = false;
// hack - use a while so we can break out
while (!same) {
if (len >= 3) {
next = (fsn == ((m_bsn + 1) & 0x7f));
if (next)
break;
}
Debug(engine(),DebugMild,"Detected loss of %u packets",(fsn - m_bsn) & 0x7f);
m_bib = !m_bib;
break;
}
unlock();
//TODO: implement Q.703 6.3.1
@ -159,19 +288,27 @@ bool SS7MTP2::receivedPacket(const DataBlock& packet)
switch (len) {
case 2:
processLSSU(buf[3] + (buf[4] << 8));
return ok;
return true;
case 1:
processLSSU(buf[3]);
return ok;
return true;
case 0:
processFISU();
return ok;
return true;
}
// just drop MSUs
if (!(ok && aligned()))
if (!(next && operational()))
return false;
m_bsn = fsn;
SS7MSU msu((void*)(buf+3),len,false);
ok = receivedMSU(msu);
bool ok = receivedMSU(msu);
if (!ok) {
String s;
s.hexify(msu.data(),msu.length(),' ');
Debug(toString(),DebugMild,"Unhandled MSU len=%u Serv: %s, Prio: %s, Net: %s, Data: %s",
msu.length(),msu.getServiceName(),msu.getPriorityName(),
msu.getIndicatorName(),s.c_str());
}
msu.clear(false);
return ok;
}
@ -179,44 +316,41 @@ bool SS7MTP2::receivedPacket(const DataBlock& packet)
// Process incoming FISU
void SS7MTP2::processFISU()
{
XDebug(toString(),DebugStub,"Please implement SS7MTP2::processFISU()");
switch (m_status) {
case EmergencyAlignment:
m_status = NormalAlignment;
case NormalAlignment:
transmitFISU();
break;
default:
transmitLSSU();
}
if (!aligned())
transmitLSSU();
}
// Process incoming LSSU
void SS7MTP2::processLSSU(unsigned int status)
{
XDebug(toString(),DebugStub,"Please implement SS7MTP2::processLSSU(%u)",status);
switch (status) {
status &= 0x07;
bool unaligned = true;
switch (m_rStatus) {
case NormalAlignment:
case EmergencyAlignment:
switch (m_status) {
case NormalAlignment:
case EmergencyAlignment:
m_status = NormalAlignment;
transmitFISU();
break;
default:
m_status = status;
transmitLSSU();
}
unaligned = false;
}
if (status == Busy) {
if (unaligned)
abortAlignment();
else
m_congestion = true;
return;
}
setRemoteStatus(status);
// cancel any timer except aborted alignment
switch (status) {
case OutOfAlignment:
case NormalAlignment:
case EmergencyAlignment:
if (!(unaligned && startProving()))
setLocalStatus(m_status);
break;
default:
switch (m_status) {
case NormalAlignment:
case EmergencyAlignment:
m_status = OutOfAlignment;
}
transmitLSSU();
break;
if (!m_interval)
abortAlignment();
else if (m_lStatus != OutOfService)
m_interval = 0;
}
}
@ -259,20 +393,38 @@ bool SS7MTP2::transmitFISU()
return ok;
}
void SS7MTP2::startAlignment()
void SS7MTP2::startAlignment(bool emergency)
{
lock();
m_status = OutOfAlignment;
m_status = emergency ? EmergencyAlignment : NormalAlignment;
m_interval = 0;
setLocalStatus(OutOfAlignment);
m_queue.clear();
unlock();
transmitLSSU();
}
void SS7MTP2::abortAlignment()
{
lock();
m_status = OutOfService;
setLocalStatus(OutOfService);
m_interval = Time::now() + 1000000;
m_queue.clear();
unlock();
}
bool SS7MTP2::startProving()
{
if (m_interval || !aligned())
return false;
lock();
Debug(engine(),DebugInfo,"Starting proving interval [%p]",this);
// proving interval is defined in octet transmission times
u_int64_t interval = (m_rStatus == EmergencyAlignment) ? 4096 : 65536;
// FIXME: assuming 64 kbit/s, 125 usec/octet
m_interval = Time::now() + (125 * interval);
unlock();
return true;
}
/* vi: set ts=8 sw=4 sts=4 noet: */

View File

@ -32,7 +32,7 @@ void SS7MTP3::attach(SS7Layer2* link)
SignallingComponent::insert(link);
}
bool SS7MTP3::receivedMSU(const SS7MSU& msu)
bool SS7MTP3::receivedMSU(const SS7MSU& msu, SS7Layer2* link)
{
Debug(toString(),DebugStub,"Please implement SS7MTP3::receivedMSU()");
return false;

View File

@ -38,4 +38,10 @@ void SS7Router::attach(SS7Layer4* service)
SignallingComponent::insert(service);
}
bool SS7Router::receivedMSU(const SS7MSU& msu, SS7Layer3* network)
{
Debug(toString(),DebugStub,"Please implement SS7Router::receivedMSU(%p,%p)",&msu,network);
return false;
}
/* vi: set ts=8 sw=4 sts=4 noet: */

View File

@ -653,6 +653,24 @@ public:
*/
inline int getNI() const
{ return null() ? -1 : 0xc0 & *(const unsigned char*)data(); }
/**
* Retrive the name of the Service as decoded from the SIF
* @return Name of the service, NULL if unknown or invalid MSU
*/
const char* getServiceName() const;
/**
* Retrive the name of the Priority as decoded from the SIF
* @return Name of the priority, NULL if unknown or invalid MSU
*/
const char* getPriorityName() const;
/**
* Retrive the name of the Network Indicator as decoded from the SIF
* @return Name of the network indicator, NULL if unknown or invalid MSU
*/
const char* getIndicatorName() const;
};
/**
@ -984,9 +1002,10 @@ protected:
/**
* Process a MSU received from the Layer 2 component
* @param msu Message data, starting with Service Indicator Octet
* @param link Data link that delivered the MSU
* @return True if the MSU was processed
*/
virtual bool receivedMSU(const SS7MSU& msu) = 0;
virtual bool receivedMSU(const SS7MSU& msu, SS7Layer2* link) = 0;
};
/**
@ -1043,11 +1062,26 @@ public:
virtual unsigned int status() const;
/**
* Check if the link is aligned
* Get the name of a Layer 2 status
* @param status Status indication value
* @param brief Request to return the short status name
* @return String describing the status
*/
virtual const char* statusName(unsigned int status, bool brief) const;
/**
* Get the name of the current local Layer 2 status
* @param brief Request to return the short status name
* @return String describing the status
*/
inline const char* statusName(bool brief = false) const
{ return statusName(status(),brief); }
/**
* Check if the link is fully operational
* @return True if the link is aligned and operational
*/
inline bool aligned() const
{ return status() == NormalAlignment; }
virtual bool operational() const = 0;
/**
* Attach a Layer 2 user component to the data link
@ -1073,6 +1107,12 @@ public:
virtual bool control(Operation oper, NamedList* params = 0);
protected:
/**
* Constructor
*/
inline SS7Layer2()
: m_l2user(0)
{ }
/**
* Push a received Message Signal Unit up the protocol stack
@ -1080,7 +1120,7 @@ protected:
* @return True if message was successfully delivered to the user component
*/
inline bool receivedMSU(const SS7MSU& msu)
{ return m_l2user && m_l2user->receivedMSU(msu); }
{ return m_l2user && m_l2user->receivedMSU(msu,this); }
private:
SS7L2User* m_l2user;
@ -1092,6 +1132,15 @@ private:
*/
class YSS7_API SS7L3User : virtual public SignallingComponent
{
friend class SS7Layer3;
protected:
/**
* Process a MSU received from the Layer 3 component
* @param msu Message data, starting with Service Indicator Octet
* @param link Network layer that delivered the MSU
* @return True if the MSU was processed
*/
virtual bool receivedMSU(const SS7MSU& msu, SS7Layer3* network) = 0;
};
/**
@ -1114,6 +1163,22 @@ public:
inline SS7L3User* user() const
{ return m_l3user; }
protected:
/**
* Constructor
*/
inline SS7Layer3()
: m_l3user(0)
{ }
/**
* Push a received Message Signal Unit up the protocol stack
* @param msu Message data, starting with Service Indicator Octet
* @return True if message was successfully delivered to the user component
*/
inline bool receivedMSU(const SS7MSU& msu)
{ return m_l3user && m_l3user->receivedMSU(msu,this); }
private:
SS7L3User* m_l3user;
};
@ -1163,6 +1228,14 @@ public:
void attach(SS7Layer4* service);
protected:
/**
* Process a MSU received from the Layer 3 component
* @param msu Message data, starting with Service Indicator Octet
* @param link Network layer that delivered the MSU
* @return True if the MSU was processed
*/
virtual bool receivedMSU(const SS7MSU& msu, SS7Layer3* network);
ObjList m_layer3;
ObjList m_layer4;
};
@ -1231,6 +1304,19 @@ public:
*/
virtual unsigned int status() const;
/**
* Check if the link is aligned.
* The link may not be operational, the other side may be still proving.
* @return True if the link is aligned
*/
virtual bool aligned() const;
/**
* Check if the link is aligned and operational
* @return True if the link is operational
*/
virtual bool operational() const;
/**
* Execute a control operation. Operations can change the link status or
* can query the aligned status.
@ -1287,19 +1373,32 @@ protected:
/**
* Initiates alignment and proving procedure
* @param emergency True if emergency alignment is desired
*/
void startAlignment();
void startAlignment(bool emergency = false);
/**
* Abort an alignment procedure if link errors occur
*/
void abortAlignment();
/**
* Start the link proving period
* @return True if proving period was started
*/
bool startProving();
private:
void setLocalStatus(unsigned int status);
void setRemoteStatus(unsigned int status);
// sent but yet unacknowledged packets
ObjList m_queue;
// data link status (alignment)
unsigned int m_status;
// data link status (alignment) - desired, local and remote
unsigned int m_status, m_lStatus, m_rStatus;
// various interval period end
u_int64_t m_interval;
// remote congestion indicator
bool m_congestion;
// backward and forward sqeuence numbers
unsigned char m_bsn, m_fsn;
// backward and forward indicator bits
@ -1323,9 +1422,10 @@ protected:
/**
* Process a MSU received from the Layer 2 component
* @param msu Message data, starting with Service Indicator Octet
* @param link Data link that delivered the MSU
* @return True if the MSU was processed
*/
virtual bool receivedMSU(const SS7MSU& msu);
virtual bool receivedMSU(const SS7MSU& msu, SS7Layer2* link);
ObjList m_links;
};

View File

@ -79,7 +79,7 @@ public:
virtual bool control(Operation oper, NamedList* params);
protected:
virtual void timerTick(const Time& when);
void receiveAttempt();
bool receiveAttempt();
private:
bool openSocket();
Socket m_socket;
@ -104,6 +104,8 @@ private:
YSIGFACTORY2(WpInterface,SignallingInterface);
static const char hex[] = "0123456789abcdef";
//class WpSigFactory : public SignallingFactory
void* WpInterface::create(const String& type, const NamedList& name)
@ -143,7 +145,6 @@ bool WpInterface::transmitPacket(const DataBlock& packet, bool repeat, PacketTyp
#ifdef XDEBUG
if (debugAt(DebugAll)) {
const char hex[] = "0123456789abcdef";
unsigned char* s = (unsigned char*) packet.data();
unsigned int len = packet.length();
String str(' ',3*len);
@ -184,27 +185,27 @@ bool WpInterface::transmitPacket(const DataBlock& packet, bool repeat, PacketTyp
DDebug(toString(),DebugWarn,"Sent %d instead of %d bytes [%p]",w,sz,this);
return false;
}
w -= WP_HEADER;
XDebug(toString(),DebugAll,"Successfully sent %d bytes packet [%p]",w,this);
// w -= WP_HEADER;
// XDebug(toString(),DebugAll,"Successfully sent %d bytes packet [%p]",w,this);
return true;
}
void WpInterface::receiveAttempt()
bool WpInterface::receiveAttempt()
{
if (!m_socket.valid())
return;
return false;
unsigned char buf[WP_HEADER + MAX_PACKET];
int r = m_socket.recv(buf,sizeof(buf));
if (Socket::socketError() == r) {
if (m_socket.canRetry())
return;
return false;
DDebug(toString(),DebugWarn,"Error on reading packet: %d: %s [%p]",
m_socket.error(),::strerror(m_socket.error()),this);
return;
return false;
}
if (r > (WP_HEADER + m_overRead)) {
r -= (WP_HEADER + m_overRead);
XDebug(toString(),DebugAll,"Received %d bytes packet [%p]",r,this);
// XDebug(toString(),DebugAll,"Received %d bytes packet [%p]",r,this);
if (buf[WP_RD_ERROR]) {
DDebug(toString(),DebugWarn,"Packet got error: %u [%p]",
buf[WP_RD_ERROR],this);
@ -214,12 +215,11 @@ void WpInterface::receiveAttempt()
notify(CksumError);
if (buf[WP_RD_ERROR] & WP_ERR_ABORT)
notify(AlignError);
return;
return true;
}
#ifdef XDEBUG
if (debugAt(DebugAll)) {
const char hex[] = "0123456789abcdef";
unsigned char* s = buf+WP_HEADER;
String str(' ',3*r);
char* d = (char*) str.c_str();
@ -238,6 +238,7 @@ void WpInterface::receiveAttempt()
receivedPacket(data);
data.clear(false);
}
return true;
}
bool WpInterface::openSocket()
@ -308,8 +309,8 @@ void WpSigThread::run()
Debug(DebugAll,"WpSigThread::run() [%p]",this);
for (;;) {
Thread::yield(true);
if (m_interface)
m_interface->receiveAttempt();
while (m_interface && m_interface->receiveAttempt())
;
}
}