/protocols/ss7/mtp/mtp-impl/src/main/java/org/mobicents/protocols/ss7/mtp/Mtp3.java
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}