PageRenderTime 38ms CodeModel.GetById 2ms app.highlight 30ms RepoModel.GetById 1ms app.codeStats 0ms

/protocols/ss7/mtp/mtp-impl/src/main/java/org/mobicents/protocols/ss7/mtp/Mtp3.java

http://mobicents.googlecode.com/
Java | 679 lines | 402 code | 101 blank | 176 comment | 82 complexity | 2fb6b143bf253185381f95b3797efd42 MD5 | raw file
  1/*
  2 * JBoss, Home of Professional Open Source
  3 * Copyright 2011, Red Hat, Inc. and individual contributors
  4 * by the @authors tag. See the copyright.txt in the distribution for a
  5 * full listing of individual contributors.
  6 *
  7 * This is free software; you can redistribute it and/or modify it
  8 * under the terms of the GNU Lesser General Public License as
  9 * published by the Free Software Foundation; either version 2.1 of
 10 * the License, or (at your option) any later version.
 11 *
 12 * This software is distributed in the hope that it will be useful,
 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 15 * Lesser General Public License for more details.
 16 *
 17 * You should have received a copy of the GNU Lesser General Public
 18 * License along with this software; if not, write to the Free
 19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 21 */
 22
 23package org.mobicents.protocols.ss7.mtp;
 24
 25import java.io.IOException;
 26import java.util.ArrayList;
 27import java.util.Arrays;
 28import java.util.List;
 29
 30import javolution.util.FastList;
 31
 32import org.apache.log4j.Level;
 33import org.apache.log4j.Logger;
 34import org.mobicents.protocols.stream.api.SelectorKey;
 35import org.mobicents.protocols.stream.api.SelectorProvider;
 36import org.mobicents.protocols.stream.api.StreamSelector;
 37
 38/**
 39 * 
 40 * @author kulikov
 41 * @author baranowb
 42 */
 43public class Mtp3 implements Runnable {
 44
 45    public final static int TIMEOUT_T1_SLTM = 12;
 46    public final static int TIMEOUT_T2_SLTM = 90;
 47    private final static int LINK_MANAGEMENT = 0;
 48    private final static int LINK_TESTING = 1;
 49    public final static int _SI_SERVICE_SCCP = 3;
 50    public final static int _SI_SERVICE_ISUP = 5;
 51    
 52    private Mtp3Listener mtp3Listener;
 53    /**
 54     * Flag indicating if we should notify upper layer
 55     */
 56    private boolean l4IsUp = false;
 57    
 58    //FIXME: MOVE THIS TO LINKSET?
 59    protected volatile boolean started = false;
 60    /** defautl value of SSI */
 61    public static final int DEFAULT_NI = 2;//NATIONAL, as default.
 62    
 63    private int dpc;
 64    private int opc;
 65    private int ni=DEFAULT_NI << 2; // << cause its faster for checks.
 66    /**
 67     * Local byte[], in which we forge messages.
 68     */
 69    private byte[] localFrame = new byte[279]; 
 70    private byte[][] localBuffers;
 71    /** List of signaling channels wrapped with MTP2*/
 72    private List<Mtp2> links = new ArrayList();
 73    
 74    /** Active links */
 75    private Linkset linkset = new Linkset();    
 76    // ss7 has subservice as 1, q704 shows the same iirc
 77    //private int subservice = -1;
 78    //private int service;
 79  
 80    
 81    // //////////////////////////
 82    // SLTM pattern for inits //
 83    // //////////////////////////
 84    private final static byte[] SLTM_PATTERN = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0F};
 85    
 86    protected String name;
 87    private StreamSelector selector;
 88    
 89    private MTPScheduler executor = new MTPScheduler();
 90
 91    private static final Logger logger = Logger.getLogger(Mtp3.class);
 92    
 93    //public Mtp3Impl(String name, Mtp1 layer1) {
 94    public Mtp3(String name) {
 95        this.name = name;
 96        try {
 97            selector = SelectorProvider.getSelector("org.mobicents.ss7.hardware.dahdi.Selector");
 98        } catch (Exception e) {
 99            e.printStackTrace();
100        }
101        this.localBuffers = new byte[300][];
102        for(int i=0;i<300;i++)
103        {
104        	this.localBuffers[i] = new byte[i];
105        }
106    }
107
108    /**
109     * Assigns originated point code
110     * 
111     * @param opc the originated point code
112     */
113    public void setOpc(int opc) {
114        this.opc = opc;
115    }
116
117    /**
118     * Assigns destination point code.
119     * 
120     * @param dpc destination point code in decimal format.
121     */
122    public void setDpc(int dpc) {
123        this.dpc = dpc;
124    }
125
126    /**
127     * @return the dpc
128     */
129    public int getDpc() {
130        return dpc;
131    }
132
133    /**
134     * @return the opc
135     */
136    public int getOpc() {
137        return opc;
138    }
139
140    
141    /**
142     * Sets network indicator to be used as part of SIO( actually SSI). It Accepts 2 bit integer.
143     * @param ni
144     */
145    public void setNetworkIndicator(int ni){
146		this.ni = (0x03 & ni) << 2; 
147	}
148    
149    public int getNetworkIndicator() {
150		return this.ni >> 2;
151	}
152
153	/** (non-Javadoc)
154     * @see org.mobicents.protocols.ss7.mtp.Mtp3#addMtp3Listener(org.mobicents.protocols.ss7.mtp.Mtp3Listener)
155     */
156    public void addMtp3Listener(Mtp3Listener lst) {
157        if (this.mtp3Listener != null) {
158            throw new IllegalStateException("Listener already present.");
159        }
160        this.mtp3Listener = lst;
161        
162    }
163
164    /* (non-Javadoc)
165     * @see org.mobicents.protocols.ss7.mtp.Mtp3#removeMtp3Listener(org.mobicents.protocols.ss7.mtp.Mtp3Listener)
166     */
167    public void removeMtp3Listener(Mtp3Listener lst) {
168        if (lst != this.mtp3Listener) {
169            throw new IllegalArgumentException("Wrong listener passed. Its not registered in this object!");
170        }
171        this.mtp3Listener = null;
172        
173    }
174
175    /* (non-Javadoc)
176     * @see org.mobicents.protocols.ss7.mtp.Mtp3#setLinks(java.util.List)
177     */
178    public void setLinks(List<Mtp2> channels) {
179        for (Mtp2 link : channels) {
180            if (link != null) {
181                link.mtp3 = this;
182                this.links.add(link);
183            }
184        }
185        
186    }
187    
188    public void addLink(Mtp2 mtp2){
189    	mtp2.mtp3 = this;
190    	this.links.add(mtp2);
191    }
192    
193    public void clearLinks(){
194    	this.links.clear();
195    }
196
197    /**
198     * Starts linkset.
199     * 
200     * @throws java.io.IOException
201     */
202    public void start() throws IOException {
203        //starting links
204        for (Mtp2 link : links) {
205            link.start();
206        }        
207    }
208
209    public void stop() throws IOException {
210        started = false;
211        for (Mtp2 link : links) {
212            //selector.unregister(link);
213            link.stop();
214        }        
215    }
216
217    public void run() {
218        try {
219            FastList<SelectorKey> selected = selector.selectNow(StreamSelector.OP_READ, 20);
220            for (FastList.Node<SelectorKey> n = selected.head(), end = selected.tail(); (n = n.getNext()) != end;) {
221                ((Mtp2)((Mtp1) n.getValue().getStream()).getLink()).doRead();
222            }
223                 
224            selected = selector.selectNow(StreamSelector.OP_WRITE, 20);
225            for (FastList.Node<SelectorKey> n = selected.head(), end = selected.tail(); (n = n.getNext()) != end;) {
226                ((Mtp2)((Mtp1)  n.getValue().getStream()).getLink()).doWrite();
227            }
228                
229            this.executor.tick();
230                
231            for (Mtp2 link : links) {
232                link.scheduler.tick();
233            }
234        } catch (Exception e) {
235                e.printStackTrace();
236        }
237    }
238
239    /**
240     * This method is called when new MSU is detected.
241     * 
242     * @param sio
243     *            service information octet.
244     * @param msg
245     *            service information field;
246     */
247    
248    public void onMessage(Mtp2Buffer rxFrame, Mtp2 mtp2)
249    {   
250    	//                   | ----------------------- TO L4 --------------------------- |
251    	//                          | --------------------- SIF ------------------------ |
252    	//FSN   BSN   LEN    SIO    DPC/OPC,SLS    HEADING     LEN
253    	//b1    b0    0e     01     01 80 00 00    21          70    01 02 03 04 05 06 0f  CRC CRC
254    	int sio = rxFrame.frame[3] & 0xFF;
255    	int subserviceIndicator = (sio >> 4) & 0x0F;
256        int serviceIndicator = sio & 0x0F;
257        int ni = (subserviceIndicator  & 0x0C) ; //local NI(network Indicator) form msg., 0x0C, since we store it as shifted value.
258        // int dpc = (sif[0] & 0xff | ((sif[1] & 0x3f) << 8));
259        // int opc = ((sif[1] & 0xC0) >> 6) | ((sif[2] & 0xff) << 2) | ((sif[3]
260        // & 0x0f) << 10);
261        // int sls = (sif[3] & 0xf0) >>> 4;
262        int dpc = dpc(rxFrame.frame, 4); //1 - cause sif contains sio, even though its also passed as arg.
263        int opc = opc(rxFrame.frame, 4);
264        int sls = sls(rxFrame.frame, 4);
265        
266        //check SSI, Q.704 Figure 25, seems like if its bad, we discard.
267        if(this.ni!=ni)
268        {
269        	if (logger.isEnabledFor(Level.ERROR)) {
270                logger.error(
271                        String.format("(%s) Received MSSU with bad SSI, discarding! [si=" + serviceIndicator + ",ssi=" + subserviceIndicator + ", dpc=" + dpc + ", opc=" + opc + ", sls=" + sls + "] data: ", mtp2.getName()) + Arrays.toString(rxFrame.frame));
272            }
273        	return;
274        }
275        
276
277        switch (serviceIndicator) {
278            case LINK_MANAGEMENT:
279                int h0 = rxFrame.frame[8] & 0x0f;
280                int h1 = (rxFrame.frame[8] & 0xf0) >>> 4;
281                
282                if (logger.isDebugEnabled()) {
283                    logger.debug(String.format("(%s) Signalling network management", mtp2.getName()));
284                }
285                
286                if (h0 == 0) {
287                    if (logger.isDebugEnabled()) {
288                        logger.debug(String.format("(%s) Changeover management", mtp2.getName()));
289                    }
290                } else if (h0 == 7 && h1 == 1) {
291                    if (logger.isDebugEnabled()) {
292                        logger.debug(String.format("(%s) TRA received", mtp2.getName()));
293                    }
294                }
295                //FIX ME
296                break;
297            case LINK_TESTING:
298                h0 = rxFrame.frame[8] & 0x0f;
299                h1 = (rxFrame.frame[8] & 0xf0) >>> 4;
300
301                int len = (rxFrame.frame[9] & 0xf0) >>> 4;
302
303                if (h0 == 1 && h1 == 1) {
304                	if (logger.isDebugEnabled()) {
305                        logger.debug(String.format("(%s) Received SLTM", mtp2.getName()));
306                    }
307                    // receive SLTM from remote end
308                    // create response
309
310                    //change order of opc/dpc in SLTA !
311                    writeRoutingLabel(this.localFrame, sio, this.ni << 2, sls, opc, dpc);
312                    //slta[0] = (byte) sio;
313                    this.localFrame[5] = 0x021;
314                    // +1 cause we copy LEN byte also.
315                    System.arraycopy(rxFrame.frame, 9, this.localFrame, 6, len + 1);
316
317                    if (logger.isDebugEnabled()) {
318                        logger.debug(String.format("(%s) Responding with SLTA", mtp2.getName()));
319                    }
320                    mtp2.send( this.localFrame, len+9);
321                } else if (h0 == 1 && h1 == 2) {
322                    // receive SLTA from remote end
323                	if (logger.isDebugEnabled()) {
324                        logger.debug(String.format("(%s) Received SLTA", mtp2.getName()));
325                    }
326
327                    //checking pattern
328                    if (checkPattern(rxFrame,len, SLTM_PATTERN)) {
329                        //test message is acknowledged
330                        mtp2.sltmTest.ack();
331
332                        //notify top layer that link is up
333                        if (!l4IsUp) {
334                            l4IsUp = true;
335                            linkUp(mtp2);
336                        }
337                    } else {
338                    	if(logger.isEnabledFor(Level.WARN))
339                    	{
340                    		logger.warn("SLTA pattern does not match: \n"+Arrays.toString(rxFrame.frame)+"\n"+Arrays.toString(SLTM_PATTERN));
341                    	}
342                    }
343                } else {
344                	if(logger.isEnabledFor(Level.WARN))
345                	{
346                		logger.warn(String.format("(%s) Unexpected message type", mtp2.getName()));
347                	}
348                }
349                break;
350            case _SI_SERVICE_SCCP:
351            	if (logger.isDebugEnabled()) {
352                    logger.debug("Received SCCP MSU");
353                }
354
355                if (mtp3Listener != null) {
356                    
357                    //                   | ----------------------- TO L4 --------------------------- |
358                	//                          | --------------------- SIF ------------------------ |
359                	//FSN   BSN   LEN    SIO    DPC/OPC,SLS    HEADING     LEN
360                	//b1    b0    0e     01     01 80 00 00    21          70    01 02 03 04 05 06 0f  CRC CRC
361                	byte[] messageBuffer = fetchBuffer(rxFrame.len-5); //-5 = FSN(1) + BSN(1) + LEN(1) +2xCRC(1)
362                    System.arraycopy(rxFrame.frame, 3, messageBuffer, 0, rxFrame.len-5);
363                    //messageBuffer[0] = (byte) sio;
364                    try {
365                        mtp3Listener.receive(messageBuffer);
366                    } catch (Exception e) {
367                        e.printStackTrace();
368                    }
369                }
370                break;
371            case _SI_SERVICE_ISUP:
372            	if (logger.isDebugEnabled()) {
373                    logger.debug("Received ISUP MSU");
374                    
375                }
376                if (mtp3Listener != null) {
377                	byte[] messageBuffer = fetchBuffer(rxFrame.len-5);
378                	 System.arraycopy(rxFrame.frame, 3, messageBuffer, 0, rxFrame.len-5);
379                     //messageBuffer[0] = (byte) sio;
380                    try {
381                        mtp3Listener.receive(messageBuffer);
382                    } catch (Exception e) {
383                        e.printStackTrace();
384                    }
385                }
386                break;
387            default:
388                if (logger.isEnabledFor(Level.WARN) ) {
389                    logger.warn("Received MSU for UNKNOWN SERVICE!!!!!!!!!!!: " + Utils.dump(rxFrame.frame, rxFrame.len, false));
390                }
391                break;
392        }
393    }
394 
395
396	/**
397     * Selects link for transmission using link selection indicator.
398     * 
399     * @param sls signaling link selection indicator.
400     * @return selected link.
401     */
402    private Mtp2 selectLink(byte sls) {
403        
404        return linkset.select(sls);
405    }
406    
407    public boolean send(byte[] msg, int len) {
408        //method expects proper message, lets pray its ok :/ - this is a way to be aligned with dialogic cards.
409        //selecting link using sls
410        //get sls;
411        byte sls = (byte) sls(msg, 1);
412        Mtp2 link = this.selectLink(sls);
413        
414        if (link == null) {
415            return false;
416        }
417        
418        return link.send(msg, len);
419    }
420
421    public void linkInService(Mtp2 link) {
422        //restart traffic 
423        if (logger.isDebugEnabled()) {
424            logger.debug(String.format("(%s) Sending TRA(Traffic Restart Allowed) message", link.getName()));
425        }
426        
427        if(link.mtp2Listener != null){
428            link.mtp2Listener.linkInService();
429        }
430        
431        restartTraffic(link);
432
433        //create and assign Tester;
434        if (link.sltmTest == null) {
435            SLTMTest tester = new SLTMTest(link);
436            link.sltmTest =(tester);
437
438            //start tester
439            if (logger.isDebugEnabled()) {
440                logger.debug(String.format("(%s) Starting link test procedure(SLTM/SLTA)", link.getName()));
441            }
442            tester.start();
443        } else {
444            link.sltmTest.start();
445        }
446    }
447
448    /**
449     * Notify that link is up.
450     * @param link
451     */
452    private void linkUp(Mtp2 link) {
453        
454        if(link.mtp2Listener != null){
455            link.mtp2Listener.linkUp();
456        }
457        
458        linkset.add(link);
459        if (linkset.isActive() && this.mtp3Listener != null) {
460            try {
461                mtp3Listener.linkUp();
462            } catch (Exception e) {
463                e.printStackTrace();
464            }
465        }
466        if (logger.isInfoEnabled()) {
467            logger.info(String.format("(%s) Link now IN_SERVICE", link.getName()));
468        }
469    }
470    
471    public void linkFailed(Mtp2 link) {
472        
473        //Call Listener
474        if(link.mtp2Listener != null){
475            link.mtp2Listener.linkFailed();
476        }
477        
478    	//FIXME: add debug or trace?
479        //remove this link from list of active links
480        linkset.remove(link);
481
482        //restart initial alignment after T17 expires
483        link.fail();
484
485        //notify mtp user part
486        if (!linkset.isActive()) {
487            l4IsUp = false;
488            if (mtp3Listener != null) {
489                try {
490                    mtp3Listener.linkDown();
491                } catch (Exception e) {
492                    e.printStackTrace();
493                }
494            }
495        }
496    }
497
498    /* (non-Javadoc)
499     * @see org.mobicents.protocols.ss7.mtp.Mtp2Listener#registerLink(org.mobicents.protocols.ss7.mtp.Mtp2)
500     */
501    public void registerLink(Mtp2 mtp2) {
502        try {
503            mtp2.getLayer1().register(selector);
504        } catch (IOException ex) {
505            ex.printStackTrace();
506        }
507    }
508
509    /* (non-Javadoc)
510     * @see org.mobicents.protocols.ss7.mtp.Mtp2Listener#unregisterLink(org.mobicents.protocols.ss7.mtp.Mtp2)
511     */
512    public void unregisterLink(Mtp2 mtp2) {
513    }
514
515    private void restartTraffic(Mtp2 link) {
516
517        writeRoutingLabel(this.localFrame, 0, this.ni, 0, dpc, opc);
518
519        // H0 and H1, see Q.704 section 15.11.2+
520        this.localFrame[5] = 0x17;
521        link.send(this.localFrame, 6);
522    }   
523    
524    private final static int PATTERN_OFFSET = 10;
525    private final static int PATTERN_LEN_OFFSET = PATTERN_OFFSET+2; //+2 becuase frame.len contains 2B for CRC
526
527    private boolean checkPattern(Mtp2Buffer frame,int sltmLen, byte[] pattern) {
528        if (frame.len - PATTERN_LEN_OFFSET != pattern.length) {
529            return false;
530        }
531        for (int i = 0; i < pattern.length; i++) {
532            if (frame.frame[ i + PATTERN_OFFSET ] != pattern[i]) {     	
533                return false;
534            }
535        }
536        return true;
537    }
538    
539    /**
540	 * @param i
541	 * @return
542	 */
543	private byte[] fetchBuffer(int size) {
544		return this.localBuffers[size];
545	}
546    protected class SLTMTest extends MTPTask {
547
548        private Mtp2 link;
549        private int tryCount;        //SLTM message buffer;
550        private byte[] sltm = new byte[7 + SLTM_PATTERN.length]; //this has to be separate, cause its async to Mtp3.send
551        
552        private SLTMTest(Mtp2 link) {
553            this.link = link;
554        }
555
556        /**
557         * 
558         */
559        public void start() {
560            //reset count of tries
561            tryCount = 0;
562
563            //sending test message to the remote terminal
564            run();
565        }
566        
567        public void stop() {
568            //disable handler
569            cancel();
570        }
571
572        /**
573         * This methods should be called to acknowledge that current tests is passed.
574         * 
575         */
576        public void ack() {
577            //disable current awaiting handler
578            cancel();
579            //reset number of tryies;
580            tryCount = 0;
581            //shcedule next ping
582            executor.schedule(this, Mtp3.TIMEOUT_T2_SLTM * 1000);
583            if (logger.isDebugEnabled()) {
584                logger.debug(String.format("(%s) SLTM acknowledged, Link test passed", link.getName()));
585            }
586        }
587
588        /**
589         * Sends SLTM message using this link.
590         * 
591         * @param timeout  the amount of time in millesecond for awaiting response.
592         */
593        public void ping(long timeout) {
594            //prepearing test message
595            
596            writeRoutingLabel(sltm, 0x01, ni, link.getSls(), dpc, opc);
597            //sltm[0] = (byte) 0x01; 
598            sltm[5] = 0x11;
599            sltm[6] = (byte) (SLTM_PATTERN.length << 4);
600            System.arraycopy(SLTM_PATTERN, 0, sltm, 7, SLTM_PATTERN.length);
601
602            //sending test message
603            link.send(sltm, sltm.length);
604
605            //incremeting number of tries.
606            tryCount++;
607
608            //scheduling timeout
609            executor.schedule(this, (int)(timeout * 1000));
610            if (logger.isDebugEnabled()) {
611                logger.debug(String.format("(%s) SLTM sent, try number = %d", link.getName(), tryCount));
612            }
613        }
614        
615        public void perform() {
616            switch (tryCount) {
617                case 0:
618                    //sending first test message
619                    ping(Mtp3.TIMEOUT_T1_SLTM);
620                    break;
621                case 1:
622                    //first message was not answered, sending second
623                    ping(Mtp3.TIMEOUT_T1_SLTM);
624                    break;
625                case 2:
626                    if (logger.isDebugEnabled()) {
627                        logger.debug(String.format("(%s) SLTM message was not acknowledged, Link failed", link.getName()));
628                    }
629                    //second message was not answered, report failure
630                    linkFailed(link);
631            }
632        }
633    }
634
635    // //////////////////
636    // Helper methods //
637    // //////////////////
638    public static final int dpc(byte[] sif, int shift) {
639        int dpc = (sif[0 + shift] & 0xff | ((sif[1 + shift] & 0x3f) << 8));
640        return dpc;
641    }
642
643    public static final int opc(byte[] sif, int shift) {
644        int opc = ((sif[1 + shift] & 0xC0) >> 6) | ((sif[2 + shift] & 0xff) << 2) | ((sif[3 + shift] & 0x0f) << 10);
645        return opc;
646    }
647
648    public static final int sls(byte[] sif, int shift) {
649        int sls = (sif[3 + shift] & 0xf0) >>> 4;
650        return sls;
651    }    
652
653    public static final int si(byte[] data) {
654        
655        int serviceIndicator = data[0] & 0x0f;
656        return serviceIndicator;
657    }
658    
659    public static final int ssi(byte[] data) {
660        //see Q.704.14.2 
661        int subserviceIndicator = (data[0] >> 4) & 0x0F;
662        return subserviceIndicator;
663    }
664    
665    public static void writeRoutingLabel(byte[] data, int si, int ssi, int sls, int dpc, int opc) {
666        //see Q.704.14.2 
667        writeRoutingLabel(0,data, si, ssi, sls, dpc, opc);
668        
669    }   
670    public static void writeRoutingLabel(int shift, byte[] data, int si, int ssi, int sls, int dpc, int opc) {
671        //see Q.704.14.2 
672        data[0+shift] = (byte) (((ssi & 0x0F) << 4) | (si & 0x0F));
673        data[1+shift] = (byte) dpc;
674        data[2+shift] = (byte) (((dpc >> 8) & 0x3F) | ((opc & 0x03) << 6));
675        data[3+shift] = (byte) (opc >> 2);
676        data[4+shift] = (byte) (((opc >> 10) & 0x0F) | ((sls & 0x0F) << 4));
677        
678    } 
679}