yate/libs/ysig/q921.cpp

2289 lines
68 KiB
C++

/**
* q921.cpp
* This file is part of the YATE Project http://YATE.null.ro
*
* Yet Another Signalling Stack - implements the support for SS7, ISDN and PSTN
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "yatesig.h"
#include <stdlib.h>
using namespace TelEngine;
/**
* DEFINEs controlling Q.921 implementation
* Q921_PASIVE_NOCHECK_PF
* Yes: Received UA/DM responses will be validated without checking the P/F bit
* No: Received UA/DM responses without P/F bit set will be dropped
*/
#ifndef Q921_PASIVE_NOCHECK_PF
#define Q921_PASIVE_NOCHECK_PF
#endif
static const char* s_linkSideNet = "NET";
static const char* s_linkSideCpe = "CPE";
#define Q921_MANAGEMENT_TEI 15 // TEI management message descriptor (first byte). See Q.921 Table 8
#define Q921_TEI_BROADCAST 127 // TEI value for broadcast and management procedures
#define Q921_SAPI_MANAGEMENT 63 // SAPI value for management procedures
static inline const char* linkSide(bool net)
{
return net ? s_linkSideNet : s_linkSideCpe;
}
static inline void fixParams(NamedList& params, const NamedList* config)
{
if (config && params.getBoolValue(YSTRING("local-config"),false))
params.copyParams(*config);
int rx = params.getIntValue(YSTRING("rxunderrun"));
if ((rx > 0) && (rx < 2500))
params.setParam("rxunderrun","2500");
}
// Drop frame reasons
static const char* s_noState = "Not allowed in this state";
// Used to set or compare values that may wrap at 127 boundary
class Modulo128
{
public:
// Increment a value. Set to 0 if greater the 127
static inline void inc(u_int8_t& value) {
if (value < 127)
value++;
else
value = 0;
}
// Check if a given value is in an interval given by it's margins
// @param value The value to check
// @param low The lower margin of the interval
// @param high The higher margin of the interval
static inline bool between(u_int8_t value, u_int8_t low, u_int8_t high) {
if (low == high)
return value == low;
if (low < high)
return value >= low && value <= high;
// low > high: counter wrapped around
return value >= low || value <= high;
}
// Get the lower margin of an interval given by it's higher margin and length
// The interval length is assumed non 0
// @param high The higher margin of the interval
// @param len The interval length
static inline u_int8_t getLow(u_int8_t high, u_int8_t len)
{ return ((high >= len) ? high - len + 1 : 128 - (len - high)); }
};
/**
* ISDNQ921
*/
// ****************************************************************************
// NOTE:
// * Private methods are not thread safe. They are called from public
// and protected methods which are thread safe
// * Always drop any lock before calling Layer 3 methods to avoid a deadlock:
// it may try to establish/release/send data from a different thread
// ****************************************************************************
// Constructor. Set data members. Print them
ISDNQ921::ISDNQ921(const NamedList& params, const char* name, ISDNQ921Management* mgmt, u_int8_t tei)
: SignallingComponent(name,&params,"isdn-q921"),
ISDNLayer2(params,name,tei),
SignallingReceiver(name),
SignallingDumpable(SignallingDumper::Q921,network()),
m_management(mgmt),
m_remoteBusy(false),
m_timerRecovery(false),
m_rejectSent(false),
m_pendingDMSabme(false),
m_lastPFBit(false),
m_vs(0),
m_va(0),
m_vr(0),
m_retransTimer(0),
m_idleTimer(0),
m_window(7),
m_n200(3),
m_txFrames(0),
m_txFailFrames(0),
m_rxFrames(0),
m_rxRejectedFrames(0),
m_rxDroppedFrames(0),
m_hwErrors(0),
m_printFrames(true),
m_extendedDebug(false),
m_errorSend(false),
m_errorReceive(false)
{
if (mgmt && network())
autoRestart(false);
m_retransTimer.interval(params,"t200",1000,1000,false);
m_idleTimer.interval(params,"t203",2000,10000,false);
// Adjust idle timeout to data link side
m_idleTimer.interval(m_idleTimer.interval() + (network() ? -500 : 500));
m_window.maxVal(params.getIntValue(YSTRING("maxpendingframes"),7));
if (!m_window.maxVal())
m_window.maxVal(7);
setDebug(params.getBoolValue(YSTRING("print-frames"),false),
params.getBoolValue(YSTRING("extended-debug"),false));
if (debugAt(DebugInfo)) {
String tmp;
#ifdef DEBUG
if (debugAt(DebugAll)) {
params.dump(tmp,"\r\n ",'\'',true);
Debug(this,DebugAll,"ISDNQ921::ISDNQ921(%p,'%s',%p,%u) [%p]%s",
&params,name,mgmt,tei,this,tmp.c_str());
tmp.clear();
}
tmp << " SAPI/TEI=" << (unsigned int)localSapi() << "/" << (unsigned int)localTei();
tmp << " auto-restart=" << String::boolText(autoRestart());
tmp << " max-user-data=" << (unsigned int)maxUserData();
tmp << " max-pending-frames: " << (unsigned int)m_window.maxVal();
tmp << " retrans/idle=" << (unsigned int)m_retransTimer.interval() << "/"
<< (unsigned int)m_idleTimer.interval();
#endif
Debug(this,DebugAll,"ISDN Data Link type=%s%s [%p]",
linkSide(network()),tmp.safe(),this);
}
if (!mgmt)
setDumper(params.getValue(YSTRING("layer2dump")));
}
// Destructor
ISDNQ921::~ISDNQ921()
{
Lock lock(l2Mutex());
ISDNLayer2::attach((ISDNLayer3*)0);
TelEngine::destruct(SignallingReceiver::attach(0));
cleanup();
DDebug(this,DebugAll,
"ISDN Data Link destroyed. Frames: sent=%u (failed=%u) recv=%u rejected=%u dropped=%u. HW errors=%u [%p]",
(unsigned int)m_txFrames,(unsigned int)m_txFailFrames,
(unsigned int)m_rxFrames,(unsigned int)m_rxRejectedFrames,
(unsigned int)m_rxDroppedFrames,(unsigned int)m_hwErrors,this);
}
// Initialize layer, attach interface if not managed
bool ISDNQ921::initialize(const NamedList* config)
{
#ifdef DEBUG
String tmp;
if (config && debugAt(DebugAll))
config->dump(tmp,"\r\n ",'\'',true);
Debug(this,DebugInfo,"ISDNQ921::initialize(%p) [%p]%s",config,this,tmp.c_str());
#endif
if (config) {
debugLevel(config->getIntValue(YSTRING("debuglevel_q921"),
config->getIntValue(YSTRING("debuglevel"),-1)));
setDebug(config->getBoolValue(YSTRING("print-frames"),false),
config->getBoolValue(YSTRING("extended-debug"),false));
}
if (config && !m_management && !iface()) {
NamedList params("");
if (resolveConfig(YSTRING("sig"),params,config) ||
resolveConfig(YSTRING("basename"),params,config)) {
params.addParam("basename",params);
params.assign(params + "/D");
fixParams(params,config);
SignallingInterface* ifc = YSIGCREATE(SignallingInterface,&params);
if (!ifc)
return false;
SignallingReceiver::attach(ifc);
if (ifc->initialize(&params)) {
SignallingReceiver::control(SignallingInterface::Enable);
multipleFrame(0,true,false);
}
else
TelEngine::destruct(SignallingReceiver::attach(0));
}
}
return m_management || iface();
}
// Set or release 'multiple frame acknowledged' mode
bool ISDNQ921::multipleFrame(u_int8_t tei, bool establish, bool force)
{
Lock lock(l2Mutex());
// Check state. Don't do anything in transition states or if TEI changes
if ((localTei() != tei) || (state() == WaitEstablish) || (state() == WaitRelease))
return false;
// The request wouldn't change our state and we are not forced to fulfill it
if (!force &&
((establish && (state() == Established)) ||
(!establish && (state() == Released))))
return false;
XDebug(this,DebugAll,"Process '%s' request, TEI=%u",establish ? "ESTABLISH" : "RELEASE",tei);
bool result = true;
if (establish) {
reset();
result = sendUFrame(ISDNFrame::SABME,true,true);
changeState(WaitEstablish,"multiple frame");
timer(true,false);
}
else {
// Already disconnected: Just notify Layer 3
if (state() == Released) {
lock.drop();
if (m_management)
m_management->multipleFrameReleased(tei,true,false,this);
else
multipleFrameReleased(tei,true,false);
return true;
}
reset();
result = sendUFrame(ISDNFrame::DISC,true,true);
changeState(WaitRelease,"multiple frame");
timer(true,false);
}
return result;
}
// Send data through the HDLC interface
bool ISDNQ921::sendData(const DataBlock& data, u_int8_t tei, bool ack)
{
if (data.null())
return false;
Lock lock(l2Mutex());
if (ack) {
if (localTei() != tei || !teiAssigned() || state() == Released || m_window.full())
return false;
// Enqueue and send outgoing data
ISDNFrame* f = new ISDNFrame(true,network(),localSapi(),localTei(),false,data);
// Update frame send seq number. Inc our send seq number and window counter
f->update(&m_vs,0);
Modulo128::inc(m_vs);
m_window.inc();
// Append and try to send frame
m_outFrames.append(f);
XDebug(this,DebugAll,"Enqueued data frame (%p). Sequence number: %u",f,f->ns());
sendOutgoingData();
return true;
}
// Unacknowledged data request
if (tei != Q921_TEI_BROADCAST) {
Debug(this,DebugInfo,"Not sending unacknowledged data with TEI %u [%p]",tei,this);
return false;
}
// P/F bit is always false for UI frames. See Q.921 5.2.2
ISDNFrame* f = new ISDNFrame(false,network(),localSapi(),localTei(),false,data);
bool result = sendFrame(f);
TelEngine::destruct(f);
return result;
}
// Send DISC. Reset data
void ISDNQ921::cleanup()
{
Lock lock(l2Mutex());
DDebug(this,DebugAll,"Cleanup in state '%s'",stateName(state()));
// Don't send DISC if we are disconnected or waiting to become disconnected
if (state() == Established)
sendUFrame(ISDNFrame::DISC,true,true);
reset();
changeState(Released,"cleanup");
}
// Method called periodically to check timeouts
// Re-sync with remote peer if necessary
void ISDNQ921::timerTick(const Time& when)
{
// If possible return early without locking
if (state() == Released)
return;
Lock lock(l2Mutex(),SignallingEngine::maxLockWait());
// Check state again after locking, to be sure it didn't change
if (!lock.locked() || (state() == Released))
return;
// T200 not started
if (!m_retransTimer.started()) {
// T203 not started: START
if (!m_idleTimer.started()) {
timer(false,true,when.msec());
m_timerRecovery = false;
return;
}
// T203 started: Timeout ?
if (!m_idleTimer.timeout(when.msec()))
return;
// Start timer
XDebug(this,DebugInfo,"T203 expired. Start T200");
timer(true,false,when.msec());
}
// T200 started
if (!m_retransTimer.timeout(when.msec()))
return;
// Q.921 5.6.7: Timeout
// Done all retransmissions ?
if (m_n200.full()) {
reset();
changeState(Released,"timeout");
lock.drop();
multipleFrameReleased(localTei(),false,true);
if (autoRestart())
multipleFrame(localTei(),true,false);
return;
}
// Waiting to establish/release ?
if (state() == WaitEstablish || state() == WaitRelease) {
ISDNFrame::Type t = (state() == WaitEstablish) ?
ISDNFrame::SABME : ISDNFrame::DISC;
XDebug(this,DebugAll,"T200 expired. Retransmit '%s'",ISDNFrame::typeName(t));
sendUFrame(t,true,true,true);
m_n200.inc();
timer(true,false,when.msec());
return;
}
// State is Established
if (!m_timerRecovery) {
m_n200.reset();
m_timerRecovery = true;
}
// Try to retransmit some data or send RR
if (!sendOutgoingData(true)) {
XDebug(this,DebugAll,"T200 expired. Send '%s'",ISDNFrame::typeName(ISDNFrame::RR));
sendSFrame(ISDNFrame::RR,true,true);
m_lastPFBit = true;
}
m_n200.inc();
timer(true,false,when.msec());
}
// Process a packet received by the receiver's interface
// Parse data. Validate received frame and process it
bool ISDNQ921::receivedPacket(const DataBlock& packet)
{
ISDNFrame* f = parsePacket(packet);
if (!f) {
if (!m_errorReceive) {
m_errorReceive = true;
Debug(this,DebugNote,"Received invalid packet with length %u [%p]",packet.length(),this);
}
return false;
}
m_errorReceive = false;
// Print & dump
if (debugAt(DebugInfo) && m_printFrames) {
String tmp;
f->toString(tmp,m_extendedDebug);
Debug(this,DebugInfo,"Received frame (%p):%s",f,tmp.c_str());
}
if (f->type() < ISDNFrame::Invalid)
dump(f->buffer(),false);
return receivedFrame(f);
}
bool ISDNQ921::receivedFrame(ISDNFrame* frame)
{
if (!frame)
return false;
Lock lock(l2Mutex());
bool reject = false;
// Not accepted:
// If not rejected, for out of range sequence number send
// REJ to request retransmission if not already sent or RR to confirm if REJ already sent
// Just drop the frame otherwise
// If rejected (unrecoverable error), re-establish data link
if (!acceptFrame(frame,reject)) {
if (!reject) {
if (frame->m_error == ISDNFrame::ErrTxSeqNo) {
if (!m_rejectSent) {
sendSFrame(ISDNFrame::REJ,true,true);
m_rejectSent = true;
m_lastPFBit = true;
}
else
sendSFrame(ISDNFrame::RR,false,frame->poll());
}
TelEngine::destruct(frame);
return true;
}
// Unrecoverable error: re-establish
Debug(this,DebugNote,"Rejected %s frame %p, reason: '%s'. Restarting",
frame->name(),frame,ISDNFrame::typeName(frame->error()));
TelEngine::destruct(frame);
reset();
changeState(WaitEstablish,"received frame");
sendUFrame(ISDNFrame::SABME,true,true);
timer(true,false);
return true;
}
// Process
XDebug(this,DebugAll,"Process %s frame %p in state '%s'",
frame->name(),frame,ISDNLayer2::stateName(state()));
bool chgState = false, confirmation = false;
State newState;
if (frame->category() == ISDNFrame::Data) {
bool ack = (frame->type() == ISDNFrame::I);
if (processDataFrame(frame,ack)) {
DataBlock tmp;
frame->getData(tmp);
lock.drop();
receiveData(tmp,localTei());
}
frame->deref();
return true;
}
if (frame->category() == ISDNFrame::Supervisory) {
if (processSFrame(frame)) {
// Exit from timer recovery
m_timerRecovery = false;
if (m_pendingDMSabme) {
m_pendingDMSabme = false;
chgState = true;
newState = WaitEstablish;
}
}
}
else
chgState = processUFrame(frame,newState,confirmation);
TelEngine::destruct(frame);
// Change state ?
if (!chgState)
return true;
reset();
changeState(newState,"received frame");
switch (newState) {
case Established:
timer(false,true);
lock.drop();
if (m_management)
m_management->multipleFrameEstablished(localTei(),confirmation,false,this);
else
multipleFrameEstablished(localTei(),confirmation,false);
break;
case Released:
lock.drop();
if (m_management)
m_management->multipleFrameReleased(localTei(),confirmation,false,this);
else
multipleFrameReleased(localTei(),confirmation,false);
break;
case WaitEstablish:
sendUFrame(ISDNFrame::SABME,true,true);
timer(true,false);
break;
case WaitRelease:
sendUFrame(ISDNFrame::DISC,true,true);
timer(true,false);
break;
}
return true;
}
// Process a notification generated by the attached interface
bool ISDNQ921::notify(SignallingInterface::Notification event)
{
Lock lock(l2Mutex());
if (event != SignallingInterface::LinkUp)
m_hwErrors++;
else {
Debug(this,DebugInfo,"Received notification %u: '%s'",
event,lookup(event,SignallingInterface::s_notifName));
return true;
}
if (event == SignallingInterface::LinkDown) {
Debug(this,DebugWarn,"Received notification %u: '%s'",
event,lookup(event,SignallingInterface::s_notifName));
reset();
changeState(Released,"interface down");
lock.drop();
multipleFrameReleased(localTei(),false,false);
if (m_management && !network()) {
teiAssigned(false);
setRi(0);
}
if (autoRestart())
multipleFrame(localTei(),true,false);
return true;
}
#ifdef DEBUG
if (!(m_hwErrors % 250))
Debug(this,DebugNote,"Received notification %u: '%s'. Total=%u",
event,lookup(event,SignallingInterface::s_notifName,"Undefined"),m_hwErrors);
#endif
return true;
}
// Reset data
void ISDNQ921::reset()
{
Lock lock(l2Mutex());
XDebug(this,DebugAll,"Reset, total frames: %d [%p]",m_outFrames.count(),this);
m_remoteBusy = false;
m_timerRecovery = false;
m_rejectSent = false;
m_lastPFBit = false;
m_n200.reset();
m_window.reset();
timer(false,false);
m_outFrames.clear();
m_va = m_vs = m_vr = 0;
}
// Acknoledge pending outgoing frames. See Q.921 5.6.3.2
// Remove ack'd frames from queue. Start idle timer
bool ISDNQ921::ackOutgoingFrames(const ISDNFrame* frame)
{
bool ack = false, unack = false;
// Acknoledge frames with N(S) up to frame->nr() (not including)
for (;;) {
ObjList* obj = m_outFrames.skipNull();
ISDNFrame* f = obj ? static_cast<ISDNFrame*>(obj->get()) : 0;
// Stop when no frames or seq number equals nr
if (!f || frame->nr() == f->ns()) {
if (f && f->sent())
unack = true;
break;
}
ack = true;
XDebug(this,DebugAll,
"Remove acknowledged data frame (%p). Sequence number: %u",f,f->ns());
m_window.dec();
m_outFrames.remove(f,true);
}
// Reset T200 if not in timer-recovery condition and ack some frame
// 5.5.3.2: Note 1: Dont't reset if we've requested a response and haven't got one
if (!m_timerRecovery && ack &&
!(frame->type() != ISDNFrame::I && m_lastPFBit))
timer(false,false);
// Start T200 if we have unacknowledged data and not already started
if (unack && !m_retransTimer.started())
timer(true,false);
return ack;
}
// Receive I/UI (data) frames (See Q.921 5.6.2)
// Send unacknowledged data to upper layer
// Ack pending outgoing data and confirm (by sending any pending data or an RR confirmation)
bool ISDNQ921::processDataFrame(const ISDNFrame* frame, bool ack)
{
// Always accept UI
if (!ack)
return true;
// Acknowledged data: accept only when established
if (state() != Established) {
dropFrame(frame,s_noState);
return false;
}
m_rejectSent = false;
m_remoteBusy = false;
m_vr = frame->ns();
Modulo128::inc(m_vr);
XDebug(this,DebugAll,"Set V(R) to %u",m_vr);
ackOutgoingFrames(frame);
m_va = frame->nr();
XDebug(this,DebugAll,"Set V(A) to %u.",m_va);
// P/F=1: Q.921 5.6.2.1 P/F=0: Q.921 5.6.2.2
if (frame->poll())
sendSFrame(ISDNFrame::RR,false,true);
else
if (!sendOutgoingData())
sendSFrame(ISDNFrame::RR,false,false);
// Start T203 if T200 not started
if (!m_retransTimer.started())
timer(false,true);
return true;
}
// Process received S (supervisory) frames: RR, REJ, RNR
// All Ack outgoing frames. Respond with RR if requested
// RR Send pending frames. Start idle timer
// REJ Send pending frames. Adjust send frame and expected frame counter if necessary
// RNR Adjust send frame counter if necessary
bool ISDNQ921::processSFrame(const ISDNFrame* frame)
{
if (!frame)
return false;
Lock lock(l2Mutex());
if (state() != Established) {
dropFrame(frame,s_noState);
return false;
}
if (frame->type() == ISDNFrame::RR) {
// Ack sent data. Send unsent data
// Respond if it's an unsolicited frame with P/F set to 1
m_remoteBusy = false;
ackOutgoingFrames(frame);
bool sent = sendOutgoingData();
if (frame->poll()) {
// Check if we requested a response. If not, respond if it is a command
if (!m_lastPFBit && frame->command())
sendSFrame(ISDNFrame::RR,false,true);
// Don't reset if we've sent any data
if (!sent) {
m_lastPFBit = false;
timer(false,true);
}
}
if (!m_retransTimer.started() && !m_idleTimer.started())
timer(false,true);
return false;
}
// Q.921 5.6.4: Receiving REJ frames
if (frame->type() == ISDNFrame::REJ) {
m_remoteBusy = false;
// Ack sent data.
ackOutgoingFrames(frame);
// Q.921 5.6.4 a) and b)
bool rspPF = !frame->command() && frame->poll();
if (!m_timerRecovery || (m_timerRecovery && rspPF)) {
m_vs = m_va = frame->nr();
XDebug(this,DebugAll,"Set V(S) and V(A) to %u.",m_vs);
if (!m_timerRecovery && frame->command() && frame->poll())
sendSFrame(ISDNFrame::RR,false,true);
// Retransmit only if we didn't sent a supervisory frame
if (!m_lastPFBit) {
bool t200 = sendOutgoingData(true);
timer(t200,!t200);
}
if (!m_timerRecovery && rspPF)
Debug(this,DebugNote,"Frame (%p) is a REJ response with P/F set",frame);
m_timerRecovery = false;
return false;
}
// Q.921 5.6.4 c)
m_va = frame->nr();
XDebug(this,DebugAll,"Set V(A) to %u.",m_va);
if (frame->command() && frame->poll())
sendSFrame(ISDNFrame::RR,false,true);
return false;
}
// Q.921 5.6.5: Receiving RNR frames
if (frame->type() == ISDNFrame::RNR) {
m_remoteBusy = true;
// Ack sent data.
ackOutgoingFrames(frame);
// Respond
if (frame->poll()) {
if (frame->command())
sendSFrame(ISDNFrame::RR,false,true);
else {
m_timerRecovery = false;
m_vs = frame->nr();
XDebug(this,DebugAll,"Set V(S) to %u.",m_vs);
}
}
if (!m_lastPFBit)
timer(true,false);
return false;
}
dropFrame(frame,s_noState);
return false;
}
// Receive U frames: UA, DM, SABME, DISC, FRMR
// UA If P/F = 0: DROP - not a valid response
// State is Wait...: it's a valid response: notify layer 3 and change state
// Otherwise: DROP
// DM State is Established or Released
// P/F = 0: It's an establish request. Send SABME. Change state
// P/F = 1: If state is Established and timer recovery: schedule establish
// State is WaitEstablish or WaitRelease and P/F = 1: Release. Notify layer 3
// Otherwise: DROP
// SABME State is Established or Released: Confirm. Notify layer 3. Reset
// State is WaitEstablish: Just confirm
// State is WaitRelease: Send DM. Release. Notify layer 3
// DISC State is Established: Confirm. Release. Notify layer 3
// State is Released: Just send a DM response
// State is WaitEstablish: Send DM response. Release. Notify layer 3
// State is WaitRelease: Just confirm
// FRMR If state is Established: re-establish
// Otherwise: DROP
bool ISDNQ921::processUFrame(const ISDNFrame* frame, State& newState,
bool& confirmation)
{
switch (frame->type()) {
case ISDNFrame::UA:
if (!(frame->poll() &&
(state() == WaitEstablish || state() == WaitRelease)))
break;
newState = (state() == WaitEstablish ? Established : Released);
confirmation = true;
return true;
case ISDNFrame::DM:
if (state() == Established || state() == Released) {
if (!frame->poll()) {
newState = WaitEstablish;
return true;
}
if (state() == Established && m_timerRecovery) {
m_pendingDMSabme = true;
return false;
}
}
if (frame->poll()) {
newState = Released;
confirmation = true;
return true;
}
break;
case ISDNFrame::SABME:
if (state() == Established || state() == Released) {
sendUFrame(ISDNFrame::UA,false,frame->poll());
newState = Established;
confirmation = false;
return true;
}
if (state() == WaitEstablish) {
sendUFrame(ISDNFrame::UA,false,frame->poll());
return false;
}
sendUFrame(ISDNFrame::DM,false,frame->poll());
newState = Released;
confirmation = true;
return true;
case ISDNFrame::DISC:
switch (state()) {
case Established:
sendUFrame(ISDNFrame::UA,false,frame->poll());
newState = Released;
confirmation = false;
return true;
case Released:
sendUFrame(ISDNFrame::DM,false,frame->poll());
return false;
case WaitEstablish:
sendUFrame(ISDNFrame::DM,false,frame->poll());
newState = Released;
confirmation = true;
return true;
case WaitRelease:
sendUFrame(ISDNFrame::UA,false,frame->poll());
return false;
}
break;
case ISDNFrame::FRMR:
if (state() == Established) {
newState = WaitEstablish;
return true;
}
break;
default: ;
}
dropFrame(frame,s_noState);
return false;
}
// Accept frame according to Q.921 5.8.5. Reasons to reject:
// Unknown command/response
// Invalid N(R)
// Information field too long
// Update receive counters
bool ISDNQ921::acceptFrame(ISDNFrame* frame, bool& reject)
{
reject = false;
// Update received frames
m_rxFrames++;
// Check frame only if it's not already invalid
for (; frame->error() < ISDNFrame::Invalid;) {
// Check SAPI/TEI
if (frame->sapi() != localSapi() || frame->tei() != localTei()) {
frame->m_error = ISDNFrame::ErrInvalidAddress;
break;
}
// Drop out of range I frames
if (frame->type() == ISDNFrame::I && frame->ns() != m_vr) {
frame->m_error = ISDNFrame::ErrTxSeqNo;
break;
}
// Check DISC/SABME commands and UA/DM responses
if (((frame->type() == ISDNFrame::SABME || frame->type() == ISDNFrame::DISC) &&
!frame->command()) ||
((frame->type() == ISDNFrame::UA || frame->type() == ISDNFrame::DM) &&
frame->command())) {
Debug(this,DebugMild,
"Received '%s': The remote peer has the same data link side type",
frame->name());
frame->m_error = ISDNFrame::ErrInvalidCR;
break;
}
// We don't support XID
if (frame->type() == ISDNFrame::XID) {
frame->m_error = ISDNFrame::ErrUnsupported;
break;
}
// Check N(R) for I or S frames (N(R) is set to 0xFF for U frames):
// N(R) should be between V(A) and V(S)
if (frame->nr() < 128 && !Modulo128::between(frame->nr(),m_va,m_vs)) {
frame->m_error = ISDNFrame::ErrRxSeqNo;
break;
}
// Check data length
if (frame->dataLength() > maxUserData()) {
frame->m_error = ISDNFrame::ErrDataLength;
break;
}
break;
}
// Accepted
if (frame->error() < ISDNFrame::Invalid)
return true;
// Frame is invalid. Reject or drop ?
if (frame->error() == ISDNFrame::ErrUnknownCR ||
frame->error() == ISDNFrame::ErrRxSeqNo ||
frame->error() == ISDNFrame::ErrDataLength) {
// Check if the state allows the rejection. Not allowed if:
// - Not in multiple frame operation mode
if (state() == Established) {
m_rxRejectedFrames++;
reject = true;
return false;
}
}
dropFrame(frame,ISDNFrame::typeName(frame->error()));
return false;
}
void ISDNQ921::dropFrame(const ISDNFrame* frame, const char* reason)
{
m_rxDroppedFrames++;
DDebug(this,DebugNote,
"Dropping frame (%p): %s. Reason: %s. V(S),V(R),V(A)=%u,%u,%u",
frame,frame->name(),reason,m_vs,m_vr,m_va);
}
// Send U frames except for UI frames
bool ISDNQ921::sendUFrame(ISDNFrame::Type type, bool command, bool pf,
bool retrans)
{
switch (type) {
case ISDNFrame::SABME:
case ISDNFrame::DISC:
case ISDNFrame::DM:
case ISDNFrame::UA:
case ISDNFrame::FRMR:
break;
default:
return false;
}
// Create and send frame
// U frames don't have an N(R) control data
ISDNFrame* f = new ISDNFrame(type,command,network(),localSapi(),localTei(),pf);
f->sent(retrans);
bool result = sendFrame(f);
TelEngine::destruct(f);
return result;
}
// Send S frames
bool ISDNQ921::sendSFrame(ISDNFrame::Type type, bool command, bool pf)
{
if (!(type == ISDNFrame::RR ||
type == ISDNFrame::RNR ||
type == ISDNFrame::REJ))
return false;
// Create and send frame
ISDNFrame* f = new ISDNFrame(type,command,network(),localSapi(),localTei(),pf,m_vr);
bool result = sendFrame(f);
TelEngine::destruct(f);
return result;
}
// Send a frame to remote peer. Dump data on success if we have a dumper
bool ISDNQ921::sendFrame(const ISDNFrame* frame)
{
if (!frame)
return false;
// This should never happen !!!
if (frame->type() >= ISDNFrame::Invalid) {
Debug(this,DebugWarn,"Refusing to send '%s' frame",frame->name());
return false;
}
// Print frame
if (debugAt(DebugInfo) && m_printFrames && !m_errorSend && frame->type() != ISDNFrame::UI) {
String tmp;
frame->toString(tmp,m_extendedDebug);
Debug(this,DebugInfo,"Sending frame (%p):%s",
frame,tmp.c_str());
}
bool result = m_management ? m_management->sendFrame(frame,this) :
SignallingReceiver::transmitPacket(frame->buffer(),false,SignallingInterface::Q921);
// Dump frame if no error and we have a dumper
if (result) {
m_txFrames++;
dump(frame->buffer(),true);
m_errorSend = false;
}
else {
m_txFailFrames++;
if (!m_errorSend)
Debug(this,DebugNote,"Error sending frame (%p): %s",frame,frame->name());
m_errorSend = true;
}
return result;
}
// Send (or re-send) enqueued data frames
bool ISDNQ921::sendOutgoingData(bool retrans)
{
bool sent = false;
for (;;) {
if (m_remoteBusy || m_window.empty())
break;
ObjList* obj = m_outFrames.skipNull();
// Queue empty ?
if (!obj)
break;
ISDNFrame* frame = 0;
// Not a retransmission: skip already sent frames
if (!retrans)
for (; obj; obj = obj->skipNext()) {
frame = static_cast<ISDNFrame*>(obj->get());
if (!frame->sent())
break;
}
// Send the remaining unsent frames in window or
// the whole queue if it is a retransmission
for (; obj ; obj = obj->skipNext()) {
frame = static_cast<ISDNFrame*>(obj->get());
// Update frame receive sequence number
frame->update(0,&m_vr);
XDebug(this,DebugAll,
"Sending data frame (%p). Sequence number: %u. Retransmission: %s",
frame,frame->ns(),String::boolText(frame->sent()));
// T200
if (!m_retransTimer.started())
timer(true,false);
// Send
sendFrame(frame);
sent = true;
frame->sent(true);
}
break;
}
return sent;
}
// Start/stop idle or retransmission timers
void ISDNQ921::timer(bool start, bool t203, u_int64_t time)
{
if (start) {
if (m_idleTimer.started()) {
m_idleTimer.stop();
XDebug(this,DebugAll,"T203 stopped");
}
// Start anyway. Even if already started
if (!time)
time = Time::msecNow();
m_retransTimer.start(time);
XDebug(this,DebugAll,"T200 started. Transmission counter: %u",
m_n200.count());
}
else {
m_n200.reset();
if (m_retransTimer.started()) {
m_retransTimer.stop();
XDebug(this,DebugAll,"T200 stopped");
}
if (t203) {
if (!m_idleTimer.started()) {
if (!time)
time = Time::msecNow();
m_idleTimer.start(time);
XDebug(this,DebugAll,"T203 started");
}
}
else if (m_idleTimer.started()) {
m_idleTimer.stop();
XDebug(this,DebugAll,"T203 stopped");
}
}
}
/**
* ISDNQ921Management
*/
// Constructor
ISDNQ921Management::ISDNQ921Management(const NamedList& params, const char* name, bool net)
: SignallingComponent(name,&params,"isdn-q921-mgm"),
ISDNLayer2(params,name),
SignallingReceiver(name),
SignallingDumpable(SignallingDumper::Q921,network()),
m_teiManTimer(0), m_teiTimer(0)
{
#ifdef DEBUG
if (debugAt(DebugAll)) {
String tmp;
params.dump(tmp,"\r\n ",'\'',true);
Debug(this,DebugAll,"ISDNQ921Management::ISDNQ921Management(%p,'%s',%s) [%p]%s",
&params,name,String::boolText(net),this,tmp.c_str());
}
#endif
String baseName = toString();
m_network = net;
m_teiManTimer.interval(params,"t202",2500,2600,false);
m_teiTimer.interval(params,"t201",1000,5000,false);
setDumper(params.getValue(YSTRING("layer2dump")));
bool set0 = true;
if (baseName.endsWith("Management")) {
baseName = baseName.substr(0,baseName.length()-10);
set0 = false;
}
// If we are NET create one ISDNQ921 for each possible TEI
for (int i = 0; i < 127; i++) {
if (network() || (i == 0)) {
String qName = baseName;
if (!network())
qName << "-CPE";
else if (set0 || (i != 0))
qName << "-" << i;
m_layer2[i] = new ISDNQ921(params,qName,this,i);
m_layer2[i]->ISDNLayer2::attach(this);
}
else
m_layer2[i] = 0;
}
if (!network()) {
m_layer2[0]->teiAssigned(false);
m_teiManTimer.start();
}
}
ISDNQ921Management::~ISDNQ921Management()
{
Lock lock(l2Mutex());
ISDNLayer2::attach((ISDNLayer3*)0);
TelEngine::destruct(SignallingReceiver::attach(0));
for (int i = 0; i < 127; i++)
TelEngine::destruct(m_layer2[i]);
}
bool ISDNQ921Management::initialize(const NamedList* config)
{
#ifdef DEBUG
String tmp;
if (config && debugAt(DebugAll))
config->dump(tmp,"\r\n ",'\'',true);
Debug(this,DebugInfo,"ISDNQ921Management::initialize(%p) [%p]%s",config,this,tmp.c_str());
#endif
if (config)
debugLevel(config->getIntValue(YSTRING("debuglevel_q921mgmt"),
config->getIntValue(YSTRING("debuglevel"),-1)));
if (config && !iface()) {
NamedList params("");
if (resolveConfig(YSTRING("sig"),params,config) ||
resolveConfig(YSTRING("basename"),params,config)) {
params.addParam("basename",params);
params.assign(params + "/D");
fixParams(params,config);
SignallingInterface* ifc = YSIGCREATE(SignallingInterface,&params);
if (!ifc)
return false;
SignallingReceiver::attach(ifc);
if (ifc->initialize(&params))
SignallingReceiver::control(SignallingInterface::Enable);
else
TelEngine::destruct(SignallingReceiver::attach(0));
}
}
return 0 != iface();
}
void ISDNQ921Management::engine(SignallingEngine* eng)
{
SignallingComponent::engine(eng);
for (int i = 0; i < 127; i++)
if (m_layer2[i])
m_layer2[i]->engine(eng);
}
void ISDNQ921Management::cleanup()
{
Lock lock(l2Mutex());
for (int i = 0;i < 127; i++)
if (m_layer2[i])
m_layer2[i]->cleanup();
}
bool ISDNQ921Management::multipleFrame(u_int8_t tei, bool establish, bool force)
{
if (tei >= 127)
return false;
m_sapi = Q921_SAPI_MANAGEMENT;
l2Mutex().lock();
RefPointer<ISDNQ921> q921 = m_layer2[network() ? tei : 0];
l2Mutex().unlock();
return q921 && q921->multipleFrame(tei,establish,force);
}
bool ISDNQ921Management::sendFrame(const ISDNFrame* frame, const ISDNQ921* q921)
{
if (!frame)
return false;
Lock lock(l2Mutex());
if (SignallingReceiver::transmitPacket(frame->buffer(),false,SignallingInterface::Q921)) {
dump(frame->buffer(),true);
return true;
}
return false;
}
bool ISDNQ921Management::sendData(const DataBlock& data, u_int8_t tei, bool ack)
{
if (tei > Q921_TEI_BROADCAST)
return false;
if (tei == Q921_TEI_BROADCAST)
ack = false;
int auxTei = tei;
Lock lock(l2Mutex());
if (!network()) {
if (m_layer2[0] && m_layer2[0]->teiAssigned())
auxTei = 0;
else
return false;
}
if (ack)
return m_layer2[auxTei] && m_layer2[auxTei]->sendData(data,tei,true);
// P/F bit is always false for UI frames. See Q.921 5.2.2
ISDNFrame* f = new ISDNFrame(false,network(),0,tei,false,data);
bool ok = sendFrame(f);
lock.drop();
TelEngine::destruct(f);
return ok;
}
void ISDNQ921Management::multipleFrameEstablished(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2)
{
m_layer3Mutex.lock();
RefPointer<ISDNLayer3> l3 = m_layer3;
m_layer3Mutex.unlock();
if (l3)
l3->multipleFrameEstablished(tei,confirm,timeout,layer2);
else
Debug(this,DebugNote,"'Established' notification. No Layer 3 attached");
}
void ISDNQ921Management::multipleFrameReleased(u_int8_t tei, bool confirm, bool timeout, ISDNLayer2* layer2)
{
m_layer3Mutex.lock();
RefPointer<ISDNLayer3> l3 = m_layer3;
m_layer3Mutex.unlock();
if (l3)
l3->multipleFrameReleased(tei,confirm,timeout,layer2);
else
Debug(this,DebugNote,"'Released' notification. No Layer 3 attached");
}
void ISDNQ921Management::dataLinkState(u_int8_t tei, bool cmd, bool value, ISDNLayer2* layer2)
{
m_layer3Mutex.lock();
RefPointer<ISDNLayer3> l3 = m_layer3;
m_layer3Mutex.unlock();
if (l3)
l3->dataLinkState(tei,cmd,value,layer2);
else
Debug(this,DebugNote,"Data link notification. No Layer 3 attached");
}
void ISDNQ921Management::receiveData(const DataBlock& data, u_int8_t tei, ISDNLayer2* layer2)
{
m_layer3Mutex.lock();
RefPointer<ISDNLayer3> l3 = m_layer3;
m_layer3Mutex.unlock();
if (!network()) {
l2Mutex().lock();
if (m_layer2[0])
tei = m_layer2[0]->localTei();
l2Mutex().unlock();
}
if (l3)
l3->receiveData(data,tei,layer2);
else
Debug(this,DebugNote,"Data received. No Layer 3 attached");
}
// Process a Signalling Packet received by the interface
bool ISDNQ921Management::receivedPacket(const DataBlock& packet)
{
Lock lock(l2Mutex());
ISDNFrame* frame = parsePacket(packet);
if (!frame)
return false;
if (frame->type() < ISDNFrame::Invalid)
dump(frame->buffer(),false);
// Non UI frame (even invalid): send it to the appropriate Layer 2
if (frame->type() != ISDNFrame::UI) {
if (network()) {
if (m_layer2[frame->tei()] && m_layer2[frame->tei()]->m_ri) {
lock.drop();
return m_layer2[frame->tei()]->receivedFrame(frame);
}
sendTeiManagement(ISDNFrame::TeiRemove,0,frame->tei());
lock.drop();
TelEngine::destruct(frame);
return false;
}
else if (m_layer2[0] && m_layer2[0]->m_ri && m_layer2[0]->localTei() == frame->tei()) {
lock.drop();
return m_layer2[0]->receivedFrame(frame);
}
return false;
}
if (!processTeiManagement(frame)) {
DataBlock tmp;
frame->getData(tmp);
u_int8_t tei = frame->tei();
TelEngine::destruct(frame);
receiveData(tmp,tei,m_layer2[0]);
return true;
}
// FIXME
TelEngine::destruct(frame);
return true;
}
// Periodically called method to take care of timers
void ISDNQ921Management::timerTick(const Time& when)
{
if (network()) {
if (m_teiTimer.started() && m_teiTimer.timeout(when.msec())) {
for (u_int8_t i = 0; i < 127; i++) {
if (m_layer2[i] && !m_layer2[i]->m_checked) {
m_layer2[i]->setRi(0);
m_layer2[i]->teiAssigned(false);
multipleFrameReleased(i,false,true,this);
}
}
m_teiTimer.stop();
}
}
else if (m_layer2[0]) {
if (m_layer2[0]->teiAssigned())
m_teiManTimer.stop();
else if (!m_teiManTimer.started())
m_teiManTimer.start();
else if (m_teiManTimer.timeout(when.msec())) {
m_teiManTimer.stop();
u_int16_t ri = m_layer2[0]->m_ri;
while (!ri)
ri = (u_int16_t)Random::random();
m_layer2[0]->m_tei = 0;
m_layer2[0]->setRi(ri);
sendTeiManagement(ISDNFrame::TeiReq,ri,Q921_TEI_BROADCAST);
}
}
}
// Forward interface notifications to controlled Q.921
bool ISDNQ921Management::notify(SignallingInterface::Notification event)
{
DDebug(this,DebugInfo,"Received notification %u: '%s'",
event,lookup(event,SignallingInterface::s_notifName));
for (u_int8_t i = 0; i < 127; i++)
if (m_layer2[i])
m_layer2[i]->notify(event);
return true;
}
// Process TEI management frames according to their type
bool ISDNQ921Management::processTeiManagement(ISDNFrame* frame)
{
if (!frame)
return false;
if (!frame->checkTeiManagement())
return false;
DataBlock data;
frame->getData(data);
u_int8_t ai = ISDNFrame::getAi(data);
u_int16_t ri = ISDNFrame::getRi(data);
u_int8_t type = ISDNFrame::getType(data);
XDebug(this,DebugAll,"Management frame type=0x%02X ri=%u ai=%u",type,ri,ai);
switch (type) {
case ISDNFrame::TeiReq:
processTeiRequest(ri,ai,frame->poll());
break;
case ISDNFrame::TeiRemove:
processTeiRemove(ai);
break;
case ISDNFrame::TeiCheckReq:
processTeiCheckRequest(ai,frame->poll());
break;
case ISDNFrame::TeiAssigned:
processTeiAssigned(ri,ai);
break;
case ISDNFrame::TeiDenied:
processTeiDenied(ri);
break;
case ISDNFrame::TeiCheckRsp:
processTeiCheckResponse(ri,ai);
break;
case ISDNFrame::TeiVerify:
processTeiVerify(ai,frame->poll());
break;
default:
Debug(this,DebugNote,"Unknown management frame type 0x%02X",type);
}
return true;
}
// Build and send a TEI management frame
bool ISDNQ921Management::sendTeiManagement(ISDNFrame::TeiManagement type,
u_int16_t ri, u_int8_t ai, u_int8_t tei, bool pf)
{
DataBlock data;
if (!ISDNFrame::buildTeiManagement(data,type,ri,ai)) {
Debug(this,DebugNote,"Could not build TEI management frame");
return false;
}
ISDNFrame* frame = new ISDNFrame(false,network(),
Q921_SAPI_MANAGEMENT,tei,pf,data);
bool ok = sendFrame(frame);
TelEngine::destruct(frame);
return ok;
}
// We are NET, a CPE has requested a TEI assignment
void ISDNQ921Management::processTeiRequest(u_int16_t ri, u_int8_t ai, bool pf)
{
if (!network() || !ri)
return;
if (ai < 127 && m_layer2[ai] && m_layer2[ai]->m_ri == ri) {
// TEI already assigned to same reference number, confirm it
sendTeiManagement(ISDNFrame::TeiAssigned,ri,ai,Q921_TEI_BROADCAST,pf);
return;
}
u_int8_t i;
for (i = 0; i < 127; i++) {
if (!m_layer2[i])
continue;
if (m_layer2[i]->m_ri == ri) {
// Reference number already used for different TEI
sendTeiManagement(ISDNFrame::TeiDenied,ri,ai,Q921_TEI_BROADCAST,pf);
return;
}
}
for (i = 64; i < 127; i++) {
if (m_layer2[i]->m_ri != 0)
continue;
// Found a free dynamic TEI slot, assign to given reference number
if (sendTeiManagement(ISDNFrame::TeiAssigned,ri,i,Q921_TEI_BROADCAST,pf)) {
m_layer2[i]->setRi(ri);
m_layer2[i]->reset();
}
return;
}
// All dynamic TEI slots are in use, deny new request
sendTeiManagement(ISDNFrame::TeiDenied,ri,Q921_TEI_BROADCAST,pf);
m_teiTimer.stop();
// Mark all dynamic TEI slots as not checked and ask them to check
for (i = 64; i < 127; i++)
if (m_layer2[i])
m_layer2[i]->m_checked = false;
sendTeiManagement(ISDNFrame::TeiCheckReq,0,Q921_TEI_BROADCAST);
m_teiTimer.start();
}
// We are CPE, NET asked us to remove our TEI
void ISDNQ921Management::processTeiRemove(u_int8_t ai)
{
if (network())
return;
u_int8_t tei = m_layer2[0]->localTei();
if ((ai == tei) || (ai == Q921_TEI_BROADCAST && tei >= 64)) {
Debug(this,((tei < 64) ? DebugMild : DebugInfo),"Removing our TEI %u",tei);
m_layer2[0]->teiAssigned(false);
m_layer2[0]->setRi(0);
multipleFrameReleased(ai,false,false,this);
m_teiManTimer.start();
}
}
// We are CPE, NET is checking our TEI
void ISDNQ921Management::processTeiCheckRequest(u_int8_t ai, bool pf)
{
if (network())
return;
if (m_layer2[0]->m_ri && ((ai == Q921_TEI_BROADCAST) || (ai == m_layer2[0]->localTei())))
sendTeiManagement(ISDNFrame::TeiCheckRsp,m_layer2[0]->m_ri,ai,Q921_TEI_BROADCAST,pf);
}
// We are NET and received a TEI check response to our request
void ISDNQ921Management::processTeiCheckResponse(u_int16_t ri, u_int8_t ai)
{
if (!network())
return;
if ((ai >= 127) || !m_layer2[ai])
return;
if (m_layer2[ai]->m_ri == ri)
m_layer2[ai]->m_checked = true;
else if (sendTeiManagement(ISDNFrame::TeiRemove,ri,ai))
m_layer2[ai]->setRi(0);
}
// We are CPE and the NET assigned a TEI, possibly to us
void ISDNQ921Management::processTeiAssigned(u_int16_t ri, u_int8_t ai)
{
if (network())
return;
if (m_layer2[0]->m_ri != ri)
return;
m_teiManTimer.stop();
m_layer2[0]->m_tei = ai;
m_layer2[0]->teiAssigned(true);
multipleFrame(ai,true,true);
}
// We are CPE and the NET denied assigning a TEI, possibly to us
void ISDNQ921Management::processTeiDenied(u_int16_t ri)
{
if (network())
return;
if (m_layer2[0]->m_ri != ri)
return;
m_layer2[0]->setRi(0);
m_teiManTimer.start();
}
// We are NET, a CPE is asking to be verified
void ISDNQ921Management::processTeiVerify(u_int8_t ai, bool pf)
{
if (!network())
return;
if ((ai < 127) && m_layer2[ai] && m_layer2[ai]->m_ri)
sendTeiManagement(ISDNFrame::TeiCheckReq,0,ai,Q921_TEI_BROADCAST,pf);
}
/**
* ISDNQ921Passive
*/
// Constructor. Set data members. Print them
ISDNQ921Passive::ISDNQ921Passive(const NamedList& params, const char* name)
: SignallingComponent(name,&params,"isdn-q921-passive"),
ISDNLayer2(params,name),
SignallingReceiver(name),
SignallingDumpable(SignallingDumper::Q921,network()),
m_checkLinkSide(false),
m_idleTimer(0),
m_lastFrame(255),
m_rxFrames(0),
m_rxDroppedFrames(0),
m_hwErrors(0),
m_printFrames(true),
m_extendedDebug(false),
m_errorReceive(false)
{
#ifdef DEBUG
if (debugAt(DebugAll)) {
String tmp;
params.dump(tmp,"\r\n ",'\'',true);
Debug(this,DebugAll,"ISDNQ921Passive::ISDNQ921Passive(%p,'%s') [%p]%s",
&params,name,this,tmp.c_str());
}
#endif
m_idleTimer.interval(params,"idletimeout",4000,30000,false);
m_checkLinkSide = detectType();
setDebug(params.getBoolValue(YSTRING("print-frames"),false),
params.getBoolValue(YSTRING("extended-debug"),false));
DDebug(this,DebugInfo,
"ISDN Passive Data Link type=%s autodetect=%s idle-timeout=%u [%p]",
linkSide(network()),String::boolText(detectType()),
(unsigned int)m_idleTimer.interval(),this);
m_idleTimer.start();
// Try to dump from specific parameter, fall back to generic
const char* dump = network() ? "layer2dump-net" : "layer2dump-cpe";
setDumper(params.getValue(dump,params.getValue(YSTRING("layer2dump"))));
}
// Destructor
ISDNQ921Passive::~ISDNQ921Passive()
{
Lock lock(l2Mutex());
ISDNLayer2::attach(0);
TelEngine::destruct(SignallingReceiver::attach(0));
cleanup();
DDebug(this,DebugAll,
"ISDN Passive Data Link destroyed. Frames: recv=%u dropped=%u. HW errors=%u [%p]",
(unsigned int)m_rxFrames,(unsigned int)m_rxDroppedFrames,(unsigned int)m_hwErrors,this);
}
bool ISDNQ921Passive::initialize(const NamedList* config)
{
#ifdef DEBUG
String tmp;
if (config && debugAt(DebugAll))
config->dump(tmp,"\r\n ",'\'',true);
Debug(this,DebugInfo,"ISDNQ921Passive::initialize(%p) [%p]%s",config,this,tmp.c_str());
#endif
if (config) {
debugLevel(config->getIntValue(YSTRING("debuglevel_q921"),
config->getIntValue(YSTRING("debuglevel"),-1)));
setDebug(config->getBoolValue(YSTRING("print-frames"),false),
config->getBoolValue(YSTRING("extended-debug"),false));
}
if (config && !iface()) {
NamedList params("");
if (resolveConfig(YSTRING("sig"),params,config) ||
resolveConfig(YSTRING("basename"),params,config)) {
params.addParam("basename",params);
params.assign(params + "/D");
params.addParam("readonly",String::boolText(true));
fixParams(params,config);
SignallingInterface* ifc = YSIGCREATE(SignallingInterface,&params);
if (!ifc)
return false;
SignallingReceiver::attach(ifc);
if (ifc->initialize(&params))
SignallingReceiver::control(SignallingInterface::Enable);
else
TelEngine::destruct(SignallingReceiver::attach(0));
}
}
return 0 != iface();
}
// Reset data
void ISDNQ921Passive::cleanup()
{
Lock lock(l2Mutex());
m_idleTimer.start();
}
// Called periodically by the engine to check timeouts
// Check idle timer. Notify upper layer on timeout
void ISDNQ921Passive::timerTick(const Time& when)
{
Lock lock(l2Mutex(),SignallingEngine::maxLockWait());
if (!(lock.locked() && m_idleTimer.timeout(when.msec())))
return;
// Timeout. Notify layer 3. Restart timer
XDebug(this,DebugNote,"Timeout. Channel was idle for " FMT64 " ms",m_idleTimer.interval());
m_idleTimer.start(when.msec());
lock.drop();
idleTimeout();
}
// Process a packet received by the receiver's interface
bool ISDNQ921Passive::receivedPacket(const DataBlock& packet)
{
if (!packet.length())
return false;
Lock lock(l2Mutex());
XDebug(this,DebugAll,"Received packet (Length: %u)",packet.length());
ISDNFrame* frame = parsePacket(packet);
if (!frame) {
if (!m_errorReceive)
Debug(this,DebugNote,"Received invalid frame (Length: %u)",packet.length());
m_errorReceive = true;
return false;
}
m_errorReceive = false;
// Print & dump
if (debugAt(DebugInfo) && m_printFrames) {
String tmp;
frame->toString(tmp,m_extendedDebug);
Debug(this,DebugInfo,"Received frame (%p):%s",frame,tmp.c_str());
}
if (frame->type() < ISDNFrame::Invalid)
dump(frame->buffer(),false);
// Received enough data to parse. Assume the channel not idle (restart timer)
// If accepted, the frame is a data frame or a unnumbered (SABME,DISC,UA,DM) one
// Drop retransmissions of data frames
// Send data or notification to the upper layer
m_idleTimer.start();
lock.drop();
bool cmd,value;
if (acceptFrame(frame,cmd,value)) {
if (frame->category() == ISDNFrame::Data) {
if (m_lastFrame != frame->ns()) {
DataBlock tmp;
frame->getData(tmp);
m_lastFrame = frame->ns();
receiveData(tmp,localTei());
}
}
else
dataLinkState(localTei(),cmd,value);
}
TelEngine::destruct(frame);
return true;
}
// Process a notification generated by the attached interface
bool ISDNQ921Passive::notify(SignallingInterface::Notification event)
{
Lock lock(l2Mutex());
if (event != SignallingInterface::LinkUp)
m_hwErrors++;
else {
Debug(this,DebugInfo,"Received notification %u: '%s'",
event,lookup(event,SignallingInterface::s_notifName));
return true;
}
if (event == SignallingInterface::LinkDown)
Debug(this,DebugWarn,"Received notification %u: '%s'",
event,lookup(event,SignallingInterface::s_notifName));
#ifdef DEBUG
else if (!(m_hwErrors % 250))
Debug(this,DebugNote,"Received notification %u: '%s'. Total=%u",
event,lookup(event,SignallingInterface::s_notifName,"Undefined"),m_hwErrors);
#endif
return true;
}
// Accept frame according to Q.921 5.8.5
// Filter received frames. Accept only frames that would generate a notification to the upper layer:
// UI/I and valid SABME/DISC/UA/DM
bool ISDNQ921Passive::acceptFrame(ISDNFrame* frame, bool& cmd, bool& value)
{
// Update received frames
m_rxFrames++;
// Frame already invalid
if (frame->error() >= ISDNFrame::Invalid)
return dropFrame(frame);
// Check SAPI/TEI
if (frame->sapi() != localSapi() || frame->tei() != localTei())
return dropFrame(frame,ISDNFrame::typeName(ISDNFrame::ErrInvalidAddress));
// Valid UI/I
if (frame->category() == ISDNFrame::Data)
return true;
// Check DISC/SABME commands and UA/DM responses
cmd = (frame->type() == ISDNFrame::SABME || frame->type() == ISDNFrame::DISC);
bool response = (frame->type() == ISDNFrame::UA || frame->type() == ISDNFrame::DM);
if (m_checkLinkSide &&
((cmd && !frame->command()) || (response && frame->command()))) {
if (detectType()) {
m_checkLinkSide = false;
changeType();
}
else {
Debug(this,DebugMild,
"Received '%s': The remote peer has the same data link side type",
frame->name());
return dropFrame(frame,ISDNFrame::typeName(ISDNFrame::ErrInvalidCR));
}
}
// Normally, SABME/DISC commands and UA/DM responses should have the P/F bit set
if (cmd || response) {
if (!frame->poll())
#ifndef Q921_PASIVE_NOCHECK_PF
return dropFrame(frame,"P/F bit not set");
#else
DDebug(this,DebugNote,"Received '%s' without P/F bit set",frame->name());
#endif
m_checkLinkSide = detectType();
if (cmd)
value = (frame->type() == ISDNFrame::SABME);
else
value = (frame->type() == ISDNFrame::UA);
return true;
}
// Drop valid frames without debug message (it would be too much) and without counting them:
// Supervisory frames (Since we don't synchronize, we don't process them)
// Unsupported valid unnumbered frames (e.g. XID, UA/DM with P/F bit set ....)
if (frame->type() < ISDNFrame::Invalid)
return false;
return dropFrame(frame);
}
bool ISDNQ921Passive::dropFrame(const ISDNFrame* frame, const char* reason)
{
m_rxDroppedFrames++;
DDebug(this,DebugNote,"Dropping frame (%p): %s. Reason: %s",
frame,frame->name(),reason ? reason : ISDNFrame::typeName(frame->error()));
return false;
}
/**
* ISDNLayer2
*/
const TokenDict ISDNLayer2::m_states[] = {
{"Released", Released},
{"WaitEstablish", WaitEstablish},
{"Established", Established},
{"WaitRelease", WaitRelease},
{0,0}
};
ISDNLayer2::ISDNLayer2(const NamedList& params, const char* name, u_int8_t tei)
: SignallingComponent(name,&params),
m_layer3(0),
m_layerMutex(true,"ISDNLayer2::layer"),
m_layer3Mutex(true,"ISDNLayer2::layer3"),
m_state(Released),
m_network(false),
m_detectType(false),
m_sapi(0),
m_tei(0),
m_ri(0),
m_lastUp(0),
m_checked(false),
m_teiAssigned(false),
m_autoRestart(true),
m_maxUserData(260)
{
XDebug(this,DebugAll,"ISDNLayer2 '%s' comp=%p [%p]",name,static_cast<const SignallingComponent*>(this),this);
m_network = params.getBoolValue(YSTRING("network"),false);
m_detectType = params.getBoolValue(YSTRING("detect"),false);
int tmp = params.getIntValue(YSTRING("sapi"),0);
m_sapi = (tmp >= 0 && tmp <= Q921_SAPI_MANAGEMENT) ? tmp : 0;
tmp = params.getIntValue(YSTRING("tei"),tei);
m_tei = (tmp >= 0 && tmp < Q921_TEI_BROADCAST) ? tmp : 0;
teiAssigned(true);
m_autoRestart = params.getBoolValue(YSTRING("auto-restart"),true);
m_maxUserData = params.getIntValue(YSTRING("maxuserdata"),260);
if (!m_maxUserData)
m_maxUserData = 260;
}
ISDNLayer2::~ISDNLayer2()
{
if (m_layer3)
Debug(this,DebugCrit,"Destroyed with Layer 3 (%p) attached",m_layer3);
attach(0);
XDebug(this,DebugAll,"~ISDNLayer2");
}
// Attach an ISDN Q.931 Layer 3 if the given parameter is different from the one we have
void ISDNLayer2::attach(ISDNLayer3* layer3)
{
Lock lock(m_layer3Mutex);
if (m_layer3 == layer3)
return;
cleanup();
ISDNLayer3* tmp = m_layer3;
m_layer3 = layer3;
lock.drop();
if (tmp) {
if (engine() && engine()->find(tmp))
tmp->attach(0);
Debug(this,DebugAll,"Detached L3 (%p,'%s') [%p]",
tmp,tmp->toString().safe(),this);
}
if (!layer3)
return;
Debug(this,DebugAll,"Attached L3 (%p,'%s') [%p]",layer3,layer3->toString().safe(),this);
insert(layer3);
layer3->attach(this);
}
// Parse a received packet, create a frame from it
ISDNFrame* ISDNLayer2::parsePacket(const DataBlock& packet)
{
if (packet.null())
return 0;
Lock lock(m_layerMutex);
ISDNFrame* frame = ISDNFrame::parse(packet,this);
#ifdef XDEBUG
if (frame) {
if (debugAt(DebugAll)) {
String tmp;
frame->toString(tmp,true);
Debug(this,DebugInfo,"Parsed frame (%p):%s",frame,tmp.c_str());
}
}
else
Debug(this,DebugWarn,"Packet with length %u invalid [%p]",packet.length(),this);
#endif
return frame;
}
// Indication/confirmation of 'multiple frame acknowledged' mode established
void ISDNLayer2::multipleFrameEstablished(u_int8_t tei, bool confirmation, bool timeout)
{
m_layer3Mutex.lock();
RefPointer<ISDNLayer3> tmp = m_layer3;
m_layer3Mutex.unlock();
if (tmp)
tmp->multipleFrameEstablished(tei,confirmation,timeout,this);
else
Debug(this,DebugNote,"'Established' notification. No Layer 3 attached");
}
// Indication/confirmation of 'multiple frame acknowledged' mode released
void ISDNLayer2::multipleFrameReleased(u_int8_t tei, bool confirmation, bool timeout)
{
m_layer3Mutex.lock();
RefPointer<ISDNLayer3> tmp = m_layer3;
m_layer3Mutex.unlock();
if (tmp)
tmp->multipleFrameReleased(tei,confirmation,timeout,this);
else
Debug(this,DebugNote,"'Released' notification. No Layer 3 attached");
}
// Data link state change command/response
void ISDNLayer2::dataLinkState(u_int8_t tei, bool cmd, bool value)
{
m_layer3Mutex.lock();
RefPointer<ISDNLayer3> tmp = m_layer3;
m_layer3Mutex.unlock();
if (tmp)
tmp->dataLinkState(tei,cmd,value,this);
else
Debug(this,DebugNote,"Data link notification. No Layer 3 attached");
}
// Notify layer 3 of data link idle timeout
void ISDNLayer2::idleTimeout()
{
m_layer3Mutex.lock();
RefPointer<ISDNLayer3> tmp = m_layer3;
m_layer3Mutex.unlock();
if (tmp)
tmp->idleTimeout(this);
else
Debug(this,DebugNote,"Data link idle timeout. No Layer 3 attached");
}
// Indication of received data
void ISDNLayer2::receiveData(const DataBlock& data, u_int8_t tei)
{
m_layer3Mutex.lock();
RefPointer<ISDNLayer3> tmp = m_layer3;
m_layer3Mutex.unlock();
if (tmp)
tmp->receiveData(data,tei,this);
else
Debug(this,DebugNote,"Data received. No Layer 3 attached");
}
// Change TEI ASSIGNED state
void ISDNLayer2::teiAssigned(bool status)
{
Lock lock(m_layerMutex);
if (m_teiAssigned == status)
return;
m_teiAssigned = status;
DDebug(this,DebugAll,"%s 'TEI assigned' state",
m_teiAssigned ? "Enter" : "Exit from");
if (!m_teiAssigned)
cleanup();
}
// Change the data link status while in TEI ASSIGNED state
void ISDNLayer2::changeState(State newState, const char* reason)
{
Lock lock(m_layerMutex);
if (m_state == newState)
return;
if (Established != newState)
m_lastUp = 0;
else if (!m_lastUp)
m_lastUp = Time::secNow();
if (!m_teiAssigned && (newState != Released))
return;
DDebug(this,DebugInfo,"Changing state from '%s' to '%s'%s%s%s",
stateName(m_state),stateName(newState),
(reason ? " (" : ""),c_safe(reason),(reason ? ")" : ""));
m_state = newState;
}
// Change the interface type
bool ISDNLayer2::changeType()
{
Lock lock(m_layerMutex);
Debug(this,DebugNote,"Interface type changed from '%s' to '%s'",
linkSide(m_network),linkSide(!m_network));
m_network = !m_network;
return true;
}
/**
* ISDNFrame
*/
// Flags used to set/get frame type
#define Q921FRAME_U 0x03 // U frame
#define Q921FRAME_S 0x01 // S frame
// U frame: P/F bit
#define Q921FRAME_U_GET_PF 0x10 // Mask to get bit 4: the P/F bit
#define Q921FRAME_U_RESET_PF 0xef // Mask to reset bit 4: the P/F bit
// Masks used to set/get command/response bits
#define Q921FRAME_CR_RR 0x01 // S frame
#define Q921FRAME_CR_UI 0x03 // U frame
#define Q921FRAME_CR_RNR 0x05 // S frame
#define Q921FRAME_CR_REJ 0x09 // S frame
#define Q921FRAME_CR_DM 0x0f // U frame
#define Q921FRAME_CR_DISC 0x43 // U frame
#define Q921FRAME_CR_FRMR 0x87 // U frame
#define Q921FRAME_CR_UA 0x63 // U frame
#define Q921FRAME_CR_SABME 0x6f // U frame
#define Q921FRAME_CR_XID 0xaf // U frame
// Set the address field of a frame header
// buf Destination buffer
// cr Command/response type
// network True if the sender is the network side of the data link
// sapi SAPI value
// tei TEI value
static inline void setAddress(u_int8_t* buf, bool cr, bool network,
u_int8_t sapi, u_int8_t tei)
{
// Bit 0 is always 0. Set SAPI and C/R bit (bit 1)
cr = cr ? ISDNFrame::commandBit(network) : ISDNFrame::responseBit(network);
buf[0] = sapi << 2;
if (cr)
buf[0] |= 0x02;
// Bit 1 is always 1. Set TEI
buf[1] = (tei << 1) | 0x01;
}
// Set the control field of an U frame header
// buf Destination buffer
// cr Command/response value: P/F bit (bit 4) is 0
// pf P/F bit
static inline void setControlU(u_int8_t* buf, u_int8_t cr, bool pf)
{
if (pf)
buf[2] = cr | Q921FRAME_U_GET_PF;
else
buf[2] = cr;
}
// Set the control field of an S or I frame header
// buf Destination buffer
// cr_ns S frame: Command/response value (P/F bit (bit 4) is 0)
// I frame: N(S) value
// nr N(R) value to set
// pf P/F bit
static inline void setControl(u_int8_t* buf, u_int8_t cr_ns, u_int8_t nr, bool pf)
{
buf[2] = cr_ns;
buf[3] = nr << 1;
if (pf)
buf[3] |= 0x01;
}
const TokenDict ISDNFrame::s_types[] = {
{"DISC", DISC},
{"DM", DM},
{"FRMR", FRMR},
{"I", I},
{"REJ", REJ},
{"RNR", RNR},
{"RR", RR},
{"SABME", SABME},
{"UA", UA},
{"UI", UI},
{"XID", XID},
{"Invalid frame", Invalid},
{"Unknown command/response", ErrUnknownCR},
{"Invalid header length", ErrHdrLength},
{"Information field too long", ErrDataLength},
{"Invalid N(R) (transmiter receive) sequence number", ErrRxSeqNo},
{"Invalid N(S) (transmiter send) sequence number", ErrTxSeqNo},
{"Invalid 'extended address' bit(s)", ErrInvalidEA},
{"Invalid SAPI/TEI", ErrInvalidAddress},
{"Unsupported command/response", ErrUnsupported},
{"Invalid command/response flag", ErrInvalidCR},
{0,0}
};
// NOTE:
// In constructors, the values of SAPI, TEI, N(S), N(R) are not checked to be in their interval:
// this is done by the parser (when receiveing) and by ISDNLayer2 when assigning these values
// Constructs an undefined frame. Used by the parser
ISDNFrame::ISDNFrame(Type type)
: m_type(type),
m_error(type),
m_category(Error),
m_command(false),
m_sapi(0),
m_tei(0),
m_poll(false),
m_ns(0xFF),
m_nr(0xFF),
m_headerLength(0),
m_dataLength(0),
m_sent(false)
{
}
// Create U/S frames: SABME/DM/DISC/UA/FRMR/XID/RR/RNR/REJ
ISDNFrame::ISDNFrame(Type type, bool command, bool senderNetwork,
u_int8_t sapi, u_int8_t tei, bool pf, u_int8_t nr)
: m_type(type),
m_error(type),
m_category(Error),
m_command(command),
m_senderNetwork(senderNetwork),
m_sapi(sapi),
m_tei(tei),
m_poll(pf),
m_ns(0xFF),
m_nr(nr),
m_headerLength(3),
m_dataLength(0),
m_sent(false)
{
u_int8_t buf[4];
setAddress(buf,m_command,m_senderNetwork,m_sapi,m_tei);
u_int8_t cr = 0;
#define Q921_CASE_SET_CRMASK(compare,rvalue,hdrLen,category) \
case compare: cr = rvalue; m_headerLength = hdrLen; m_category = category; break;
switch (m_type) {
Q921_CASE_SET_CRMASK(SABME,Q921FRAME_CR_SABME,3,Unnumbered)
Q921_CASE_SET_CRMASK(DM,Q921FRAME_CR_DM,3,Unnumbered)
Q921_CASE_SET_CRMASK(DISC,Q921FRAME_CR_DISC,3,Unnumbered)
Q921_CASE_SET_CRMASK(UA,Q921FRAME_CR_UA,3,Unnumbered)
Q921_CASE_SET_CRMASK(FRMR,Q921FRAME_CR_FRMR,3,Unnumbered)
Q921_CASE_SET_CRMASK(RR,Q921FRAME_CR_RR,4,Supervisory)
Q921_CASE_SET_CRMASK(RNR,Q921FRAME_CR_RNR,4,Supervisory)
Q921_CASE_SET_CRMASK(REJ,Q921FRAME_CR_REJ,4,Supervisory)
Q921_CASE_SET_CRMASK(XID,Q921FRAME_CR_XID,3,Unnumbered)
default:
return;
}
#undef Q921_CASE_SET_CRMASK
// Set control field
if (m_headerLength == 3)
setControlU(buf,cr,m_poll);
else
setControl(buf,cr,m_nr,m_poll);
// Set frame buffer
m_buffer.assign(buf,m_headerLength);
}
// Create I/UI frames
ISDNFrame::ISDNFrame(bool ack, bool senderNetwork, u_int8_t sapi, u_int8_t tei,
bool pf, const DataBlock& data)
: m_type(I),
m_error(I),
m_category(Data),
m_command(true),
m_senderNetwork(senderNetwork),
m_sapi(sapi),
m_tei(tei),
m_poll(pf),
m_ns(0),
m_nr(0),
m_headerLength(4),
m_dataLength(data.length()),
m_sent(false)
{
if (!ack) {
m_type = m_error = UI;
m_headerLength = 3;
m_ns = m_nr = 0xff;
}
u_int8_t buf[4];
setAddress(buf,m_command,m_senderNetwork,m_sapi,m_tei);
if (m_type == I)
setControl(buf,m_ns << 1,m_nr << 1,m_poll);
else
setControlU(buf,Q921FRAME_CR_UI,m_poll);
m_buffer.assign(buf,m_headerLength);
m_buffer += data;
}
ISDNFrame::~ISDNFrame()
{
}
// Update transmitter send and transmitter receive values for I (data) frames
void ISDNFrame::update(u_int8_t* ns, u_int8_t* nr)
{
#define NS (((u_int8_t*)(m_buffer.data()))[2])
#define NR (((u_int8_t*)(m_buffer.data()))[3])
if (m_type != I)
return;
if (ns) {
m_ns = *ns;
// For I frames bit 0 of N(S) is always 0
NS = m_ns << 1;
}
if (nr) {
m_nr = *nr;
// Keep the P/F bit (bit 0)
NR = (m_nr << 1) | (NR & 0x01);
}
#undef NR
#undef NS
}
// Put the frame in a string for debug purposes
void ISDNFrame::toString(String& dest, bool extendedDebug) const
{
#define STARTLINE(indent) "\r\n" << indent
const char* enclose = "\r\n-----";
const char* ind = " ";
dest << enclose;
dest << STARTLINE("") << name();
// Dump header
if (extendedDebug) {
String tmp;
tmp.hexify((void*)buffer().data(),headerLength(),' ');
dest << " - Header dump: " << tmp;
}
if (m_error >= Invalid)
dest << STARTLINE(ind) << "Error: " << typeName(m_error);
// Address
dest << STARTLINE(ind) << "SAPI=" << (unsigned int)m_sapi;
dest << " TEI=" << (unsigned int)m_tei;
dest << " Type=" << (m_command ? "Command" : "Response");
// Control
dest << " Poll/Final=" << (m_poll ? '1' : '0');
dest << " Sequence numbers: ";
switch (m_type) {
case I:
dest << "Send=" << (unsigned int)m_ns;
dest << " Recv=" << (unsigned int)m_nr;
break;
case RR:
case RNR:
case REJ:
dest << "Send=N/A Recv=" << (unsigned int)m_nr;
break;
default: ;
dest << "Send=N/A Recv=N/A";
}
// Data
dest << STARTLINE(ind) << "Retransmission=" << String::boolText(m_sent);
dest << " Length: Header=" << (unsigned int)m_headerLength;
dest << " Data=" << (unsigned int)m_dataLength;
// Dump data
if (extendedDebug && m_dataLength) {
String tmp;
tmp.hexify((char*)buffer().data() + headerLength(),m_dataLength,' ');
dest << STARTLINE(ind) << "Data dump: " << tmp;
}
dest << enclose;
#undef STARTLINE
}
// Parse received buffer. Set frame data. Header description:
// Address: 2 bytes
// Control: 1 or 2 bytes
// Data: Variable
//
// Address field: 2 bytes (1 and 2)
// Check EA bits: bit 0 of byte 0 must be 0; bit 0 of byte 1 must be 1
// C/R (command/response) bit: bit 1 of byte 0
// SAPI: Bits 2-7 of byte 0
// TEI: Bits 1-7 of byte 1
// Control field: 1 byte (byte 2) for U frames and 2 bytes (bytes 2 and 3) for I/S frames
// Frame type: Bits 0,1 of of byte 2
// P/F (Poll/Final) bit: I/S frame: bit 0 of byte 3. U frame: bit 4 of the byte 2
// Command/response code: I frame: none. S frame: byte 2. U frame: byte 2 with P/F bit reset
ISDNFrame* ISDNFrame::parse(const DataBlock& data, ISDNLayer2* receiver)
{
// We MUST have 2 bytes for address and at least 1 byte for control field
if (!receiver || data.length() < 3)
return 0;
ISDNFrame* frame = new ISDNFrame(Invalid);
const u_int8_t* buf = (const u_int8_t*)(data.data());
// *** Address field: 2 bytes
// Check EA bits
if ((buf[0] & 0x01) || !(buf[1] & 0x01)) {
frame->m_buffer = data;
frame->m_headerLength = frame->m_buffer.length();
frame->m_error = ErrInvalidEA;
return frame;
}
// Get C/R bit, SAPI, TEI
// C/R: (Q.921 Table 1):
// network --> user Command: 1 Response: 0
// user --> network Command: 0 Response: 1
// The sender of this frame is the other side of the receiver
frame->m_senderNetwork = !receiver->network();
frame->m_command = isCommand(buf[0] & 0x02,frame->m_senderNetwork);
frame->m_sapi = buf[0] >> 2;
frame->m_tei = buf[1] >> 1;
// *** Control field: 1 (U frame) or 2 (I/S frame) bytes
// Get frame type: I/U/S. I/S frame type control field is 2 bytes long
u_int8_t type = buf[2] & 0x03;
if (type != Q921FRAME_U && data.length() < 4) {
frame->m_buffer = data;
frame->m_headerLength = 3;
frame->m_error = ErrHdrLength;
return frame;
}
// Adjust frame header length. Get P/F bit
// Get counters. Set frame type
#define Q921_CASE_SETTYPE(compare,rvalue,category)\
case compare: frame->m_type = frame->m_error = rvalue; frame->m_category = category; break;
switch (type) {
case Q921FRAME_U:
frame->m_headerLength = 3;
frame->m_poll = (buf[2] & Q921FRAME_U_GET_PF) ? true : false;
switch (buf[2] & Q921FRAME_U_RESET_PF) {
Q921_CASE_SETTYPE(Q921FRAME_CR_UA,UA,Unnumbered)
Q921_CASE_SETTYPE(Q921FRAME_CR_DM,DM,Unnumbered)
Q921_CASE_SETTYPE(Q921FRAME_CR_DISC,DISC,Unnumbered)
Q921_CASE_SETTYPE(Q921FRAME_CR_SABME,SABME,Unnumbered)
Q921_CASE_SETTYPE(Q921FRAME_CR_UI,UI,Data)
Q921_CASE_SETTYPE(Q921FRAME_CR_FRMR,FRMR,Unnumbered)
Q921_CASE_SETTYPE(Q921FRAME_CR_XID,XID,Unnumbered)
default:
frame->m_type = Invalid;
frame->m_error = ErrUnknownCR;
}
break;
case Q921FRAME_S:
frame->m_headerLength = 4;
frame->m_poll = (buf[3] & 0x01) ? true : false;
frame->m_nr = buf[3] >> 1;
switch (buf[2]) {
Q921_CASE_SETTYPE(Q921FRAME_CR_RR,RR,Supervisory)
Q921_CASE_SETTYPE(Q921FRAME_CR_RNR,RNR,Supervisory)
Q921_CASE_SETTYPE(Q921FRAME_CR_REJ,REJ,Supervisory)
default:
frame->m_type = Invalid;
frame->m_error = ErrUnknownCR;
}
break;
default: // I frame
frame->m_type = frame->m_error = I;
frame->m_category = Data;
frame->m_headerLength = 4;
frame->m_poll = (buf[3] & 0x01) ? true : false;
frame->m_ns = buf[2] >> 1;
frame->m_nr = buf[3] >> 1;
}
#undef Q921_CASE_SETTYPE
// Copy buffer. Set data length
frame->m_buffer = data;
frame->m_dataLength = data.length() - frame->m_headerLength;
return frame;
}
// Get the Reference number from a frame data block
u_int16_t ISDNFrame::getRi(const DataBlock& data)
{
int i = data.at(2);
if (i < 0)
return 0;
return (u_int16_t)((data.at(1) << 8) | i);
}
// Build a TEI management message buffer
bool ISDNFrame::buildTeiManagement(DataBlock& data, u_int8_t type, u_int16_t ri, u_int8_t ai)
{
u_int8_t d[5] = { Q921_MANAGEMENT_TEI, (u_int8_t)(ri >> 8), (u_int8_t)ri,
type, (u_int8_t)((ai << 1) | 1) };
data.assign(d,5);
return true;
}
// Check if a message buffer holds a TEI management frame
bool ISDNFrame::checkTeiManagement() const
{
const u_int8_t* d = m_buffer.data(m_headerLength);
return (d && (type() == UI) && (m_dataLength >= 5) && (d[0] == Q921_MANAGEMENT_TEI));
}
/* vi: set ts=8 sw=4 sts=4 noet: */