Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.restcomm.protocols.ss7.mtp;
import java.io.IOException;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.restcomm.protocols.ss7.mtp.Mtp3.SLTMTest;
import org.restcomm.protocols.ss7.scheduler.Scheduler;
import org.restcomm.protocols.ss7.scheduler.Task;
/**
*
* @author kulikov
* @author baranowb
*/
public class Mtp2 {
static final int MTP2_OUT_OF_SERVICE = 0;
/**
* Initial state of IAC phase, we send "O" here. "E","O","N" have never been received.
*/
static final int MTP2_NOT_ALIGNED = 1;
/**
* Second state of IAC, we received one of: E,O,N. Depending on state we send E or N.
*/
static final int MTP2_ALIGNED = 2;
/**
* Third state, entered from ALIGNED on receival of N or E
*/
static final int MTP2_PROVING = 3;
/**
* Etnered on certain condition when T4 fires.
*/
static final int MTP2_ALIGNED_READY = 4;
/**
* In service state, entered from ALIGNED_READY on FISU/MSU
*/
static final int MTP2_INSERVICE = 5;
/**
* Timers
*/
private static final int T2_TIMEOUT = 50;
private static final int T3_TIMEOUT = 10;
private static final int T4_TIMEOUT_NORMAL = 82;
private static final int T4_TIMEOUT_EMERGENCY = 5; // not a static, since it can change.
private int T4_TIMEOUT = T4_TIMEOUT_NORMAL;
private int T7_TIMEOUT = 20; // see Q704 - after alignemtn failure we need to wait before we retry
// 800-1500.
private static final int T17_TIMEOUT = 15;
private static final int[] fcstab = new int[] { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48,
0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7,
0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726,
0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291,
0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204,
0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a,
0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9,
0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c,
0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf,
0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e,
0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad,
0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618,
0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b,
0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5,
0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60,
0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7,
0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46,
0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9,
0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 };
/**
* status indicator of out of alignment (SIO). This condition occurs when a signal unit is received that has a ones-density
* violation (the data field simulated a flag) or the SIF has exceeded its maximum capacity of 272 octets. The SIO is sent
* when a link has failed and the alignment procedure is initiated.
*/
private static final int FRAME_STATUS_INDICATION_O = 0;
/**
* Link status indicator of normal (SIN) or emergency (SIE) indicates that the transmitting signaling point has initiated
* the alignment procedure. The link is made unavailable for MSUs, and only FISUs are transmitted to the affected signaling
* point until a proving period has been passed. After successful completion of a proving period, MSUs can be transmitted
* over the link to the affected signaling point.
*/
private static final int FRAME_STATUS_INDICATION_N = 1;
private static final int FRAME_STATUS_INDICATION_E = 2;
/**
* An LSSU status indicator of out of service (SIOS) indicates that the sending signaling point cannot receive or transmit
* any MSUs for reasons other than a processor outage. On receipt of an SIOS, the receiving signaling point stops the
* transmission of MSUs and begins transmitting FISUs. The SIOS is also sent at the beginning of the alignment procedure.
*/
private static final int FRAME_STATUS_INDICATION_OS = 3;
/**
* status indicator of processor outage (SIPO) indicates that the transmitting signaling point cannot communicate with
* levels 3 and 4.
*/
private static final int FRAME_STATUS_INDICATION_PO = 4;
private static final int FRAME_STATUS_INDICATION_B = 5;
private static final int FRAME_FISU = 6;
/** matches frame code to frame name,FRAME_NAMES[0] gives string name of frame with code 0 */
private static final String[] FRAME_NAMES;
static {
FRAME_NAMES = new String[] { "SIO", "SIN", "SIE", "SIOS", "SIPO", "SIPB", "FISU" };
}
public static final int AERM_THRESHOLD_NORMAL = 4;
public static final int AERM_THRESHOLD_EMERGENCY = 1; // /////////////////////////////////////////////
// States for MTP2, for now we merge IAC,LSC //
// /////////////////////////////////////////////
// Some static vals used for debug.
private static final String[] STATE_NAMES;
static {
STATE_NAMES = new String[] { "OUT_OF_SERVICE", "NOT_ALIGNED", "ALIGNED", "PROVING", "ALIGNED_READY", "IN_SERVICE" };
}
// //////////////////
// MTP2/1 resoruces //
// //////////////////
protected SLTMTest sltmTest;
/** MTP1 layer reference. */
private Mtp1 channel;
private int ioBufferSize;
// ///////////////////
// MTP2 stuff only //
// ///////////////////
// be careful using this, we do not sync for now.
private int state;
/** MTP3 Layer reference */
protected Mtp3 mtp3;
private volatile boolean started;
/** HDLC components */
private FastHDLC hdlc = new FastHDLC();
private HdlcState rxState = new HdlcState();
private HdlcState txState = new HdlcState();
// Buffers used for IO
private byte[] txBuffer;
private byte[] rxBuffer;
/**
* Buffer to store incoming frame.
*/
private Mtp2Buffer rxFrame = new Mtp2Buffer();
/**
* Buffer to store frame to send, this ref will be set when next frame is scheduled, no copy&paste
*/
private Mtp2Buffer txFrame;
/**
* frame used to create LSSU/FISU
*/
private Mtp2Buffer stateFrame = new Mtp2Buffer();
/**
* frame used to create NEXT LSSU, it is used in case when link transits from NOT_ALIGNED to ALIGNED state
*/
private Mtp2Buffer nextStateFrame = new Mtp2Buffer();
/**
* Indicates that there is something to send in {@link #nextStateFrame}.
*/
private boolean pendingLSSU = false;
/**
* Last write timestamp, it is required for case when we must start sending but nothing comes on wire.
*/
// private long lastWriteStamp = 0;
// Used only for LSSU, in some case we need to schedule LSSU frame, before
// last is processed,
// private TxQueue txQueue = new TxQueue();
private int doCRC = 0;
private int rxCRC = 0xffff;
private int txCRC = 0xffff;
// //////////////////////
// SEQUENCING AND RTR //
// //////////////////////
/**
* Holds byte[] sent on link, indexed by FSN, this is the place to get data from when we send and retransmission is
* requested. FIXME: add this-> ACKED buffers will have len == 0(isFree() == true);
*/
private Mtp2Buffer[] transmissionBuffer = new Mtp2Buffer[128];
private static final int _OFF_RTR = -1;
/**
* Determines posistion in transmissionBuffer. If != _OFF_RTR, it points next frame to be put into retransmission.
* Retransmission will occur until ticks wont get to point where buffer.isFree() == true. It also point to next MSU if
* scheduledby MTP3. This number is set as FSN.
*/
private int retransmissionFSN = _OFF_RTR;
/**
* This is last ACKED FSN(meaning this is BSN received in SU comming from other peer.
*/
private int retransmissionFSN_LastAcked = 0;
/**
* This is FSN we used as last for send SU. If we spin many times and it equals retransmissionFSN_LastAcked, it means we
* have queue full, and have to discard SU
*/
private int retransmissionFSN_LastSent = 0;
private int sendFIB;
private int sendBSN;
private int sendBIB;
/**
* if bsnErrors > 2, link fails. See Q.703 section 5.3.[1,2]
*/
private int bsnErrors = 0;
protected String name; // AERM variables. we use eCounter for AERM errors
private static final int PROVING_ATTEMPTS_THRESHOLD = 5;
private int aermThreshold;
private boolean aermEnabled;
private boolean emergency = false;
private int provingAttempts;
private boolean futureProving;
private int nCount;
private int dCount;
private int eCount;
/**
* SLS, range is 0-15
*/
protected int sls = -1;
/**
* Subservice field of mtp msg. See Q.704.14.2.2
*/
protected Mtp2Listener mtp2Listener = null;
private static final Logger ROOT_LOGGER = Logger.getLogger(Mtp2.class);
private final Logger logger; // actual logger.
private Scheduler scheduler;
public Mtp2(String name, Mtp1 channel, Scheduler scheduler) {
this.name = name;
this.logger = ROOT_LOGGER.getLogger(name);
this.channel = channel;
channel.setLink(this);
this.sls = channel.getCode();
this.scheduler = scheduler;
if (scheduler != null) {
t2Action = new T2Action(scheduler);
t3Action = new T3Action(scheduler);
t4Action = new T4Action(scheduler);
t7Action = new T7Action(scheduler);
t17Action = new T17Action(scheduler);
}
// init HDLC
hdlc.fasthdlc_precalc();
hdlc.fasthdlc_init(rxState);
hdlc.fasthdlc_init(txState);
// init error rate monitor
aermThreshold = AERM_THRESHOLD_NORMAL;
// init buffer
for (int i = 0; i < this.transmissionBuffer.length; i++) {
this.transmissionBuffer[i] = new Mtp2Buffer();
}
}
public void setScheduler(Scheduler scheduler) {
this.scheduler = scheduler;
if (t2Action == null && scheduler != null) {
t2Action = new T2Action(scheduler);
t3Action = new T3Action(scheduler);
t4Action = new T4Action(scheduler);
t7Action = new T7Action(scheduler);
t17Action = new T17Action(scheduler);
}
}
public Mtp2Listener getMtp2Listener() {
return mtp2Listener;
}
public void setMtp2Listener(Mtp2Listener mtp2Listener) {
this.mtp2Listener = mtp2Listener;
}
public boolean isEmergency() {
return emergency;
}
public void setEmergency(boolean emergency) {
this.emergency = emergency;
}
public int getState() {
return this.state;
}
/**
* Assigns layer 1 implementation.
*
* @param layer1 the implementation of MTP1 layer.
*/
public void setLayer1(Mtp1 layer1) {
this.channel = layer1;
}
public Mtp1 getLayer1() {
return channel;
}
/*
* (non-Javadoc)
*
* @see org.restcomm.protocols.ss7.mtp.Mtp2#getName()
*/
public String getName() {
return this.name;
}
/*
* (non-Javadoc)
*
* @see org.restcomm.protocols.ss7.mtp.Mtp2#getSls()
*/
public int getSls() {
return this.sls;
}
/*
* (non-Javadoc)
*
* @see org.restcomm.protocols.ss7.mtp.Mtp2#setName(java.lang.String)
*/
public void setName(String name) {
this.name = name;
}
/*
* (non-Javadoc)
*
* @see org.restcomm.protocols.ss7.mtp.Mtp2#setSls(int)
*/
public void setSls(int sls) {
this.sls = sls;
}
public void start() throws IOException {
if (channel == null) {
throw new IllegalStateException("Layer1 is not set in Layer2!");
}
if (mtp3 == null) {
throw new IllegalStateException("Layer3 is not set in Layer2!");
}
if (started) {
throw new IllegalStateException("Link already running");
}
// init buffers
this.ioBufferSize = this.channel.getIOBufferSize();
this.rxBuffer = new byte[this.ioBufferSize];
this.txBuffer = new byte[this.ioBufferSize];
// open channel and notify L3
channel.open();
mtp3.registerLink(this);
started = true;
this.reset();
if (logger.isInfoEnabled()) {
logger.info("Is out of service now. Starting.");
}
this.txFrame = stateFrame;
// state = Mtp2.MTP2_OUT_OF_SERVICE;
setState(MTP2_OUT_OF_SERVICE);
// send Out of service indicator when link starts.
queueLSSU(Mtp2.FRAME_STATUS_INDICATION_OS);
processTx(this.ioBufferSize);
startInitialAlignment();
}
public void stop() {
if (logger.isInfoEnabled()) {
logger.info("Stopping link.");
}
mtp3.unregisterLink(this);
started = false;
reset();
this.channel.close();
}
// not used
private void startInitialAlignment() {
startInitialAlignment(true);
}
// should not be used
protected void startInitialAlignment(boolean resetTxOffset) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("(%s) Starting initial alignment", name));
}
// Comment from Oleg: this is done initialy to setup correct spot in tx
// buffer: dunno, I just believe, for now.
if (resetTxOffset) {
// txOffset = 3;
this.txFrame.offset = 3; // I really dont get this shift.
}
this.reset();
// switch state
// this.state = MTP2_NOT_ALIGNED;
this.setState(MTP2_NOT_ALIGNED);
// starting T2 timer
start_T2();
}
private void setState(int newState) {
if (this.state == newState) {
return;
}
if (logger.isInfoEnabled()) {
// iirc it should be info
logger.info("State changed in link. " + STATE_NAMES[this.state] + " --> " + STATE_NAMES[newState]);
}
this.state = newState;
}
private void reset() {
// reset.
this.nCount = 0;
this.eCount = 0;
this.dCount = 0;
// set proper values of FIBOS, FISSIES And other...
this.sendFIB = 1;
this.sendBSN = 0x7F;
this.sendBIB = 1;
this.retransmissionFSN = _OFF_RTR;
this.retransmissionFSN_LastAcked = 0x7F;
this.retransmissionFSN_LastSent = 0x7F;
}
public void fail() {
cleanupTimers();
this.reset();
this.setState(MTP2_OUT_OF_SERVICE);
if (logger.isDebugEnabled()) {
logger.debug(String.format("(%s) Link Out of service", name));
}
start_T17();
}
public boolean send(byte[] msg, int len) {
// FIXME: this operation actually could be split into two....
if (this.state != MTP2_INSERVICE) {
return false;
}
// Here we will queue MSU if there is place in transmission buffer
int possibleFSN = NEXT_FSN(this.retransmissionFSN_LastSent);
// check if transmission buffer is full
if (possibleFSN == this.retransmissionFSN_LastAcked) {
// This means buffer is full;
return false;
}
// FSN and all that will be set before puting this into txFrame buffer.
this.transmissionBuffer[possibleFSN].frame[0] = 0;
this.transmissionBuffer[possibleFSN].frame[1] = 0;
// LI: see Q.703 2.3.3, FIXME: add check for msg.length <3 ?
this.transmissionBuffer[possibleFSN].frame[2] = (byte) (len >= 63 ? 63 : len);
System.arraycopy(msg, 0, this.transmissionBuffer[possibleFSN].frame, 3, len);
// 3 == FSN(1) + BSN(1) + LI(1)
this.transmissionBuffer[possibleFSN].len = 3 + len;
this.retransmissionFSN_LastSent = possibleFSN;
if (this.retransmissionFSN == _OFF_RTR) {
this.retransmissionFSN = possibleFSN;
}
this.start_T7();
// txQueue.offer(frame);
return true;
}
private void queueLSSU(int indicator) {
// if (this.txLen != txOffset) {
if (this.txFrame != null && this.txFrame.len != this.txFrame.offset) {
fillLSSUBuffer(this.nextStateFrame, indicator);
this.pendingLSSU = true; // should we check on entering here?
} else {
this.txFrame = this.stateFrame; // set proper frame
fillLSSUBuffer(this.txFrame, indicator);
}
}
private void fillLSSUBuffer(Mtp2Buffer b, int indicator) {
b.len = 4;
b.offset = 0;
b.frame[0] = (byte) (this.sendBSN | (this.sendBIB << 7));
b.frame[1] = (byte) (this.retransmissionFSN_LastSent | (this.sendFIB << 7));
b.frame[2] = 1;
b.frame[3] = (byte) indicator;
}
private void queueFISU() {
this.txFrame.len = 3;
this.txFrame = this.stateFrame;
this.txFrame.frame[0] = (byte) (this.sendBSN | (this.sendBIB << 7));
this.txFrame.frame[1] = (byte) (this.retransmissionFSN_LastSent | (this.sendFIB << 7));
this.txFrame.frame[2] = 0;
this.txFrame.offset = 0;
}
private void queueNextFrame() {
if (this.state != MTP2_INSERVICE && this.pendingLSSU) {
this.txFrame = nextStateFrame;
this.pendingLSSU = false;
} else {
switch (state) {
case MTP2_OUT_OF_SERVICE:
// send SIOS ?
queueLSSU(FRAME_STATUS_INDICATION_OS);
break;
case MTP2_NOT_ALIGNED:
queueLSSU(FRAME_STATUS_INDICATION_O);
break;
case MTP2_PROVING:
case MTP2_ALIGNED:
// which way is correct?
if (emergency) {
queueLSSU(FRAME_STATUS_INDICATION_E);
} else {
queueLSSU(FRAME_STATUS_INDICATION_N);
}
break;
default:
// in service, we need to check buffer, otherwise its RTR
if (this.retransmissionFSN != _OFF_RTR) {
Mtp2Buffer buffer = this.transmissionBuffer[this.retransmissionFSN];
// we shoudl use getters, but its faster with "."
this.txFrame = buffer;
this.txFrame.offset = 0;
this.txFrame.frame[0] = (byte) (this.sendBSN | (this.sendBIB << 7));
this.txFrame.frame[1] = (byte) (this.retransmissionFSN | (this.sendFIB << 7));
if (this.retransmissionFSN == this.retransmissionFSN_LastSent) {
this.retransmissionFSN = _OFF_RTR;
} else {
// set up next :)
this.retransmissionFSN = NEXT_FSN(this.retransmissionFSN);
}
return;
}
// else queue FISU
queueFISU();
}
}
}
public static final int PPP_FCS(int fcs, int c) {
return ((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff];
}
private void processLssu() {
// Q703 Figure 4
int type = rxFrame.frame[3] & 0x07;
switch (this.state) {
case MTP2_NOT_ALIGNED:
// here we wait for O/N/E
switch (type) {
case FRAME_STATUS_INDICATION_O:
case FRAME_STATUS_INDICATION_N:
stop_T2();
if (emergency) {
this.T4_TIMEOUT = T4_TIMEOUT_EMERGENCY;
queueLSSU(FRAME_STATUS_INDICATION_E);
} else {
this.T4_TIMEOUT = T4_TIMEOUT_NORMAL;
queueLSSU(FRAME_STATUS_INDICATION_N);
}
start_T3();
this.setState(MTP2_ALIGNED);
break;
case FRAME_STATUS_INDICATION_E:
// 1. stop t2
stop_T2();
// 2. set T4 time here we set always to emergency, only LSSU,
// differs depending on flag.
this.T4_TIMEOUT = T4_TIMEOUT_EMERGENCY;
// 3. queue response
if (emergency) {
queueLSSU(FRAME_STATUS_INDICATION_E);
} else {
queueLSSU(FRAME_STATUS_INDICATION_N);
}
// 4. start T3
start_T3();
// 5. set state
this.setState(MTP2_ALIGNED);
break;
default:
}
// BREAK For NOT_ALIGNED state
break;
case MTP2_ALIGNED:
switch (type) {
// Here we ignore "O"
case FRAME_STATUS_INDICATION_E:
// 1. set T4 time.
this.T4_TIMEOUT = Mtp2.T4_TIMEOUT_EMERGENCY;
case FRAME_STATUS_INDICATION_N:
// 1. stop T3
stop_T3();
// 2. determine threashold for AERM.
if (this.T4_TIMEOUT == Mtp2.T4_TIMEOUT_EMERGENCY) {
this.aermThreshold = Mtp2.AERM_THRESHOLD_EMERGENCY;
}
// 3. start AERM, but we do start, it works alwys? this should
// reset CP also?
startAERM();
// 4. start T4;
start_T4();
// 5. cp = 0;
this.provingAttempts = 0;
// 6. cancel further proving.
this.futureProving = false;
// 7. set state
this.setState(MTP2_PROVING);
break;
case FRAME_STATUS_INDICATION_OS:
// we should invoke ALI first, but we cancel timer
// 1. stop T3
stop_T3();
// 2. ALI not possible, this will switch to OUT_OF_SERVICE(in
// IAC its IDLE.) state, possibly fire OS, set T17.
alignmentNotPossible("Receievd SIOS in state ALIGNED");
// 3. cancel E
this.emergency = false;
break;
default:
}
// BREAK for ALIGNED state.
break;
case MTP2_PROVING:
switch (type) {
case FRAME_STATUS_INDICATION_O:
// 1. stop T4
stop_T4();
// 2. stop AERM, hmm
stopAERM();
// 3. start T3
start_T3();
// 4. swithc state
this.setState(MTP2_ALIGNED);
break;
case FRAME_STATUS_INDICATION_E:
// NOTE: this one is not covered in Olegs src.
// 1. determine actions
if (this.T4_TIMEOUT == Mtp2.T4_TIMEOUT_EMERGENCY) {
// do nothing
return;
}
// 2. stop T4
stop_T4();
// 3. stop AERM,
stopAERM();
// 4. set AERM T
this.aermThreshold = Mtp2.AERM_THRESHOLD_EMERGENCY;
// 5. start AERM
startAERM();
// 6. Cancel Further Proving
this.futureProving = false;
// 7. start T4
start_T4();
// 8. state is still proving.
break;
case FRAME_STATUS_INDICATION_OS:
// 1. stop T4
stop_T4();
// 2. action;
// specs shows : == Aligment Complete ....
alignmentNotPossible("Received SIOS in state PROVING");
// 3. stop AERM
stopAERM();
// 4. cancel E
this.emergency = false;
// 5. should move to state IDLE, alignmentNotPossible will shift
// state.
break;
default:
}
// BREAK for PROVING state
break;
case MTP2_ALIGNED_READY:
switch (type) {
case FRAME_STATUS_INDICATION_O:
// link failed.
// FIXME: add more state here.
alignmentNotPossible("Received SIO in state ALIGNED_READY");
break;
case FRAME_STATUS_INDICATION_OS:
// link failed, pass to MTP3?
// mpt3.linkFailure();
// FIXME: add more
alignmentNotPossible("Received SIOS in state ALIGNED_READY");
default:
}
// ... dont forget to break!!!
// this caused link to go down ;[
break;
case MTP2_INSERVICE:
switch (type) {
case FRAME_STATUS_INDICATION_O:
case FRAME_STATUS_INDICATION_E:
case FRAME_STATUS_INDICATION_N:
// link failed, pass to MTP3?
// mpt3.linkFailure();
// FIXME: add more
alignmentNotPossible("Received " + FRAME_NAMES[type] + " in state IN_SERVICE");
break;
case FRAME_STATUS_INDICATION_OS:
// link failed, pass to MTP3?
// mpt3.linkFailure();
// FIXME: add more
alignmentNotPossible("Received SIOS in state IN_SERVICE");
default:
}
// BREAK for ALIGNED_READY
break;
default:
}
}
private void processMSU(int len) {
if (this.mtp3 != null) {
mtp3.onMessage(rxFrame, this);
}
}
private void processFrame() {
int bsn = rxFrame.frame[0] & 0x7F;
int bib = (rxFrame.frame[0] >> 7) & 0x01;
int fsn = rxFrame.frame[1] & 0x7F;
int fib = (rxFrame.frame[1] >> 7) & 0x01;
int li = rxFrame.frame[2] & 0x3f;
// Why it was 5?
// if (li + 3 > rxLen) {
if (li + 3 > rxFrame.len) {
return;
}
if (li == 1 || li == 2) {
// LSSU does not count for IAM check for SU
processLssu();
// LSSU does not go into BSN/FSN check, its always static for this
// part.
// and if we get here from IN_SERVICE, everything is reset.
return;
}
if (this.state != MTP2_INSERVICE) {
switch (this.state) {
case MTP2_PROVING:
// Decision;
if (futureProving) {
// 1. start AERM, maybe restart?
startAERM();
// 2. cancel FP
futureProving = false;
// 3. start T4
start_T4();
// 4. state is still proving
} else {
// 1. do nothing, stay in PROVING state,
}
// FIXME: return here?
break;
case MTP2_ALIGNED_READY:
this.eCount = 0;
this.dCount = 0;
this.stop_T7();
this.sendFIB = bib;
this.sendBSN = fsn;
this.sendBIB = fib;
this.retransmissionFSN_LastAcked = bsn;
if (logger.isDebugEnabled()) {
logger.debug(String.format("(%s) MTP now IN_SERVICE, Notifing layer 3", name));
}
this.setState(MTP2_INSERVICE);
mtp3.linkInService(this);
break;
}
}
// Q.703 section 5.3.1 and 5.3.2, paras like:
// If any two backward sequence number values in three consecutively
// received message signal units or
// fill-in signal units are not the same as the previous one or any of
// the forward sequence number values
// of the signal units in the retransmission buffer at the time that
// they are received, then level 3 is
// informed that the link is faulty.
// This means we have to check BSN if it falls into space available for
// rtr
// its a bit complicated since we have "ring" buffer. where indexes are
// reused.
// CASE_I, buffer did not flip.
if ((this.retransmissionFSN_LastAcked <= this.retransmissionFSN_LastSent && (bsn < this.retransmissionFSN_LastAcked || bsn > this.retransmissionFSN_LastSent))
||
// CASE_II, buffer flipped, lastSent is less than ack, since it
// starts from "0"
(this.retransmissionFSN_LastAcked > this.retransmissionFSN_LastSent && (bsn < this.retransmissionFSN_LastAcked && bsn > this.retransmissionFSN_LastSent))) {
this.bsnErrors++;
if (this.bsnErrors > 2) {
this.bsnErrors = 0;
this.mtp3.linkFailed(this);
alignmentBroken("Broken BSN constrains: fsn_lasAcked = " + this.retransmissionFSN_LastAcked
+ ", fsn_LastSent = " + this.retransmissionFSN_LastSent + ", bsn = " + bsn);
}
return;
}
this.bsnErrors = 0;
// Q.703, Section 5.3.1, T7. it is weird, its in positive section
// but it says "ACK", any?
if (bib == this.sendFIB) {
if (bsn != this.retransmissionFSN_LastAcked) {
this.stop_T7();
this.retransmissionFSN_LastAcked = bsn;
if (this.retransmissionFSN_LastAcked != this.retransmissionFSN_LastSent) {
// there is something we can retransmit, we wait for ACK
this.start_T7();
}
}
} else {
// negative, start rtr
this.sendFIB = bib;
if (bsn == this.retransmissionFSN_LastSent) {
// there is nothing, should we error here?
this.retransmissionFSN = _OFF_RTR;
} else {
this.retransmissionFSN = NEXT_FSN(bsn);
}
}
if (li == 0) {
if (fsn != this.sendBSN) {
// something is not correctm link lost msg?
// Q.703 section 5.2.2.a.ii
if (fib == this.sendBIB) {
// flip, to make negative ACK, ask for rtr.
this.sendBIB = NEXT_INDICATOR(this.sendBIB);
}
}
// thats it for FISU
} else {
if (fsn == this.sendBSN) {
// Q.703 section 5.2.2.c.i
// we already got this once.
return;
} else if (fsn == NEXT_FSN(this.sendBSN)) {
// Q.703 section 5.2.2.c.ii
if (fib == this.sendBIB) {
// positive ACK, lets send it.
this.sendBSN = fsn;
} else {
// drop frame? since it negative?
return;
}
} else {
// Q.703 section 5.2.2.c.iii
// lost some frame, make negative
if (fib == this.sendBIB) {
// flip, to make negative ACK, ask for rtr.
this.sendBIB = NEXT_INDICATOR(this.sendBIB);
}
return;
}
processMSU(li);
}
}
/**
* Handles received data.
*
* @param buff the buffer which conatins received data.
* @param len the number of received bytes.
*/
private void processRx(byte[] buff, int len) {
int i = 0;
// start HDLC alg
while (i < len) {
while (rxState.bits <= 24 && i < len) {
int b = buff[i++] & 0xff;
hdlc.fasthdlc_rx_load_nocheck(rxState, b);
if (rxState.state == 0) {
// octet counting mode
nCount = (nCount + 1) % 16;
if (nCount == 0) {
countError("on receive");
}
}
}
int res = hdlc.fasthdlc_rx_run(rxState);
switch (res) {
case FastHDLC.RETURN_COMPLETE_FLAG:
// frame received and we count it
countFrame();
// checking length and CRC of the received frame
if (rxFrame.len == 0) {
} else if (rxFrame.len < 5) {
// frame must be at least 5 bytes in length
countError("hdlc error, frame LI<5");
} else if (rxCRC == 0xF0B8) {
// good frame received
processFrame();
} else {
countError("hdlc complete, wrong terms.");
}
rxFrame.len = 0;
rxCRC = 0xffff;
break;
case FastHDLC.RETURN_DISCARD_FLAG:
// looking for next flag
rxCRC = 0xffff;
rxFrame.len = 0;
// eCount = 0;
countFrame();
// "on receive, hdlc discard"
countError("hdlc discard.");
break;
case FastHDLC.RETURN_EMPTY_FLAG:
rxFrame.len = 0;
break;
default:
if (rxFrame.len > 279) {
rxState.state = 0;
rxFrame.len = 0;
rxCRC = 0xffff;
eCount = 0;
countFrame();
countError("Overlong MTP frame, entering octet mode on link '" + name + "'");
} else {
rxFrame.frame[rxFrame.len++] = (byte) res;
rxCRC = PPP_FCS(rxCRC, res & 0xff);
}
}
}
}
private void processTx(int bytesRead) throws IOException {
for (int i = 0; i < bytesRead && i < this.ioBufferSize; i++) {
if (txState.bits < 8) {
// need more bits
if (doCRC == 0 && txFrame.offset < txFrame.len) {
int data = txFrame.frame[txFrame.offset++] & 0xff;
hdlc.fasthdlc_tx_load(txState, data);
txCRC = PPP_FCS(txCRC, data);
if (txFrame.offset == txFrame.len) {
doCRC = 1;
txCRC ^= 0xffff;
}
} else if (doCRC == 1) {
hdlc.fasthdlc_tx_load_nocheck(txState, (txCRC) & 0xff);
doCRC = 2;
} else if (doCRC == 2) {
hdlc.fasthdlc_tx_load_nocheck(txState, (txCRC >> 8) & 0xff);
doCRC = 0;
} else {
// nextFrame();
queueNextFrame();
txFrame.offset = 0;
txCRC = 0xffff;
hdlc.fasthdlc_tx_frame_nocheck(txState);
}
}
// txBuffer.put(i, (byte) hdlc.fasthdlc_tx_run_nocheck(txState));
txBuffer[i] = (byte) hdlc.fasthdlc_tx_run_nocheck(txState);
}
}
public void doRead() {
if (started) {
try {
int bytesRead = channel.read(rxBuffer);
if (bytesRead > 0) {
processRx(rxBuffer, bytesRead);
}
} catch (Exception e) {
if (logger.isEnabledFor(Level.ERROR)) {
logger.error(String.format("(%s) Can not read data from channel", name), e);
}
this.setState(MTP2_OUT_OF_SERVICE);
mtp3.linkFailed(this);
}
}
}
public void doWrite() {
if (!started) {
return;
}
try {
processTx(this.ioBufferSize);
channel.write(txBuffer, this.ioBufferSize);
} catch (Exception e) {
if (logger.isEnabledFor(Level.ERROR)) {
logger.error(String.format("(%s) Can not write data to channel", name), e);
}
this.setState(MTP2_OUT_OF_SERVICE);
mtp3.linkFailed(this);
}
}
// //////////////////////////
// PRIVATE HELPER METHODS //
// //////////////////////////
private void stopAERM() {
this.aermEnabled = false;
}
private void startAERM() {
this.eCount = 0;
// we reset, for octet counting mode.
this.nCount = 0;
this.aermEnabled = true;
}
private void alignmentNotPossible(String cause) {
this.cleanupTimers();
this.reset();
if (this.state == MTP2_INSERVICE) {
if (this.mtp3 != null) {
this.mtp3.linkFailed(this);
}
}
this.setState(MTP2_OUT_OF_SERVICE);
// NOTE: buffer is flushed on start alignment.
start_T17();
if (logger.isDebugEnabled()) {
logger.debug(String.format("(%s) Alignment not possible, initiating T17 for restart. Cause: %s", name, cause));
}
}
private void alignmentBroken(String cause) {
// FIXME: check if we can merge with above method.
this.cleanupTimers();
this.reset();
if (this.state == MTP2_INSERVICE) {
if (this.mtp3 != null) {
this.mtp3.linkFailed(this);
}
}
this.setState(MTP2_OUT_OF_SERVICE);
// NOTE: buffer is flushed on start alignment.
start_T17();
if (logger.isDebugEnabled()) {
logger.debug("Alignment broken, initiating T17 for restart. Cause: " + cause);
}
}
private void cleanupTimers() {
this.stop_T2();
this.stop_T3();
this.stop_T4();
this.stop_T7();
this.stop_T17();
}
private void alignmentComplete() {
stop_T2();
stop_T3();
stop_T4();
if (logger.isDebugEnabled()) {
logger.debug(String.format("(%s) Aligned ready", name));
}
this.setState(MTP2_ALIGNED_READY);
// now we wait for proper data
}
private void countError(String info) {
eCount++;
// logger.info("ERROR:" + info);
switch (state) {
case MTP2_ALIGNED_READY:
case MTP2_INSERVICE:
if (eCount >= 64) {
if (this.mtp3 != null) {
mtp3.linkFailed(this);
}
this.setState(MTP2_OUT_OF_SERVICE);
}
break;
case MTP2_PROVING:
if (this.aermEnabled) {
if (eCount >= aermThreshold) {
// start T17 ? check for alignemtn count;
// see Q.703 p.55
this.provingAttempts++;
if (this.provingAttempts < PROVING_ATTEMPTS_THRESHOLD) {
this.futureProving = true;
if (this.logger.isEnabledFor(Level.WARN)) {
// FIXME: should this remain warn ?
logger.warn("Exceeded AERM threshold[ " + aermThreshold + " ] errors[ " + eCount
+ " ], proving attempts[ " + provingAttempts + " ], continue...");
}
return;
}
// 1. Alignment not possible ---> ?
alignmentNotPossible("Exceeded AERM threshold[" + aermThreshold + "] errors[" + eCount
+ "], proving attempts[" + provingAttempts + "]");
// 2. stop T3
stop_T3();
// 3. cancel E
this.emergency = false;
// 4. stop AERM
stopAERM();
// 5. set state, should be done in #1.
}
}
break;
}
}
/**
* Increment number of received frames decrement error monitor countor for each 256 good frames.
*
*/
private void countFrame() {
if (state == MTP2_ALIGNED_READY || state == MTP2_INSERVICE) {
dCount = (dCount + 1) % 256;
// decrement error countor for each 256 frames
if (dCount == 0 && eCount > 0) {
eCount--;
}
}
}
// //////////////////////
// Timers and actions //
// //////////////////////
private T2Action t2Action;
private T3Action t3Action;
private T4Action t4Action;
private T7Action t7Action;
private T17Action t17Action;
private class T2Action extends Task {
private int ttl;
public T2Action(Scheduler scheduler) {
super(scheduler);
}
public int getQueueNumber() {
return scheduler.HEARTBEAT_QUEUE;
}
public void start() {
this.activate(true);
ttl = T2_TIMEOUT;
scheduler.submitHeatbeat(this);
}
public long perform() {
if (ttl > 0) {
ttl--;
scheduler.submitHeatbeat(this);
return 0;
}
// remove ref
// T2.cancel(false);
// T2 = null;
stop_T2();
int tmpState = state;
if (tmpState == MTP2_NOT_ALIGNED) {
// 1. ALI not possible
alignmentNotPossible("T2 Expired.");
// 2. cancel E
emergency = false;
if (logger.isEnabledFor(Level.WARN)) {
// FIXME: should this be debug ?
logger.warn("Timer T2 has expired, Alignment not possible. ");
}
} else {
if (logger.isEnabledFor(Level.WARN)) {
logger.warn("T2 fired in state[ " + STATE_NAMES[tmpState] + " ]");
}
}
return 0;
}
}
private class T3Action extends Task {
private int ttl;
public T3Action(Scheduler scheduler) {
super(scheduler);
}
public int getQueueNumber() {
return scheduler.HEARTBEAT_QUEUE;
}
public void start() {
this.activate(true);
ttl = T3_TIMEOUT;
scheduler.submitHeatbeat(this);
}
public long perform() {
if (ttl > 0) {
ttl--;
scheduler.submitHeatbeat(this);
return 0;
}
// remove ref
// T3.cancel(false);
// T3 = null;
stop_T3();
int tmpState = state;
if (tmpState == MTP2_ALIGNED) {
// 1. ALI not possible
alignmentNotPossible("T3 Expired.");
// 2.cancel E
emergency = false;
if (logger.isEnabledFor(Level.WARN)) {
logger.warn("Timer T3 has expired, Alignment not possible. ");
}
} else {
if (logger.isEnabledFor(Level.WARN)) {
logger.warn("T3 fired in state[ " + STATE_NAMES[tmpState] + " ]");
}
}
return 0;
}
}
private class T4Action extends Task {
private int ttl;
public T4Action(Scheduler scheduler) {
super(scheduler);
}
public int getQueueNumber() {
return scheduler.HEARTBEAT_QUEUE;
}
public void start() {
this.activate(true);
ttl = T4_TIMEOUT;
scheduler.submitHeatbeat(this);
}
public long perform() {
if (ttl > 0) {
ttl--;
scheduler.submitHeatbeat(this);
return 0;
}
// remove ref;
// T4.cancel(false);
// T4 = null;
stop_T4();
int tmpState = state;
if (tmpState == MTP2_PROVING) {
// decision
if (futureProving) {
// 1. start AERM, maybe restart?
startAERM();
// 2. cancel FP
futureProving = false;
// 3. start myself :)
start_T4();
// 4. state is still proving
} else {
alignmentComplete();
}
} else {
if (logger.isEnabledFor(Level.WARN)) {
logger.warn("T4 fired in state[ " + STATE_NAMES[tmpState] + " ]");
}
}
return 0;
}
}
private class T7Action extends Task {
private int ttl;
public T7Action(Scheduler scheduler) {
super(scheduler);
}
public int getQueueNumber() {
return scheduler.HEARTBEAT_QUEUE;
}
public void start() {
this.activate(true);
ttl = T7_TIMEOUT;
scheduler.submitHeatbeat(this);
}
public long perform() {
if (ttl > 0) {
ttl--;
scheduler.submitHeatbeat(this);
return 0;
}
// FIXME: add this
stop_T7();
return 0;
}
}
private class T17Action extends Task {
private int ttl;
public T17Action(Scheduler scheduler) {
super(scheduler);
}
public int getQueueNumber() {
return scheduler.HEARTBEAT_QUEUE;
}
public void start() {
this.activate(true);
ttl = T17_TIMEOUT;
scheduler.submitHeatbeat(this);
}
// first alig works different.
private boolean initial = true;
public long perform() {
if (ttl > 0) {
ttl--;
scheduler.submitHeatbeat(this);
return 0;
}
logger.info(String.format("(%s) Restarting initial alignment", name));
// there is somethin
// T17.cancel(true);
// T17 = null;
stop_T17();
if (state == MTP2_OUT_OF_SERVICE) {
startInitialAlignment(initial);
}
if (initial) {
initial = false;
}
return 0;
}
}
private void start_T2() {
this.stop_T2();
t2Action.start();
}
private void stop_T2() {
t2Action.cancel();
}
private void start_T3() {
this.stop_T3();
t3Action.start();
}
private void stop_T3() {
t3Action.cancel();
}
private void start_T4() {
this.stop_T4();
t4Action.start();
}
private void stop_T4() {
t4Action.cancel();
}
private void start_T7() {
this.stop_T7();
t7Action.start();
}
private void stop_T7() {
t7Action.cancel();
}
// MTP3 actaully, its accessed by it, we just keep some state in mtp2
public void start_T17() {
this.stop_T17();
t17Action.start();
}
public void stop_T17() {
t17Action.cancel();
}
public boolean isT17() {
return t17Action.isActive();
}
private static int NEXT_FSN(int x) {
// kill FIB if present
return ((x & 0x7F) + 1) % 128;
}
/**
* neat way to flip indicator regardles of value.
*/
private static int NEXT_INDICATOR(int x) {
return (x + 1) % 2;
}
}