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

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

The newest version!
package org.restcomm.protocols.ss7.sccp.impl;

import org.apache.log4j.Logger;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnAkMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnDt2MessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnItMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.parameter.ReceiveSequenceNumberImpl;
import org.restcomm.protocols.ss7.sccp.impl.parameter.ResetCauseImpl;
import org.restcomm.protocols.ss7.sccp.impl.parameter.SequenceNumberImpl;
import org.restcomm.protocols.ss7.sccp.parameter.ResetCauseValue;
import org.restcomm.protocols.ss7.sccp.parameter.SequenceNumber;

public class SccpFlowControl {
    private static final double EARLY_ACK_LEVEL_LARGE_WINDOW = 0.5;
    private static final double EARLY_ACK_LEVEL_SMALL_WINDOW = 0.1;
    private static final int EARLY_ACK_LARGE_WINDOW = 50;

    private final Logger logger;

    // send sequence number fields
    private SequenceNumber sendSequenceNumber; // latest message number

    // receive sequence number fields
    private boolean expectingFirstMessageInputAfterInit = true; // i. e. received no messages yet
    private SequenceNumber sendSequenceNumberExpectedAtInput;
    private SequenceNumber lastReceiveSequenceNumberReceived;

    private int maximumWindowSize;
    private SccpFlowControlWindow inputWindow;
    private SccpFlowControlWindow outputWindow;

    private boolean preemptiveAk = false; // send AK before input window exhaustion

    public SccpFlowControl(String name, int maximumWindowSize) {
        this.logger = Logger.getLogger(SccpFlowControl.class.getCanonicalName() + "-" + name);

        if (maximumWindowSize > 127) {
            throw new IllegalArgumentException();
        }
        this.maximumWindowSize = maximumWindowSize;

        reinitialize(); // init send sequence number
    }

    // IT, DT2, AK
    public void initializeMessageNumbering(SccpConnDt2MessageImpl msg) {
        sendSequenceNumber = getNextSequenceNumber();
        msg.setSequencing(sendSequenceNumber, sendSequenceNumberExpectedAtInput);
        inputWindow.setLowerEdge(sendSequenceNumberExpectedAtInput);
    }

    public void initializeMessageNumbering(SccpConnItMessageImpl msg) {
        // sendSequenceNumber = getNextSequenceNumber(); // use last value without increment
        msg.setSequencing(sendSequenceNumber, sendSequenceNumberExpectedAtInput);
        inputWindow.setLowerEdge(sendSequenceNumberExpectedAtInput);
    }

    public void initializeMessageNumbering(SccpConnAkMessageImpl msg) {
        initializeMessageNumbering(msg, sendSequenceNumberExpectedAtInput);
    }
    public void initializeMessageNumbering(SccpConnAkMessageImpl msg, SequenceNumber receiveSequenceNumber) {
        sendSequenceNumberExpectedAtInput = receiveSequenceNumber;
        inputWindow.setLowerEdge(receiveSequenceNumber);

        msg.setReceiveSequenceNumber(new ReceiveSequenceNumberImpl(receiveSequenceNumber));
    }

    protected SequenceNumber getNextSequenceNumber() {
        return sendSequenceNumber.nextNumber();
    }

    public void checkOutputMessageNumbering(SccpConnDt2MessageImpl msg) {
        if (!outputWindow.contains(msg.getSequencingSegmenting().getSendSequenceNumber())) {
            throw new MessageSequenceNumberException("P(S) outside sending window");
        }
    }

    public void checkOutputMessageNumbering(SccpConnItMessageImpl msg) {
        if (!outputWindow.contains(msg.getSequencingSegmenting().getSendSequenceNumber())) {
            throw new MessageSequenceNumberException("P(S) outside sending window");
        }
    }

    public boolean isAuthorizedToTransmitAnotherMessage() {
        SequenceNumber nextSequenceNumber = getNextSequenceNumber();
        boolean result = outputWindow.contains(nextSequenceNumber);
        if (result && logger.isDebugEnabled()) {
            logger.debug(String.format("output window %3s,(%3d) contains %3s: %b", outputWindow.lowerEdge.getValue(), outputWindow.w, nextSequenceNumber.getValue(), result));
        }
        return result;
    }

    public boolean isAkSendCriterion(SccpConnDt2MessageImpl msg) {
        //logger.warn("***** - " + inputWindow.getUpperEdge() + " - " + msg);
        if (!preemptiveAk) {
            return inputWindow.getW() == 0 || msg.getSequencingSegmenting().getSendSequenceNumber().equals(inputWindow.getUpperEdge());
        } else {
            return inputWindow.getW() == 0 || msg.getSequencingSegmenting().getSendSequenceNumber().equals(inputWindow.getEarlyAckEdge());
        }
    }

    public void checkInputMessageNumbering(SccpConnectionImpl conn, SequenceNumber receiveSequenceNumber) throws Exception {
        checkInputMessageNumbering(conn,null, receiveSequenceNumber);
    }

    // if true then message can be accepted
    public boolean checkInputMessageNumbering(SccpConnectionImpl conn, SequenceNumber sendSequenceNumber,
                                              SequenceNumber receiveSequenceNumber) throws Exception {
        if (sendSequenceNumber != null) {
            if (expectingFirstMessageInputAfterInit && !sendSequenceNumber.equals(new SequenceNumberImpl(0))) {
                // local procedure error
                conn.reset(new ResetCauseImpl(ResetCauseValue.MESSAGE_OUT_OF_ORDER_INCORRECT_PS));
                return false;

            } else if (sendSequenceNumber.equals(sendSequenceNumberExpectedAtInput) && inputWindow.contains(sendSequenceNumber)) {
                sendSequenceNumberExpectedAtInput = sendSequenceNumberExpectedAtInput.nextNumber();

            } else {
                if (!inputWindow.contains(sendSequenceNumber)) {
                    conn.reset(new ResetCauseImpl(ResetCauseValue.REMOTE_PROCEDURE_ERROR_MESSAGE_OUT_OF_WINDOW));

                } else if (!sendSequenceNumber.equals(sendSequenceNumberExpectedAtInput)) {
                    // local procedure error
                    conn.resetSection(new ResetCauseImpl(ResetCauseValue.MESSAGE_OUT_OF_ORDER_INCORRECT_PS));
                }
                return false;
            }
        }

        if (rangeContains(lastReceiveSequenceNumberReceived, this.sendSequenceNumber.nextNumber(), receiveSequenceNumber)) {
            outputWindow.setLowerEdge(receiveSequenceNumber);
        } else {
            conn.resetSection(new ResetCauseImpl(ResetCauseValue.MESSAGE_OUT_OF_ORDER_INCORRECT_PS));
            return false;
        }

        lastReceiveSequenceNumberReceived = receiveSequenceNumber;
        expectingFirstMessageInputAfterInit = false;
        return true;
    }


    // after connection establishing and on connection reset
    public void reinitialize() {
        inputWindow = new SccpFlowControlWindow(new SequenceNumberImpl(0), maximumWindowSize);
        outputWindow = new SccpFlowControlWindow(new SequenceNumberImpl(0), maximumWindowSize);

        sendSequenceNumberExpectedAtInput = new SequenceNumberImpl(0);
        // inputWindow.setLowerEdge(sendSequenceNumberExpectedAtInput);

        lastReceiveSequenceNumberReceived = new SequenceNumberImpl(0);

        sendSequenceNumber = new SequenceNumberImpl(0, false, true);
        expectingFirstMessageInputAfterInit = true;
    }

    public SequenceNumber getSendSequenceNumberExpectedAtInput() {
        return sendSequenceNumberExpectedAtInput;
    }

    public void setSendCredit(int sendCredit) {
        this.outputWindow.setW(sendCredit);
    }

    public void setReceiveCredit(int receiveCredit) {
        this.inputWindow.setW(receiveCredit);
    }

    public boolean isPreemptiveAk() {
        return preemptiveAk;
    }

    public int getSendCredit() {
        return outputWindow.getW();
    }

    public int getReceiveCredit() {
        return inputWindow.getW();
    }

    public int getMaximumWindowSize() {
        return maximumWindowSize;
    }

    // set of consecutive DT message send sequence numbers authorized for transfer
    public static class SccpFlowControlWindow {
        private int w;
        private SequenceNumber lowerEdge;

        public SccpFlowControlWindow(SequenceNumber lowerEdge, int size) {
            this.lowerEdge = lowerEdge;

            this.w = size;
        }

        // number of DT messages authorized for transfer
        public int getW() {
            return w;
        }

        public void setW(int w) {
            this.w = w;
        }

        public SequenceNumber getLowerEdge() {
            return lowerEdge;
        }

        public SequenceNumber getUpperEdge() {
            if (w == 0) {
                throw new MessageSequenceEmptyWindowException("Getting upper edge of window with w=0");
            }
            return new SequenceNumberImpl(lowerEdge.getValue() + w - 1, true);
        }

        public SequenceNumber getEarlyAckEdge() {
            if (w == 0) {
                throw new MessageSequenceEmptyWindowException("Getting early ack edge of window with w=0");
            }

            int earlyW = (int) (this.w * ((this.w > EARLY_ACK_LARGE_WINDOW) ?  EARLY_ACK_LEVEL_LARGE_WINDOW : EARLY_ACK_LEVEL_SMALL_WINDOW));
            if (earlyW == 0) {
                earlyW = 1;
            }
            return new SequenceNumberImpl(lowerEdge.getValue() + earlyW - 1, true);
        }

//        public SequenceNumber getFirstNotAuthorized() {
//            return new SequenceNumberImpl(lowerEdge.getValue() + w, true);
//        }


        public void setLowerEdge(SequenceNumber newLowerEdge) {
            if (newLowerEdge.equals(this.lowerEdge)) {
                return;
            }

            this.lowerEdge = newLowerEdge;
        }

        public boolean contains(SequenceNumber sendSequenceNumber) {
            if (w == 0) {
                return false;
            }
            SequenceNumber upperEdge = getUpperEdge();
            return SccpFlowControl.rangeContains(lowerEdge, upperEdge, sendSequenceNumber);
        }
    }

    public static class MessageSequenceNumberException extends IllegalStateException {
        public MessageSequenceNumberException(String message) {
            super(message);
        }
    }

    public static class MessageSequenceEmptyWindowException extends IllegalStateException {
        public MessageSequenceEmptyWindowException(String message) {
            super(message);
        }
    }

    public static boolean rangeContains(SequenceNumber lowerEdge, SequenceNumber upperEdge, SequenceNumber number) {
        if (number.equals(lowerEdge) || number.equals(upperEdge)) {
            return true;
        }

        if (upperEdge.getValue() < lowerEdge.getValue()) {
            return lowerEdge.getValue() < number.getValue() && number.getValue() <= SequenceNumber.MAX_VALUE
                    || 0 <= number.getValue() && number.getValue() < upperEdge.getValue();
        } else {
            return lowerEdge.getValue() < number.getValue() && number.getValue() < upperEdge.getValue();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy