All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.restcomm.protocols.ss7.sccp.impl.SccpStackImpl Maven / Gradle / Ivy

The newest version!
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * Copyright 2019, Mobius Software LTD 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.sccp.impl;

import static org.restcomm.protocols.ss7.sccp.impl.message.MessageUtil.calculateLudtFieldsLengthWithoutData;
import static org.restcomm.protocols.ss7.sccp.impl.message.MessageUtil.calculateUdtFieldsLengthWithoutData;
import static org.restcomm.protocols.ss7.sccp.impl.message.MessageUtil.calculateXudtFieldsLengthWithoutData;
import static org.restcomm.protocols.ss7.sccp.impl.message.MessageUtil.calculateXudtFieldsLengthWithoutData2;

import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.restcomm.protocols.ss7.indicator.RoutingIndicator;
import org.restcomm.protocols.ss7.mtp.Mtp3EndCongestionPrimitive;
import org.restcomm.protocols.ss7.mtp.Mtp3PausePrimitive;
import org.restcomm.protocols.ss7.mtp.Mtp3ResumePrimitive;
import org.restcomm.protocols.ss7.mtp.Mtp3StatusCause;
import org.restcomm.protocols.ss7.mtp.Mtp3StatusPrimitive;
import org.restcomm.protocols.ss7.mtp.Mtp3TransferPrimitive;
import org.restcomm.protocols.ss7.mtp.Mtp3UserPart;
import org.restcomm.protocols.ss7.mtp.Mtp3UserPartBaseImpl;
import org.restcomm.protocols.ss7.mtp.Mtp3UserPartListener;
import org.restcomm.protocols.ss7.sccp.LongMessageRule;
import org.restcomm.protocols.ss7.sccp.LongMessageRuleType;
import org.restcomm.protocols.ss7.sccp.MaxConnectionCountReached;
import org.restcomm.protocols.ss7.sccp.Mtp3ServiceAccessPoint;
import org.restcomm.protocols.ss7.sccp.RemoteSignalingPointCode;
import org.restcomm.protocols.ss7.sccp.Router;
import org.restcomm.protocols.ss7.sccp.Rule;
import org.restcomm.protocols.ss7.sccp.SccpCongestionControlAlgo;
import org.restcomm.protocols.ss7.sccp.SccpConnection;
import org.restcomm.protocols.ss7.sccp.SccpConnectionState;
import org.restcomm.protocols.ss7.sccp.SccpManagementEventListener;
import org.restcomm.protocols.ss7.sccp.SccpProtocolVersion;
import org.restcomm.protocols.ss7.sccp.SccpProvider;
import org.restcomm.protocols.ss7.sccp.SccpResource;
import org.restcomm.protocols.ss7.sccp.SccpStack;
import org.restcomm.protocols.ss7.sccp.impl.message.MessageFactoryImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpAddressedMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpDataNoticeTemplateMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpSegmentableMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.parameter.LocalReferenceImpl;
import org.restcomm.protocols.ss7.sccp.impl.parameter.SccpAddressImpl;
import org.restcomm.protocols.ss7.sccp.impl.parameter.SegmentationImpl;
import org.restcomm.protocols.ss7.sccp.impl.router.RouterImpl;
import org.restcomm.protocols.ss7.sccp.message.SccpConnMessage;
import org.restcomm.protocols.ss7.sccp.message.SccpMessage;
import org.restcomm.protocols.ss7.sccp.parameter.GlobalTitle;
import org.restcomm.protocols.ss7.sccp.parameter.LocalReference;
import org.restcomm.protocols.ss7.sccp.parameter.ProtocolClass;
import org.restcomm.protocols.ss7.sccp.parameter.ReturnCauseValue;
import org.restcomm.protocols.ss7.sccp.parameter.SccpAddress;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.DefaultThreadFactory;

/**
 *
 * @author amit bhayani
 * @author baranowb
 * @author sergey vetyutnev
 * @author yulianoifa
 *
 */
public class SccpStackImpl implements SccpStack, Mtp3UserPartListener {
    protected final Logger logger;

    /**
     * Interval in milliseconds in which new coming for an affected PC MTP-STATUS messages will be logged
     */
    private static final int STATUS_MSG_LOGGING_INTERVAL_MILLISEC_CONG = 10000;
    private static final int STATUS_MSG_LOGGING_INTERVAL_MILLISEC_UNAVAIL = 100;

     // If the XUDT message data length greater this value, segmentation is
    // needed
    protected int zMarginXudtMessage = 240;

    // sccp segmented message reassembling timeout
    protected int connEstTimerDelay = 15000;
    protected int iasTimerDelay = 7500 * 60;
    protected int iarTimerDelay = 16000 * 60;
    protected int relTimerDelay = 15000;
    protected int repeatRelTimerDelay = 15000;
    protected int intTimerDelay = 30000;
    protected int guardTimerDelay = 24000 * 60;
    protected int resetTimerDelay = 15000;
    protected int reassemblyTimerDelay = 15000;

    // Max available Sccp message data for all messages
    protected int maxDataMessage = 2560;
    // period logging warning in msec
    private int periodOfLogging = 60000;
    // remove PC from calledPartyAddress when sending to MTP3
    private boolean removeSpc = true;
    // respect point-code for outgoing messages
    //https://github.com/RestComm/jss7/issues/13
    private boolean respectPc = false;
    // can relay when relay with coupling condition is met
    private boolean canRelay = false;
    // min (starting) SST sending interval (millisec)
    protected int sstTimerDuration_Min = 10000;
    // max (after increasing) SST sending interval (millisec)
    protected int sstTimerDuration_Max = 600000;
    // multiplicator of SST sending interval (next interval will be greater the
    // current by sstTimerDuration_IncreaseFactor)
    // TODO: make it configurable
    protected double sstTimerDuration_IncreaseFactor = 1.5;
    // Which SCCP protocol version stack processes (ITU / ANSI)
    private SccpProtocolVersion sccpProtocolVersion = SccpProtocolVersion.ITU;

    // SCCP congestion control - the count of levels for restriction level - RLM
    protected int congControl_N = 8;
    // SCCP congestion control - the count of sublevels for restriction level - RSLM
    protected int congControl_M = 4;
    // Timer Ta value - started at next MTP-STATUS(cong) primitive coming; during this timer no more MTP-STATUS(cong) are
    // accepted
    protected int congControl_TIMER_A = 400; // 200
    // Timer Td value - started after last MTP-STATUS(cong) primitive coming; after end of this timer (without new coming
    // MTP-STATUS(cong)) RSLM will be reduced
    protected int congControl_TIMER_D = 2000; // 2000
    // sccp congestion control
    // international: international algorithm - only one level is provided by MTP3 level (in MTP-STATUS primitive). Each
    // MTP-STATUS increases N / M levels
    // levelDepended: MTP3 level (MTP-STATUS primitive) provides 3 levels of a congestion (1-3) and SCCP congestion will
    // increase to the
    // next level after MTP-STATUS next level increase (MTP-STATUS 1 to N up to 3, MTP-STATUS 2 to N up to 5, MTP-STATUS 3
    // to N up to 7)
    protected SccpCongestionControlAlgo congControl_Algo = SccpCongestionControlAlgo.international;
    // if true outgoing SCCP messages will be blocked (depending on message type, UDP messages from level N=6)
    protected boolean congControl_blockingOutgoungSccpMessages = false;

    // The count of threads that will be used for message delivering to
    // SccpListener's for SCCP user -> SCCP -> SCCP user transit (without MTP part)
    protected int deliveryTransferMessageThreadCount = 4;
    
    protected volatile State state = State.IDLE;

    // provider ref, this can be real provider or pipe, for tests.
    protected SccpProviderImpl sccpProvider;

    protected RouterImpl router;
    protected SccpResourceImpl sccpResource;

    protected MessageFactoryImpl messageFactory;

    protected SccpManagement sccpManagement;
    protected SccpRoutingControl sccpRoutingControl;

    protected ConcurrentHashMap connections = new ConcurrentHashMap();

    protected int referenceNumberCounterMax = 0xffffff;

    protected ConcurrentHashMap mtp3UserParts = new ConcurrentHashMap();
    protected ConcurrentHashMap reassemplyCache = new ConcurrentHashMap();

    protected ScheduledExecutorService msgDeliveryExecutors;
    
    public static final int slsFilter = 0x0f;
    protected int[] slsTable = null;

    // protected int localSpc;
    // protected int ni = 2;

    protected final String name;

    protected boolean rspProhibitedByDefault;

    private volatile AtomicInteger segmentationLocalRef = new AtomicInteger(0);
    private volatile AtomicInteger slsCounter = new AtomicInteger(0);
    private volatile AtomicBoolean selectorCounter = new AtomicBoolean(false);
    protected volatile AtomicInteger referenceNumberCounter = new AtomicInteger(0);

    private ConcurrentHashMap lastCongNotice = new ConcurrentHashMap();
    private ConcurrentHashMap lastUserPartUnavailNotice = new ConcurrentHashMap();

    private ConcurrentHashMap messagesSentByType=new ConcurrentHashMap();
    private ConcurrentHashMap messagesReceivedByType=new ConcurrentHashMap();
    private ConcurrentHashMap bytesSentByType=new ConcurrentHashMap();
    private ConcurrentHashMap bytesReceivedByType=new ConcurrentHashMap();
    private ConcurrentHashMap> messagesSentByTypeAndNetwork=new ConcurrentHashMap>();
    private ConcurrentHashMap> messagesReceivedByTypeAndNetwork=new ConcurrentHashMap>();
    private ConcurrentHashMap> bytesSentByTypeAndNetwork=new ConcurrentHashMap>();
    private ConcurrentHashMap> bytesReceivedByTypeAndNetwork=new ConcurrentHashMap>();
        
    public static List allMessageTypes=Arrays.asList(new String[] {SccpMessageImpl.MESSAGE_NAME_OTHER,
    		SccpMessageImpl.MESSAGE_NAME_CR, SccpMessageImpl.MESSAGE_NAME_CC, SccpMessageImpl.MESSAGE_NAME_CREF,
    		SccpMessageImpl.MESSAGE_NAME_RLSD, SccpMessageImpl.MESSAGE_NAME_RLC, SccpMessageImpl.MESSAGE_NAME_DT1,
    		SccpMessageImpl.MESSAGE_NAME_DT2, SccpMessageImpl.MESSAGE_NAME_AK, SccpMessageImpl.MESSAGE_NAME_RSR,
    		SccpMessageImpl.MESSAGE_NAME_RSC, SccpMessageImpl.MESSAGE_NAME_ERR, SccpMessageImpl.MESSAGE_NAME_IT,
    		SccpMessageImpl.MESSAGE_NAME_UDT, SccpMessageImpl.MESSAGE_NAME_UDTS, SccpMessageImpl.MESSAGE_NAME_XUDT,
    		SccpMessageImpl.MESSAGE_NAME_XUDTS, SccpMessageImpl.MESSAGE_NAME_LUDT,SccpMessageImpl.MESSAGE_NAME_LUDTS });
    
    /*
     * For non-connection oriented protocol class usage
     */
    public SccpStackImpl(String name) {    
        this.name = name;
        this.logger = LogManager.getLogger(SccpStackImpl.class.getCanonicalName() + "-" + this.name);

        this.messageFactory = new MessageFactoryImpl(this);
        this.sccpProvider = new SccpProviderImpl(this);

        this.state = State.CONFIGURED;
        
        for(String currType:allMessageTypes) {
        	messagesSentByType.put(currType, new AtomicLong(0L));
        	messagesReceivedByType.put(currType, new AtomicLong(0L));
        	bytesSentByType.put(currType, new AtomicLong(0L));
        	bytesReceivedByType.put(currType, new AtomicLong(0L));
        }
    }

    public String getName() {
        return this.name;
    }

    public void setRspProhibitedByDefault(boolean rspProhibitedByDefault) {
        this.rspProhibitedByDefault = rspProhibitedByDefault;
    }

    public boolean isRspProhibitedByDefault() {
        return rspProhibitedByDefault;
    }

    public SccpProvider getSccpProvider() {
        return sccpProvider;
    }

    public Map getMtp3UserParts() {
        return mtp3UserParts;
    }

    public void setMtp3UserParts(ConcurrentHashMap mtp3UserPartsTemp) {
        if (mtp3UserPartsTemp != null) {
        	this.mtp3UserParts=mtp3UserPartsTemp;
        }
    }

    public Mtp3UserPart getMtp3UserPart(int id) {
        return mtp3UserParts.get(id);
    }

    public void setMtp3UserPart(int id, Mtp3UserPart mtp3UserPart) {
        if (mtp3UserPart == null) {
            this.removeMtp3UserPart(id);
        } else {
        	this.mtp3UserParts.put(id, mtp3UserPart);
        }
    }

    public void removeMtp3UserPart(int id) {
    	this.mtp3UserParts.remove(id);
    }

    public void setRemoveSpc(boolean removeSpc) throws Exception {
        if (!this.isStarted())
            throw new Exception("RemoveSpc parameter can be updated only when SCCP stack is running");

        this.removeSpc = removeSpc;
    }

    public void setRespectPc(boolean respectPc) throws Exception {
        if (!this.isStarted())
            throw new Exception("RespectPc parameter can be updated only when SCCP stack is running");

        this.respectPc = respectPc;
    }

    public void setCanRelay(boolean canRelay) throws Exception {
        if (!this.isStarted())
            throw new Exception("CanRelay parameter can be updated only when SCCP stack is running");

        this.canRelay = canRelay;
    }

    public void setSccpProtocolVersion(SccpProtocolVersion sccpProtocolVersion) throws Exception {
        if (!this.isStarted())
            throw new Exception("SccpProtocolVersion parameter can be updated only when SCCP stack is running");

        if (sccpProtocolVersion != null)
            this.sccpProtocolVersion = sccpProtocolVersion;
    }

    public int getDeliveryMessageThreadCount() {
        return this.deliveryTransferMessageThreadCount;
    }

    public void setDeliveryMessageThreadCount(int deliveryMessageThreadCount) throws Exception {
        if (this.isStarted())
            throw new Exception("DeliveryMessageThreadCount parameter can be updated only when SCCP stack is NOT running");

        if (deliveryMessageThreadCount > 0 && deliveryMessageThreadCount <= 100)
            this.deliveryTransferMessageThreadCount = deliveryMessageThreadCount;
    }

    public void setSstTimerDuration_Min(int sstTimerDuration_Min) throws Exception {
        if (!this.isStarted())
            throw new Exception("SstTimerDuration_Min parameter can be updated only when SCCP stack is running");

        // 5-10 seconds
        if (sstTimerDuration_Min < 5000)
            sstTimerDuration_Min = 5000;
        if (sstTimerDuration_Min > 10000)
            sstTimerDuration_Min = 10000;
        this.sstTimerDuration_Min = sstTimerDuration_Min;
    }

    public void setSstTimerDuration_Max(int sstTimerDuration_Max) throws Exception {
        if (!this.isStarted())
            throw new Exception("SstTimerDuration_Max parameter can be updated only when SCCP stack is running");

        // 10-20 minutes
        if (sstTimerDuration_Max < 600000)
            sstTimerDuration_Max = 600000;
        if (sstTimerDuration_Max > 1200000)
            sstTimerDuration_Max = 1200000;
        this.sstTimerDuration_Max = sstTimerDuration_Max;
    }

    public void setSstTimerDuration_IncreaseFactor(double sstTimerDuration_IncreaseFactor) throws Exception {
        if (!this.isStarted())
            throw new Exception("SstTimerDuration_IncreaseFactor parameter can be updated only when SCCP stack is running");

        // acceptable factor from 1 to 4
        if (sstTimerDuration_IncreaseFactor < 1)
            sstTimerDuration_IncreaseFactor = 1;
        if (sstTimerDuration_IncreaseFactor > 4)
            sstTimerDuration_IncreaseFactor = 4;
        this.sstTimerDuration_IncreaseFactor = sstTimerDuration_IncreaseFactor;
    }

    public boolean isRemoveSpc() {
        return this.removeSpc;
    }

    public boolean isRespectPc() {
        return this.respectPc;
    }

    public boolean isCanRelay() {
        return this.canRelay;
    }

    public SccpProtocolVersion getSccpProtocolVersion() {
        return this.sccpProtocolVersion;
    }

    public int getSstTimerDuration_Min() {
        return this.sstTimerDuration_Min;
    }

    public int getSstTimerDuration_Max() {
        return this.sstTimerDuration_Max;
    }

    public double getSstTimerDuration_IncreaseFactor() {
        return this.sstTimerDuration_IncreaseFactor;
    }

    public int getZMarginXudtMessage() {
        return zMarginXudtMessage;
    }

    public void setZMarginXudtMessage(int zMarginXudtMessage) throws Exception {
        if (!this.isStarted())
            throw new Exception("ZMarginXudtMessage parameter can be updated only when SCCP stack is running");

        // value from 160 to 255 bytes
        if (zMarginXudtMessage < 160)
            zMarginXudtMessage = 160;
        if (zMarginXudtMessage > 255)
            zMarginXudtMessage = 255;
        this.zMarginXudtMessage = zMarginXudtMessage;
    }

    public int getMaxDataMessage() {
        return maxDataMessage;
    }

    public void setMaxDataMessage(int maxDataMessage) throws Exception {
        if (!this.isStarted())
            throw new Exception("MaxDataMessage parameter can be updated only when SCCP stack is running");

        // from 2560 to 3952 bytes
        if (maxDataMessage < 2560)
            maxDataMessage = 2560;
        if (maxDataMessage > 3952)
            maxDataMessage = 3952;
        this.maxDataMessage = maxDataMessage;
    }

    public int getConnEstTimerDelay() {
        return this.connEstTimerDelay;
    }

    public void setConnEstTimerDelay(int connEstTimerDelay) throws Exception {
        if (!this.isStarted())
            throw new Exception("ConnEstTimerDelay parameter can be updated only when SCCP stack is running");

        // from 1 to 2 minutes
        if (connEstTimerDelay < 60000)
            connEstTimerDelay = 60000;
        if (connEstTimerDelay > 120000)
            connEstTimerDelay = 120000;
        this.connEstTimerDelay = connEstTimerDelay;
    }

    public int getIasTimerDelay() {
        return this.iasTimerDelay;
    }

    public void setIasTimerDelay(int iasTimerDelay) throws Exception {
        if (!this.isStarted())
            throw new Exception("IasTimerDelay parameter can be updated only when SCCP stack is running");

        // from 5 to 10 minutes
        if (iasTimerDelay < 300000)
            iasTimerDelay = 300000;
        if (iasTimerDelay > 600000)
            iasTimerDelay = 600000;
        this.iasTimerDelay = iasTimerDelay;
    }

    public int getIarTimerDelay() {
        return this.iarTimerDelay;
    }

    public void setIarTimerDelay(int iarTimerDelay) throws Exception {
        this.iarTimerDelay = iarTimerDelay;

        if (!this.isStarted())
            throw new Exception("IarTimerDelay parameter can be updated only when SCCP stack is running");

        // from 11 to 21 minutes
        if (iarTimerDelay < 660000)
            iarTimerDelay = 660000;
        if (iarTimerDelay > 1260000)
            iarTimerDelay = 1260000;
        this.iarTimerDelay = iarTimerDelay;
    }

    public int getRelTimerDelay() {
        return this.relTimerDelay;
    }

    public void setRelTimerDelay(int relTimerDelay) throws Exception {
        if (!this.isStarted())
            throw new Exception("RelTimerDelay parameter can be updated only when SCCP stack is running");

        // from 10 to 20 seconds
        if (relTimerDelay < 10000)
            relTimerDelay = 10000;
        if (relTimerDelay > 20000)
            relTimerDelay = 20000;
        this.relTimerDelay = relTimerDelay;
    }

    public int getRepeatRelTimerDelay() {
        return this.repeatRelTimerDelay;
    }

    public void setRepeatRelTimerDelay(int repeatRelTimerDelay) throws Exception {
        if (!this.isStarted())
            throw new Exception("RepeatRelTimerDelay parameter can be updated only when SCCP stack is running");

        // from 10 to 20 seconds
        if (repeatRelTimerDelay < 10000)
            repeatRelTimerDelay = 10000;
        if (repeatRelTimerDelay > 20000)
            repeatRelTimerDelay = 20000;
        this.repeatRelTimerDelay = repeatRelTimerDelay;
    }

    public int getIntTimerDelay() {
        return this.intTimerDelay;
    }

    public void setIntTimerDelay(int intTimerDelay) throws Exception {
        if (!this.isStarted())
            throw new Exception("IntTimerDelay parameter can be updated only when SCCP stack is running");

        // extending to 1 minute
        if (intTimerDelay < 0)
            intTimerDelay = 0;
        if (intTimerDelay > 60000)
            intTimerDelay = 60000;
        this.intTimerDelay = intTimerDelay;
    }

    public int getGuardTimerDelay() {
        return this.guardTimerDelay;
    }

    public void setGuardTimerDelay(int guardTimerDelay) throws Exception {
        if (!this.isStarted())
            throw new Exception("GuardTimerDelay parameter can be updated only when SCCP stack is running");

        // from 23 to 25 minutes
        if (guardTimerDelay < 1380000)
            guardTimerDelay = 1380000;
        if (guardTimerDelay > 1500000)
            guardTimerDelay = 1500000;
        this.guardTimerDelay = guardTimerDelay;
    }

    public int getResetTimerDelay() {
        return this.resetTimerDelay;
    }

    public void setResetTimerDelay(int resetTimerDelay) throws Exception {
        if (!this.isStarted())
            throw new Exception("ResetTimerDelay parameter can be updated only when SCCP stack is running");

        // from 10 to 20 seconds
        if (resetTimerDelay < 10000)
            resetTimerDelay = 10000;
        if (resetTimerDelay > 20000)
            resetTimerDelay = 20000;
        this.resetTimerDelay = resetTimerDelay;
    }

    public int getPeriodOfLogging() {
        return periodOfLogging;
    }

    public void setPeriodOfLogging(int periodOfLogging) throws Exception {
        if (!this.isStarted())
            throw new Exception("periodOfLogging parameter can be updated only when SCCP stack is running");

        this.periodOfLogging = periodOfLogging;
    }

    public int getReassemblyTimerDelay() {
        return this.reassemblyTimerDelay;
    }

    public void setReassemblyTimerDelay(int reassemblyTimerDelay) throws Exception {
        if (!this.isStarted())
            throw new Exception("ReassemblyTimerDelay parameter can be updated only when SCCP stack is running");

        // from 10 to 20 seconds
        if (reassemblyTimerDelay < 10000)
            reassemblyTimerDelay = 10000;
        if (reassemblyTimerDelay > 20000)
            reassemblyTimerDelay = 20000;
        this.reassemblyTimerDelay = reassemblyTimerDelay;
    }

    public int newSegmentationLocalRef() {
        return segmentationLocalRef.incrementAndGet();
    }

    public int newSls() {
    	this.slsCounter.incrementAndGet();
    	this.slsCounter.compareAndSet(256, 0);
        return this.slsCounter.get();
    }

    public boolean newSelector() {
    	if(!this.selectorCounter.compareAndSet(false, true))
    		this.selectorCounter.set(true);
        
    	return this.selectorCounter.get();
    }

    protected void createSLSTable(int maxSls, int minimumBoundThread) {
        int stream = 0;
        for (int i = 0; i < maxSls; i++) {
            if (stream >= minimumBoundThread) {
                stream = 0;
            }
            slsTable[i] = stream++;
        }
    }

    public void start() throws IllegalStateException {
    	start(new SccpRoutingControl(sccpProvider, this));
    }
    
    public void start(SccpRoutingControl routingControl) throws IllegalStateException {
        logger.info("Starting ...");

        // FIXME: make this configurable
        // FIXME: move creation to constructor ?
        this.sccpManagement = new SccpManagement(name, sccpProvider, this);
        this.sccpRoutingControl = routingControl;
        
        this.sccpManagement.setSccpRoutingControl(sccpRoutingControl);
        this.sccpRoutingControl.setSccpManagement(sccpManagement);
        
        this.router = new RouterImpl(this.name, this);
        this.router.start();

        this.sccpResource = new SccpResourceImpl(this.name, this.rspProhibitedByDefault);
        this.sccpResource.start();

        logger.info("Starting routing engine...");
        this.sccpRoutingControl.start();
        logger.info("Starting management ...");
        this.sccpManagement.start();
        logger.info("Starting MSU handler...");

        this.msgDeliveryExecutors = Executors.newScheduledThreadPool(deliveryTransferMessageThreadCount, new DefaultThreadFactory("SccpTransit-DeliveryExecutor"));
        
        // initiating of SCCP delivery executors
        // TODO: we do it for ITU standard, may be we may configure it for other standard's (different SLS count) maxSls and
        // slsFilter values initiating
        int maxSls = 16;
        this.slsTable = new int[maxSls];
        this.createSLSTable(maxSls, this.deliveryTransferMessageThreadCount);
        
        Iterator mtp3Iterator=this.mtp3UserParts.values().iterator();
        while(mtp3Iterator.hasNext()) {
            Mtp3UserPart mup = mtp3Iterator.next();
            mup.addMtp3UserPartListener(this);
        }

        Iterator iterator=this.sccpProvider.managementEventListeners.values().iterator();
        while(iterator.hasNext()) {
            try {
            	iterator.next().onServiceStarted();
            } catch (Throwable ee) {
                logger.error("Exception while invoking onServiceStarted", ee);
            }
        }

        this.state = State.RUNNING;
    }

    public void stop() {
        logger.info("Stopping ...");
        // stateLock.lock();
        // try
        // {
        this.state = State.IDLE;
        // executor = null;
        //
        // layer3exec = null;

        if (this.msgDeliveryExecutors != null) {
        	this.msgDeliveryExecutors.shutdown();
            this.msgDeliveryExecutors = null;
        }

        Iterator iterator=this.sccpProvider.managementEventListeners.values().iterator();
        while(iterator.hasNext()) {
            try {
            	iterator.next().onServiceStopped();
            } catch (Throwable ee) {
                logger.error("Exception while invoking onServiceStopped", ee);
            }
        }

        Iterator mtp3Iterator=this.mtp3UserParts.values().iterator();
        while(mtp3Iterator.hasNext()) {
            Mtp3UserPart mup = mtp3Iterator.next();
            mup.removeMtp3UserPartListener(this);
        }

        logger.info("Stopping management...");
        this.sccpManagement.stop();
        logger.info("Stopping routing engine...");
        this.sccpRoutingControl.stop();
        logger.info("Stopping MSU handler...");

        this.sccpResource.stop();

        this.router.stop();

        reassemplyCache.clear();
        
        // }finally
        // {
        // stateLock.unlock();
        // }
    }

    public boolean isStarted() {
        return this.state == State.RUNNING;
    }

    public Router getRouter() {
        return this.router;
    }

    public SccpResource getSccpResource() {
        return sccpResource;
    }

    public SccpConnectionImpl getConnection(LocalReference number) {
        if (number == null) {
            logger.error("Reference number can't be null");
            throw new IllegalArgumentException("Reference number can't be null");
        }
        return (SccpConnectionImpl)connections.get(number.getValue());
    }

    public int getConnectionsNumber() {
        return connections.size();
    }

    protected enum State {
        IDLE, CONFIGURED, RUNNING;
    }

    public SccpConnectionImpl newConnection(int localSsn, ProtocolClass protocol) throws MaxConnectionCountReached {
        SccpConnectionImpl conn;
        Integer refNumber = newReferenceNumber();

        if (protocol.getProtocolClass() == 2) {
            conn = new SccpConnectionImpl(localSsn, new LocalReferenceImpl(refNumber), protocol, this, sccpRoutingControl);
        } else if (protocol.getProtocolClass() == 3) {
            conn = new SccpConnectionWithFlowControlImpl(localSsn, new LocalReferenceImpl(refNumber), protocol, this, sccpRoutingControl);
        } else {
            logger.error(String.format("Unsupported connection class %d", protocol.getProtocolClass()));
            throw new IllegalArgumentException();
        }

        connections.put(refNumber, conn);
        return conn;
    }

    public void removeConnection(LocalReference ref) {
        SccpConnectionImpl conn = (SccpConnectionImpl)connections.remove(ref.getValue());
        if (conn != null) {
            conn.stopTimers();
            conn.setState(SccpConnectionState.CLOSED);
        }
    }

    protected int newReferenceNumber() throws MaxConnectionCountReached {
    	referenceNumberCounter.incrementAndGet();
    	referenceNumberCounter.compareAndSet(referenceNumberCounterMax, 0);
    	
        // 0 and reference numbers values which are > 0
        if (connections.size() >= referenceNumberCounterMax + 1) {
            logger.error(String.format("Can't open more connections than %d", referenceNumberCounterMax + 1));
            throw new MaxConnectionCountReached(String.format("Can't open more connections than %d", referenceNumberCounterMax + 1));
        }

        while (connections.containsKey(referenceNumberCounter.get())) {
            referenceNumberCounter.incrementAndGet();
        }
        return referenceNumberCounter.get();
    }

    protected void send(SccpDataNoticeTemplateMessageImpl message) throws Exception {

        if (this.state != State.RUNNING) {
            logger.error("Trying to send SCCP message from SCCP user but SCCP stack is not RUNNING");
            return;
        }

        if (message.getCalledPartyAddress() == null || message.getCallingPartyAddress() == null || message.getData() == null
                || message.getData().readableBytes() == 0) {
            logger.error("Message to send must has filled CalledPartyAddress, CallingPartyAddress and data fields");
            throw new IOException("Message to send must has filled CalledPartyAddress, CallingPartyAddress and data fields");
        }

        try {
            this.sccpRoutingControl.routeMssgFromSccpUser(message);
        } catch (Exception e) {
            // log here Exceptions from MTP3 level
            logger.error("IOException when sending the message to MTP3 level: " + e.getMessage(), e);
            throw e;
        }
    }

    protected int getMaxUserDataLength(SccpAddress calledPartyAddress, SccpAddress callingPartyAddress, int msgNetworkId) {

        GlobalTitle gt = calledPartyAddress.getGlobalTitle();
        int dpc = calledPartyAddress.getSignalingPointCode();
        int ssn = calledPartyAddress.getSubsystemNumber();

        if (calledPartyAddress.getAddressIndicator().isPCPresent()) {
            if (this.router.spcIsLocal(dpc)) {
                if (ssn > 0) {
                    // local destination - unlimited length
                    return this.getMaxDataMessage();
                } else if (gt != null) {
                    return getMaxUserDataLengthForGT(calledPartyAddress, callingPartyAddress, msgNetworkId);
                } else {
                    return 0;
                }
            } else {
                return getMaxUserDataLengthForDpc(dpc, calledPartyAddress, callingPartyAddress);
            }
        } else {
            if (gt != null) {
                return getMaxUserDataLengthForGT(calledPartyAddress, callingPartyAddress, msgNetworkId);
            } else {
                return 0;
            }
        }
    }

    private int getMaxUserDataLengthForDpc(int dpc, SccpAddress calledPartyAddress, SccpAddress callingPartyAddress) {

        LongMessageRule lmr = this.router.findLongMessageRule(dpc);
        LongMessageRuleType lmrt = LongMessageRuleType.LONG_MESSAGE_FORBBIDEN;
        if (lmr != null)
            lmrt = lmr.getLongMessageRuleType();
        Mtp3ServiceAccessPoint sap = this.router.findMtp3ServiceAccessPoint(dpc, 0);
        if (sap == null) {
            return 0;
        }
        Mtp3UserPart mup = this.getMtp3UserPart(sap.getMtp3Id());
        if (mup == null) {
            return 0;
        }

        try {
            int fieldsLen = 0;
            ByteBuf cdp=Unpooled.buffer();
            ByteBuf cnp=Unpooled.buffer();
            ((SccpAddressImpl) calledPartyAddress).encode(cdp, isRemoveSpc(), this.getSccpProtocolVersion());
            ((SccpAddressImpl) callingPartyAddress).encode(cnp, isRemoveSpc(), this.getSccpProtocolVersion());
            switch (lmrt) {
                case LONG_MESSAGE_FORBBIDEN:
                    fieldsLen = calculateUdtFieldsLengthWithoutData(cdp.readableBytes(), cnp.readableBytes());
                    break;
                case LUDT_ENABLED:
                case LUDT_ENABLED_WITH_SEGMENTATION:
                    fieldsLen = calculateLudtFieldsLengthWithoutData(cdp.readableBytes(), cnp.readableBytes(), true, true);
                    break;
                case XUDT_ENABLED:
                    fieldsLen = calculateXudtFieldsLengthWithoutData(cdp.readableBytes(), cnp.readableBytes(), true, true);
                    int fieldsLen2 = calculateXudtFieldsLengthWithoutData2(cdp.readableBytes(), cnp.readableBytes());
                    if (fieldsLen > fieldsLen2)
                        fieldsLen = fieldsLen2;
                    break;
            }

            int availLen = mup.getMaxUserDataLength(dpc) - fieldsLen;
            if ((lmrt == LongMessageRuleType.LONG_MESSAGE_FORBBIDEN || lmrt == LongMessageRuleType.XUDT_ENABLED)
                    && availLen > 255)
                availLen = 255;
            if (lmrt == LongMessageRuleType.XUDT_ENABLED)
                availLen *= 16;
            if (availLen > this.getMaxDataMessage())
                availLen = this.getMaxDataMessage();
            return availLen;

        } catch (Exception e) {
            // this can not occur
            // dont be so sure!
            e.printStackTrace();
            return 0;
        }
    }

    private int getMaxUserDataLengthForGT(SccpAddress calledPartyAddress, SccpAddress callingPartyAddress, int msgNetworkId) {

        Rule rule = this.router.findRule(calledPartyAddress, callingPartyAddress, false, msgNetworkId);
        if (rule == null) {
            return 0;
        }
        SccpAddress translationAddressPri = this.router.getRoutingAddress(rule.getPrimaryAddressId());
        if (translationAddressPri == null) {
            return 0;
        }

        return getMaxUserDataLengthForDpc(translationAddressPri.getSignalingPointCode(), calledPartyAddress,
                callingPartyAddress);
    }

    protected void broadcastChangedSsnState(int affectedSsn, boolean inService) {
        this.sccpManagement.broadcastChangedSsnState(affectedSsn, inService);
    }

    public void removeAllResourses() {

        if (this.state != State.RUNNING) {
            return;
        }

        this.router.removeAllResourses();
        this.sccpResource.removeAllResourses();

        Iterator iterator=this.sccpProvider.managementEventListeners.values().iterator();
        while(iterator.hasNext()) {
            try {
            	iterator.next().onRemoveAllResources();
            } catch (Throwable ee) {
                logger.error("Exception while invoking onRemoveAllResources", ee);
            }
        }
    }

    public void onMtp3PauseMessage(Mtp3PausePrimitive msg) {

        logger.warn(String.format("Rx : %s", msg));

        if (this.state != State.RUNNING) {
            logger.error("Cannot consume MTP3 PASUE message as SCCP stack is not RUNNING");
            return;
        }

        sccpManagement.handleMtp3Pause(msg.getAffectedDpc());
    }

    public void onMtp3ResumeMessage(Mtp3ResumePrimitive msg) {
        logger.warn(String.format("Rx : %s", msg));

        if (this.state != State.RUNNING) {
            logger.error("Cannot consume MTP3 RESUME message as SCCP stack is not RUNNING");
            return;
        }

        sccpManagement.handleMtp3Resume(msg.getAffectedDpc());
    }

    public void onMtp3StatusMessage(Mtp3StatusPrimitive msg) {
        int affectedDpc = msg.getAffectedDpc();

        // we are making of announcing of MTP-STATUS only each 10 seconds
        Date lastNotice;
        if (msg.getCause() == Mtp3StatusCause.SignallingNetworkCongested) {
            lastNotice = lastCongNotice.get(affectedDpc);
            if (lastNotice == null) {
                lastCongNotice.put(affectedDpc, new Date());
                logger.warn(String.format("Rx : %s  (one message in %d seconds)", msg, STATUS_MSG_LOGGING_INTERVAL_MILLISEC_UNAVAIL));
            } else {
                if (System.currentTimeMillis() - lastNotice.getTime() > STATUS_MSG_LOGGING_INTERVAL_MILLISEC_CONG) {
                    lastNotice.setTime(System.currentTimeMillis());
                    logger.warn(String.format("Rx : %s (one message in %d seconds)", msg, STATUS_MSG_LOGGING_INTERVAL_MILLISEC_UNAVAIL));
                }
            }
        } else {
            lastNotice = lastUserPartUnavailNotice.get(affectedDpc);
            if (lastNotice == null) {
                lastUserPartUnavailNotice.put(affectedDpc, new Date());
                logger.warn(String.format("Rx : %s (one message in %d seconds)", msg, STATUS_MSG_LOGGING_INTERVAL_MILLISEC_UNAVAIL));
            } else {
                if (System.currentTimeMillis() - lastNotice.getTime() > STATUS_MSG_LOGGING_INTERVAL_MILLISEC_UNAVAIL) {
                    lastNotice.setTime(System.currentTimeMillis());
                    logger.warn(String.format("Rx : %s (one message in %d seconds)", msg, STATUS_MSG_LOGGING_INTERVAL_MILLISEC_UNAVAIL));
                }
            }
        }

        if (this.state != State.RUNNING) {
            logger.error("Cannot consume MTP3 STATUS message as SCCP stack is not RUNNING");
            return;
        }

        sccpManagement.handleMtp3Status(msg.getCause(), affectedDpc, msg.getCongestionLevel());
    }

    @Override
    public void onMtp3EndCongestionMessage(Mtp3EndCongestionPrimitive msg) {
        int affectedDpc = msg.getAffectedDpc();

        logger.warn(String.format("Rx : %s", msg));

        sccpManagement.handleMtp3EndCongestion(affectedDpc);
    }

    public void onMtp3TransferMessage(Mtp3TransferPrimitive mtp3Msg) {

        if (this.state != State.RUNNING) {
            logger.error("Received MTP3TransferPrimitive from lower layer but SCCP stack is not RUNNING");
            return;
        }

        SccpMessageImpl msg = null;
        int dpc = mtp3Msg.getDpc();
        int opc = mtp3Msg.getOpc();

        try {
        	// decoding of a message
            ByteBuf data=mtp3Msg.getData();
            int bytes=data.readableBytes();
            int mt = data.readUnsignedByte();
            msg = ((MessageFactoryImpl) sccpProvider.getMessageFactory()).createMessage(mt, mtp3Msg.getOpc(), mtp3Msg.getDpc(), mtp3Msg.getSls(), data,
                    this.sccpProtocolVersion, 0);
            
            messagesReceivedByType.get(SccpMessageImpl.getName(msg.getType())).incrementAndGet();
        	bytesReceivedByType.get(SccpMessageImpl.getName(msg.getType())).addAndGet(bytes);
        	
            // checking if incoming dpc is local
            if (!this.router.spcIsLocal(dpc)) {

                // incoming dpc is not local - trying to find the target SAP and
                // send a message to MTP3 (MTP transit function)
                int sls = mtp3Msg.getSls();

                RemoteSignalingPointCode remoteSpc = this.getSccpResource().getRemoteSpcByPC(dpc);
                if (remoteSpc == null) {
                    if (logger.isWarnEnabled()) {
                        logger.warn(String.format("Incoming Mtp3 Message for nonlocal dpc=%d. But RemoteSpc is not found", dpc));
                    }
                    return;
                }
                if (remoteSpc.isRemoteSpcProhibited()) {
                    if (logger.isWarnEnabled()) {
                        logger.warn(String
                                .format("Incoming Mtp3 Message for nonlocal dpc=%d. But RemoteSpc is Prohibited", dpc));
                    }
                    // TODO: ***** SSP should we send SSP message to a peer ?
                    return;
                }
                if (remoteSpc.getCurrentRestrictionLevel() > 1) {
                    if (logger.isWarnEnabled()) {
                        logger.warn(String
                                .format("Incoming Mtp3 Message for nonlocal dpc=%d. But RemoteSpc is Congested", dpc));
                    }
                    // TODO: ***** SSC should we send SSC message to a peer ?
                    return;
                }
                Mtp3ServiceAccessPoint sap2 = this.router.findMtp3ServiceAccessPoint(dpc, sls);
                if (sap2 == null) {
                    if (logger.isWarnEnabled()) {
                        logger.warn(String.format("Incoming Mtp3 Message for nonlocal dpc=%d / sls=%d. But SAP is not found",
                                dpc, sls));
                    }
                    return;
                }
                Mtp3UserPart mup = this.getMtp3UserPart(sap2.getMtp3Id());
                if (mup == null) {
                    if (logger.isWarnEnabled()) {
                        logger.warn(String.format(
                                "Incoming Mtp3 Message for nonlocal dpc=%d / sls=%d. no matching Mtp3UserPart found", dpc, sls));
                    }
                    return;
                }

                mup.sendMessage(mtp3Msg);
                return;
            }

            // process only SCCP messages
            if (mtp3Msg.getSi() != Mtp3UserPartBaseImpl._SI_SERVICE_SCCP) {
                logger.warn(String
                        .format("Received Mtp3TransferPrimitive from lower layer with Service Indicator=%d which is not SCCP. Dropping this message",
                                mtp3Msg.getSi()));
                return;
            }

            // finding sap and networkId for a message
            dpc = mtp3Msg.getDpc();
            opc = mtp3Msg.getOpc();
            String localGtDigits = null;
            if (msg instanceof SccpAddressedMessageImpl) {
                SccpAddressedMessageImpl msgAddr = (SccpAddressedMessageImpl) msg;
                SccpAddress addr = msgAddr.getCalledPartyAddress();
                if (addr != null) {
                    GlobalTitle gt = addr.getGlobalTitle();
                    if (gt != null) {
                        localGtDigits = gt.getDigits();
                    }
                }
            }
            Mtp3ServiceAccessPoint sap = this.router.findMtp3ServiceAccessPointForIncMes(dpc, opc, localGtDigits);
            int networkId = 0;
            if (sap == null) {
                if (logger.isWarnEnabled()) {
                    logger.warn(String.format("Incoming Mtp3 Message for local address for localPC=%d, remotePC=%d, sls=%d. But SAP is not found for localPC", dpc, opc, mtp3Msg.getSls()));
                }
            } else {
                networkId = sap.getNetworkId();
            }
            msg.setNetworkId(networkId);
            Integer networkID=msg.getNetworkId();
         	ConcurrentHashMap messagesReceivedByTypeAndNetwork=this.messagesReceivedByTypeAndNetwork.get(networkID);
         	if(messagesReceivedByTypeAndNetwork==null) {
         		messagesReceivedByTypeAndNetwork=new ConcurrentHashMap();
         		for(String currType:allMessageTypes)
                	messagesReceivedByTypeAndNetwork.put(currType, new AtomicLong(0L));            	
                
         		ConcurrentHashMap oldValue=this.messagesReceivedByTypeAndNetwork.putIfAbsent(networkID, messagesReceivedByTypeAndNetwork);
         		if(oldValue!=null)
         			messagesReceivedByTypeAndNetwork=oldValue;     			
         	}
         	
         	ConcurrentHashMap bytesReceivedByTypeAndNetwork=this.bytesReceivedByTypeAndNetwork.get(networkID);
         	if(bytesReceivedByTypeAndNetwork==null) {
         		bytesReceivedByTypeAndNetwork=new ConcurrentHashMap();
         		for(String currType:allMessageTypes)
         			bytesReceivedByTypeAndNetwork.put(currType, new AtomicLong(0L));            	
                
         		ConcurrentHashMap oldValue=this.bytesReceivedByTypeAndNetwork.putIfAbsent(networkID, bytesReceivedByTypeAndNetwork);
         		if(oldValue!=null)
         			bytesReceivedByTypeAndNetwork=oldValue;     			
         	}
         	
         	messagesReceivedByTypeAndNetwork.get(SccpMessageImpl.getName(msg.getType())).incrementAndGet();
        	bytesReceivedByTypeAndNetwork.get(SccpMessageImpl.getName(msg.getType())).addAndGet(bytes);
        	
        	if (logger.isDebugEnabled()) {
                logger.debug(String.format("Rx : SCCP message from MTP %s", msg));
            }

            // when segmented messages - make a reassembly operation
            if (msg instanceof SccpSegmentableMessageImpl) {
            	SccpSegmentableMessageImpl sgmMsg = (SccpSegmentableMessageImpl) msg;
                SegmentationImpl segm = (SegmentationImpl) sgmMsg.getSegmentation();
                if (segm != null) {
                    // segmentation info is present - segmentation is possible
                    if (segm.isFirstSegIndication() && segm.getRemainingSegments() == 0) {                    	
                        // the single segment - no reassembly is needed
                    	//not need to change the ref count here
                        sgmMsg.setReceivedSingleSegment();
                    } else {
                        
                        // multiple segments - reassembly is needed
                        if (segm.isFirstSegIndication()) {
                            // first segment
                        	//incrementing the count for current segment
                        	ReferenceCountUtil.retain(sgmMsg.getData());
                            sgmMsg.setReceivedFirstSegment();
                            MessageReassemblyProcess msp = new MessageReassemblyProcess(segm.getSegmentationLocalRef(),
                                    sgmMsg.getCallingPartyAddress());
                            this.reassemplyCache.put(msp, sgmMsg);
                            sgmMsg.setMessageReassemblyProcess(msp);
                            msp.startTimer();
                            return;
                        } else {

                            // nonfirst segment
                            MessageReassemblyProcess msp = new MessageReassemblyProcess(segm.getSegmentationLocalRef(),
                                    sgmMsg.getCallingPartyAddress());
                            SccpSegmentableMessageImpl sgmMsgFst = null;
                            sgmMsgFst = this.reassemplyCache.get(msp);
                            
                            if (sgmMsgFst == null) {
                                // previous segments cache is not found -
                                // discard a segment                            	
                                if (logger.isWarnEnabled()) {
                                    logger.warn(String
                                            .format("Reassembly function failure: received a non first segment without the first segement having recieved. SccpMessageSegment=%s",
                                                    msg));
                                }
                                return;
                            }
                            if (sgmMsgFst.getRemainingSegments() - 1 != segm.getRemainingSegments()) {
                                // segments bad order
                            	 this.reassemplyCache.remove(msp);
                            	 //need to release buffers stored till now
                            	 sgmMsgFst.releaseBuffers();
                                 MessageReassemblyProcess mspMain = sgmMsgFst.getMessageReassemblyProcess();
                                 if (mspMain != null)
                                     mspMain.stopTimer();
                                 
                                if (logger.isWarnEnabled()) {
                                    logger.warn(String
                                            .format("Reassembly function failure: when receiving a next segment message order is missing. SccpMessageSegment=%s",
                                                    msg));
                                }
                                this.sccpRoutingControl.sendSccpError(sgmMsgFst, ReturnCauseValue.CANNOT_REASEMBLE, null);
                                return;
                            }

                            //incrementing the count for current segment
                        	ReferenceCountUtil.retain(sgmMsg.getData());                            
                            if (sgmMsgFst.getRemainingSegments() == 1) {
                                // last segment
                            	MessageReassemblyProcess mspMain = sgmMsgFst.getMessageReassemblyProcess();
                                if (mspMain != null)
                                    mspMain.stopTimer();
                                this.reassemplyCache.remove(msp);
                                
                                if (sgmMsgFst.getRemainingSegments() != 1)
                                    return;

                                sgmMsgFst.setReceivedNextSegment(sgmMsg);
                                msg = sgmMsgFst;
                                //completed the process lest release count for all
                                ((SccpSegmentableMessageImpl) msg).releaseBuffers();
                            } else {
                                // not last segment
                                sgmMsgFst.setReceivedNextSegment(sgmMsg);
                                return;
                            }
                        }
                    }
                }
            }

            if (msg instanceof SccpAddressedMessageImpl) {
                // CR or connectionless messages
                SccpAddressedMessageImpl msgAddr = (SccpAddressedMessageImpl) msg;

                // adding OPC into CallingPartyAddress if it is absent there and
                // "RouteOnSsn"
                SccpAddress addr = msgAddr.getCallingPartyAddress();
                if (addr != null
                        && addr.getAddressIndicator().getRoutingIndicator() == RoutingIndicator.ROUTING_BASED_ON_DPC_AND_SSN) {
                    if (!addr.getAddressIndicator().isPCPresent()) {
                        msgAddr.setCallingPartyAddress(new SccpAddressImpl(RoutingIndicator.ROUTING_BASED_ON_DPC_AND_SSN, null, msgAddr.getIncomingOpc(), addr
                                .getSubsystemNumber()));
                    }
                }

                sccpRoutingControl.routeMssgFromMtp(msgAddr);

            } else if (msg instanceof SccpConnMessage) {
                // non-addressed message processing (these are connected-oriented messages in the connected phase)
                sccpRoutingControl.routeMssgFromMtpConn((SccpConnMessage) msg);

            } else {
                logger.warn(String
                        .format("Rx SCCP message which is not instance of SccpAddressedMessage or SccpSegmentableMessage" +
                                " and doesn't implement SccpConnMessage. Will be dropped. Message=", msg));
            }
        } catch (Exception e) {
            logger.error("IOException while handling SCCP message: " + e.getMessage(), e);
        }
    }

    public class MessageReassemblyProcess implements Runnable {
        private int segmentationLocalRef;
        private SccpAddress callingPartyAddress;

        private Future timer;

        public MessageReassemblyProcess(int segmentationLocalRef, SccpAddress callingPartyAddress) {
            this.segmentationLocalRef = segmentationLocalRef;
            this.callingPartyAddress = callingPartyAddress;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null)
                return false;
            if (!(obj instanceof MessageReassemblyProcess))
                return false;
            MessageReassemblyProcess x = (MessageReassemblyProcess) obj;
            if (this.segmentationLocalRef != x.segmentationLocalRef)
                return false;

            if (this.callingPartyAddress == null || x.callingPartyAddress == null)
                return false;

            return this.callingPartyAddress.equals(x.callingPartyAddress);
        }

        @Override
        public int hashCode() {
            return this.segmentationLocalRef;
        }

        public void startTimer() {
            this.timer = msgDeliveryExecutors.schedule(this, reassemblyTimerDelay, TimeUnit.MILLISECONDS);
        }

        public void stopTimer() {
            if (this.timer != null) {
                this.timer.cancel(false);
                this.timer = null;
            }
        }

        public void run() {
            SccpSegmentableMessageImpl msg = null;
            msg = reassemplyCache.remove(this);
            if (msg == null)
                return;

            msg.cancelSegmentation();

            try {
                sccpRoutingControl.sendSccpError(msg, ReturnCauseValue.CANNOT_REASEMBLE, null);
            } catch (Exception e) {
                logger.warn("IOException when sending an error message", e);
            }
        }
    }
    
    public void sendMessageToMTP(SccpMessage message,Mtp3UserPart mup,Mtp3TransferPrimitive mtp3Message) throws IOException {
    	messagesSentByType.get(SccpMessageImpl.getName(message.getType())).incrementAndGet();
     	bytesSentByType.get(SccpMessageImpl.getName(message.getType())).addAndGet(mtp3Message.getData().readableBytes());
     	
     	Integer networkID=message.getNetworkId();
     	ConcurrentHashMap messagesSentByTypeAndNetwork=this.messagesSentByTypeAndNetwork.get(networkID);
     	if(messagesSentByTypeAndNetwork==null) {
     		messagesSentByTypeAndNetwork=new ConcurrentHashMap();
     		for(String currType:allMessageTypes)
            	messagesSentByTypeAndNetwork.put(currType, new AtomicLong(0L));            	
            
     		ConcurrentHashMap oldValue=this.messagesSentByTypeAndNetwork.putIfAbsent(networkID, messagesSentByTypeAndNetwork);
     		if(oldValue!=null)
     			messagesSentByTypeAndNetwork=oldValue;     			
     	}
     	
     	ConcurrentHashMap bytesSentByTypeAndNetwork=this.bytesSentByTypeAndNetwork.get(networkID);
     	if(bytesSentByTypeAndNetwork==null) {
     		bytesSentByTypeAndNetwork=new ConcurrentHashMap();
     		for(String currType:allMessageTypes)
            	bytesSentByTypeAndNetwork.put(currType, new AtomicLong(0L));            	
            
     		ConcurrentHashMap oldValue=this.bytesSentByTypeAndNetwork.putIfAbsent(networkID, bytesSentByTypeAndNetwork);
     		if(oldValue!=null)
     			bytesSentByTypeAndNetwork=oldValue;     			
     	}
     	
     	messagesSentByTypeAndNetwork.get(SccpMessageImpl.getName(message.getType())).incrementAndGet();
     	bytesSentByTypeAndNetwork.get(SccpMessageImpl.getName(message.getType())).addAndGet(mtp3Message.getData().readableBytes());
     	mup.sendMessage(mtp3Message);
    }

	@Override
	public Map getMessagesSentByType() {
		Map result=new HashMap();
		Iterator> iterator=messagesSentByType.entrySet().iterator();
		while(iterator.hasNext()) {
			Entry currEntry=iterator.next();
			result.put(currEntry.getKey(), currEntry.getValue().get());
		}
		
		return result;
	}

	@Override
	public Map getMessagesReceivedByType() {
		Map result=new HashMap();
		Iterator> iterator=messagesReceivedByType.entrySet().iterator();
		while(iterator.hasNext()) {
			Entry currEntry=iterator.next();
			result.put(currEntry.getKey(), currEntry.getValue().get());
		}
		
		return result;
	}

	@Override
	public Map getBytesSentByType() {
		Map result=new HashMap();
		Iterator> iterator=bytesSentByType.entrySet().iterator();
		while(iterator.hasNext()) {
			Entry currEntry=iterator.next();
			result.put(currEntry.getKey(), currEntry.getValue().get());
		}
		
		return result;
	}

	@Override
	public Map getBytesReceivedByType() {
		Map result=new HashMap();
		Iterator> iterator=bytesReceivedByType.entrySet().iterator();
		while(iterator.hasNext()) {
			Entry currEntry=iterator.next();
			result.put(currEntry.getKey(), currEntry.getValue().get());
		}
		
		return result;
	}

	@Override
	public Long getDataMessagesSent() {
		return messagesSentByType.get(SccpMessageImpl.MESSAGE_NAME_DT1).get() + messagesSentByType.get(SccpMessageImpl.MESSAGE_NAME_DT2).get() + messagesSentByType.get(SccpMessageImpl.MESSAGE_NAME_UDT).get() + messagesSentByType.get(SccpMessageImpl.MESSAGE_NAME_LUDT).get() + messagesSentByType.get(SccpMessageImpl.MESSAGE_NAME_XUDT).get();
	}

	@Override
	public Long getDataMessagesReceived() {
		return messagesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_DT1).get() + messagesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_DT2).get() + messagesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_UDT).get() + messagesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_LUDT).get() + messagesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_XUDT).get();
	}

	@Override
	public Long getDataBytesSent() {
		return bytesSentByType.get(SccpMessageImpl.MESSAGE_NAME_DT1).get() + bytesSentByType.get(SccpMessageImpl.MESSAGE_NAME_DT2).get() + bytesSentByType.get(SccpMessageImpl.MESSAGE_NAME_UDT).get() + bytesSentByType.get(SccpMessageImpl.MESSAGE_NAME_LUDT).get() + bytesSentByType.get(SccpMessageImpl.MESSAGE_NAME_XUDT).get();
	}

	@Override
	public Long getDataBytesReceived() {
		return bytesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_DT1).get() + bytesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_DT2).get() + bytesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_UDT).get() + bytesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_LUDT).get() + bytesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_XUDT).get();
	}

	@Override
	public Map getMessagesSentByTypeAndNetworkID(int networkID) {
		ConcurrentHashMap messagesSentByType=messagesSentByTypeAndNetwork.get(networkID);
		Map result=new HashMap();
		
		if(messagesSentByType!=null) {
			Iterator> iterator=messagesSentByType.entrySet().iterator();
			while(iterator.hasNext()) {
				Entry currEntry=iterator.next();
				result.put(currEntry.getKey(), currEntry.getValue().get());
			}
		}
		
		return result;
	}

	@Override
	public Map getMessagesReceivedByTypeAndNetworkID(int networkID) {
		ConcurrentHashMap messagesReceivedByType=messagesReceivedByTypeAndNetwork.get(networkID);
		Map result=new HashMap();
		
		if(messagesReceivedByType!=null) {
			Iterator> iterator=messagesReceivedByType.entrySet().iterator();
			while(iterator.hasNext()) {
				Entry currEntry=iterator.next();
				result.put(currEntry.getKey(), currEntry.getValue().get());
			}
		}
		
		return result;
	}

	@Override
	public Map getBytesSentByTypeAndNetworkID(int networkID) {
		ConcurrentHashMap bytesSentByType=bytesSentByTypeAndNetwork.get(networkID);
		Map result=new HashMap();
		
		if(bytesSentByType!=null) {
			Iterator> iterator=bytesSentByType.entrySet().iterator();
			while(iterator.hasNext()) {
				Entry currEntry=iterator.next();
				result.put(currEntry.getKey(), currEntry.getValue().get());
			}
		}
		
		return result;
	}

	@Override
	public Map getBytesReceivedByTypeAndNetworkID(int networkID) {
		ConcurrentHashMap bytesReceivedByType=bytesReceivedByTypeAndNetwork.get(networkID);
		Map result=new HashMap();
		
		if(bytesReceivedByType!=null) {
			Iterator> iterator=bytesReceivedByType.entrySet().iterator();
			while(iterator.hasNext()) {
				Entry currEntry=iterator.next();
				result.put(currEntry.getKey(), currEntry.getValue().get());
			}
		}
		
		return result;
	}

	@Override
	public Long getDataMessagesSentByTypeAndNetworkID(int networkID) {
		ConcurrentHashMap messagesSentByType=messagesSentByTypeAndNetwork.get(networkID);
		if(messagesSentByType==null)
			return 0L;
		
		return messagesSentByType.get(SccpMessageImpl.MESSAGE_NAME_DT1).get() + messagesSentByType.get(SccpMessageImpl.MESSAGE_NAME_DT2).get() + messagesSentByType.get(SccpMessageImpl.MESSAGE_NAME_UDT).get() + messagesSentByType.get(SccpMessageImpl.MESSAGE_NAME_LUDT).get() + messagesSentByType.get(SccpMessageImpl.MESSAGE_NAME_XUDT).get();
	}

	@Override
	public Long getDataMessagesReceivedByTypeAndNetworkID(int networkID) {
		ConcurrentHashMap messagesReceivedByType=messagesReceivedByTypeAndNetwork.get(networkID);
		if(messagesReceivedByType==null)
			return 0L;
		
		return messagesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_DT1).get() + messagesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_DT2).get() + messagesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_UDT).get() + messagesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_LUDT).get() + messagesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_XUDT).get();
	}

	@Override
	public Long getDataBytesSentByTypeAndNetworkID(int networkID) {
		ConcurrentHashMap bytesSentByType=bytesSentByTypeAndNetwork.get(networkID);
		if(bytesSentByType==null)
			return 0L;
		
		return bytesSentByType.get(SccpMessageImpl.MESSAGE_NAME_DT1).get() + bytesSentByType.get(SccpMessageImpl.MESSAGE_NAME_DT2).get() + bytesSentByType.get(SccpMessageImpl.MESSAGE_NAME_UDT).get() + bytesSentByType.get(SccpMessageImpl.MESSAGE_NAME_LUDT).get() + bytesSentByType.get(SccpMessageImpl.MESSAGE_NAME_XUDT).get();
	}

	@Override
	public Long getDataBytesReceivedByTypeAndNetworkID(int networkID) {
		ConcurrentHashMap bytesReceivedByType=bytesReceivedByTypeAndNetwork.get(networkID);
		if(bytesReceivedByType==null)
			return 0L;
		
		return bytesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_DT1).get() + bytesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_DT2).get() + bytesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_UDT).get() + bytesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_LUDT).get() + bytesReceivedByType.get(SccpMessageImpl.MESSAGE_NAME_XUDT).get();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy