summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <zecke@selfish.org>2010-08-01 16:58:34 +0800
committerHolger Hans Peter Freyther <zecke@selfish.org>2010-08-01 17:01:26 +0800
commit00e6f692e9ee7bc7001e5c31b519abe711585503 (patch)
tree064c86fd25485cad21f0d3a8200169bb12f42334
parent68b7e8bd60d0d5b39b8ac19ec5a33689486ac117 (diff)
mtp: Add the MTP Level3 code to the SCCP repoistory
SCCP can be wrapped inside the MTP Level3, and one can use it for link testing as well. This repository should be renamed to libosmo-itu or libosmo-ss7 and be a host to SS7 related encapsulation... The code is coming from the cellmgr-ng code.
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac3
-rw-r--r--include/Makefile.am2
-rw-r--r--include/mtp/Makefile.am2
-rw-r--r--include/mtp/mtp_data.h85
-rw-r--r--include/mtp/mtp_level3.h165
-rw-r--r--include/mtp/mtp_pcap.h29
-rw-r--r--libosmo-mtp.pc.in10
-rw-r--r--src/Makefile.am3
-rw-r--r--src/mtp_level3.c512
-rw-r--r--src/mtp_pcap.c86
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/mtp/Makefile.am4
-rw-r--r--tests/mtp/mtp_parse_test.c629
14 files changed, 1530 insertions, 4 deletions
diff --git a/Makefile.am b/Makefile.am
index a0f4730..51d65f8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,7 +4,7 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include
SUBDIRS = include src tests
pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = libosmo-sccp.pc
+pkgconfig_DATA = libosmo-sccp.pc libosmo-mtp.pc
BUILT_SOURCES = $(top_srcdir)/.version
$(top_srcdir)/.version:
diff --git a/configure.ac b/configure.ac
index 4fb8ce5..94969f7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -29,10 +29,13 @@ AC_SUBST(SYMBOL_VISIBILITY)
AC_OUTPUT(
libosmo-sccp.pc
+ libosmo-mtp.pc
include/sccp/Makefile
+ include/mtp/Makefile
include/Makefile
src/Makefile
tests/Makefile
tests/sccp/Makefile
+ tests/mtp/Makefile
Makefile)
diff --git a/include/Makefile.am b/include/Makefile.am
index d0ed7f4..bf7855d 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1 +1 @@
-SUBDIRS = sccp
+SUBDIRS = sccp mtp
diff --git a/include/mtp/Makefile.am b/include/mtp/Makefile.am
new file mode 100644
index 0000000..077805b
--- /dev/null
+++ b/include/mtp/Makefile.am
@@ -0,0 +1,2 @@
+mtp_HEADERS = mtp_data.h mtp_level3.h mtp_pcap.h
+mtpdir = $(includedir)/mtp
diff --git a/include/mtp/mtp_data.h b/include/mtp/mtp_data.h
new file mode 100644
index 0000000..b8f6af0
--- /dev/null
+++ b/include/mtp/mtp_data.h
@@ -0,0 +1,85 @@
+/*
+ * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2010 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#ifndef mtp_data_h
+#define mtp_data_h
+
+#include <osmocore/msgb.h>
+#include <osmocore/timer.h>
+#include <osmocore/utils.h>
+
+/* MTP Level3 timers */
+
+/* Timers for SS7 */
+#define MTP_T1 12, 0
+#define MTP_T2 30, 0
+#define START_DELAY 8, 0
+
+/**
+ * The state of the mtp_link in terms of layer3 and upwards
+ */
+struct mtp_link {
+ /* routing info.. */
+ int dpc, opc;
+
+ /* internal state */
+ /* the MTP1 link is up */
+ int available;
+ int running;
+ int sccp_up;
+
+ /* misc data */
+ uint8_t test_ptrn[14];
+
+ int sltm_pending;
+ struct llist_head pending_msgs;
+ int sltm_once;
+ int was_up;
+
+
+ /* the associated link */
+ int link;
+
+ int slta_misses;
+ struct timer_list t1_timer;
+ struct timer_list t2_timer;
+
+ struct timer_list delay_timer;
+};
+
+
+struct mtp_link *mtp_link_alloc(void);
+void mtp_link_stop(struct mtp_link *link);
+void mtp_link_reset(struct mtp_link *link);
+int mtp_link_data(struct mtp_link *link, struct msgb *msg);
+int mtp_link_submit_sccp_data(struct mtp_link *link, int sls, const uint8_t *data, unsigned int length);
+
+
+/* one time init function */
+void mtp_link_init(void);
+
+/* to be implemented for MSU sending */
+void mtp_link_submit(struct mtp_link *link, struct msgb *msg);
+void mtp_link_forward_sccp(struct mtp_link *link, struct msgb *msg, int sls);
+void mtp_link_restart(struct mtp_link *link);
+void mtp_link_slta_recv(struct mtp_link *link);
+void mtp_link_sccp_down(struct mtp_link *link);
+
+#endif
diff --git a/include/mtp/mtp_level3.h b/include/mtp/mtp_level3.h
new file mode 100644
index 0000000..7d72d9e
--- /dev/null
+++ b/include/mtp/mtp_level3.h
@@ -0,0 +1,165 @@
+/* Q.701-Q.704, Q.706, Q.707 handling code */
+/*
+ * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2010 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef mtp_level3_h
+#define mtp_level3_h
+
+#include <endian.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+
+/*
+ * pssible service information octets..
+ */
+#define MTP_NI_NATION_NET 0x02
+
+#define MTP_SI_MNT_SNM_MSG 0x00
+#define MTP_SI_MNT_REG_MSG 0x01
+#define MTP_SI_MNT_SCCP 0x03
+
+/*
+ * h0 contains the group, h1 the semantic of it
+ */
+
+#define MTP_TST_MSG_GRP 0x01
+#define MTP_PROHIBIT_MSG_GRP 0x04
+#define MTP_TRF_RESTR_MSG_GRP 0x07
+
+/* h1 values for different groups */
+#define MTP_TST_MSG_SLTM 0x01
+#define MTP_TST_MSG_SLTA 0x02
+
+#define MTP_RESTR_MSG_ALLWED 0x01
+
+#define MTP_PROHIBIT_MSG_SIG 0x01
+
+
+#define SCCP_SST 0x03
+#define SCCP_SSA 0x01
+
+#define MTP_LINK_MASK 0x0F
+#define MTP_ADDR_MASK 0x0FFF
+#define MTP_APOC_MASK 0x3f
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define MTP_LINK_SLS(addr) ((addr >>28) & MTP_LINK_MASK)
+#define MTP_ADDR(link, dpc, opc) \
+ (((dpc) & MTP_ADDR_MASK) << 0 | \
+ ((opc) & MTP_ADDR_MASK) << 14| \
+ ((link) & MTP_LINK_MASK) << 28)
+#define MTP_MAKE_APOC(apoc) \
+ (apoc & 0x3fff)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+static inline uint32_t c_swap_32(uint32_t in)
+{
+ return (((in & 0x000000ff) << 24) |
+ ((in & 0x0000ff00) << 8) |
+ ((in & 0x00ff0000) >> 8) |
+ ((in & 0xff000000) >> 24));
+}
+static inline uint16_t c_swap_16(uint16_t in)
+{
+ return (((in & 0x00ff) << 8) |
+ (in & 0xff00) >> 8);
+}
+#define MTP_LINK_SLS(addr) ((c_swap_32(addr)>>28) & MTP_LINK_MASK)
+#define MTP_ADDR(link, dpc, opc) \
+ c_swap_32(((dpc) & MTP_ADDR_MASK) << 0 | \
+ ((opc) & MTP_ADDR_MASK) << 14| \
+ ((link) & MTP_LINK_MASK) << 28)
+#define MTP_MAKE_APOC(apoc) \
+ c_swap_16((apoc & 0x3fff))
+#endif
+
+
+
+/*
+ * not the on wire address...
+ */
+struct mtp_addr {
+ uint16_t dpc;
+ uint16_t opc;
+ uint8_t link;
+} __attribute__((packed));
+
+/*
+ * the struct is defined in Q.704 and can be seen in the
+ * wireshark dissectors too
+ */
+struct mtp_level_3_hdr {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t ser_ind : 4,
+ spare : 2,
+ ni : 2;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ uint8_t ni : 2,
+ spare : 2,
+ ser_ind : 4;
+#endif
+ uint32_t addr;
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct mtp_level_3_cmn {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t h0 : 4,
+ h1 : 4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ uint8_t h1 : 4,
+ h0 : 4;
+#endif
+} __attribute__((packed));
+
+struct mtp_level_3_mng {
+ struct mtp_level_3_cmn cmn;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t spare : 4,
+ length : 4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ uint8_t length : 4,
+ spare : 4;
+#endif
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct mtp_level_3_prohib {
+ struct mtp_level_3_cmn cmn;
+
+ uint16_t apoc;
+} __attribute__((packed));
+
+struct sccp_con_ctrl_prt_mgt {
+ uint8_t sst;
+ uint8_t assn; /* affected sub system number */
+ uint16_t apoc;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t mul_ind : 2,
+ spare : 6;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ uint8_t spare : 6,
+ mul_ind : 2;
+#endif
+} __attribute__((packed));
+
+#endif
diff --git a/include/mtp/mtp_pcap.h b/include/mtp/mtp_pcap.h
new file mode 100644
index 0000000..5e8f7d3
--- /dev/null
+++ b/include/mtp/mtp_pcap.h
@@ -0,0 +1,29 @@
+/*
+ * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2010 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#ifndef mtp_pcap_h
+#define mtp_pcap_h
+
+#include <stdint.h>
+
+int mtp_pcap_write_header(int fd);
+int mtp_pcap_write_msu(int fd, const uint8_t *data, int length);
+
+#endif
diff --git a/libosmo-mtp.pc.in b/libosmo-mtp.pc.in
new file mode 100644
index 0000000..675d0d3
--- /dev/null
+++ b/libosmo-mtp.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Osmo MTP Lib
+Description: Osmo MTP Lib
+Version: @VERSION@
+Libs: -L${libdir} -lmtp
+Cflags: -I${includedir}/
diff --git a/src/Makefile.am b/src/Makefile.am
index 7e01537..963dea5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,6 +2,7 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
sccpdir = $(libdir)
-sccp_LIBRARIES = libsccp.a
+sccp_LIBRARIES = libsccp.a libmtp.a
libsccp_a_SOURCES = sccp.c
+libmtp_a_SOURCES = mtp_pcap.c mtp_level3.c
diff --git a/src/mtp_level3.c b/src/mtp_level3.c
new file mode 100644
index 0000000..d1101eb
--- /dev/null
+++ b/src/mtp_level3.c
@@ -0,0 +1,512 @@
+/* MTP level3 main handling code */
+/*
+ * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2010 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <mtp/mtp_data.h>
+#include <mtp/mtp_level3.h>
+
+#include <osmocore/talloc.h>
+#include <osmocore/logging.h>
+
+#include <sccp/sccp.h>
+
+#include <arpa/inet.h>
+
+#include <string.h>
+
+static void *tall_mtp_ctx = NULL;
+
+// HACK
+#define DINP 0
+
+static struct msgb *mtp_msg_alloc(struct mtp_link *link)
+{
+ struct mtp_level_3_hdr *hdr;
+ struct msgb *msg = msgb_alloc_headroom(4096, 128, "mtp-msg");
+ if (!msg) {
+ LOGP(DINP, LOGL_ERROR, "Failed to allocate mtp msg\n");
+ return NULL;
+ }
+
+ msg->l2h = msgb_put(msg, sizeof(*hdr));
+ hdr = (struct mtp_level_3_hdr *) msg->l2h;
+ hdr->addr = MTP_ADDR(0x0, link->dpc, link->opc);
+ return msg;
+}
+
+static struct msgb *mtp_create_sltm(struct mtp_link *link)
+{
+ const uint8_t test_ptrn[14] = { 'G', 'S', 'M', 'M', 'M', 'S', };
+ struct mtp_level_3_hdr *hdr;
+ struct mtp_level_3_mng *mng;
+ struct msgb *msg = mtp_msg_alloc(link);
+ uint8_t *data;
+ if (!msg)
+ return NULL;
+
+ hdr = (struct mtp_level_3_hdr *) msg->l2h;
+ hdr->ni = MTP_NI_NATION_NET;
+ hdr->ser_ind = MTP_SI_MNT_REG_MSG;
+
+ mng = (struct mtp_level_3_mng *) msgb_put(msg, sizeof(*mng));
+ mng->cmn.h0 = MTP_TST_MSG_GRP;
+ mng->cmn.h1 = MTP_TST_MSG_SLTM;
+ mng->length = ARRAY_SIZE(test_ptrn);
+
+ data = msgb_put(msg, ARRAY_SIZE(test_ptrn));
+ memcpy(data, test_ptrn, ARRAY_SIZE(test_ptrn));
+
+ /* remember the last tst ptrn... once we have some */
+ memcpy(link->test_ptrn, test_ptrn, ARRAY_SIZE(test_ptrn));
+
+ return msg;
+}
+
+static struct msgb *mtp_create_slta(struct mtp_link *link, struct mtp_level_3_mng *in_mng, int l3_len)
+{
+ struct mtp_level_3_hdr *hdr;
+ struct mtp_level_3_mng *mng;
+ struct msgb *out = mtp_msg_alloc(link);
+
+ if (!out)
+ return NULL;
+
+ hdr = (struct mtp_level_3_hdr *) out->l2h;
+ hdr->ni = MTP_NI_NATION_NET;
+ hdr->ser_ind = MTP_SI_MNT_REG_MSG;
+ mng = (struct mtp_level_3_mng *) msgb_put(out, sizeof(*mng));
+ mng->cmn.h0 = MTP_TST_MSG_GRP;
+ mng->cmn.h1 = MTP_TST_MSG_SLTA;
+ mng->length = l3_len - 2;
+ msgb_put(out, mng->length);
+ memcpy(mng->data, in_mng->data, mng->length);
+
+ return out;
+}
+
+static struct msgb *mtp_tfp_alloc(struct mtp_link *link, int apoc)
+{
+ struct mtp_level_3_hdr *hdr;
+ struct mtp_level_3_prohib *prb;
+ struct msgb *out = mtp_msg_alloc(link);
+
+ if (!out)
+ return NULL;
+
+ hdr = (struct mtp_level_3_hdr *) out->l2h;
+ hdr->ni = MTP_NI_NATION_NET;
+ hdr->ser_ind = MTP_SI_MNT_SNM_MSG;
+ prb = (struct mtp_level_3_prohib *) msgb_put(out, sizeof(*prb));
+ prb->cmn.h0 = MTP_PROHIBIT_MSG_GRP;
+ prb->cmn.h1 = MTP_PROHIBIT_MSG_SIG;
+ prb->apoc = MTP_MAKE_APOC(apoc);
+ return out;
+}
+
+static struct msgb *mtp_tra_alloc(struct mtp_link *link)
+{
+ struct mtp_level_3_hdr *hdr;
+ struct mtp_level_3_cmn *cmn;
+ struct msgb *out = mtp_msg_alloc(link);
+
+ if (!out)
+ return NULL;
+
+ hdr = (struct mtp_level_3_hdr *) out->l2h;
+ hdr->ni = MTP_NI_NATION_NET;
+ hdr->ser_ind = MTP_SI_MNT_SNM_MSG;
+ cmn = (struct mtp_level_3_cmn *) msgb_put(out, sizeof(*cmn));
+ cmn->h0 = MTP_TRF_RESTR_MSG_GRP;
+ cmn->h1 = MTP_RESTR_MSG_ALLWED;
+ return out;
+}
+
+static struct msgb *mtp_sccp_alloc_ssa(struct mtp_link *link, int sls)
+{
+ struct sccp_data_unitdata *udt;
+ struct sccp_con_ctrl_prt_mgt *prt;
+ struct mtp_level_3_hdr *hdr;
+ uint8_t *data;
+
+
+ struct msgb *out = mtp_msg_alloc(link);
+
+ if (!out)
+ return NULL;
+
+ hdr = (struct mtp_level_3_hdr *) out->l2h;
+ hdr->ni = MTP_NI_NATION_NET;
+ hdr->ser_ind = MTP_SI_MNT_SCCP;
+
+ /* this appears to be round robin or such.. */
+ hdr->addr = MTP_ADDR(sls % 16, link->dpc, link->opc);
+
+ /* generate the UDT message... libsccp does not offer formating yet */
+ udt = (struct sccp_data_unitdata *) msgb_put(out, sizeof(*udt));
+ udt->type = SCCP_MSG_TYPE_UDT;
+ udt->proto_class = SCCP_PROTOCOL_CLASS_0;
+ udt->variable_called = 3;
+ udt->variable_calling = 5;
+ udt->variable_data = 7;
+
+ /* put the called and calling address. It is LV */
+ data = msgb_put(out, 2 + 1);
+ data[0] = 2;
+ data[1] = 0x42;
+ data[2] = 0x1;
+
+ data = msgb_put(out, 2 + 1);
+ data[0] = 2;
+ data[1] = 0x42;
+ data[2] = 0x1;
+
+ data = msgb_put(out, 1);
+ data[0] = sizeof(*prt);
+
+ prt = (struct sccp_con_ctrl_prt_mgt *) msgb_put(out, sizeof(*prt));
+ prt->sst = SCCP_SSA;
+ prt->assn = 254;
+ prt->apoc = MTP_MAKE_APOC(link->opc);
+ prt->mul_ind = 0;
+
+ return out;
+}
+
+void mtp_link_init(void)
+{
+ tall_mtp_ctx = talloc_named_const(NULL, 1, "mtp-link");
+}
+
+static void mtp_send_sltm(struct mtp_link *link)
+{
+ struct msgb *msg;
+
+ link->sltm_pending = 1;
+ msg = mtp_create_sltm(link);
+ if (!msg) {
+ LOGP(DINP, LOGL_ERROR, "Failed to allocate SLTM.\n");
+ return;
+ }
+
+ mtp_link_submit(link, msg);
+}
+
+static void mtp_sltm_t1_timeout(void *_link)
+{
+ struct mtp_link *link = (struct mtp_link *) _link;
+
+ if (link->slta_misses == 0) {
+ LOGP(DINP, LOGL_ERROR, "No SLTM response. Retrying. Link: %p\n", link);
+ ++link->slta_misses;
+ mtp_send_sltm(link);
+ bsc_schedule_timer(&link->t1_timer, MTP_T1);
+ } else {
+ LOGP(DINP, LOGL_ERROR, "Two missing SLTAs. Restart link: %p\n", link);
+ link->sccp_up = 0;
+ link->running = 0;
+ bsc_del_timer(&link->t2_timer);
+ mtp_link_sccp_down(link);
+ mtp_link_restart(link);
+ }
+}
+
+static void mtp_sltm_t2_timeout(void *_link)
+{
+ struct mtp_link *link = (struct mtp_link *) _link;
+
+ if (!link->running) {
+ LOGP(DINP, LOGL_INFO, "Not restarting SLTM timer on link: %p\n", link);
+ return;
+ }
+
+ link->slta_misses = 0;
+ mtp_send_sltm(link);
+
+ bsc_schedule_timer(&link->t1_timer, MTP_T1);
+
+ if (link->sltm_once && link->was_up)
+ LOGP(DINP, LOGL_INFO, "Not sending SLTM again as configured.\n");
+ else
+ bsc_schedule_timer(&link->t2_timer, MTP_T2);
+}
+
+static void mtp_delayed_start(void *link)
+{
+ mtp_sltm_t2_timeout(link);
+}
+
+struct mtp_link *mtp_link_alloc(void)
+{
+ struct mtp_link *link;
+
+ link = talloc_zero(tall_mtp_ctx, struct mtp_link);
+ if (!link)
+ return NULL;
+
+ link->t1_timer.data = link;
+ link->t1_timer.cb = mtp_sltm_t1_timeout;
+ link->t2_timer.data = link;
+ link->t2_timer.cb = mtp_sltm_t2_timeout;
+ link->delay_timer.data = link;
+ link->delay_timer.cb = mtp_delayed_start;
+ INIT_LLIST_HEAD(&link->pending_msgs);
+ return link;
+}
+
+void mtp_link_stop(struct mtp_link *link)
+{
+ bsc_del_timer(&link->t1_timer);
+ bsc_del_timer(&link->t2_timer);
+ bsc_del_timer(&link->delay_timer);
+ link->sccp_up = 0;
+ link->running = 0;
+ link->sltm_pending = 0;
+
+ mtp_link_sccp_down(link);
+}
+
+void mtp_link_reset(struct mtp_link *link)
+{
+ mtp_link_stop(link);
+ link->running = 1;
+ bsc_schedule_timer(&link->delay_timer, START_DELAY);
+}
+
+static int mtp_link_sign_msg(struct mtp_link *link, struct mtp_level_3_hdr *hdr, int l3_len)
+{
+ struct msgb *msg;
+ struct mtp_level_3_cmn *cmn;
+
+ if (hdr->spare != 0 || hdr->ni != MTP_NI_NATION_NET || l3_len < 1) {
+ LOGP(DINP, LOGL_ERROR, "Unhandled data (%d, %d, %d)\n",
+ hdr->spare, hdr->ni, l3_len);
+ return -1;
+ }
+
+ cmn = (struct mtp_level_3_cmn *) &hdr->data[0];
+ LOGP(DINP, LOGL_DEBUG, "reg msg: h0: 0x%x h1: 0x%x\n",
+ cmn->h0, cmn->h1);
+
+ switch (cmn->h0) {
+ case MTP_TRF_RESTR_MSG_GRP:
+ switch (cmn->h1) {
+ case MTP_RESTR_MSG_ALLWED:
+ LOGP(DINP, LOGL_INFO, "Received Restart Allowed. SST should be next: %p\n", link);
+ link->sccp_up = 0;
+ mtp_link_sccp_down(link);
+
+ msg = mtp_tfp_alloc(link, 0);
+ if (!msg)
+ return -1;
+ mtp_link_submit(link, msg);
+
+ msg = mtp_tra_alloc(link);
+ if (!msg)
+ return -1;
+
+ mtp_link_submit(link, msg);
+ return 0;
+ break;
+ }
+ break;
+ }
+
+ abort();
+ return -1;
+}
+
+static int mtp_link_regular_msg(struct mtp_link *link, struct mtp_level_3_hdr *hdr, int l3_len)
+{
+ struct msgb *out;
+ struct mtp_level_3_mng *mng;
+
+ if (hdr->spare != 0 || hdr->ni != MTP_NI_NATION_NET || l3_len < 2) {
+ LOGP(DINP, LOGL_ERROR, "Unhandled data (%d, %d, %d)\n",
+ hdr->spare, hdr->ni, l3_len);
+ return -1;
+ }
+
+ mng = (struct mtp_level_3_mng *) &hdr->data[0];
+ LOGP(DINP, LOGL_DEBUG, "reg msg: h0: 0x%x h1: 0x%x\n",
+ mng->cmn.h0, mng->cmn.h1);
+
+ switch (mng->cmn.h0) {
+ case MTP_TST_MSG_GRP:
+ switch (mng->cmn.h1) {
+ case MTP_TST_MSG_SLTM:
+ /* simply respond to the request... */
+ out = mtp_create_slta(link, mng, l3_len);
+ if (!out)
+ return -1;
+ mtp_link_submit(link, out);
+ return 0;
+ break;
+ case MTP_TST_MSG_SLTA:
+ if (mng->length != 14) {
+ LOGP(DINP, LOGL_ERROR, "Wrongly sized SLTA: %u\n", mng->length);
+ return -1;
+ }
+
+ if (l3_len != 16) {
+ LOGP(DINP, LOGL_ERROR, "Wrongly sized SLTA: %u\n", mng->length);
+ return -1;
+ }
+
+ if (memcmp(mng->data, link->test_ptrn, sizeof(link->test_ptrn)) != 0) {
+ LOGP(DINP, LOGL_ERROR, "Wrong test pattern SLTA\n");
+ return -1;
+ }
+
+ /* we had a matching slta */
+ bsc_del_timer(&link->t1_timer);
+ link->sltm_pending = 0;
+ mtp_link_slta_recv(link);
+ return 0;
+ break;
+ }
+ break;
+ }
+
+ return -1;
+}
+
+static int mtp_link_sccp_data(struct mtp_link *link, struct mtp_level_3_hdr *hdr, struct msgb *msg, int l3_len)
+{
+ struct msgb *out;
+ struct sccp_con_ctrl_prt_mgt *prt;
+
+ msg->l2h = &hdr->data[0];
+ if (msgb_l2len(msg) != l3_len) {
+ LOGP(DINP, LOGL_ERROR, "Size is wrong after playing with the l2h header.\n");
+ return -1;
+ }
+
+
+ if (link->sccp_up) {
+ mtp_link_forward_sccp(link, msg, MTP_LINK_SLS(hdr->addr));
+ return 0;
+ } else {
+ struct sccp_parse_result sccp;
+ memset(&sccp, 0, sizeof(sccp));
+ if (sccp_parse_header(msg, &sccp) != 0) {
+ LOGP(DINP, LOGL_ERROR, "Failed to parsed SCCP header.\n");
+ return -1;
+ }
+
+ if (sccp_determine_msg_type(msg) != SCCP_MSG_TYPE_UDT) {
+ LOGP(DINP, LOGL_ERROR, "Dropping sccp data: 0x%x\n",
+ sccp_determine_msg_type(msg));
+ return -1;
+ }
+
+ if (msgb_l3len(msg) != 5) {
+ LOGP(DINP, LOGL_ERROR, "SCCP UDT msg of unexpected size: %u\n",
+ msgb_l3len(msg));
+ return -1;
+ }
+
+ if (msg->l3h[0] != SCCP_SST) {
+ LOGP(DINP, LOGL_ERROR, "Expected SCCP SST but got 0x%x\n",
+ msg->l3h[0]);
+ return -1;
+ }
+
+ prt = (struct sccp_con_ctrl_prt_mgt *) &msg->l3h[0];
+ if (prt->assn != 254 || prt->apoc != MTP_MAKE_APOC(link->opc)) {
+ LOGP(DINP, LOGL_ERROR, "Unknown SSN/APOC assn: %u, apoc: %u/%u\n",
+ prt->assn, ntohs(prt->apoc), prt->apoc);
+ return -1;
+ }
+
+ out = mtp_sccp_alloc_ssa(link, MTP_LINK_SLS(hdr->addr));
+ if (!out)
+ return -1;
+
+ link->sccp_up = 1;
+ link->was_up = 1;
+ LOGP(DINP, LOGL_INFO, "SCCP is established. %p\n", link);
+ mtp_link_submit(link, out);
+ }
+ return 0;
+}
+
+int mtp_link_data(struct mtp_link *link, struct msgb *msg)
+{
+ int rc = -1;
+ struct mtp_level_3_hdr *hdr;
+ int l3_len;
+
+ if (!msg->l2h || msgb_l2len(msg) < sizeof(*hdr))
+ return -1;
+
+ if (!link->running) {
+ LOGP(DINP, LOGL_ERROR, "Link is not running. Call mtp_link_reset first: %p\n", link);
+ return -1;
+ }
+
+ hdr = (struct mtp_level_3_hdr *) msg->l2h;
+ l3_len = msgb_l2len(msg) - sizeof(*hdr);
+
+ switch (hdr->ser_ind) {
+ case MTP_SI_MNT_SNM_MSG:
+ rc = mtp_link_sign_msg(link, hdr, l3_len);
+ break;
+ case MTP_SI_MNT_REG_MSG:
+ rc = mtp_link_regular_msg(link, hdr, l3_len);
+ break;
+ case MTP_SI_MNT_SCCP:
+ rc = mtp_link_sccp_data(link, hdr, msg, l3_len);
+ break;
+ default:
+ fprintf(stderr, "Unhandled: %u\n", hdr->ser_ind);
+ break;
+ }
+
+ return rc;
+}
+
+int mtp_link_submit_sccp_data(struct mtp_link *link, int sls, const uint8_t *data, unsigned int length)
+{
+ uint8_t *put_ptr;
+ struct mtp_level_3_hdr *hdr;
+ struct msgb *msg;
+
+ if (!link->sccp_up) {
+ LOGP(DINP, LOGL_ERROR, "SCCP msg after TRA and before SSA. Dropping it.\n");
+ return -1;
+ }
+
+ msg = mtp_msg_alloc(link);
+ if (!msg)
+ return -1;
+
+ hdr = (struct mtp_level_3_hdr *) msg->l2h;
+ hdr->ni = MTP_NI_NATION_NET;
+ hdr->ser_ind = MTP_SI_MNT_SCCP;
+
+ hdr->addr = MTP_ADDR(sls % 16, link->dpc, link->opc);
+
+ /* copy the raw sccp data */
+ put_ptr = msgb_put(msg, length);
+ memcpy(put_ptr, data, length);
+
+ mtp_link_submit(link, msg);
+ return 0;
+}
diff --git a/src/mtp_pcap.c b/src/mtp_pcap.c
new file mode 100644
index 0000000..052813f
--- /dev/null
+++ b/src/mtp_pcap.c
@@ -0,0 +1,86 @@
+/* PCAP code from OpenBSC done by Holger Freyther */
+/*
+ * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2010 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <mtp/mtp_pcap.h>
+
+#include <sys/time.h>
+
+#include <unistd.h>
+
+#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
+
+/*
+ * pcap writing of the misdn load
+ * pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat
+ */
+struct pcap_hdr {
+ uint32_t magic_number;
+ uint16_t version_major;
+ uint16_t version_minor;
+ int32_t thiszone;
+ uint32_t sigfigs;
+ uint32_t snaplen;
+ uint32_t network;
+} __attribute__((packed));
+
+struct pcaprec_hdr {
+ uint32_t ts_sec;
+ uint32_t ts_usec;
+ uint32_t incl_len;
+ uint32_t orig_len;
+} __attribute__((packed));
+
+int mtp_pcap_write_header(int fd)
+{
+ static struct pcap_hdr hdr = {
+ .magic_number = 0xa1b2c3d4,
+ .version_major = 2,
+ .version_minor = 4,
+ .thiszone = 0,
+ .sigfigs = 0,
+ .snaplen = 65535,
+ .network = 141,
+ };
+
+ return write(fd, &hdr, sizeof(hdr));
+}
+
+int mtp_pcap_write_msu(int fd, const uint8_t *data, int length)
+{
+ int rc_h, rc_d;
+ struct timeval tv;
+ struct pcaprec_hdr payload_header = {
+ .ts_sec = 0,
+ .ts_usec = 0,
+ .incl_len = length,
+ .orig_len = length,
+ };
+
+ gettimeofday(&tv, NULL);
+ payload_header.ts_sec = tv.tv_sec;
+ payload_header.ts_usec = tv.tv_usec;
+
+ rc_h = write(fd, &payload_header, sizeof(payload_header));
+ rc_d = write(fd, data, length);
+
+ return rc_h == sizeof(payload_header) && rc_d == length;
+}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index d0ed7f4..bf7855d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1 +1 @@
-SUBDIRS = sccp
+SUBDIRS = sccp mtp
diff --git a/tests/mtp/Makefile.am b/tests/mtp/Makefile.am
new file mode 100644
index 0000000..1f364b6
--- /dev/null
+++ b/tests/mtp/Makefile.am
@@ -0,0 +1,4 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include -Wall
+noinst_PROGRAMS = mtp_parse_test
+
+mtp_parse_test_SOURCES = mtp_parse_test.c
diff --git a/tests/mtp/mtp_parse_test.c b/tests/mtp/mtp_parse_test.c
new file mode 100644
index 0000000..434f825
--- /dev/null
+++ b/tests/mtp/mtp_parse_test.c
@@ -0,0 +1,629 @@
+/* MTP Layer3 parsing tests */
+#include <mtp/mtp_level3.h>
+
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+struct mtp_test {
+ const uint8_t *input;
+ const uint16_t length;
+ struct mtp_level_3_hdr hdr;
+
+ int has_mng;
+ struct mtp_level_3_mng mng;
+
+ int has_prohib;
+ struct mtp_level_3_prohib prohib;
+};
+
+static const unsigned char pkt1[] = {
+0x81, 0x88, 0xc0, 0x16, 0x00, 0x11, 0xe0, 0x62,
+0x61, 0x72, 0x62, 0x61, 0x6e, 0x6f, 0x62, 0x61,
+0x72, 0x6e, 0x61, 0x62, 0x6f };
+
+static const unsigned char pkt2[] = {
+0x81, 0x5b, 0x00, 0x22, 0x00, 0x11, 0xe0, 0x41,
+0x6d, 0x69, 0x74, 0x20, 0x43, 0x68, 0x61, 0x6e,
+0x64, 0x72, 0x61, 0x00, 0x00 };
+
+static const unsigned char pkt3[] = {
+0x81, 0x88, 0xc0, 0x16, 0x00, 0x21, 0xe0, 0x41,
+0x6d, 0x69, 0x74, 0x20, 0x43, 0x68, 0x61, 0x6e,
+0x64, 0x72, 0x61, 0x00, 0x00 };
+
+static const unsigned char pkt4[] = {
+0x81, 0x5b, 0x00, 0x22, 0x00, 0x21, 0xe0, 0x62,
+0x61, 0x72, 0x62, 0x61, 0x6e, 0x6f, 0x62, 0x61,
+0x72, 0x6e, 0x61, 0x62, 0x6f };
+
+static const unsigned char pkt5[] = {
+0x81, 0x88, 0xc0, 0x16, 0x00, 0x21, 0xe0, 0x62,
+0x61, 0x72, 0x62, 0x61, 0x6e, 0x6f, 0x62, 0x61,
+0x72, 0x6e, 0x61, 0x62, 0x6f };
+
+static const unsigned char pkt6[] = {
+0x80, 0x5b, 0x00, 0x22, 0x00, 0x17 };
+
+static const unsigned char pkt7[] = {
+0x80, 0x88, 0xc0, 0x16, 0x00, 0x14, 0x56, 0x00 };
+
+static const unsigned char pkt8[] = {
+0x80, 0x88, 0xc0, 0x16, 0x00, 0x14, 0x00, 0x00 };
+
+static const unsigned char pkt9[] = {
+0x80, 0x88, 0xc0, 0x16, 0x00, 0x17 };
+
+static const unsigned char pkt10[] = {
+0x83, 0x5b, 0x00, 0x22, 0x20, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0x01, 0x04,
+0xc3, 0x88, 0x00, 0x01, 0x05, 0x03, 0xfe, 0x5b,
+0x00, 0x01 };
+
+static const unsigned char pkt11[] = {
+0x83, 0x88, 0xc0, 0x16, 0xd0, 0x09, 0x00, 0x03,
+0x05, 0x07, 0x02, 0x42, 0x01, 0x02, 0x42, 0x01,
+0x05, 0x01, 0xfe, 0x5b, 0x00, 0x00 };
+
+static const unsigned char pkt12[] = {
+0x83, 0x5b, 0x00, 0x22, 0x30, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x30,
+0x04, 0x01, 0x20 };
+
+static const unsigned char pkt13[] = {
+0x83, 0x5b, 0x00, 0x22, 0x40, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x0f, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt14[] = {
+0x83, 0x5b, 0x00, 0x22, 0x50, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x10, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt15[] = {
+0x83, 0x5b, 0x00, 0x22, 0x60, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x11, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt16[] = {
+0x83, 0x5b, 0x00, 0x22, 0x70, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x12, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt17[] = {
+0x83, 0x5b, 0x00, 0x22, 0x80, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x13, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt18[] = {
+0x83, 0x5b, 0x00, 0x22, 0x90, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x14, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt19[] = {
+0x83, 0x5b, 0x00, 0x22, 0xa0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x15, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt20[] = {
+0x83, 0x5b, 0x00, 0x22, 0xb0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x16, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt21[] = {
+0x83, 0x5b, 0x00, 0x22, 0xc0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x17, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt22[] = {
+0x83, 0x5b, 0x00, 0x22, 0xd0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x18, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt23[] = {
+0x83, 0x5b, 0x00, 0x22, 0xe0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x19, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt24[] = {
+0x83, 0x5b, 0x00, 0x22, 0xf0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x1a, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt25[] = {
+0x83, 0x5b, 0x00, 0x22, 0x00, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x1b, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt26[] = {
+0x83, 0x5b, 0x00, 0x22, 0x10, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x1c, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt27[] = {
+0x83, 0x5b, 0x00, 0x22, 0x20, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x1d, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt28[] = {
+0x83, 0x5b, 0x00, 0x22, 0x30, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x1e, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt29[] = {
+0x83, 0x88, 0xc0, 0x16, 0x60, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x0f };
+
+static const unsigned char pkt30[] = {
+0x83, 0x88, 0xc0, 0x16, 0x70, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x10 };
+
+static const unsigned char pkt31[] = {
+0x83, 0x88, 0xc0, 0x16, 0x80, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x11 };
+
+static const unsigned char pkt32[] = {
+0x83, 0x88, 0xc0, 0x16, 0x90, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x12 };
+
+static const unsigned char pkt33[] = {
+0x83, 0x88, 0xc0, 0x16, 0xa0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x13 };
+
+static const unsigned char pkt34[] = {
+0x83, 0x88, 0xc0, 0x16, 0xb0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x14 };
+
+static const unsigned char pkt35[] = {
+0x83, 0x88, 0xc0, 0x16, 0xc0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x15 };
+
+static const unsigned char pkt36[] = {
+0x83, 0x88, 0xc0, 0x16, 0xd0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x16 };
+
+static const unsigned char pkt37[] = {
+0x83, 0x88, 0xc0, 0x16, 0xe0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x17 };
+
+static const unsigned char pkt38[] = {
+0x83, 0x88, 0xc0, 0x16, 0xf0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x18 };
+
+static const unsigned char pkt39[] = {
+0x83, 0x88, 0xc0, 0x16, 0x00, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x19 };
+
+static const unsigned char pkt40[] = {
+0x83, 0x88, 0xc0, 0x16, 0x10, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x1a };
+
+static const unsigned char pkt41[] = {
+0x83, 0x88, 0xc0, 0x16, 0x20, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x1b };
+
+static const unsigned char pkt42[] = {
+0x83, 0x88, 0xc0, 0x16, 0x30, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x1c };
+
+static const unsigned char pkt43[] = {
+0x83, 0x88, 0xc0, 0x16, 0x40, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x1d };
+
+static const unsigned char pkt44[] = {
+0x83, 0x88, 0xc0, 0x16, 0x50, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x1e };
+
+static const unsigned char pkt45[] = {
+0x83, 0x5b, 0x00, 0x22, 0x40, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x0f, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt46[] = {
+0x83, 0x5b, 0x00, 0x22, 0x50, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x10, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt47[] = {
+0x83, 0x5b, 0x00, 0x22, 0x60, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x11, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt48[] = {
+0x83, 0x5b, 0x00, 0x22, 0x70, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x12, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt49[] = {
+0x83, 0x5b, 0x00, 0x22, 0x80, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x13, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt50[] = {
+0x83, 0x5b, 0x00, 0x22, 0x90, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x14, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt51[] = {
+0x83, 0x5b, 0x00, 0x22, 0xa0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x15, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt52[] = {
+0x83, 0x5b, 0x00, 0x22, 0xb0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x16, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt53[] = {
+0x83, 0x5b, 0x00, 0x22, 0xc0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x17, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt54[] = {
+0x83, 0x5b, 0x00, 0x22, 0xd0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x18, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt55[] = {
+0x83, 0x5b, 0x00, 0x22, 0xe0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x19, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt56[] = {
+0x83, 0x5b, 0x00, 0x22, 0xf0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x1a, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt57[] = {
+0x83, 0x5b, 0x00, 0x22, 0x00, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x1b, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt58[] = {
+0x83, 0x5b, 0x00, 0x22, 0x10, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x1c, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt59[] = {
+0x83, 0x5b, 0x00, 0x22, 0x20, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x1d, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt60[] = {
+0x83, 0x5b, 0x00, 0x22, 0x30, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0xc3, 0x5b, 0x00, 0xfe, 0x04,
+0x43, 0x88, 0x00, 0xfe, 0x09, 0x00, 0x07, 0x40,
+0x01, 0x00, 0x1e, 0x04, 0x01, 0x07 };
+
+static const unsigned char pkt61[] = {
+0x83, 0x88, 0xc0, 0x16, 0x60, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x0f };
+
+static const unsigned char pkt62[] = {
+0x83, 0x88, 0xc0, 0x16, 0x70, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x10 };
+
+static const unsigned char pkt63[] = {
+0x83, 0x88, 0xc0, 0x16, 0x80, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x11 };
+
+static const unsigned char pkt64[] = {
+0x83, 0x88, 0xc0, 0x16, 0x90, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x12 };
+
+static const unsigned char pkt65[] = {
+0x83, 0x88, 0xc0, 0x16, 0xa0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x13 };
+
+static const unsigned char pkt66[] = {
+0x83, 0x88, 0xc0, 0x16, 0xb0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x14 };
+
+static const unsigned char pkt67[] = {
+0x83, 0x88, 0xc0, 0x16, 0xc0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x15 };
+
+static const unsigned char pkt68[] = {
+0x83, 0x88, 0xc0, 0x16, 0xd0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x16 };
+
+static const unsigned char pkt69[] = {
+0x83, 0x88, 0xc0, 0x16, 0xe0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x17 };
+
+static const unsigned char pkt70[] = {
+0x83, 0x88, 0xc0, 0x16, 0xf0, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x18 };
+
+static const unsigned char pkt71[] = {
+0x83, 0x88, 0xc0, 0x16, 0x00, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x19 };
+
+static const unsigned char pkt72[] = {
+0x83, 0x88, 0xc0, 0x16, 0x10, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x1a };
+
+static const unsigned char pkt73[] = {
+0x83, 0x88, 0xc0, 0x16, 0x20, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x1b };
+
+static const unsigned char pkt74[] = {
+0x83, 0x88, 0xc0, 0x16, 0x30, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x1c };
+
+static const unsigned char pkt75[] = {
+0x83, 0x88, 0xc0, 0x16, 0x40, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x1d };
+
+static const unsigned char pkt76[] = {
+0x83, 0x88, 0xc0, 0x16, 0x50, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x06, 0x00, 0x04, 0x48,
+0x01, 0x00, 0x1e };
+
+static const unsigned char pkt77[] = {
+0x83, 0x88, 0xc0, 0x16, 0x60, 0x09, 0x00, 0x03,
+0x07, 0x0b, 0x04, 0x43, 0x88, 0x00, 0xfe, 0x04,
+0x43, 0x5b, 0x00, 0xfe, 0x03, 0x00, 0x01, 0x31 };
+
+static struct mtp_test tests[] = {
+ {
+ .input = pkt1,
+ .length = sizeof(pkt1),
+ .hdr = {
+ .ni = 0x02,
+ .spare = 0x00,
+ .ser_ind = 0x01,
+ },
+ .has_mng = 1,
+ .mng = {
+ .cmn = {
+ .h0 = 0x01,
+ .h1 = 0x01,
+ },
+ .length = 14,
+ },
+ },
+
+ {
+ .input = pkt3,
+ .length = sizeof(pkt3),
+ .hdr = {
+ .ni = 0x02,
+ .spare = 0x00,
+ .ser_ind = 0x01,
+ },
+ .has_mng = 1,
+ .mng = {
+ .cmn = {
+ .h0 = 0x01,
+ .h1 = 0x02,
+ },
+
+ .length = 14,
+ },
+ },
+
+ {
+ .input = pkt7,
+ .length = sizeof(pkt7),
+ .hdr = {
+ .ni = 2,
+ .spare = 0,
+ .ser_ind = 0,
+ },
+
+ .has_prohib = 1,
+ .prohib = {
+ .cmn = {
+ .h0 = 0x04,
+ .h1 = 0x1,
+ },
+
+ },
+ }
+};
+
+static void check_hdr(const uint8_t *data, const struct mtp_level_3_hdr *t_hdr)
+{
+ struct mtp_level_3_hdr *hdr;
+ hdr = (struct mtp_level_3_hdr *) data;
+ if (memcmp(hdr, t_hdr, sizeof(*hdr)) == 0)
+ return;
+
+ if (hdr->ni != t_hdr->ni)
+ fprintf(stderr, "NI failed.\n");
+ if (hdr->spare != t_hdr->spare)
+ fprintf(stderr, "spare not equal\n");
+ if (hdr->ser_ind != t_hdr->ser_ind)
+ fprintf(stderr, "ser_ind not equal\n");
+ if (hdr->addr != t_hdr->addr)
+ fprintf(stderr, "routing data not equal\n");
+
+ fprintf(stderr, "FAIL: Comparing headers failed.\n");
+ abort();
+}
+
+static void check_mng(const uint8_t *data, const struct mtp_level_3_mng *t_mng)
+{
+ struct mtp_level_3_hdr *hdr = (struct mtp_level_3_hdr *) data;
+ struct mtp_level_3_mng *mng = (struct mtp_level_3_mng *) &hdr->data[0];
+
+ if (memcmp(mng, t_mng, sizeof(*mng)) == 0)
+ return;
+
+ if (mng->cmn.h0 != t_mng->cmn.h0)
+ fprintf(stderr, "h0 not equal.\n");
+ if (mng->cmn.h1 != t_mng->cmn.h1)
+ fprintf(stderr, "h1 not equal.\n");
+ if (mng->length != t_mng->length)
+ fprintf(stderr, "length not euqal.\n");
+ fprintf(stderr, "FAIL: Comparing the mtp_level_3_mng\n");
+ abort();
+}
+
+static void check_prohib(const uint8_t *data, const struct mtp_level_3_prohib *t_prohib)
+{
+ struct mtp_level_3_hdr *hdr = (struct mtp_level_3_hdr *) data;
+ struct mtp_level_3_prohib *prohib = (struct mtp_level_3_prohib *) &hdr->data[0];
+
+ if (memcmp(prohib, t_prohib, sizeof(*prohib)) == 0)
+ return;
+
+ if (prohib->cmn.h0 != t_prohib->cmn.h0)
+ fprintf(stderr, "h0 not equal.\n");
+ if (prohib->cmn.h1 != t_prohib->cmn.h1)
+ fprintf(stderr, "h1 not equal.\n");
+ if (ntohs(prohib->apoc) != t_prohib->apoc)
+ fprintf(stderr, "apoc not euqal.\n");
+ fprintf(stderr, "FAIL: Comparing the mtp_level_3_prohib\n");
+ abort();
+}
+
+int main(int argc, char **argv)
+{
+ uint32_t addr;
+ int i;
+
+ /* set the addresses here due big endian MTP_ADDRESS macro */
+ tests[0].hdr.addr = MTP_ADDR(0x00, 136, 91);
+ tests[1].hdr.addr = MTP_ADDR(0x00, 136, 91);
+ tests[2].hdr.addr = MTP_ADDR(0x00, 136, 91);
+ tests[2].prohib.apoc = MTP_MAKE_APOC(86);
+
+ for (i = 0; i < ARRAY_SIZE(tests); ++i) {
+ check_hdr(tests[i].input, &tests[i].hdr);
+ if (tests[i].has_mng)
+ check_mng(tests[i].input, &tests[i].mng);
+ if (tests[i].has_prohib)
+ check_prohib(tests[i].input, &tests[i].prohib);
+ }
+
+ /* check the SCCP unitdata */
+ {
+ struct sccp_con_ctrl_prt_mgt prt = {
+ .sst = 0x03,
+ .assn = 254,
+ .apoc = MTP_MAKE_APOC(91),
+ .mul_ind = 1,
+ };
+
+ uint8_t data[] = { 0x03, 0xfe, 0x5b, 0x00, 0x01 };
+ if (memcmp(&prt, data, 5) != 0) {
+ uint8_t *d = (uint8_t *) &prt;
+ fprintf(stderr, "GOT: 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n",
+ d[0], d[1], d[2], d[3], d[4]);
+ abort();
+ }
+ }
+
+ /* verify decoding of the sls */
+ for (i = 0; i < 16; ++i) {
+ addr = MTP_ADDR(i, 136, 91);
+ if (MTP_LINK_SLS(addr) != i) {
+ fprintf(stderr, "0x%x/0x%x does not match 0x%x\n", addr, MTP_LINK_SLS(addr), i);
+ abort();
+ }
+ }
+
+ fprintf(stderr, "SUCCESS\n");
+ return 0;
+}