summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Undheim <ruben.undheim@gmail.com>2016-02-05 16:22:38 +0100
committerRuben Undheim <ruben.undheim@gmail.com>2016-02-05 16:22:38 +0100
commite9e10edaf79a7919495e1efce810a54b167870de (patch)
tree9660d8de7d024e5c55833189945706c7c3f0f536
Imported Upstream version 0.7.0
-rw-r--r--.gitignore63
-rw-r--r--COPYING339
-rw-r--r--Makefile.am15
-rw-r--r--configure.ac44
-rw-r--r--debian/changelog53
-rw-r--r--debian/compat1
-rw-r--r--debian/control16
-rw-r--r--debian/copyright36
-rw-r--r--debian/docs0
-rw-r--r--debian/patches/debian-changes-0.0.2-129
-rw-r--r--debian/patches/series1
-rwxr-xr-xdebian/rules23
-rw-r--r--debian/source/format1
-rwxr-xr-xgit-version-gen151
-rw-r--r--include/Makefile.am1
-rw-r--r--include/mtp/Makefile.am2
-rw-r--r--include/mtp/mtp_level3.h182
-rw-r--r--include/mtp/mtp_pcap.h29
-rw-r--r--include/sccp/Makefile.am2
-rw-r--r--include/sccp/sccp.h202
-rw-r--r--include/sccp/sccp_types.h420
-rw-r--r--include/sigtran/Makefile.am2
-rw-r--r--include/sigtran/m2ua_types.h255
-rw-r--r--include/sigtran/m3ua_types.h128
-rw-r--r--include/sigtran/xua_msg.h53
-rw-r--r--include/sigtran/xua_types.h45
-rw-r--r--libosmo-mtp.pc.in10
-rw-r--r--libosmo-sccp.pc.in10
-rw-r--r--src/Makefile.am9
-rw-r--r--src/mtp_pcap.c86
-rw-r--r--src/sccp.c1469
-rw-r--r--src/xua_msg.c184
-rw-r--r--tests/Makefile.am40
-rw-r--r--tests/m2ua/Makefile.am8
-rw-r--r--tests/m2ua/m2ua_test.c116
-rw-r--r--tests/m2ua/m2ua_test.ok3
-rw-r--r--tests/mtp/Makefile.am6
-rw-r--r--tests/mtp/mtp_parse_test.c641
-rw-r--r--tests/mtp/mtp_parse_test.ok2
-rw-r--r--tests/sccp/Makefile.am10
-rw-r--r--tests/sccp/sccp_test.c1027
-rw-r--r--tests/sccp/sccp_test.ok86
-rw-r--r--tests/testsuite.at20
43 files changed, 5820 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..48b2e76
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,63 @@
+*.o
+*.a
+.deps
+Makefile
+Makefile.in
+bscconfig.h
+bscconfig.h.in
+openbsc.pc
+bsc_hack
+bsc_msc_ip
+bsc_mgcp
+*.*~
+*.sw?
+
+#configure
+aclocal.m4
+autom4te.cache/
+compile
+config.log
+config.status
+configure
+configure.lineno
+depcomp
+install-sh
+missing
+stamp-h1
+
+# git-version-gen magic
+.tarball-version
+.version
+
+
+# apps and app data
+hlr.sqlite3
+bs11_config
+ipaccess-config
+ipaccess-find
+ipaccess-firmware
+ipaccess-proxy
+isdnsync
+bsc_nat
+osmo-sgsn
+osmo-gbproxy
+
+#tests
+tests/channel/channel_test
+tests/db/db_test
+tests/debug/debug_test
+tests/gsm0408/gsm0408_test
+tests/m2ua/m2ua_test
+tests/mtp/mtp_parse_test
+tests/sccp/sccp_test
+tests/sms/sms_test
+tests/timer/timer_test
+
+tests/atconfig
+tests/package.m4
+tests/testsuite
+tests/testsuite.log
+
+
+*.pc
+config.*
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..534fdc2
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,15 @@
+AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
+
+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
+SUBDIRS = include src tests
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libosmo-sccp.pc libosmo-mtp.pc
+
+EXTRA_DIST = .version
+
+BUILT_SOURCES = $(top_srcdir)/.version
+$(top_srcdir)/.version:
+ echo $(VERSION) > $@-t && mv $@-t $@
+dist-hook:
+ echo $(VERSION) > $(distdir)/.tarball-version
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..49c5f7e
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,44 @@
+dnl Process this file with autoconf to produce a configure script
+AC_INIT([libosmo-sccp],
+ m4_esyscmd([./git-version-gen .tarball-version]),
+ [openbsc@lists.osmocom.org])
+
+AM_INIT_AUTOMAKE([dist-bzip2])
+AC_CONFIG_TESTDIR(tests)
+
+dnl kernel style compile messages
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+dnl checks for programs
+AC_PROG_MAKE_SET
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.0)
+
+# The following test is taken from WebKit's webkit.m4
+saved_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -fvisibility=hidden "
+AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])],
+ [ AC_MSG_RESULT([yes])
+ SYMBOL_VISIBILITY="-fvisibility=hidden"],
+ AC_MSG_RESULT([no]))
+CFLAGS="$saved_CFLAGS"
+AC_SUBST(SYMBOL_VISIBILITY)
+
+AC_OUTPUT(
+ libosmo-sccp.pc
+ libosmo-mtp.pc
+ include/sccp/Makefile
+ include/mtp/Makefile
+ include/sigtran/Makefile
+ include/Makefile
+ src/Makefile
+ tests/Makefile
+ tests/sccp/Makefile
+ tests/mtp/Makefile
+ tests/m2ua/Makefile
+ Makefile)
+
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..9e5884e
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,53 @@
+libosmo-sccp (0.7.0) unstable; urgency=medium
+
+ * New release.
+
+ -- Holger Hans Peter Freyther <holger@moiji-mobile.com> Sat, 01 Aug 2015 20:12:05 +0200
+
+libosmo-sccp (0.0.6.5) unstable; urgency=medium
+
+ * Non-maintainer upload.
+
+ -- Holger Hans Peter Freyther <holger@freyther.de> Thu, 16 Apr 2015 21:16:25 +0200
+
+libosmo-sccp (0.0.6.4) unstable; urgency=medium
+
+ * Bump the version due the package changes.
+
+ -- Holger Hans Peter Freyther <holger@freyther.de> Sat, 14 Mar 2015 19:47:20 +0100
+
+libosmo-sccp (0.0.6.3) unstable; urgency=low
+
+ * New upstream release 0.0.6.3.
+
+ -- Holger Hans Peter Freyther <holger@freyther.de> Tue, 06 Nov 2012 13:24:14 +0100
+
+libosmo-sccp (0.0.6.2+git2-1) precise; urgency=low
+
+ * Fix version issue.
+
+ -- Eric Butler <eric@codebutler.com> Tue, 14 Aug 2012 20:52:45 -0700
+
+libosmo-sccp (0.0.6.2+git2) precise; urgency=low
+
+ * Update debian package.
+
+ -- Eric Butler <eric@codebutler.com> Tue, 14 Aug 2012 17:08:14 -0700
+
+libosmo-sccp (0.0.5) unstable; urgency=low
+
+ * New upstream release 0.0.5
+
+ -- Harald Welte <laforge@gnumonks.org> Tue, 10 May 2011 17:30:34 +0200
+
+libosmo-sccp (0.0.3) natty; urgency=low
+
+ * New upstream release with 0.0.3 and more.
+
+ -- Holger Hans Peter Freyther <holger@freyther.de> Thu, 13 Jan 2011 18:16:25 +0800
+
+libosmo-sccp (0.0.2-1) unstable; urgency=low
+
+ * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP>
+
+ -- Harald Welte <laforge@gnumonks.org> Tue, 24 Aug 2010 14:08:42 +0200
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..5b1a623
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,16 @@
+Source: libosmo-sccp
+Priority: optional
+Maintainer: Harald Welte <laforge@gnumonks.org>
+Build-Depends: debhelper (>= 9), autotools-dev, pkg-config, libosmocore-dev, autoconf, automake, libtool, dh-autoreconf, git, libdpkg-perl
+Standards-Version: 3.9.6
+Section: libs
+#Homepage: <insert the upstream URL, if relevant>
+Vcs-Git: git://git.osmocom.org/libosmo-sccp.git
+Vcs-Browser: http://git.osmocom.org/gitweb?p=libosmo-sccp.git;a=summary
+
+Package: libosmo-sccp-dev
+Section: libdevel
+Architecture: any
+Depends: ${misc:Depends}
+Multi-Arch: same
+Description: Development files for Osmcoom SCCP library
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..97328f2
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,36 @@
+This work was packaged for Debian by:
+
+ Harald Welte <laforge@gnumonks.org> on Tue, 24 Aug 2010 10:55:04 +0200
+
+It was downloaded from:
+
+ git://git.osmocom.org/libosmo-sccp.git
+
+Upstream Author(s):
+
+ Holger Hans Peter Freyther <zecke@selfish.org>
+
+Copyright:
+
+ Copyright (C) 2009-2010 Holger Hans Peter Freyther <zecke@selfish.org>
+ Copyright (C) 2009-2010 On-Waves
+
+License:
+
+ GNU General Public License, Version 2 or later
+
+The Debian packaging is:
+
+ Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>
+
+# Please chose a license for your packaging work. If the program you package
+# uses a mainstream license, using the same license is the safest choice.
+# Please avoid to pick license terms that are more restrictive than the
+# packaged work, as it may make Debian's contributions unacceptable upstream.
+# If you just want it to be GPL version 3, leave the following lines in.
+
+and is licensed under the GPL version 3,
+see "/usr/share/common-licenses/GPL-3".
+
+# Please also look if there are files or directories which have a
+# different copyright/license attached and list them here.
diff --git a/debian/docs b/debian/docs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/debian/docs
diff --git a/debian/patches/debian-changes-0.0.2-1 b/debian/patches/debian-changes-0.0.2-1
new file mode 100644
index 0000000..ed4a4df
--- /dev/null
+++ b/debian/patches/debian-changes-0.0.2-1
@@ -0,0 +1,29 @@
+Description: Upstream changes introduced in version 0.0.2-1
+ This patch has been created by dpkg-source during the package build.
+ Here's the last changelog entry, hopefully it gives details on why
+ those changes were made:
+ .
+ libosmo-sccp (0.0.2-1) unstable; urgency=low
+ .
+ * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP>
+ .
+ The person named in the Author field signed this changelog entry.
+Author: Harald Welte <laforge@gnumonks.org>
+
+---
+The information above should follow the Patch Tagging Guidelines, please
+checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
+are templates for supplementary fields that you might want to add:
+
+Origin: <vendor|upstream|other>, <url of original patch>
+Bug: <url in upstream bugtracker>
+Bug-Debian: http://bugs.debian.org/<bugnumber>
+Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
+Forwarded: <no|not-needed|url proving that it has been forwarded>
+Reviewed-By: <name and email of someone who approved the patch>
+Last-Update: <YYYY-MM-DD>
+
+--- /dev/null
++++ libosmo-sccp-0.0.2/.version
+@@ -0,0 +1 @@
++0.0.2
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..a3659e3
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1 @@
+debian-changes-0.0.2-1
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..1895067
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,23 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+DEBIAN := $(shell dpkg-parsechangelog | grep ^Version: | cut -d' ' -f2)
+DEBVERS := $(shell echo '$(DEBIAN)' | cut -d- -f1)
+VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g')
+
+export DEB_BUILD_HARDENING=1
+
+%:
+ dh $@ --with autoreconf
+
+override_dh_autoreconf:
+ echo $(VERSION) > .tarball-version
+ dh_autoreconf
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..89ae9db
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (native)
diff --git a/git-version-gen b/git-version-gen
new file mode 100755
index 0000000..8e59c5a
--- /dev/null
+++ b/git-version-gen
@@ -0,0 +1,151 @@
+#!/bin/sh
+# Print a version string.
+scriptversion=2010-01-28.01
+
+# Copyright (C) 2007-2010 Free Software Foundation, Inc.
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
+# It may be run two ways:
+# - from a git repository in which the "git describe" command below
+# produces useful output (thus requiring at least one signed tag)
+# - from a non-git-repo directory containing a .tarball-version file, which
+# presumes this script is invoked like "./git-version-gen .tarball-version".
+
+# In order to use intra-version strings in your project, you will need two
+# separate generated version string files:
+#
+# .tarball-version - present only in a distribution tarball, and not in
+# a checked-out repository. Created with contents that were learned at
+# the last time autoconf was run, and used by git-version-gen. Must not
+# be present in either $(srcdir) or $(builddir) for git-version-gen to
+# give accurate answers during normal development with a checked out tree,
+# but must be present in a tarball when there is no version control system.
+# Therefore, it cannot be used in any dependencies. GNUmakefile has
+# hooks to force a reconfigure at distribution time to get the value
+# correct, without penalizing normal development with extra reconfigures.
+#
+# .version - present in a checked-out repository and in a distribution
+# tarball. Usable in dependencies, particularly for files that don't
+# want to depend on config.h but do want to track version changes.
+# Delete this file prior to any autoconf run where you want to rebuild
+# files to pick up a version string change; and leave it stale to
+# minimize rebuild time after unrelated changes to configure sources.
+#
+# It is probably wise to add these two files to .gitignore, so that you
+# don't accidentally commit either generated file.
+#
+# Use the following line in your configure.ac, so that $(VERSION) will
+# automatically be up-to-date each time configure is run (and note that
+# since configure.ac no longer includes a version string, Makefile rules
+# should not depend on configure.ac for version updates).
+#
+# AC_INIT([GNU project],
+# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+# [bug-project@example])
+#
+# Then use the following lines in your Makefile.am, so that .version
+# will be present for dependencies, and so that .tarball-version will
+# exist in distribution tarballs.
+#
+# BUILT_SOURCES = $(top_srcdir)/.version
+# $(top_srcdir)/.version:
+# echo $(VERSION) > $@-t && mv $@-t $@
+# dist-hook:
+# echo $(VERSION) > $(distdir)/.tarball-version
+
+case $# in
+ 1) ;;
+ *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
+esac
+
+tarball_version_file=$1
+nl='
+'
+
+# First see if there is a tarball-only version file.
+# then try "git describe", then default.
+if test -f $tarball_version_file
+then
+ v=`cat $tarball_version_file` || exit 1
+ case $v in
+ *$nl*) v= ;; # reject multi-line output
+ [0-9]*) ;;
+ *) v= ;;
+ esac
+ test -z "$v" \
+ && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
+fi
+
+if test -n "$v"
+then
+ : # use $v
+elif test -d ./.git \
+ && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
+ || git describe --abbrev=4 HEAD 2>/dev/null` \
+ && case $v in
+ [0-9]*) ;;
+ v[0-9]*) ;;
+ *) (exit 1) ;;
+ esac
+then
+ # Is this a new git that lists number of commits since the last
+ # tag or the previous older version that did not?
+ # Newer: v6.10-77-g0f8faeb
+ # Older: v6.10-g0f8faeb
+ case $v in
+ *-*-*) : git describe is okay three part flavor ;;
+ *-*)
+ : git describe is older two part flavor
+ # Recreate the number of commits and rewrite such that the
+ # result is the same as if we were using the newer version
+ # of git describe.
+ vtag=`echo "$v" | sed 's/-.*//'`
+ numcommits=`git rev-list "$vtag"..HEAD | wc -l`
+ v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
+ ;;
+ esac
+
+ # Change the first '-' to a '.', so version-comparing tools work properly.
+ # Remove the "g" in git describe's output string, to save a byte.
+ v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
+else
+ v=UNKNOWN
+fi
+
+v=`echo "$v" |sed 's/^v//'`
+
+# Don't declare a version "dirty" merely because a time stamp has changed.
+git status > /dev/null 2>&1
+
+dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
+case "$dirty" in
+ '') ;;
+ *) # Append the suffix only if there isn't one already.
+ case $v in
+ *-dirty) ;;
+ *) v="$v-dirty" ;;
+ esac ;;
+esac
+
+# Omit the trailing newline, so that m4_esyscmd can use the result directly.
+echo "$v" | tr -d '\012'
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 0000000..8045946
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = sccp mtp sigtran
diff --git a/include/mtp/Makefile.am b/include/mtp/Makefile.am
new file mode 100644
index 0000000..dbd0e79
--- /dev/null
+++ b/include/mtp/Makefile.am
@@ -0,0 +1,2 @@
+mtp_HEADERS = mtp_level3.h mtp_pcap.h
+mtpdir = $(includedir)/osmocom/mtp
diff --git a/include/mtp/mtp_level3.h b/include/mtp/mtp_level3.h
new file mode 100644
index 0000000..d0d24a1
--- /dev/null
+++ b/include/mtp/mtp_level3.h
@@ -0,0 +1,182 @@
+/* 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.
+ *
+ */
+
+#pragma once
+
+#include <osmocom/core/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
+#define MTP_SI_MNT_ISUP 0x05
+
+/*
+ * h0 contains the group, h1 the semantic of it
+ */
+
+#define MTP_TST_MSG_GRP 0x01
+#define MTP_PROHIBIT_MSG_GRP 0x04
+#define MTP_SROUTE_MSG_GRP 0x05
+#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
+
+/* For the prohibit group */
+#define MTP_PROHIBIT_MSG_SIG 0x01
+#define MTP_PROHIBIT_MSG_TFA 0x05
+
+/* For the Signalling-route-set-test */
+#define MTP_SROUTE_MSG_TEST 0x01
+
+
+#define SCCP_SST 0x03
+#define SCCP_SSP 0x02
+#define SCCP_SSA 0x01
+
+#define MTP_LINK_MASK 0x0F
+#define MTP_ADDR_MASK 0x3FFF
+#define MTP_APOC_MASK 0x3f
+
+
+#if OSMO_IS_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)
+#define MTP_READ_DPC(addr) \
+ (((addr) >> 0) & MTP_ADDR_MASK)
+#define MTP_READ_OPC(addr) \
+ (((addr) >> 14) & MTP_ADDR_MASK)
+#elif OSMO_IS_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))
+#define MTP_READ_DPC(addr) \
+ (c_swap_32(addr) & MTP_ADDR_MASK)
+#define MTP_READ_OPC(addr) \
+ ((c_swap_32(addr) >> 14) & MTP_ADDR_MASK)
+#else
+#error "Unknown endian"
+#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 OSMO_IS_LITTLE_ENDIAN
+ uint8_t ser_ind : 4,
+ spare : 2,
+ ni : 2;
+#elif OSMO_IS_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 OSMO_IS_LITTLE_ENDIAN
+ uint8_t h0 : 4,
+ h1 : 4;
+#elif OSMO_IS_BIG_ENDIAN
+ uint8_t h1 : 4,
+ h0 : 4;
+#endif
+} __attribute__((packed));
+
+struct mtp_level_3_mng {
+ struct mtp_level_3_cmn cmn;
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t spare : 4,
+ length : 4;
+#elif OSMO_IS_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 OSMO_IS_LITTLE_ENDIAN
+ uint8_t mul_ind : 2,
+ spare : 6;
+#elif OSMO_IS_BIG_ENDIAN
+ uint8_t spare : 6,
+ mul_ind : 2;
+#endif
+} __attribute__((packed));
+
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/include/sccp/Makefile.am b/include/sccp/Makefile.am
new file mode 100644
index 0000000..c64db26
--- /dev/null
+++ b/include/sccp/Makefile.am
@@ -0,0 +1,2 @@
+sccp_HEADERS = sccp_types.h sccp.h
+sccpdir = $(includedir)/osmocom/sccp
diff --git a/include/sccp/sccp.h b/include/sccp/sccp.h
new file mode 100644
index 0000000..36b424f
--- /dev/null
+++ b/include/sccp/sccp.h
@@ -0,0 +1,202 @@
+/*
+ * SCCP management code
+ *
+ * (C) 2009, 2010, 2013 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009, 2010, 2013 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 SCCP_H
+#define SCCP_H
+
+#include <stdlib.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "sccp_types.h"
+
+struct msgb;
+struct sccp_system;
+
+enum {
+ SCCP_CONNECTION_STATE_NONE,
+ SCCP_CONNECTION_STATE_REQUEST,
+ SCCP_CONNECTION_STATE_CONFIRM,
+ SCCP_CONNECTION_STATE_ESTABLISHED,
+ SCCP_CONNECTION_STATE_RELEASE,
+ SCCP_CONNECTION_STATE_RELEASE_COMPLETE,
+ SCCP_CONNECTION_STATE_REFUSED,
+ SCCP_CONNECTION_STATE_SETUP_ERROR,
+};
+
+struct sockaddr_sccp {
+ sa_family_t sccp_family; /* AF_SCCP in the future??? */
+ uint8_t sccp_ssn; /* subssystem number for routing */
+
+ /* TODO fill in address indicator... if that is ever needed */
+
+ /* optional gti information */
+ uint8_t *gti;
+ int gti_len;
+
+ /* any of SCCP_TITLE_IND_* */
+ uint8_t gti_ind;
+
+ int use_poi;
+ uint8_t poi[2];
+
+ /* not sure about these */
+ /* uint8_t sccp_class; */
+};
+
+/*
+ * parsed structure of an address
+ */
+struct sccp_address {
+ struct sccp_called_party_address address;
+ uint8_t ssn;
+ uint8_t poi[2];
+
+ uint8_t *gti_data;
+ int gti_len;
+};
+
+struct sccp_optional_data {
+ uint8_t data_len;
+ uint8_t data_start;
+};
+
+struct sccp_connection {
+ /* public */
+ void *data_ctx;
+ void (*data_cb)(struct sccp_connection *conn, struct msgb *msg, unsigned int len);
+
+ void *state_ctx;
+ void (*state_cb)(struct sccp_connection *, int old_state);
+
+ struct sccp_source_reference source_local_reference;
+ struct sccp_source_reference destination_local_reference;
+
+ int connection_state;
+
+ /* private */
+ /* list of active connections */
+ struct llist_head list;
+ struct sccp_system *system;
+ int incoming;
+};
+
+/**
+ * system functionality to implement on top of any other transport layer:
+ * call sccp_system_incoming for incoming data (from the network)
+ * sccp will call outgoing whenever outgoing data exists
+ * The conn is NULL for UDT and other messages without a connection
+ */
+int sccp_system_init(void (*outgoing)(struct sccp_connection *conn, struct msgb *data, void *gctx, void *ctx), void *context);
+int sccp_system_incoming_ctx(struct msgb *data, void *ctx);
+int sccp_system_incoming(struct msgb *data);
+
+/**
+ * Send data on an existing connection
+ */
+int sccp_connection_write(struct sccp_connection *connection, struct msgb *data);
+int sccp_connection_send_it(struct sccp_connection *connection);
+int sccp_connection_close(struct sccp_connection *connection, int cause);
+int sccp_connection_free(struct sccp_connection *connection);
+
+/**
+ * internal..
+ */
+int sccp_connection_force_free(struct sccp_connection *conn);
+
+/**
+ * Create a new socket. Set your callbacks and then call bind to open
+ * the connection.
+ */
+struct sccp_connection *sccp_connection_socket(void);
+
+/**
+ * Open the connection and send additional data
+ */
+int sccp_connection_connect(struct sccp_connection *conn,
+ const struct sockaddr_sccp *sccp_called,
+ struct msgb *data);
+
+/**
+ * mostly for testing purposes only. Set the accept callback.
+ * TODO: add true routing information... in analogy to socket, bind, accept
+ */
+int sccp_connection_set_incoming(const struct sockaddr_sccp *sock,
+ int (*accept_cb)(struct sccp_connection *connection, void *data),
+ void *user_data);
+
+/**
+ * Send data in terms of unit data. A fixed address indicator will be used.
+ */
+int sccp_write(struct msgb *data,
+ const struct sockaddr_sccp *sock_sender,
+ const struct sockaddr_sccp *sock_target,
+ int class, void *ctx);
+int sccp_set_read(const struct sockaddr_sccp *sock,
+ int (*read_cb)(struct msgb *msgb, unsigned int, void *user_data),
+ void *user_data);
+
+/* generic sock addresses */
+extern const struct sockaddr_sccp sccp_ssn_bssap;
+
+/* helpers */
+uint32_t sccp_src_ref_to_int(struct sccp_source_reference *ref);
+struct sccp_source_reference sccp_src_ref_from_int(uint32_t);
+
+struct msgb *sccp_create_cr(const struct sccp_source_reference *src_ref, const struct sockaddr_sccp *called, const uint8_t *data, size_t length);
+struct msgb *sccp_create_refuse(struct sccp_source_reference *src_ref, int cause, uint8_t *data, int length);
+struct msgb *sccp_create_cc(struct sccp_source_reference *src_ref, struct sccp_source_reference *dst_ref);
+struct msgb *sccp_create_rlsd(struct sccp_source_reference *src_ref, struct sccp_source_reference *dst_ref, int cause);
+struct msgb *sccp_create_dt1(struct sccp_source_reference *dst_ref, uint8_t *data, uint8_t len);
+struct msgb *sccp_create_udt(int _class, const struct sockaddr_sccp *sock_sender,
+ const struct sockaddr_sccp *sock_target, uint8_t *data, int len);
+
+/**
+ * Below this are helper functions and structs for parsing SCCP messages
+ */
+struct sccp_parse_result {
+ struct sccp_address called;
+ struct sccp_address calling;
+
+ /* point to the msg packet */
+ struct sccp_source_reference *source_local_reference;
+ struct sccp_source_reference *destination_local_reference;
+
+ /* data pointer */
+ int data_len;
+};
+
+/*
+ * helper functions for the nat code
+ */
+int sccp_determine_msg_type(struct msgb *msg);
+int sccp_parse_header(struct msgb *msg, struct sccp_parse_result *result);
+
+/*
+ * osmocore logging features
+ */
+void sccp_set_log_area(int log_area);
+
+#endif
diff --git a/include/sccp/sccp_types.h b/include/sccp/sccp_types.h
new file mode 100644
index 0000000..986de0d
--- /dev/null
+++ b/include/sccp/sccp_types.h
@@ -0,0 +1,420 @@
+/*
+ * ITU Q.713 defined types for SCCP
+ *
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ *
+ * 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 SCCP_TYPES_H
+#define SCCP_TYPES_H
+
+#include <osmocom/core/endian.h>
+
+/* Table 1/Q.713 - SCCP message types */
+enum sccp_message_types {
+ SCCP_MSG_TYPE_CR = 1,
+ SCCP_MSG_TYPE_CC = 2,
+ SCCP_MSG_TYPE_CREF = 3,
+ SCCP_MSG_TYPE_RLSD = 4,
+ SCCP_MSG_TYPE_RLC = 5,
+ SCCP_MSG_TYPE_DT1 = 6,
+ SCCP_MSG_TYPE_DT2 = 7,
+ SCCP_MSG_TYPE_AK = 8,
+ SCCP_MSG_TYPE_UDT = 9,
+ SCCP_MSG_TYPE_UDTS = 10,
+ SCCP_MSG_TYPE_ED = 11,
+ SCCP_MSG_TYPE_EA = 12,
+ SCCP_MSG_TYPE_RSR = 13,
+ SCCP_MSG_TYPE_RSC = 14,
+ SCCP_MSG_TYPE_ERR = 15,
+ SCCP_MSG_TYPE_IT = 16,
+ SCCP_MSG_TYPE_XUDT = 17,
+ SCCP_MSG_TYPE_XUDTS = 18,
+ SCCP_MSG_TYPE_LUDT = 19,
+ SCCP_MSG_TYPE_LUDTS = 20
+};
+
+/* Table 2/Q.713 - SCCP parameter name codes */
+enum sccp_parameter_name_codes {
+ SCCP_PNC_END_OF_OPTIONAL = 0,
+ SCCP_PNC_DESTINATION_LOCAL_REFERENCE = 1,
+ SCCP_PNC_SOURCE_LOCAL_REFERENCE = 2,
+ SCCP_PNC_CALLED_PARTY_ADDRESS = 3,
+ SCCP_PNC_CALLING_PARTY_ADDRESS = 4,
+ SCCP_PNC_PROTOCOL_CLASS = 5,
+ SCCP_PNC_SEGMENTING = 6,
+ SCCP_PNC_RECEIVE_SEQ_NUMBER = 7,
+ SCCP_PNC_SEQUENCING = 8,
+ SCCP_PNC_CREDIT = 9,
+ SCCP_PNC_RELEASE_CAUSE = 10,
+ SCCP_PNC_RETURN_CAUSE = 11,
+ SCCP_PNC_RESET_CAUSE = 12,
+ SCCP_PNC_ERROR_CAUSE = 13,
+ SCCP_PNC_REFUSAL_CAUSE = 14,
+ SCCP_PNC_DATA = 15,
+ SCCP_PNC_SEGMENTATION = 16,
+ SCCP_PNC_HOP_COUNTER = 17,
+ SCCP_PNC_IMPORTANCE = 18,
+ SCCP_PNC_LONG_DATA = 19,
+};
+
+/* Figure 3/Q.713 Called/calling party address */
+enum {
+ SCCP_TITLE_IND_NONE = 0,
+ SCCP_TITLE_IND_NATURE_ONLY = 1,
+ SCCP_TITLE_IND_TRANSLATION_ONLY = 2,
+ SCCP_TITLE_IND_TRANS_NUM_ENC = 3,
+ SCCP_TITLE_IND_TRANS_NUM_ENC_NATURE = 4,
+};
+
+enum {
+ SCCP_CALL_ROUTE_ON_SSN = 1,
+ SCCP_CALL_ROUTE_ON_GT = 0,
+};
+
+struct sccp_called_party_address {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t point_code_indicator : 1,
+ ssn_indicator : 1,
+ global_title_indicator : 4,
+ routing_indicator : 1,
+ reserved : 1;
+#elif OSMO_IS_BIG_ENDIAN
+ uint8_t reserved : 1,
+ routing_indicator : 1,
+ global_title_indicator : 4,
+ ssn_indicator : 1,
+ point_code_indicator : 1;
+#endif
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* indicator indicates presence in the above order */
+
+/* Figure 6/Q.713 */
+struct sccp_signalling_point_code {
+ uint8_t lsb;
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t msb : 6,
+ reserved : 2;
+#elif OSMO_IS_BIG_ENDIAN
+ uint8_t reserved : 2,
+ msb : 6;
+#endif
+} __attribute__((packed));
+
+/* SSN == subsystem number */
+enum sccp_subsystem_number {
+ SCCP_SSN_NOT_KNOWN_OR_USED = 0,
+ SCCP_SSN_MANAGEMENT = 1,
+ SCCP_SSN_RESERVED_ITU = 2,
+ SCCP_SSN_ISDN_USER_PART = 3,
+ SCCP_SSN_OMAP = 4, /* operation, maint and administration part */
+ SCCP_SSN_MAP = 5, /* mobile application part */
+ SCCP_SSN_HLR = 6,
+ SCCP_SSN_VLR = 7,
+ SCCP_SSN_MSC = 8,
+ SCCP_SSN_EIC = 9, /* equipent identifier centre */
+ SCCP_SSN_AUC = 10, /* authentication centre */
+ SCCP_SSN_ISDN_SUPPL_SERVICES = 11,
+ SCCP_SSN_RESERVED_INTL = 12,
+ SCCP_SSN_ISDN_EDGE_TO_EDGE = 13,
+ SCCP_SSN_TC_TEST_RESPONDER = 14,
+
+ /* From GSM 03.03 8.2 */
+ SCCP_SSN_BSSAP = 254,
+ SCCP_SSN_BSSOM = 253,
+};
+
+/* Q.713, 3.4.2.3 */
+enum {
+ SCCP_NAI_UNKNOWN = 0,
+ SCCP_NAI_SUBSCRIBER_NUMBER = 1,
+ SCCP_NAI_RESERVED_NATIONAL = 2,
+ SCCP_NAI_NATIONAL_SIGNIFICANT = 3,
+ SCCP_NAI_INTERNATIONAL = 4,
+};
+
+struct sccp_global_title {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t nature_of_addr_ind : 7,
+ odd_even : 1;
+#elif OSMO_IS_BIG_ENDIAN
+ uint8_t odd_even : 1,
+ nature_of_addr_ind : 7;
+#endif
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Q.713, 3.3 */
+struct sccp_source_reference {
+ uint8_t octet1;
+ uint8_t octet2;
+ uint8_t octet3;
+} __attribute__((packed));
+
+/* Q.714, 3.6 */
+enum sccp_protocol_class {
+ SCCP_PROTOCOL_CLASS_0 = 0,
+ SCCP_PROTOCOL_CLASS_1 = 1,
+ SCCP_PROTOCOL_CLASS_2 = 2,
+ SCCP_PROTOCOL_CLASS_3 = 3,
+};
+
+/* bits 5-8 when class0, class1 is used */
+enum sccp_protocol_options {
+ SCCP_PROTOCOL_NO_SPECIAL = 0,
+ SCCP_PROTOCOL_RETURN_MESSAGE = 8,
+};
+
+enum sccp_release_cause {
+ SCCP_RELEASE_CAUSE_END_USER_ORIGINATED = 0,
+ SCCP_RELEASE_CAUSE_END_USER_CONGESTION = 1,
+ SCCP_RELEASE_CAUSE_END_USER_FAILURE = 2,
+ SCCP_RELEASE_CAUSE_SCCP_USER_ORIGINATED = 3,
+ SCCP_RELEASE_CAUSE_REMOTE_PROCEDURE_ERROR = 4,
+ SCCP_RELEASE_CAUSE_INCONSISTENT_CONN_DATA = 5,
+ SCCP_RELEASE_CAUSE_ACCESS_FAILURE = 6,
+ SCCP_RELEASE_CAUSE_ACCESS_CONGESTION = 7,
+ SCCP_RELEASE_CAUSE_SUBSYSTEM_FAILURE = 8,
+ SCCP_RELEASE_CAUSE_SUBSYSTEM_CONGESTION = 9,
+ SCCP_RELEASE_CAUSE_MTP_FAILURE = 10,
+ SCCP_RELEASE_CAUSE_NETWORK_CONGESTION = 11,
+ SCCP_RELEASE_CAUSE_EXPIRATION_RESET = 12,
+ SCCP_RELEASE_CAUSE_EXPIRATION_INACTIVE = 13,
+ SCCP_RELEASE_CAUSE_RESERVED = 14,
+ SCCP_RELEASE_CAUSE_UNQUALIFIED = 15,
+ SCCP_RELEASE_CAUSE_SCCP_FAILURE = 16,
+};
+
+enum sccp_return_cause {
+ SCCP_RETURN_CAUSE_NO_TRANSLATION_NATURE = 0,
+ SCCP_RETURN_CAUSE_NO_TRANSLATION = 1,
+ SCCP_RETURN_CAUSE_SUBSYSTEM_CONGESTION = 2,
+ SCCP_RETURN_CAUSE_SUBSYSTEM_FAILURE = 3,
+ SCCP_RETURN_CAUSE_UNEQUIPPED_USER = 4,
+ SCCP_RETURN_CAUSE_MTP_FAILURE = 5,
+ SCCP_RETURN_CAUSE_NETWORK_CONGESTION = 6,
+ SCCP_RETURN_CAUSE_UNQUALIFIED = 7,
+ SCCP_RETURN_CAUSE_ERROR_IN_MSG_TRANSPORT = 8,
+ SCCP_RETURN_CAUSE_ERROR_IN_LOCAL_PROCESSING = 9,
+ SCCP_RETURN_CAUSE_DEST_CANNOT_PERFORM_REASSEMBLY = 10,
+ SCCP_RETURN_CAUSE_SCCP_FAILURE = 11,
+ SCCP_RETURN_CAUSE_HOP_COUNTER_VIOLATION = 12,
+ SCCP_RETURN_CAUSE_SEGMENTATION_NOT_SUPPORTED= 13,
+ SCCP_RETURN_CAUSE_SEGMENTATION_FAOLURE = 14
+};
+
+enum sccp_reset_cause {
+ SCCP_RESET_CAUSE_END_USER_ORIGINATED = 0,
+ SCCP_RESET_CAUSE_SCCP_USER_ORIGINATED = 1,
+ SCCP_RESET_CAUSE_MSG_OUT_OF_ORDER_PS = 2,
+ SCCP_RESET_CAUSE_MSG_OUT_OF_ORDER_PR = 3,
+ SCCP_RESET_CAUSE_RPC_OUT_OF_WINDOW = 4,
+ SCCP_RESET_CAUSE_RPC_INCORRECT_PS = 5,
+ SCCP_RESET_CAUSE_RPC_GENERAL = 6,
+ SCCP_RESET_CAUSE_REMOTE_END_USER_OPERATIONAL= 7,
+ SCCP_RESET_CAUSE_NETWORK_OPERATIONAL = 8,
+ SCCP_RESET_CAUSE_ACCESS_OPERATIONAL = 9,
+ SCCP_RESET_CAUSE_NETWORK_CONGESTION = 10,
+ SCCP_RESET_CAUSE_RESERVED = 11,
+};
+
+enum sccp_error_cause {
+ SCCP_ERROR_LRN_MISMATCH_UNASSIGNED = 0, /* local reference number */
+ SCCP_ERROR_LRN_MISMATCH_INCONSISTENT = 1,
+ SCCP_ERROR_POINT_CODE_MISMATCH = 2,
+ SCCP_ERROR_SERVICE_CLASS_MISMATCH = 3,
+ SCCP_ERROR_UNQUALIFIED = 4,
+};
+
+enum sccp_refusal_cause {
+ SCCP_REFUSAL_END_USER_ORIGINATED = 0,
+ SCCP_REFUSAL_END_USER_CONGESTION = 1,
+ SCCP_REFUSAL_END_USER_FAILURE = 2,
+ SCCP_REFUSAL_SCCP_USER_ORIGINATED = 3,
+ SCCP_REFUSAL_DESTINATION_ADDRESS_UKNOWN = 4,
+ SCCP_REFUSAL_DESTINATION_INACCESSIBLE = 5,
+ SCCP_REFUSAL_NET_QOS_NON_TRANSIENT = 6,
+ SCCP_REFUSAL_NET_QOS_TRANSIENT = 7,
+ SCCP_REFUSAL_ACCESS_FAILURE = 8,
+ SCCP_REFUSAL_ACCESS_CONGESTION = 9,
+ SCCP_REFUSAL_SUBSYSTEM_FAILURE = 10,
+ SCCP_REFUSAL_SUBSYTEM_CONGESTION = 11,
+ SCCP_REFUSAL_EXPIRATION = 12,
+ SCCP_REFUSAL_INCOMPATIBLE_USER_DATA = 13,
+ SCCP_REFUSAL_RESERVED = 14,
+ SCCP_REFUSAL_UNQUALIFIED = 15,
+ SCCP_REFUSAL_HOP_COUNTER_VIOLATION = 16,
+ SCCP_REFUSAL_SCCP_FAILURE = 17,
+ SCCP_REFUSAL_UNEQUIPPED_USER = 18,
+};
+
+/*
+ * messages... as of Q.713 Chapter 4
+ */
+struct sccp_connection_request {
+ /* mandantory */
+ uint8_t type;
+ struct sccp_source_reference source_local_reference;
+ uint8_t proto_class;
+
+
+ /* variable */
+ uint8_t variable_called;
+#if VARIABLE
+ called_party_address
+#endif
+
+ /* optional */
+ uint8_t optional_start;
+
+#if OPTIONAL
+ credit 3
+ callingparty var 4-n
+ data 3-130
+ hop_counter 3
+ importance 3
+ end_of_optional 1
+#endif
+
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct sccp_connection_confirm {
+ /* mandantory */
+ uint8_t type;
+ struct sccp_source_reference destination_local_reference;
+ struct sccp_source_reference source_local_reference;
+ uint8_t proto_class;
+
+ /* optional */
+ uint8_t optional_start;
+
+ /* optional */
+#if OPTIONAL
+ credit 3
+ called party 4
+ data 3-130
+ importance 3
+ end_of_optional 1
+#endif
+
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct sccp_connection_refused {
+ /* mandantory */
+ uint8_t type;
+ struct sccp_source_reference destination_local_reference;
+ uint8_t cause;
+
+ /* optional */
+ uint8_t optional_start;
+
+ /* optional */
+#if OPTIONAL
+ called party 4
+ data 3-130
+ importance 3
+ end_of_optional 1
+#endif
+
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct sccp_connection_released {
+ /* mandantory */
+ uint8_t type;
+ struct sccp_source_reference destination_local_reference;
+ struct sccp_source_reference source_local_reference;
+ uint8_t release_cause;
+
+
+ /* optional */
+ uint8_t optional_start;
+
+#if OPTIONAL
+ data 3-130
+ importance 3
+ end_of_optional 1
+#endif
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct sccp_connection_release_complete {
+ uint8_t type;
+ struct sccp_source_reference destination_local_reference;
+ struct sccp_source_reference source_local_reference;
+} __attribute__((packed));
+
+struct sccp_data_form1 {
+ /* mandantory */
+ uint8_t type;
+ struct sccp_source_reference destination_local_reference;
+ uint8_t segmenting;
+
+ /* variable */
+ uint8_t variable_start;
+
+#if VARIABLE
+ data 2-256;
+#endif
+
+ uint8_t data[0];
+} __attribute__((packed));
+
+
+struct sccp_data_unitdata {
+ /* mandantory */
+ uint8_t type;
+ uint8_t proto_class;
+
+
+ /* variable */
+ uint8_t variable_called;
+ uint8_t variable_calling;
+ uint8_t variable_data;
+
+#if VARIABLE
+ called party address
+ calling party address
+#endif
+
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct sccp_data_it {
+ /* mandantory */
+ uint8_t type;
+ struct sccp_source_reference destination_local_reference;
+ struct sccp_source_reference source_local_reference;
+ uint8_t proto_class;
+
+ uint8_t sequencing[2];
+ uint8_t credit;
+} __attribute__((packed));
+
+struct sccp_proto_err {
+ uint8_t type;
+ struct sccp_source_reference destination_local_reference;
+ uint8_t error_cause;
+};
+
+#endif
diff --git a/include/sigtran/Makefile.am b/include/sigtran/Makefile.am
new file mode 100644
index 0000000..aa0f95c
--- /dev/null
+++ b/include/sigtran/Makefile.am
@@ -0,0 +1,2 @@
+sigtran_HEADERS = m3ua_types.h xua_types.h xua_msg.h m2ua_types.h
+sigtrandir = $(includedir)/osmocom/sigtran
diff --git a/include/sigtran/m2ua_types.h b/include/sigtran/m2ua_types.h
new file mode 100644
index 0000000..7184f54
--- /dev/null
+++ b/include/sigtran/m2ua_types.h
@@ -0,0 +1,255 @@
+#ifndef m2ua_types_h
+#define m2ua_types_h
+
+/**
+ * Types found in the M2UA RFC 3331
+ */
+
+#include <stdint.h>
+
+#define M2UA_VERSION 1
+#define M2UA_SPARE 0
+
+enum {
+ M2UA_CLS_MGMT, /* Management (MGMT) Message [IUA/M2UA/M3UA/SUA] */
+ M2UA_CLS_TRANS, /* Transfer Messages [M3UA] */
+ M2UA_CLS_SSNM, /* SS7 Signalling Network Management (SSNM) Messages [M3UA/SUA] */
+ M2UA_CLS_ASPSM, /* ASP State Maintenance (ASPSM) Messages [IUA/M2UA/M3UA/SUA] */
+ M2UA_CLS_ASPTM, /* ASP Traffic Maintenance (ASPTM) Messages [IUA/M2UA/M3UA/SUA] */
+ M2UA_CLS_QPTM, /* Q.921/Q.931 Boundary Primitives Transport (QPTM) */
+ M2UA_CLS_MAUP, /* MTP2 User Adaptation (MAUP) Messages [M2UA] */
+ M2UA_CLS_SUA_LESS, /* Connectionless Messages [SUA] */
+ M2UA_CLS_SUA_CONN, /* Connection-Oriented Messages [SUA] */
+ M2UA_CLS_RKM, /* Routing Key Management (RKM) Messages (M3UA) */
+ M2UA_CLS_IIM, /* Interface Identifier Management (IIM) Messages (M2UA) */
+};
+
+/**
+ * MTP2 User Adaption = MAUP messages
+ */
+enum {
+ M2UA_MAUP_RESERVED, /* Reserved */
+ M2UA_MAUP_DATA, /* Data */
+ M2UA_MAUP_EST_REQ, /* Establish Request */
+ M2UA_MAUP_EST_CON, /* Establish Confirm */
+ M2UA_MAUP_REL_REQ, /* Release Request */
+ M2UA_MAUP_REL_CON, /* Release Confirm */
+ M2UA_MAUP_REL_IND, /* Release Indication */
+ M2UA_MAUP_STATE_REQ, /* State Request */
+ M2UA_MAUP_STATE_CON, /* State Confirm */
+ M2UA_MAUP_STATE_IND, /* State Indication */
+ M2UA_MAUP_RETR_REQ, /* Data Retrieval Request */
+ M2UA_MAUP_D_RETR_CON, /* Data Retrieval Confirm */
+ M2UA_MAUP_D_RETR_IND, /* Data Retrieval Indication */
+ M2UA_MAUP_D_RETR_COMPL, /* Data Retrieval Complete Indication */
+ M2UA_MAUP_CONG_IND, /* Congestion Indication */
+ M2UA_MAUP_DATA_ACK, /* Data Acknowledge */
+};
+
+/**
+ * Application Server Process State Maintaenance (ASPSM) messages
+ */
+enum {
+ M2UA_ASPSM_RESERVED, /* Reserved */
+ M2UA_ASPSM_UP, /* ASP Up (UP) */
+ M2UA_ASPSM_DOWN, /* ASP Down (DOWN) */
+ M2UA_ASPSM_BEAT, /* Heartbeat (BEAT) */
+ M2UA_ASPSM_UP_ACK, /* ASP Up Ack (UP ACK) */
+ M2UA_ASPSM_DOWN_ACK, /* ASP Down Ack (DOWN ACK) */
+ M2UA_ASPSM_BEAT_ACK, /* Heartbeat Ack (BEAT ACK) */
+};
+
+/**
+ * Application Server Process Traffic Maintaenance (ASPTM) messages.
+ */
+enum {
+ M2UA_ASPTM_RESERVED, /* Reserved */
+ M2UA_ASPTM_ACTIV, /* ASP Active (ACTIVE) */
+ M2UA_ASPTM_INACTIV, /* ASP Inactive (INACTIVE) */
+ M2UA_ASPTM_ACTIV_ACK, /* ASP Active Ack (ACTIVE ACK) */
+ M2UA_ASPTM_INACTIV_ACK, /* ASP Inactive Ack (INACTIVE ACK) */
+};
+
+/**
+ * Management (MGMT) messages
+ */
+enum {
+ M2UA_MGMT_ERROR, /* Error (ERR) */
+ M2UA_MGMT_NTFY, /* Notify (NTFY) */
+};
+
+/**
+ * Interface Identifier Management (IIM) Messages
+ */
+enum {
+ M2UA_IIM_RESERVED, /* Reserved */
+ M2UA_IIM_REG_REQ, /* Registration Request (REG REQ) */
+ M2UA_IIM_REG_RSP, /* Registration Response (REG RSP) */
+ M2UA_IIM_DEREG_REQ, /* Deregistration Request (DEREG REQ) */
+ M2UA_IIM_DEREG_RSP, /* Deregistration Response (DEREG RSP) */
+};
+
+/**
+ * Tag Values for M2UA
+ */
+enum {
+ __m2ua_tag_start = 767,
+
+ M2UA_TAG_DATA, /* Protocol Data 1 */
+ M2UA_TAG_DATA_TTC, /* Protocol Data 2 (TTC) */
+ M2UA_TAG_STATE_REQ, /* State Request */
+ M2UA_TAG_STATE_EVENT, /* State Event */
+ M2UA_TAG_CONG_STATUS, /* Congestion Status */
+ M2UA_TAG_DISC_STATUS, /* Discard Status */
+ M2UA_TAG_ACTION, /* Action */
+ M2UA_TAG_SEQ_NO, /* Sequence Number */
+ M2UA_TAG_RETR_RES, /* Retrieval Result */
+ M2UA_TAG_LNK_KEY, /* Link Key */
+ M2UA_TAG_L_LNK_KEY_ID, /* Local-LK-Identifier */
+ M2UA_TAG_SDT, /* Signalling Data Terminal (SDT) Identifier */
+ M2UA_TAG_SDL, /* Signalling Data Link (SDL) Identifier */
+ M2UA_TAG_REG_RES, /* Registration Result */
+ M2UA_TAG_RES_STATUS, /* Registration Status */
+ M2UA_TAG_DEREG_RES, /* De-Registration Result */
+ M2UA_TAG_DEREG_STATUS, /* De-Registration Status */
+};
+
+/**
+ * 3.3.1.5 State Request
+ */
+enum {
+ M2UA_STATUS_LPO_SET, /* Request local processor outage */
+ M2UA_STATUS_LPO_CLEAR, /* Request local processor outage recovered */
+ M2UA_STATUS_EMER_SET, /* Request emergency alignment */
+ M2UA_STATUS_EMER_CLEAR, /* Request normal alignment (cancel emergency) */
+ M2UA_STATUS_FLUSH_BUFFERS, /* Flush or clear receive, transmit and retransmit queues */
+ M2UA_STATUS_CONTINUE, /* Continue or Resume */
+ M2UA_STATUS_CLEAR_RTB, /* Clear the retransmit queue */
+ M2UA_STATUS_AUDIT, /* Audit state of link */
+ M2UA_STATUS_CONG_CLEAR, /* Congestion cleared */
+ M2UA_STATUS_CONG_ACCEPT, /* Congestion accept */
+ M2UA_STATUS_CONG_DISCARD, /* Congestion discard */
+};
+
+/**
+ * 3.3.1.7 State Indication
+ */
+enum {
+ __m2ua_event_dummy,
+ M2UA_EVENT_RPO_ENTER, /* Remote entered processor outage */
+ M2UA_EVENT_RPO_EXIT, /* Remote exited processor outage */
+ M2UA_EVENT_LPO_ENTER, /* Link entered processor outage */
+ M2UA_EVENT_LPO_EXIT, /* Link exited processor outage */
+};
+
+/**
+ * 3.3.1.8 Congestion Indication
+ */
+enum {
+ M2UA_LEVEL_NONE, /* No congestion */
+ M2UA_LEVEL_1, /* Congestion Level 1 */
+ M2UA_LEVEL_2, /* Congestion Level 2 */
+ M2UA_LEVEL_3, /* Congestion Level 3 */
+};
+
+/**
+ * 3.3.1.9 Retrieval Request
+ */
+enum {
+ M2UA_ACTION_RTRV_BSN, /* Retrieve the backward sequence number */
+ M2UA_ACTION_RTRV_MSGS, /* Retrieve the PDUs from the transmit and retransmit queues. */
+};
+
+/**
+ * 3.3.1.10 Retrieval Confirm
+ */
+enum {
+ M2UA_RESULT_SUCCESS, /* Action successful */
+ M2UA_RESULT_FAILURE, /* Action failed */
+};
+
+/**
+ * 3.3.2.7 ASP Active (ASPAC)
+ */
+enum {
+ M2UA_TRA_OVERRIDE = 1, /* Override */
+ M2UA_TRA_LOAD_SHARE = 2, /* Load-share */
+ M2UA_TRA_BROADCAST = 3, /* Broadcast */
+};
+
+/**
+ * 3.3.3.1 Error (ERR)
+ */
+enum {
+ __m2ua_err_unused,
+ M2UA_ERR_INV_VER, /* Invalid Version */
+ M2UA_ERR_INV_INT_IDENT, /* Invalid Interface Identifier */
+ M2UA_ERR_UNS_MSG_CLASS, /* Unsupported Message Class */
+ M2UA_ERR_UNS_MSG_TYPE, /* Unsupported Message Type */
+ M2UA_ERR_UNS_TRA_MODE, /* Unsupported Traffic Handling Mode */
+ M2UA_ERR_UNE_MSG, /* Unexpected Message */
+ M2UA_ERR_PROTO_ERROR, /* Protocol Error */
+ M2UA_ERR_UNS_INT_IDENT_T, /* Unsupported Interface Identifier Type */
+ M2UA_ERR_INV_STR_IDENT, /* Invalid Stream Identifier */
+ M2UA_ERR_UNUSED1, /* Unused in M2UA */
+ M2UA_ERR_UNUSED2, /* Unused in M2UA */
+ M2UA_ERR_UNUSED3, /* Unused in M2UA */
+ M2UA_ERR_REFUSED, /* Refused - Management Blocking */
+ M2UA_ERR_ASP_IDENT_REQ, /* ASP Identifier Required */
+ M2UA_ERR_INV_ASP_IDENT, /* Invalid ASP Identifier */
+ M2UA_ERR_ASP_ACT_FOR_IDENT, /* ASP Active for Interface Identifier(s) */
+ M2UA_ERR_INV_PARAM_VAL, /* Invalid Parameter Value */
+ M2UA_ERR_PARAM_FIELD_ERR, /* Parameter Field Error */
+ M2UA_ERR_UNEXP_PARAM, /* Unexpected Parameter */
+ M2UA_ERR_UNUSED4, /* Unused in M2UA */
+ M2UA_ERR_UNUSED5, /* Unused in M2UA */
+ M2UA_ERR_MISSING_PARAM, /* Missing Parameter */
+};
+
+/**
+ * 3.3.3.2 Notify (NTFY)
+ */
+enum {
+ M2UA_STP_AS_STATE_CHG = 1, /* Application Server state change (AS_State_Change) */
+ M2UA_STP_OTHER = 2, /* Other */
+};
+
+enum {
+ /* this is for M2UA_STP_AS_STATE_CHG */
+ M2UA_STP_AS_INACTIVE = 2, /* Application Server Inactive (AS_Inactive) */
+ M2UA_STP_AS_ACTIVE = 3, /* Application Server Active (AS_Active) */
+ M2UA_STP_AS_PENDING = 4, /* Application Server Pending (AS_Pending) */
+
+ /* this is for the other */
+ M2UA_STP_O_INSUFF_ASP_RES = 1, /* Insufficient ASP resources active in AS */
+ M2UA_STP_O_ALT_ASP_ACTIVR = 2, /* Alternate ASP Active */
+ M2UA_STP_O_ASP_FAILURE = 3, /* ASP Failure */
+};
+
+/**
+ * 3.3.4.3 Registration Response (REG RSP)
+ */
+enum {
+ M2UA_REG_SUCC, /* Successfully Registered */
+ M2UA_REG_ERR_UNK, /* Error - Unknown */
+ M2UA_REG_ERR_INV_SDLI, /* Error - Invalid SDLI */
+ M2UA_REG_ERR_INV_SDTI, /* Error - Invalid SDTI */
+ M2UA_REG_ERR_INV_LNK_KEY, /* Error - Invalid Link Key */
+ M2UA_REG_ERR_PERM_DENIED, /* Error - Permission Denied */
+ M2UA_REG_ERR_OVERLAP_KEY, /* Error - Overlapping (Non-unique) Link Key */
+ M2UA_REG_ERR_LNK_KEY_NOT_PROV, /* Error - Link Key not Provisioned */
+ M2UA_REG_ERR_INSUFF_RES, /* Error - Insufficient Resources */
+};
+
+/**
+ * 3.3.4.4 De-Registration Response (DEREG RSP)
+ */
+enum {
+ M2UA_DEREG_SUCC, /* Successfully De-registered */
+ M2UA_DEREG_ERR_UNK, /* Error - Unknown */
+ M2UA_DEREG_ERR_INV_IDENT, /* Error - Invalid Interface Identifier */
+ M2UA_DEREG_ERR_PERM_DENIED, /* Error - Permission Denied */
+ M2UA_DEREG_ERR_NOT_REG, /* Error - Not Registered */
+};
+
+#endif
diff --git a/include/sigtran/m3ua_types.h b/include/sigtran/m3ua_types.h
new file mode 100644
index 0000000..c8e62b4
--- /dev/null
+++ b/include/sigtran/m3ua_types.h
@@ -0,0 +1,128 @@
+#pragma once
+
+/**
+ * Types found in the M3UA RFC 4666
+ */
+
+#include <stdint.h>
+
+
+#define M3UA_VERSION 1
+
+enum {
+ M3UA_CLS_MGMT, /* Management (MGMT) Message [IUA/M2UA/M3UA/SUA] */
+ M3UA_CLS_TRANS, /* Transfer Messages [M3UA] */
+ M3UA_CLS_SSNM, /* SS7 Signalling Network Management (SSNM) Messages [M3UA/SUA] */
+ M3UA_CLS_ASPSM, /* ASP State Maintenance (ASPSM) Messages [IUA/M2UA/M3UA/SUA] */
+ M3UA_CLS_ASPTM, /* ASP Traffic Maintenance (ASPTM) Messages [IUA/M2UA/M3UA/SUA] */
+ M3UA_CLS_RESERVED1, /* Reserved for Other SIGTRAN Adaptation Layers */
+ M3UA_CLS_RESERVED2, /* Reserved for Other SIGTRAN Adaptation Layers */
+ M3UA_CLS_RESERVED3, /* Reserved for Other SIGTRAN Adaptation Layers */
+ M3UA_CLS_RESERVED4, /* Reserved for Other SIGTRAN Adaptation Layers */
+ M3UA_CLS_RKM, /* Routing Key Management (RKM) Messages (M3UA) */
+};
+
+/**
+ * Management (MGMT) messages
+ */
+enum {
+ M3UA_MGMT_ERROR, /* Error (ERR) */
+ M3UA_MGMT_NTFY, /* Notify (NTFY) */
+};
+
+/**
+ * Transfer Messages
+ */
+enum {
+ M3UA_TRANS_RESERVED, /* Reserved */
+ M3UA_TRANS_DATA, /* Payload Data (DATA) */
+};
+
+/**
+ * SS7 Signalling Network Management (SSNM) Messages
+ */
+enum {
+ M3UA_SSNM_RESERVED, /* Reserved */
+ M3UA_SSNM_DUNA, /* Destination Unavailable (DUNA) */
+ M3UA_SSNM_DAVA, /* Destination Available (DAVA) */
+ M3UA_SSNM_DAUD, /* Destination State Audit (DAUD) */
+ M3UA_SSNM_SCON, /* Signalling Congestion (SCON) */
+ M3UA_SSNM_DUPU, /* Destination User Part Unavailable (DUPU) */
+ M3UA_SSNM_DRST, /* Destination Restricted (DRST) */
+};
+
+/**
+ * Application Server Process State Maintaenance (ASPSM) messages
+ */
+enum {
+ M3UA_ASPSM_RESERVED, /* Reserved */
+ M3UA_ASPSM_UP, /* ASP Up (UP) */
+ M3UA_ASPSM_DOWN, /* ASP Down (DOWN) */
+ M3UA_ASPSM_BEAT, /* Heartbeat (BEAT) */
+ M3UA_ASPSM_UP_ACK, /* ASP Up Ack (UP ACK) */
+ M3UA_ASPSM_DOWN_ACK, /* ASP Down Ack (DOWN ACK) */
+ M3UA_ASPSM_BEAT_ACK, /* Heartbeat Ack (BEAT ACK) */
+};
+
+/**
+ * Application Server Process Traffic Maintaenance (ASPTM) messages.
+ */
+enum {
+ M3UA_ASPTM_RESERVED, /* Reserved */
+ M3UA_ASPTM_ACTIV, /* ASP Active (ACTIVE) */
+ M3UA_ASPTM_INACTIV, /* ASP Inactive (INACTIVE) */
+ M3UA_ASPTM_ACTIV_ACK, /* ASP Active Ack (ACTIVE ACK) */
+ M3UA_ASPTM_INACTIV_ACK, /* ASP Inactive Ack (INACTIVE ACK) */
+};
+
+/**
+ * Routing Key Management (RKM) Messages
+ */
+enum {
+ M3UA_RKM_RESERVED, /* Reserved */
+ M3UA_RKM_REG_REQ, /* Registration Request (REG REQ) */
+ M3UA_RKM_REG_RSP, /* Registration Response (REG RSP) */
+ M3UA_RKM_DEREG_REQ, /* Deregistration Request (DEREG REQ) */
+ M3UA_RKM_DEREG_RSP, /* Deregistration Response (DEREG RSP) */
+};
+
+/**
+ * Tag Values for M3UA
+ */
+enum {
+ M3UA_TAG_NET_APPEAR = 0x0200, /* Network Appearance */
+ M3UA_TAG_RESERVED1, /* Reserved */
+ M3UA_TAG_RESERVED2, /* Reserved */
+ M3UA_TAG_RESERVED3, /* Reserved */
+ M3UA_TAG_USER_CAUSE, /* User/Cause */
+ M3UA_TAG_CONGEST_IND, /* Congestion Indications */
+ M3UA_TAG_CONCERN_DEST, /* Concerned Destination */
+ M3UA_TAG_ROUTING_KEY, /* Routing Key */
+ M3UA_TAG_REG_RESULT, /* Registration Result */
+ M3UA_TAG_DEREG_RESULT, /* Deregistration Result */
+ M3UA_TAG_LOCAL_ROUT_KEY_IDENT, /* Local Routing Key Identifier */
+ M3UA_TAG_DEST_PC, /* Destination Point Code */
+ M3UA_TAG_SERV_IND, /* Service Indicators */
+ M3UA_TAG_RESERVED4, /* Reserved */
+ M3UA_TAG_ORIG_PC_LIST, /* Originating Point Code List */
+ M3UA_TAG_RESERVED5, /* Reserved */
+ M3UA_TAG_PROTO_DATA, /* Protocol Data */
+ M3UA_TAG_RESERVED6, /* Reserved */
+ M3UA_TAG_REG_STATUS, /* Registration Status */
+ M3UA_TAG_DEREG_STATUS, /* Deregistration Status */
+};
+
+
+/**
+ * Protocol data for transport messages. This is
+ * replacing the MTP L3 header
+ */
+struct m3ua_protocol_data {
+ uint32_t opc;
+ uint32_t dpc;
+ uint8_t si;
+ uint8_t ni;
+ uint8_t mp;
+ uint8_t sls;
+ uint8_t data[0];
+} __attribute__((packed));
diff --git a/include/sigtran/xua_msg.h b/include/sigtran/xua_msg.h
new file mode 100644
index 0000000..9cc8632
--- /dev/null
+++ b/include/sigtran/xua_msg.h
@@ -0,0 +1,53 @@
+/* Routines for generating and parsing messages */
+/* (C) 2011 by Holger Hans Peter Freyther <zecke@selfish.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "xua_types.h"
+
+#include <osmocom/core/linuxlist.h>
+
+struct msgb;
+
+struct xua_msg {
+ struct xua_common_hdr hdr;
+
+ struct llist_head headers;
+};
+
+struct xua_msg_part {
+ struct llist_head entry;
+
+ uint16_t tag;
+ uint16_t len;
+ uint8_t *dat;
+
+ /* TODO: keep small data in the struct for perf reasons */
+};
+
+
+struct xua_msg *xua_msg_alloc(void);
+void xua_msg_free(struct xua_msg *msg);
+
+int xua_msg_add_data(struct xua_msg *msg, uint16_t tag, uint16_t len, uint8_t *dat);
+
+struct xua_msg_part *xua_msg_find_tag(struct xua_msg *msg, uint16_t tag);
+
+struct xua_msg *xua_from_msg(const int version, uint16_t len, uint8_t *data);
+struct msgb *xua_to_msg(const int version, struct xua_msg *msg);
+
+void xua_set_log_area(int log_area);
diff --git a/include/sigtran/xua_types.h b/include/sigtran/xua_types.h
new file mode 100644
index 0000000..6568283
--- /dev/null
+++ b/include/sigtran/xua_types.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <stdint.h>
+
+/**
+ * Common tag values used by all user adaption layers
+ */
+enum {
+ MUA_TAG_RESERVED, /* Reserved */
+ MUA_TAG_IDENT_INT, /* Interface Identifier (Integer) (M2UA) */
+ MUA_TAG_UNUSED1, /* Unused */
+ MUA_TAG_IDENT_TEXT, /* Interface Identifier (Text) (M2UA) */
+ MUA_TAG_INFO, /* Info String */
+ MUA_TAG_UNUSED2, /* Unused */
+ MUA_TAG_ROUTING_CTX, /* Routing Context (M3UA) */
+ MUA_TAG_DIAG_INF, /* Diagnostic Information */
+ MUA_TAG_IDENT_RANGE, /* Interface Identifier (Integer Range) */
+ MUA_TAG_BEAT_DATA, /* Heartbeat Data */
+ MUA_TAG_UNUSED4, /* Unused */
+ MUA_TAG_TRA_MODE, /* Traffic Mode Type */
+ MUA_TAG_ERR_CODE, /* Error Code */
+ MUA_TAG_STATUS, /* Status Type/Information */
+ MUA_TAG_UNUSED5, /* Unused */
+ MUA_TAG_UNUSED6, /* Unused */
+ MUA_TAG_UNUSED7, /* Unused */
+ MUA_TAG_ASP_IDENT, /* ASP Identifier */
+ MUA_TAG_AFF_PC, /* Affected Point Code (M3UA) */
+ MUA_TAG_CORREL_ID, /* Correlation Id */
+};
+
+struct xua_common_hdr {
+ uint8_t version;
+ uint8_t spare;
+ uint8_t msg_class;
+ uint8_t msg_type;
+ uint32_t msg_length;
+ uint8_t data[0];
+} __attribute__((packed));
+
+
+struct xua_parameter_hdr {
+ uint16_t tag;
+ uint16_t len;
+ uint8_t data[0];
+} __attribute__((packed));
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/libosmo-sccp.pc.in b/libosmo-sccp.pc.in
new file mode 100644
index 0000000..eda8d49
--- /dev/null
+++ b/libosmo-sccp.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: OpenBSC SCCP Lib
+Description: OpenBSC SCCP Lib
+Version: @VERSION@
+Libs: -L${libdir} -lsccp
+Cflags: -I${includedir}/
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..fa47e85
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,9 @@
+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
+AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
+
+sccpdir = $(libdir)
+sccp_LIBRARIES = libsccp.a libmtp.a libxua.a
+
+libsccp_a_SOURCES = sccp.c
+libmtp_a_SOURCES = mtp_pcap.c
+libxua_a_SOURCES = xua_msg.c
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/src/sccp.c b/src/sccp.c
new file mode 100644
index 0000000..f533edf
--- /dev/null
+++ b/src/sccp.c
@@ -0,0 +1,1469 @@
+/*
+ * SCCP management code
+ *
+ * (C) 2009, 2010, 2013 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009, 2010, 2013 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 <string.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/gsm/tlv.h>
+
+#include <sccp/sccp.h>
+
+// Unassigned debug area
+static int DSCCP = 0;
+
+static void *tall_sccp_ctx;
+static LLIST_HEAD(sccp_connections);
+
+#define SCCP_MSG_SIZE 4096
+#define SCCP_MSG_HEADROOM 128
+
+/* global data */
+const struct sockaddr_sccp sccp_ssn_bssap = {
+ .sccp_family = 0,
+ .sccp_ssn = SCCP_SSN_BSSAP,
+};
+
+struct sccp_system {
+ /* layer3 -> layer2 */
+ void (*write_data)(struct sccp_connection *conn, struct msgb *data,
+ void *gctx, void *ctx);
+ void *write_context;
+};
+
+
+static struct sccp_system sccp_system = {
+ .write_data = NULL,
+};
+
+struct sccp_data_callback {
+ /* connection based */
+ int (*accept_cb)(struct sccp_connection *, void *);
+ void *accept_context;
+
+ /* connection less */
+ int (*read_cb)(struct msgb *, unsigned int, void *);
+ void *read_context;
+
+ uint8_t ssn;
+ struct llist_head callback;
+};
+
+static LLIST_HEAD(sccp_callbacks);
+
+static struct sccp_data_callback *_find_ssn(uint8_t ssn)
+{
+ struct sccp_data_callback *cb;
+
+ llist_for_each_entry(cb, &sccp_callbacks, callback) {
+ if (cb->ssn == ssn)
+ return cb;
+ }
+
+ /* need to add one */
+ cb = talloc_zero(tall_sccp_ctx, struct sccp_data_callback);
+ if (!cb) {
+ LOGP(DSCCP, LOGL_ERROR, "Failed to allocate sccp callback.\n");
+ return NULL;
+ }
+
+ cb->ssn = ssn;
+ llist_add_tail(&cb->callback, &sccp_callbacks);
+ return cb;
+}
+
+
+static void _send_msg(struct sccp_connection *conn, struct msgb *msg, void *ctx)
+{
+ sccp_system.write_data(conn, msg, sccp_system.write_context, ctx);
+}
+
+/*
+ * parsing routines
+ */
+static int copy_address(struct sccp_address *addr, uint8_t offset, struct msgb *msgb)
+{
+ struct sccp_called_party_address *party;
+
+ int room = msgb_l2len(msgb) - offset;
+ uint8_t read = 0;
+ uint8_t length;
+
+ if (room <= 0) {
+ LOGP(DSCCP, LOGL_ERROR, "Not enough room for an address: %u\n", room);
+ return -1;
+ }
+
+ length = msgb->l2h[offset];
+ if (room <= length) {
+ LOGP(DSCCP, LOGL_ERROR, "Not enough room for optional data %u %u\n", room, length);
+ return -1;
+ }
+
+
+ party = (struct sccp_called_party_address *)(msgb->l2h + offset + 1);
+ if (party->point_code_indicator) {
+ if (length <= read + 2) {
+ LOGP(DSCCP, LOGL_ERROR, "POI does not fit %u\n", length);
+ return -1;
+ }
+
+
+ memcpy(&addr->poi, &party->data[read], 2);
+ read += 2;
+ }
+
+ if (party->ssn_indicator) {
+ if (length <= read + 1) {
+ LOGP(DSCCP, LOGL_ERROR, "SSN does not fit %u\n", length);
+ return -1;
+ }
+
+ addr->ssn = party->data[read];
+ read += 1;
+ }
+
+ /* copy the GTI over */
+ if (party->global_title_indicator) {
+ addr->gti_len = length - read - 1;
+ addr->gti_data = &party->data[read];
+ }
+
+ addr->address = *party;
+ return 0;
+}
+
+static int _sccp_parse_optional_data(const int offset,
+ struct msgb *msgb, struct sccp_optional_data *data)
+{
+ uint16_t room = msgb_l2len(msgb) - offset;
+ uint16_t read = 0;
+
+ while (room > read) {
+ uint8_t type = msgb->l2h[offset + read];
+ if (type == SCCP_PNC_END_OF_OPTIONAL)
+ return 0;
+
+ if (read + 1 >= room) {
+ LOGP(DSCCP, LOGL_ERROR, "no place for length\n");
+ return 0;
+ }
+
+ uint8_t length = msgb->l2h[offset + read + 1];
+ read += 2 + length;
+
+
+ if (room <= read) {
+ LOGP(DSCCP, LOGL_ERROR,
+ "no space for the data: type: %d read: %d room: %d l2: %d\n",
+ type, read, room, msgb_l2len(msgb));
+ return 0;
+ }
+
+ if (type == SCCP_PNC_DATA) {
+ data->data_len = length;
+ data->data_start = offset + read - length;
+ }
+
+ }
+
+ return -1;
+}
+
+int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result *result)
+{
+ static const uint32_t header_size =
+ sizeof(struct sccp_connection_request);
+ static const uint32_t optional_offset =
+ offsetof(struct sccp_connection_request, optional_start);
+ static const uint32_t called_offset =
+ offsetof(struct sccp_connection_request, variable_called);
+
+ struct sccp_connection_request *req = (struct sccp_connection_request *)msgb->l2h;
+ struct sccp_optional_data optional_data;
+
+ /* header check */
+ if (msgb_l2len(msgb) < header_size) {
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ /* copy out the calling and called address. Add the offset */
+ if (copy_address(&result->called, called_offset + req->variable_called, msgb) != 0)
+ return -1;
+
+ result->source_local_reference = &req->source_local_reference;
+
+ /*
+ * parse optional data.
+ */
+ memset(&optional_data, 0, sizeof(optional_data));
+ if (_sccp_parse_optional_data(optional_offset + req->optional_start, msgb, &optional_data) != 0) {
+ LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n");
+ return -1;
+ }
+
+ if (optional_data.data_len != 0) {
+ msgb->l3h = &msgb->l2h[optional_data.data_start];
+ result->data_len = optional_data.data_len;
+ } else {
+ result->data_len = 0;
+ }
+
+ return 0;
+}
+
+int _sccp_parse_connection_released(struct msgb *msgb, struct sccp_parse_result *result)
+{
+ static size_t header_size = sizeof(struct sccp_connection_released);
+ static size_t optional_offset = offsetof(struct sccp_connection_released, optional_start);
+
+ struct sccp_optional_data optional_data;
+ struct sccp_connection_released *rls = (struct sccp_connection_released *) msgb->l2h;
+
+ /* we don't have enough size for the struct */
+ if (msgb_l2len(msgb) < header_size) {
+ LOGP(DSCCP, LOGL_ERROR, "msgb > header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ memset(&optional_data, 0, sizeof(optional_data));
+ if (_sccp_parse_optional_data(optional_offset + rls->optional_start, msgb, &optional_data) != 0) {
+ LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n");
+ return -1;
+ }
+
+ result->source_local_reference = &rls->source_local_reference;
+ result->destination_local_reference = &rls->destination_local_reference;
+
+ if (optional_data.data_len != 0) {
+ msgb->l3h = &msgb->l2h[optional_data.data_start];
+ result->data_len = optional_data.data_len;
+ } else {
+ result->data_len = 0;
+ }
+
+ return 0;
+}
+
+int _sccp_parse_connection_refused(struct msgb *msgb, struct sccp_parse_result *result)
+{
+ static const uint32_t header_size =
+ sizeof(struct sccp_connection_refused);
+ static int optional_offset = offsetof(struct sccp_connection_refused, optional_start);
+
+ struct sccp_optional_data optional_data;
+ struct sccp_connection_refused *ref;
+
+ /* header check */
+ if (msgb_l2len(msgb) < header_size) {
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ ref = (struct sccp_connection_refused *) msgb->l2h;
+
+ result->destination_local_reference = &ref->destination_local_reference;
+
+ memset(&optional_data, 0, sizeof(optional_data));
+ if (_sccp_parse_optional_data(optional_offset + ref->optional_start, msgb, &optional_data) != 0) {
+ LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n");
+ return -1;
+ }
+
+ /* optional data */
+ if (optional_data.data_len != 0) {
+ msgb->l3h = &msgb->l2h[optional_data.data_start];
+ result->data_len = optional_data.data_len;
+ } else {
+ result->data_len = 0;
+ }
+
+ return 0;
+}
+
+int _sccp_parse_connection_confirm(struct msgb *msgb, struct sccp_parse_result *result)
+{
+ static uint32_t header_size =
+ sizeof(struct sccp_connection_confirm);
+ static const uint32_t optional_offset =
+ offsetof(struct sccp_connection_confirm, optional_start);
+
+ struct sccp_optional_data optional_data;
+ struct sccp_connection_confirm *con;
+
+ /* header check */
+ if (msgb_l2len(msgb) < header_size) {
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ con = (struct sccp_connection_confirm *) msgb->l2h;
+ result->destination_local_reference = &con->destination_local_reference;
+ result->source_local_reference = &con->source_local_reference;
+
+ memset(&optional_data, 0, sizeof(optional_data));
+ if (_sccp_parse_optional_data(optional_offset + con->optional_start, msgb, &optional_data) != 0) {
+ LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n");
+ return -1;
+ }
+
+ if (optional_data.data_len != 0) {
+ msgb->l3h = &msgb->l2h[optional_data.data_start];
+ result->data_len = optional_data.data_len;
+ } else {
+ result->data_len = 0;
+ }
+
+ return 0;
+}
+
+int _sccp_parse_connection_release_complete(struct msgb *msgb, struct sccp_parse_result *result)
+{
+ static size_t header_size = sizeof(struct sccp_connection_release_complete);
+
+ struct sccp_connection_release_complete *cmpl;
+
+ /* header check */
+ if (msgb_l2len(msgb) < header_size) {
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ cmpl = (struct sccp_connection_release_complete *) msgb->l2h;
+ result->source_local_reference = &cmpl->source_local_reference;
+ result->destination_local_reference = &cmpl->destination_local_reference;
+
+ return 0;
+}
+
+int _sccp_parse_connection_dt1(struct msgb *msgb, struct sccp_parse_result *result)
+{
+ static size_t header_size = sizeof(struct sccp_data_form1);
+ static size_t variable_offset = offsetof(struct sccp_data_form1, variable_start);
+
+ struct sccp_data_form1 *dt1 = (struct sccp_data_form1 *)msgb->l2h;
+
+ /* we don't have enough size for the struct */
+ if (msgb_l2len(msgb) < header_size) {
+ LOGP(DSCCP, LOGL_ERROR, "msgb > header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ if (dt1->segmenting != 0) {
+ LOGP(DSCCP, LOGL_ERROR, "This packet has segmenting, not supported: %d\n", dt1->segmenting);
+ return -1;
+ }
+
+ result->destination_local_reference = &dt1->destination_local_reference;
+
+ /* some more size checks in here */
+ if (msgb_l2len(msgb) < variable_offset + dt1->variable_start + 1) {
+ LOGP(DSCCP, LOGL_ERROR, "Not enough space for variable start: %u %u\n",
+ msgb_l2len(msgb), dt1->variable_start);
+ return -1;
+ }
+
+ result->data_len = msgb->l2h[variable_offset + dt1->variable_start];
+ msgb->l3h = &msgb->l2h[dt1->variable_start + variable_offset + 1];
+
+ if (msgb_l3len(msgb) < result->data_len) {
+ LOGP(DSCCP, LOGL_ERROR, "Not enough room for the payload: %u %u\n",
+ msgb_l3len(msgb), result->data_len);
+ return -1;
+ }
+
+ return 0;
+}
+
+int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result)
+{
+ static const uint32_t header_size = sizeof(struct sccp_data_unitdata);
+ static const uint32_t called_offset = offsetof(struct sccp_data_unitdata, variable_called);
+ static const uint32_t calling_offset = offsetof(struct sccp_data_unitdata, variable_calling);
+ static const uint32_t data_offset = offsetof(struct sccp_data_unitdata, variable_data);
+
+ struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msgb->l2h;
+
+ if (msgb_l2len(msgb) < header_size) {
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ /* copy out the calling and called address. Add the off */
+ if (copy_address(&result->called, called_offset + udt->variable_called, msgb) != 0)
+ return -1;
+
+ if (copy_address(&result->calling, calling_offset + udt->variable_calling, msgb) != 0)
+ return -1;
+
+ /* we don't have enough size for the data */
+ if (msgb_l2len(msgb) < data_offset + udt->variable_data + 1) {
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header + offset %u %u %u\n",
+ msgb_l2len(msgb), header_size, udt->variable_data);
+ return -1;
+ }
+
+
+ msgb->l3h = &udt->data[udt->variable_data];
+ result->data_len = msgb_l3len(msgb);
+
+ if (msgb_l3len(msgb) < msgb->l3h[-1]) {
+ LOGP(DSCCP, LOGL_ERROR, "msgb is truncated is: %u should: %u\n",
+ msgb_l3len(msgb), msgb->l3h[-1]);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int _sccp_parse_it(struct msgb *msgb, struct sccp_parse_result *result)
+{
+ static const uint32_t header_size = sizeof(struct sccp_data_it);
+
+ struct sccp_data_it *it;
+
+ if (msgb_l2len(msgb) < header_size) {
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ it = (struct sccp_data_it *) msgb->l2h;
+ result->data_len = 0;
+ result->source_local_reference = &it->source_local_reference;
+ result->destination_local_reference = &it->destination_local_reference;
+ return 0;
+}
+
+static int _sccp_parse_err(struct msgb *msgb, struct sccp_parse_result *result)
+{
+ static const uint32_t header_size = sizeof(struct sccp_proto_err);
+
+ struct sccp_proto_err *err;
+
+ if (msgb_l2len(msgb) < header_size) {
+ LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
+ msgb_l2len(msgb), header_size);
+ return -1;
+ }
+
+ err = (struct sccp_proto_err *) msgb->l2h;
+ result->data_len = 0;
+ result->destination_local_reference = &err->destination_local_reference;
+ return 0;
+}
+
+int sccp_create_sccp_addr(struct msgb *msg, const struct sockaddr_sccp *sock)
+{
+ uint8_t *len, *ai, *gti;
+
+ len = msgb_put(msg, 1);
+ ai = msgb_put(msg, 1);
+
+
+ if (sock->gti)
+ ai[0] = 0 << 6 | (sock->gti_ind & 0x0f) << 2 | 1 << 1;
+ else
+ ai[0] = 1 << 6 | 1 << 1;
+
+ /* store a point code */
+ if (sock->use_poi) {
+ uint8_t *poi;
+
+ ai[0] |= 0x01;
+ poi = msgb_put(msg, 2);
+ poi[0] = sock->poi[0];
+ poi[1] = sock->poi[1];
+ }
+
+ /* copy the SSN */
+ msgb_v_put(msg, sock->sccp_ssn);
+
+ /* copy the gti if it is present */
+ gti = msgb_put(msg, sock->gti_len);
+ memcpy(gti, sock->gti, sock->gti_len);
+
+ /* update the length now */
+ len[0] = msg->tail - len - 1;
+ return len[0] + 1;
+}
+
+/*
+ * Send UDT. Currently we have a fixed address...
+ */
+struct msgb *sccp_create_udt(int class, const struct sockaddr_sccp *in,
+ const struct sockaddr_sccp *out, uint8_t *in_data, int len)
+{
+ struct sccp_data_unitdata *udt;
+ uint8_t *data;
+ int out_len, inp_len;
+
+ if (len > 256) {
+ LOGP(DSCCP, LOGL_ERROR, "The payload is too big for one udt\n");
+ return NULL;
+ }
+
+ struct msgb *msg = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp: udt");
+ if (!msg)
+ return NULL;
+
+ msg->l2h = &msg->data[0];
+ udt = (struct sccp_data_unitdata *)msgb_put(msg, sizeof(*udt));
+
+ udt->type = SCCP_MSG_TYPE_UDT;
+ udt->proto_class = class;
+
+ /* for variable data we start with a size and the data */
+ out_len = sccp_create_sccp_addr(msg, out);
+ inp_len = sccp_create_sccp_addr(msg, in);
+
+ /* update the offsets now */
+ udt->variable_called = 3;
+ udt->variable_calling = 2 + out_len;
+ udt->variable_data = 1 + out_len + inp_len;
+
+ /* copy the payload */
+ data = msgb_put(msg, 1 + len);
+ data[0] = len;
+ memcpy(&data[1], in_data, len);
+
+ return msg;
+}
+
+static int _sccp_send_data(int class, const struct sockaddr_sccp *in,
+ const struct sockaddr_sccp *out,
+ struct msgb *payload, void *ctx)
+{
+ struct msgb *msg;
+
+ msg = sccp_create_udt(class, in, out, payload->l3h, msgb_l3len(payload));
+ if (!msg)
+ return -1;
+
+ _send_msg(NULL, msg, ctx);
+ return 0;
+}
+
+static int _sccp_handle_read(struct msgb *msgb)
+{
+ struct sccp_data_callback *cb;
+ struct sccp_parse_result result;
+
+ if (_sccp_parse_udt(msgb, &result) != 0)
+ return -1;
+
+ cb = _find_ssn(result.called.ssn);
+ if (!cb || !cb->read_cb) {
+ LOGP(DSCCP, LOGL_ERROR, "No routing for UDT for called SSN: %u\n", result.called.ssn);
+ return -1;
+ }
+
+ /* sanity check */
+ return cb->read_cb(msgb, msgb_l3len(msgb), cb->read_context);
+}
+
+/*
+ * handle connection orientated methods
+ */
+static int source_local_reference_is_free(struct sccp_source_reference *reference)
+{
+ struct sccp_connection *connection;
+
+ llist_for_each_entry(connection, &sccp_connections, list) {
+ if (memcmp(reference, &connection->source_local_reference, sizeof(*reference)) == 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int destination_local_reference_is_free(struct sccp_source_reference *reference)
+{
+ struct sccp_connection *connection;
+
+ llist_for_each_entry(connection, &sccp_connections, list) {
+ if (memcmp(reference, &connection->destination_local_reference, sizeof(*reference)) == 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int assign_source_local_reference(struct sccp_connection *connection)
+{
+ static uint32_t last_ref = 0x30000;
+ int wrapped = 0;
+
+ do {
+ struct sccp_source_reference reference;
+ reference.octet1 = (last_ref >> 0) & 0xff;
+ reference.octet2 = (last_ref >> 8) & 0xff;
+ reference.octet3 = (last_ref >> 16) & 0xff;
+
+ ++last_ref;
+ /* do not use the reversed word and wrap around */
+ if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) {
+ LOGP(DSCCP, LOGL_DEBUG, "Wrapped searching for a free code\n");
+ last_ref = 0;
+ ++wrapped;
+ }
+
+ if (source_local_reference_is_free(&reference) == 0) {
+ connection->source_local_reference = reference;
+ return 0;
+ }
+ } while (wrapped != 2);
+
+ LOGP(DSCCP, LOGL_ERROR, "Finding a free reference failed\n");
+ return -1;
+}
+
+static void _sccp_set_connection_state(struct sccp_connection *connection, int new_state)
+{
+ int old_state = connection->connection_state;
+
+ connection->connection_state = new_state;
+ if (connection->state_cb)
+ connection->state_cb(connection, old_state);
+}
+
+struct msgb *sccp_create_refuse(struct sccp_source_reference *src_ref, int cause, uint8_t *inp, int length)
+{
+ struct msgb *msgb;
+ struct sccp_connection_refused *ref;
+ uint8_t *data;
+
+ msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp ref");
+ if (!msgb) {
+ LOGP(DSCCP, LOGL_ERROR, "Failed to allocate refusal msg.\n");
+ return NULL;
+ }
+
+ msgb->l2h = &msgb->data[0];
+
+ ref = (struct sccp_connection_refused *) msgb_put(msgb, sizeof(*ref));
+ ref->type = SCCP_MSG_TYPE_CREF;
+ memcpy(&ref->destination_local_reference, src_ref,
+ sizeof(struct sccp_source_reference));
+ ref->cause = cause;
+ ref->optional_start = 1;
+
+ if (inp) {
+ data = msgb_put(msgb, 1 + 1 + length);
+ data[0] = SCCP_PNC_DATA;
+ data[1] = length;
+ memcpy(&data[2], inp, length);
+ }
+
+ data = msgb_put(msgb, 1);
+ data[0] = SCCP_PNC_END_OF_OPTIONAL;
+ return msgb;
+}
+
+static int _sccp_send_refuse(struct sccp_source_reference *src_ref, int cause, void *ctx)
+{
+ struct msgb *msgb = sccp_create_refuse(src_ref, cause, NULL, 0);
+ if (!msgb)
+ return -1;
+
+ _send_msg(NULL, msgb, ctx);
+ return 0;
+}
+
+struct msgb *sccp_create_cc(struct sccp_source_reference *src_ref,
+ struct sccp_source_reference *dst_ref)
+{
+ struct msgb *response;
+ struct sccp_connection_confirm *confirm;
+ uint8_t *optional_data;
+
+ response = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp confirm");
+ if (!response) {
+ LOGP(DSCCP, LOGL_ERROR, "Failed to create SCCP Confirm.\n");
+ return NULL;
+ }
+
+ response->l2h = &response->data[0];
+
+ confirm = (struct sccp_connection_confirm *) msgb_put(response, sizeof(*confirm));
+
+ confirm->type = SCCP_MSG_TYPE_CC;
+ memcpy(&confirm->destination_local_reference,
+ dst_ref, sizeof(*dst_ref));
+ memcpy(&confirm->source_local_reference,
+ src_ref, sizeof(*src_ref));
+ confirm->proto_class = 2;
+ confirm->optional_start = 1;
+
+ optional_data = (uint8_t *) msgb_put(response, 1);
+ optional_data[0] = SCCP_PNC_END_OF_OPTIONAL;
+ return response;
+}
+
+static int _sccp_send_connection_confirm(struct sccp_connection *connection)
+{
+ struct msgb *response;
+
+ if (assign_source_local_reference(connection) != 0)
+ return -1;
+
+ response = sccp_create_cc(&connection->source_local_reference,
+ &connection->destination_local_reference);
+ if (!response)
+ return -1;
+
+ _send_msg(connection, response, NULL);
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_ESTABLISHED);
+ return 0;
+}
+
+struct msgb *sccp_create_cr(const struct sccp_source_reference *src_ref,
+ const struct sockaddr_sccp *called,
+ const uint8_t *l3_data, size_t l3_length)
+{
+ struct msgb *request;
+ struct sccp_connection_request *req;
+ uint8_t *data;
+ uint8_t extra_size = 3 + 1;
+ int called_len;
+
+ if (l3_data && (l3_length < 3 || l3_length > 130)) {
+ LOGP(DSCCP, LOGL_ERROR, "Invalid amount of data... %zu\n", l3_length);
+ return NULL;
+ }
+
+ if (l3_data)
+ extra_size += 2 + l3_length;
+ request = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp connection request");
+ request->l2h = &request->data[0];
+ req = (struct sccp_connection_request *) msgb_put(request, sizeof(*req));
+
+ req->type = SCCP_MSG_TYPE_CR;
+ memcpy(&req->source_local_reference, src_ref, sizeof(*src_ref));
+ req->proto_class = 2;
+
+ /* write the called party address */
+ called_len = sccp_create_sccp_addr(request, called);
+
+ /* update the offsets */
+ req->variable_called = 2;
+ req->optional_start = 1 + called_len;
+
+ /* write the payload */
+ if (l3_data) {
+ data = msgb_put(request, 2 + l3_length);
+ data[0] = SCCP_PNC_DATA;
+ data[1] = l3_length;
+ memcpy(&data[2], l3_data, l3_length);
+ }
+
+ data = msgb_put(request, 1);
+ data[0] = SCCP_PNC_END_OF_OPTIONAL;
+
+ return request;
+}
+
+static int _sccp_send_connection_request(struct sccp_connection *connection,
+ const struct sockaddr_sccp *called, struct msgb *msg)
+{
+ struct msgb *request;
+
+ /* try to find an id */
+ if (assign_source_local_reference(connection) != 0) {
+ LOGP(DSCCP, LOGL_ERROR, "Assigning a local reference failed.\n");
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_SETUP_ERROR);
+ return -1;
+ }
+
+ request = sccp_create_cr(&connection->source_local_reference, called,
+ msg ? msg->l3h : NULL,
+ msg ? msgb_l3len(msg) : 0);
+ if (!request) {
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_SETUP_ERROR);
+ return -1;
+ }
+
+ llist_add_tail(&connection->list, &sccp_connections);
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REQUEST);
+
+ _send_msg(connection, request, NULL);
+ return 0;
+}
+
+struct msgb *sccp_create_dt1(struct sccp_source_reference *dst_ref, uint8_t *inp_data, uint8_t len)
+{
+ struct msgb *msgb;
+ struct sccp_data_form1 *dt1;
+ uint8_t *data;
+
+ msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp dt1");
+ if (!msgb) {
+ LOGP(DSCCP, LOGL_ERROR, "Failed to create DT1 msg.\n");
+ return NULL;
+ }
+
+ msgb->l2h = &msgb->data[0];
+
+ dt1 = (struct sccp_data_form1 *) msgb_put(msgb, sizeof(*dt1));
+ dt1->type = SCCP_MSG_TYPE_DT1;
+ memcpy(&dt1->destination_local_reference, dst_ref,
+ sizeof(struct sccp_source_reference));
+ dt1->segmenting = 0;
+
+ /* copy the data */
+ dt1->variable_start = 1;
+ data = msgb_put(msgb, 1 + len);
+ data[0] = len;
+ memcpy(&data[1], inp_data, len);
+
+ return msgb;
+}
+
+static int _sccp_send_connection_data(struct sccp_connection *conn, struct msgb *_data)
+{
+ struct msgb *msgb;
+
+ if (msgb_l3len(_data) < 2 || msgb_l3len(_data) > 256) {
+ LOGP(DSCCP, LOGL_ERROR, "data size too big, segmenting unimplemented.\n");
+ return -1;
+ }
+
+ msgb = sccp_create_dt1(&conn->destination_local_reference,
+ _data->l3h, msgb_l3len(_data));
+ if (!msgb)
+ return -1;
+
+ _send_msg(conn, msgb, NULL);
+ return 0;
+}
+
+static int _sccp_send_connection_it(struct sccp_connection *conn)
+{
+ struct msgb *msgb;
+ struct sccp_data_it *it;
+
+ msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp it");
+ msgb->l2h = &msgb->data[0];
+ it = (struct sccp_data_it *) msgb_put(msgb, sizeof(*it));
+ it->type = SCCP_MSG_TYPE_IT;
+ memcpy(&it->destination_local_reference, &conn->destination_local_reference,
+ sizeof(struct sccp_source_reference));
+ memcpy(&it->source_local_reference, &conn->source_local_reference,
+ sizeof(struct sccp_source_reference));
+
+ it->proto_class = 0x2;
+ it->sequencing[0] = it->sequencing[1] = 0;
+ it->credit = 0;
+
+ _send_msg(conn, msgb, NULL);
+ return 0;
+}
+
+struct msgb *sccp_create_rlsd(struct sccp_source_reference *src_ref,
+ struct sccp_source_reference *dst_ref, int cause)
+{
+ struct msgb *msg;
+ struct sccp_connection_released *rel;
+ uint8_t *data;
+
+ msg = msgb_alloc_headroom(SCCP_MSG_SIZE, SCCP_MSG_HEADROOM,
+ "sccp: connection released");
+ if (!msg) {
+ LOGP(DSCCP, LOGL_ERROR, "Failed to allocate RLSD.\n");
+ return NULL;
+ }
+
+ msg->l2h = &msg->data[0];
+ rel = (struct sccp_connection_released *) msgb_put(msg, sizeof(*rel));
+ rel->type = SCCP_MSG_TYPE_RLSD;
+ rel->release_cause = cause;
+
+ /* copy the source references */
+ memcpy(&rel->destination_local_reference, dst_ref,
+ sizeof(struct sccp_source_reference));
+ memcpy(&rel->source_local_reference, src_ref,
+ sizeof(struct sccp_source_reference));
+
+ data = msgb_put(msg, 1);
+ data[0] = SCCP_PNC_END_OF_OPTIONAL;
+ return msg;
+}
+
+static int _sccp_send_connection_released(struct sccp_connection *conn, int cause)
+{
+ struct msgb *msg;
+
+ msg = sccp_create_rlsd(&conn->source_local_reference,
+ &conn->destination_local_reference,
+ cause);
+ if (!msg)
+ return -1;
+
+ _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_RELEASE);
+ _send_msg(conn, msg, NULL);
+ return 0;
+}
+
+/*
+ * Open a connection. The following is going to happen:
+ *
+ * - Verify the packet, e.g. that we have no other connection
+ * that id.
+ * - Ask the user if he wants to accept the connection
+ * - Try to open the connection by assigning a source local reference
+ * and sending the packet
+ */
+static int _sccp_handle_connection_request(struct msgb *msgb, void *ctx)
+{
+ struct sccp_parse_result result;
+
+ struct sccp_data_callback *cb;
+ struct sccp_connection *connection;
+
+ if (_sccp_parse_connection_request(msgb, &result) != 0)
+ return -1;
+
+ cb = _find_ssn(result.called.ssn);
+ if (!cb || !cb->accept_cb) {
+ LOGP(DSCCP, LOGL_ERROR, "No routing for CR for called SSN: %u\n", result.called.ssn);
+ return -1;
+ }
+
+ /* check if the system wants this connection */
+ connection = talloc_zero(tall_sccp_ctx, struct sccp_connection);
+ if (!connection) {
+ LOGP(DSCCP, LOGL_ERROR, "Allocation failed\n");
+ return -1;
+ }
+
+ /*
+ * sanity checks:
+ * - Is the source_local_reference in any other connection?
+ * then will call accept, assign a "destination" local reference
+ * and send a connection confirm, otherwise we will send a refuseed
+ * one....
+ */
+ if (destination_local_reference_is_free(result.source_local_reference) != 0) {
+ LOGP(DSCCP, LOGL_ERROR, "Need to reject connection with existing reference\n");
+ _sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE, ctx);
+ talloc_free(connection);
+ return -1;
+ }
+
+ connection->incoming = 1;
+ connection->destination_local_reference = *result.source_local_reference;
+
+ if (cb->accept_cb(connection, cb->accept_context) != 0) {
+ _sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_END_USER_ORIGINATED, ctx);
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED);
+ talloc_free(connection);
+ return 0;
+ }
+
+
+ llist_add_tail(&connection->list, &sccp_connections);
+
+ if (_sccp_send_connection_confirm(connection) != 0) {
+ LOGP(DSCCP, LOGL_ERROR, "Sending confirm failed... no available source reference?\n");
+
+ _sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE, ctx);
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED);
+ llist_del(&connection->list);
+ talloc_free(connection);
+
+ return -1;
+ }
+
+ /*
+ * If we have data let us forward things.
+ */
+ if (result.data_len != 0 && connection->data_cb) {
+ connection->data_cb(connection, msgb, result.data_len);
+ }
+
+ return 0;
+}
+
+/* Handle the release confirmed */
+static int _sccp_handle_connection_release_complete(struct msgb *msgb)
+{
+ struct sccp_parse_result result;
+ struct sccp_connection *conn;
+
+ if (_sccp_parse_connection_release_complete(msgb, &result) != 0)
+ return -1;
+
+ /* find the connection */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ result.destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0
+ && memcmp(&conn->destination_local_reference,
+ result.source_local_reference,
+ sizeof(conn->destination_local_reference)) == 0) {
+ goto found;
+ }
+ }
+
+
+ LOGP(DSCCP, LOGL_ERROR, "Release complete of unknown connection\n");
+ return -1;
+
+found:
+ llist_del(&conn->list);
+ _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_RELEASE_COMPLETE);
+ return 0;
+}
+
+/* Handle the Data Form 1 message */
+static int _sccp_handle_connection_dt1(struct msgb *msgb)
+{
+ struct sccp_parse_result result;
+ struct sccp_connection *conn;
+
+ if (_sccp_parse_connection_dt1(msgb, &result) != 0)
+ return -1;
+
+ /* lookup if we have a connection with the given reference */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ result.destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0) {
+ goto found;
+ }
+ }
+
+ LOGP(DSCCP, LOGL_ERROR, "No connection found for dt1 data\n");
+ return -1;
+
+found:
+ conn->data_cb(conn, msgb, result.data_len);
+ return 0;
+}
+
+/* confirm a connection release */
+static int _sccp_send_connection_release_complete(struct sccp_connection *connection)
+{
+ struct msgb *msgb;
+ struct sccp_connection_release_complete *rlc;
+
+ msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
+ SCCP_MSG_HEADROOM, "sccp rlc");
+ msgb->l2h = &msgb->data[0];
+
+ rlc = (struct sccp_connection_release_complete *) msgb_put(msgb, sizeof(*rlc));
+ rlc->type = SCCP_MSG_TYPE_RLC;
+ memcpy(&rlc->destination_local_reference,
+ &connection->destination_local_reference, sizeof(struct sccp_source_reference));
+ memcpy(&rlc->source_local_reference,
+ &connection->source_local_reference, sizeof(struct sccp_source_reference));
+
+ _send_msg(connection, msgb, NULL);
+
+ /*
+ * Remove from the list of active connections and set the state. User code
+ * should now free the entry.
+ */
+ llist_del(&connection->list);
+ _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_RELEASE_COMPLETE);
+ return 0;
+}
+
+/* connection released, send a released confirm */
+static int _sccp_handle_connection_released(struct msgb *msgb)
+{
+ struct sccp_parse_result result;
+ struct sccp_connection *conn;
+
+ if (_sccp_parse_connection_released(msgb, &result) == -1)
+ return -1;
+
+ /* lookup if we have a connection with the given reference */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ result.destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0
+ && memcmp(&conn->destination_local_reference,
+ result.source_local_reference,
+ sizeof(conn->destination_local_reference)) == 0) {
+ goto found;
+ }
+ }
+
+
+ LOGP(DSCCP, LOGL_ERROR, "Unknown connection was released.\n");
+ return -1;
+
+ /* we have found a connection */
+found:
+ /* optional data */
+ if (result.data_len != 0 && conn->data_cb) {
+ conn->data_cb(conn, msgb, result.data_len);
+ }
+
+ /* generate a response */
+ if (_sccp_send_connection_release_complete(conn) != 0) {
+ LOGP(DSCCP, LOGL_ERROR, "Sending release confirmed failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int _sccp_handle_connection_refused(struct msgb *msgb)
+{
+ struct sccp_parse_result result;
+ struct sccp_connection *conn;
+
+ if (_sccp_parse_connection_refused(msgb, &result) != 0)
+ return -1;
+
+ /* lookup if we have a connection with the given reference */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->incoming == 0 && conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ result.destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0) {
+ goto found;
+ }
+ }
+
+ LOGP(DSCCP, LOGL_ERROR, "Refused but no connection found\n");
+ return -1;
+
+found:
+ /* optional data */
+ if (result.data_len != 0 && conn->data_cb) {
+ conn->data_cb(conn, msgb, result.data_len);
+ }
+
+
+ llist_del(&conn->list);
+ _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_REFUSED);
+ return 0;
+}
+
+static int _sccp_handle_connection_confirm(struct msgb *msgb)
+{
+ struct sccp_parse_result result;
+ struct sccp_connection *conn;
+
+ if (_sccp_parse_connection_confirm(msgb, &result) != 0)
+ return -1;
+
+ /* lookup if we have a connection with the given reference */
+ llist_for_each_entry(conn, &sccp_connections, list) {
+ if (conn->incoming == 0 && conn->data_cb
+ && memcmp(&conn->source_local_reference,
+ result.destination_local_reference,
+ sizeof(conn->source_local_reference)) == 0) {
+ goto found;
+ }
+ }
+
+ LOGP(DSCCP, LOGL_ERROR, "Confirmed but no connection found\n");
+ return -1;
+
+found:
+ /* copy the addresses of the connection */
+ conn->destination_local_reference = *result.source_local_reference;
+ _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_ESTABLISHED);
+
+ /* optional data */
+ if (result.data_len != 0 && conn->data_cb) {
+ conn->data_cb(conn, msgb, result.data_len);
+ }
+
+ return 0;
+}
+
+
+int sccp_system_init(void (*outgoing)(struct sccp_connection *conn, struct msgb *data, void *, void *), void *ctx)
+{
+ sccp_system.write_data = outgoing;
+ sccp_system.write_context = ctx;
+
+ return 0;
+}
+
+/* oh my god a real SCCP packet. need to dispatch it now */
+int sccp_system_incoming(struct msgb *msgb)
+{
+ return sccp_system_incoming_ctx(msgb, NULL);
+}
+
+int sccp_system_incoming_ctx(struct msgb *msgb, void *ctx)
+{
+ if (msgb_l2len(msgb) < 1 ) {
+ LOGP(DSCCP, LOGL_ERROR, "Too short packet\n");
+ return -1;
+ }
+
+ int type = msgb->l2h[0];
+
+ switch(type) {
+ case SCCP_MSG_TYPE_CR:
+ return _sccp_handle_connection_request(msgb, ctx);
+ break;
+ case SCCP_MSG_TYPE_RLSD:
+ return _sccp_handle_connection_released(msgb);
+ break;
+ case SCCP_MSG_TYPE_CREF:
+ return _sccp_handle_connection_refused(msgb);
+ break;
+ case SCCP_MSG_TYPE_CC:
+ return _sccp_handle_connection_confirm(msgb);
+ break;
+ case SCCP_MSG_TYPE_RLC:
+ return _sccp_handle_connection_release_complete(msgb);
+ break;
+ case SCCP_MSG_TYPE_DT1:
+ return _sccp_handle_connection_dt1(msgb);
+ break;
+ case SCCP_MSG_TYPE_UDT:
+ return _sccp_handle_read(msgb);
+ break;
+ default:
+ LOGP(DSCCP, LOGL_ERROR, "unimplemented msg type: %d\n", type);
+ };
+
+ return -1;
+}
+
+/* create a packet from the data */
+int sccp_connection_write(struct sccp_connection *connection, struct msgb *data)
+{
+ if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
+ || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
+ LOGP(DSCCP, LOGL_ERROR, "sccp_connection_write: Wrong connection state: %p %d\n",
+ connection, connection->connection_state);
+ return -1;
+ }
+
+ return _sccp_send_connection_data(connection, data);
+}
+
+/*
+ * Send a Inactivity Test message. The owner of the connection
+ * should start a timer and call this method regularily. Calling
+ * this every 60 seconds should be good enough.
+ */
+int sccp_connection_send_it(struct sccp_connection *connection)
+{
+ if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
+ || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
+ LOGP(DSCCP, LOGL_ERROR, "sccp_connection_write: Wrong connection state: %p %d\n",
+ connection, connection->connection_state);
+ return -1;
+ }
+
+ return _sccp_send_connection_it(connection);
+}
+
+/* send a connection release and wait for the connection released */
+int sccp_connection_close(struct sccp_connection *connection, int cause)
+{
+ if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
+ || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
+ LOGP(DSCCP, LOGL_ERROR, "Can not close the connection. It was never opened: %p %d\n",
+ connection, connection->connection_state);
+ return -1;
+ }
+
+ return _sccp_send_connection_released(connection, cause);
+}
+
+int sccp_connection_free(struct sccp_connection *connection)
+{
+ if (connection->connection_state > SCCP_CONNECTION_STATE_NONE
+ && connection->connection_state < SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
+ LOGP(DSCCP, LOGL_ERROR, "The connection needs to be released before it is freed");
+ return -1;
+ }
+
+ talloc_free(connection);
+ return 0;
+}
+
+int sccp_connection_force_free(struct sccp_connection *con)
+{
+ if (con->connection_state > SCCP_CONNECTION_STATE_NONE &&
+ con->connection_state < SCCP_CONNECTION_STATE_RELEASE_COMPLETE)
+ llist_del(&con->list);
+
+ con->connection_state = SCCP_CONNECTION_STATE_REFUSED;
+ sccp_connection_free(con);
+ return 0;
+}
+
+struct sccp_connection *sccp_connection_socket(void)
+{
+ return talloc_zero(tall_sccp_ctx, struct sccp_connection);
+}
+
+int sccp_connection_connect(struct sccp_connection *conn,
+ const struct sockaddr_sccp *local,
+ struct msgb *data)
+{
+ return _sccp_send_connection_request(conn, local, data);
+}
+
+int sccp_connection_set_incoming(const struct sockaddr_sccp *sock,
+ int (*accept_cb)(struct sccp_connection *, void *), void *context)
+{
+ struct sccp_data_callback *cb;
+
+ if (!sock)
+ return -2;
+
+ cb = _find_ssn(sock->sccp_ssn);
+ if (!cb)
+ return -1;
+
+ cb->accept_cb = accept_cb;
+ cb->accept_context = context;
+ return 0;
+}
+
+int sccp_write(struct msgb *data, const struct sockaddr_sccp *in,
+ const struct sockaddr_sccp *out, int class, void *ctx)
+{
+ return _sccp_send_data(class, in, out, data, ctx);
+}
+
+int sccp_set_read(const struct sockaddr_sccp *sock,
+ int (*read_cb)(struct msgb *, unsigned int, void *), void *context)
+{
+ struct sccp_data_callback *cb;
+
+ if (!sock)
+ return -2;
+
+ cb = _find_ssn(sock->sccp_ssn);
+ if (!cb)
+ return -1;
+
+ cb->read_cb = read_cb;
+ cb->read_context = context;
+ return 0;
+}
+
+osmo_static_assert(sizeof(struct sccp_source_reference) <= sizeof(uint32_t), enough_space);
+
+uint32_t sccp_src_ref_to_int(struct sccp_source_reference *ref)
+{
+ uint32_t src_ref = 0;
+ memcpy(&src_ref, ref, sizeof(*ref));
+ return src_ref;
+}
+
+struct sccp_source_reference sccp_src_ref_from_int(uint32_t int_ref)
+{
+ struct sccp_source_reference ref;
+ memcpy(&ref, &int_ref, sizeof(ref));
+ return ref;
+}
+
+int sccp_determine_msg_type(struct msgb *msg)
+{
+ if (msgb_l2len(msg) < 1)
+ return -1;
+
+ return msg->l2h[0];
+}
+
+int sccp_parse_header(struct msgb *msg, struct sccp_parse_result *result)
+{
+ int type;
+
+ if (msgb_l2len(msg) < 1)
+ return -1;
+
+ type = msg->l2h[0];
+ switch(type) {
+ case SCCP_MSG_TYPE_CR:
+ return _sccp_parse_connection_request(msg, result);
+ break;
+ case SCCP_MSG_TYPE_RLSD:
+ return _sccp_parse_connection_released(msg, result);
+ break;
+ case SCCP_MSG_TYPE_CREF:
+ return _sccp_parse_connection_refused(msg, result);
+ break;
+ case SCCP_MSG_TYPE_CC:
+ return _sccp_parse_connection_confirm(msg, result);
+ break;
+ case SCCP_MSG_TYPE_RLC:
+ return _sccp_parse_connection_release_complete(msg, result);
+ break;
+ case SCCP_MSG_TYPE_DT1:
+ return _sccp_parse_connection_dt1(msg, result);
+ break;
+ case SCCP_MSG_TYPE_UDT:
+ return _sccp_parse_udt(msg, result);
+ break;
+ case SCCP_MSG_TYPE_IT:
+ return _sccp_parse_it(msg, result);
+ break;
+ case SCCP_MSG_TYPE_ERR:
+ return _sccp_parse_err(msg, result);
+ break;
+ };
+
+ LOGP(DSCCP, LOGL_ERROR, "Unimplemented MSG Type: 0x%x\n", type);
+ return -1;
+}
+
+static __attribute__((constructor)) void on_dso_load(void)
+{
+ tall_sccp_ctx = talloc_named_const(NULL, 1, "sccp");
+}
+
+static __attribute__((destructor)) void on_dso_unload(void)
+{
+ talloc_report_full(tall_sccp_ctx, stderr);
+}
+
+void sccp_set_log_area(int log_area)
+{
+ DSCCP = log_area;
+}
diff --git a/src/xua_msg.c b/src/xua_msg.c
new file mode 100644
index 0000000..e10b942
--- /dev/null
+++ b/src/xua_msg.c
@@ -0,0 +1,184 @@
+/* Routines for generating and parsing messages */
+/* (C) 2011 by Holger Hans Peter Freyther <zecke@selfish.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sigtran/xua_msg.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/talloc.h>
+
+#include <arpa/inet.h>
+
+#include <string.h>
+
+static void *tall_xua;
+static int DXUA = -1;
+
+struct xua_msg *xua_msg_alloc(void)
+{
+ struct xua_msg *msg;
+
+ msg = talloc_zero(tall_xua, struct xua_msg);
+ if (!msg) {
+ LOGP(DXUA, LOGL_ERROR, "Failed to allocate.\n");
+ return NULL;
+ }
+
+ INIT_LLIST_HEAD(&msg->headers);
+ return msg;
+}
+
+void xua_msg_free(struct xua_msg *msg)
+{
+ talloc_free(msg);
+}
+
+int xua_msg_add_data(struct xua_msg *msg, uint16_t tag,
+ uint16_t len, uint8_t *dat)
+{
+ struct xua_msg_part *part;
+
+ part = talloc_zero(msg, struct xua_msg_part);
+ if (!part)
+ return -1;
+
+ part->tag = tag;
+ part->len = len;
+
+ /* do we have any data? */
+ if (part->len != 0) {
+ part->dat = talloc_memdup(part, dat, len);
+ if (!part->dat) {
+ talloc_free(part);
+ return -1;
+ }
+ }
+
+ llist_add_tail(&part->entry, &msg->headers);
+ return 0;
+}
+
+struct xua_msg_part *xua_msg_find_tag(struct xua_msg *xua, uint16_t tag)
+{
+ struct xua_msg_part *part;
+
+ llist_for_each_entry(part, &xua->headers, entry)
+ if (part->tag == tag)
+ return part;
+
+ return NULL;
+}
+
+struct xua_msg *xua_from_msg(const int version, uint16_t len, uint8_t *data)
+{
+ struct xua_parameter_hdr *par;
+ struct xua_common_hdr *hdr;
+ struct xua_msg *msg;
+ uint16_t pos, par_len, padding;
+ int rc;
+
+ msg = xua_msg_alloc();
+ if (!msg)
+ return NULL;
+
+ if (len < sizeof(*hdr))
+ goto fail;
+
+ hdr = (struct xua_common_hdr *) data;
+ if (hdr->version != version)
+ goto fail;
+ if (ntohl(hdr->msg_length) > len)
+ goto fail;
+
+ msg->hdr = *hdr;
+ pos = sizeof(*hdr);
+
+ while (pos + sizeof(*par) < len) {
+ par = (struct xua_parameter_hdr *) &data[pos];
+ par_len = ntohs(par->len);
+
+ if (pos + par_len > len || par_len < 4)
+ goto fail;
+
+ rc = xua_msg_add_data(msg, ntohs(par->tag),
+ par_len - 4, par->data);
+ if (rc != 0)
+ goto fail;
+
+ pos += par_len;
+
+ /* move over the padding */
+ padding = (4 - (par_len % 4)) & 0x3;
+ pos += padding;
+ }
+
+ /* TODO: parse */
+ return msg;
+
+fail:
+ LOGP(DXUA, LOGL_ERROR, "Failed to parse.\n");
+ xua_msg_free(msg);
+ return NULL;
+}
+
+struct msgb *xua_to_msg(const int version, struct xua_msg *xua)
+{
+ struct xua_msg_part *part;
+ struct xua_common_hdr *hdr;
+ struct msgb *msg;
+ uint8_t rest;
+
+ msg = msgb_alloc_headroom(2048, 512, "xua msg");
+ if (!msg) {
+ LOGP(DXUA, LOGL_ERROR, "Failed to allocate.\n");
+ return NULL;
+ }
+
+ msg->l2h = msgb_put(msg, sizeof(*hdr));
+ hdr = (struct xua_common_hdr *) msg->l2h;
+ memcpy(hdr, &xua->hdr, sizeof(*hdr));
+
+ /* make sure that is right */
+ hdr->version = version;
+ hdr->spare = 0;
+
+ llist_for_each_entry(part, &xua->headers, entry) {
+ msgb_put_u16(msg, part->tag);
+ msgb_put_u16(msg, part->len + 4);
+ if (part->dat) {
+ uint8_t *dat = msgb_put(msg, part->len);
+ memcpy(dat, part->dat, part->len);
+
+ /* padding */
+ rest = (4 - (part->len % 4)) & 0x3;
+ if (rest > 0) {
+ dat = msgb_put(msg, rest);
+ memset(dat, 0, rest);
+ }
+ }
+ }
+
+ /* update the size of the data */
+ hdr->msg_length = htonl(msgb_l2len(msg));
+ return msg;
+}
+
+void xua_set_log_area(int log_area)
+{
+ DXUA = log_area;
+}
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..2bf1089
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,40 @@
+SUBDIRS = sccp mtp m2ua
+
+# The `:;' works around a Bash 3.2 bug when the output is not writeable.
+$(srcdir)/package.m4: $(top_srcdir)/configure.ac
+ :;{ \
+ echo '# Signature of the current package.' && \
+ echo 'm4_define([AT_PACKAGE_NAME],' && \
+ echo ' [$(PACKAGE_NAME)])' && \
+ echo 'm4_define([AT_PACKAGE_TARNAME],' && \
+ echo ' [$(PACKAGE_TARNAME)])' && \
+ echo 'm4_define([AT_PACKAGE_VERSION],' && \
+ echo ' [$(PACKAGE_VERSION)])' && \
+ echo 'm4_define([AT_PACKAGE_STRING],' && \
+ echo ' [$(PACKAGE_STRING)])' && \
+ echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \
+ echo ' [$(PACKAGE_BUGREPORT)])'; \
+ echo 'm4_define([AT_PACKAGE_URL],' && \
+ echo ' [$(PACKAGE_URL)])'; \
+ } >'$(srcdir)/package.m4'
+
+EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE)
+TESTSUITE = $(srcdir)/testsuite
+DISTCLEANFILES = atconfig
+
+check-local: atconfig $(TESTSUITE)
+ $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
+
+installcheck-local: atconfig $(TESTSUITE)
+ $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \
+ $(TESTSUITEFLAGS)
+
+clean-local:
+ test ! -f '$(TESTSUITE)' || \
+ $(SHELL) '$(TESTSUITE)' --clean
+
+AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te
+AUTOTEST = $(AUTOM4TE) --language=autotest
+$(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4
+ $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
+ mv $@.tmp $@
diff --git a/tests/m2ua/Makefile.am b/tests/m2ua/Makefile.am
new file mode 100644
index 0000000..33618ef
--- /dev/null
+++ b/tests/m2ua/Makefile.am
@@ -0,0 +1,8 @@
+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -Wall
+AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
+
+EXTRA_DIST = m2ua_test.ok
+
+noinst_PROGRAMS = m2ua_test
+m2ua_test_SOURCES = m2ua_test.c
+m2ua_test_LDADD = $(top_builddir)/src/libxua.a $(LIBOSMOCORE_LIBS)
diff --git a/tests/m2ua/m2ua_test.c b/tests/m2ua/m2ua_test.c
new file mode 100644
index 0000000..e432f96
--- /dev/null
+++ b/tests/m2ua/m2ua_test.c
@@ -0,0 +1,116 @@
+/* (C) 2011 by Holger Hans Peter Freyther <zecke@selfish.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <sigtran/xua_msg.h>
+#include <sigtran/m2ua_types.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define FAIL(msg) \
+ do { \
+ fprintf(stderr, "FAILURE: %s on line %d\n", msg, __LINE__); \
+ abort(); \
+ } while(0);
+
+static uint8_t asp_up[] = {
+ 0x01, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x11, 0x00, 0x08, 0xac, 0x10, 0x01, 0x51,
+};
+
+static uint8_t data[] = {
+ 0x01, 0x00, 0x06, 0x01, 0x00, 0x00, 0x00, 0x2c,
+ 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x1a, 0x81, 0x5c, 0x00, 0x07,
+ 0x00, 0x11, 0xf0, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0x00, 0x00
+};
+
+static void test_asp_up(void)
+{
+ struct xua_msg_part *part;
+ struct xua_msg *m2u = xua_from_msg(M2UA_VERSION, ARRAY_SIZE(asp_up), asp_up);
+ struct msgb *msg = xua_to_msg(M2UA_VERSION, m2u);
+ const uint8_t res[] = { 0xac, 0x10, 0x01, 0x51 };
+
+ printf("Testing ASP UP parsing.\n");
+
+ if (msg->len != ARRAY_SIZE(asp_up)) {
+ printf("Got %d wanted %d\n", msg->len, ARRAY_SIZE(asp_up));
+ FAIL("Wrong size");
+ }
+
+ if (memcmp(msg->data, asp_up, msg->len) != 0) {
+ printf("Got '%s'\n", osmo_hexdump(msg->data, msg->len));
+ FAIL("Wrong memory");
+ }
+
+ part = xua_msg_find_tag(m2u, 0x11);
+ if (!part)
+ FAIL("Could not find part");
+ if (part->len != 4)
+ FAIL("Part is not of length four\n");
+ if (memcmp(part->dat, res, 4) != 0)
+ FAIL("Wrong result for the tag\n");
+
+ xua_msg_free(m2u);
+ msgb_free(msg);
+}
+
+static void test_data(void)
+{
+ struct xua_msg_part *part;
+ struct xua_msg *m2u = xua_from_msg(M2UA_VERSION, ARRAY_SIZE(data), data);
+ struct msgb *msg = xua_to_msg(M2UA_VERSION, m2u);
+
+ printf("Testing parsing of data.\n");
+
+ if (msg->len != ARRAY_SIZE(data)) {
+ printf("Got %d wanted %d\n", msg->len, ARRAY_SIZE(data));
+ FAIL("Wrong size");
+ }
+
+ if (memcmp(msg->data, data, msg->len) != 0) {
+ printf("Got '%s'\n", osmo_hexdump(msg->data, msg->len));
+ FAIL("Wrong memory");
+ }
+
+ part = xua_msg_find_tag(m2u, 0x300);
+ if (!part)
+ FAIL("Could not find part");
+ if (part->len != 22) {
+ printf("Got the length %d\n", part->len);
+ FAIL("Part is not of length 22\n");
+ }
+
+ xua_msg_free(m2u);
+ msgb_free(msg);
+}
+
+int main(int argc, char **argv)
+{
+ test_asp_up();
+ test_data();
+
+ printf("All tests passed.\n");
+ return 0;
+}
diff --git a/tests/m2ua/m2ua_test.ok b/tests/m2ua/m2ua_test.ok
new file mode 100644
index 0000000..ab74b86
--- /dev/null
+++ b/tests/m2ua/m2ua_test.ok
@@ -0,0 +1,3 @@
+Testing ASP UP parsing.
+Testing parsing of data.
+All tests passed.
diff --git a/tests/mtp/Makefile.am b/tests/mtp/Makefile.am
new file mode 100644
index 0000000..21cc2c0
--- /dev/null
+++ b/tests/mtp/Makefile.am
@@ -0,0 +1,6 @@
+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -Wall
+noinst_PROGRAMS = mtp_parse_test
+
+EXTRA_DIST = mtp_parse_test.ok
+
+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..80f1e4b
--- /dev/null
+++ b/tests/mtp/mtp_parse_test.c
@@ -0,0 +1,641 @@
+/* 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;
+
+ printf("Basic MTP Structure testing.\n");
+
+ /* 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);
+ }
+
+ if (MTP_READ_OPC(tests[0].hdr.addr) != 91) {
+ fprintf(stderr, "Failed to read OPC address\n");
+ abort();
+ }
+
+ if (MTP_READ_DPC(tests[1].hdr.addr) != 136) {
+ fprintf(stderr, "Failed to read DPC address\n");
+ abort();
+ }
+
+ /* 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();
+ }
+ }
+
+ printf("All tests passed.\n");
+ return 0;
+}
diff --git a/tests/mtp/mtp_parse_test.ok b/tests/mtp/mtp_parse_test.ok
new file mode 100644
index 0000000..cee2aeb
--- /dev/null
+++ b/tests/mtp/mtp_parse_test.ok
@@ -0,0 +1,2 @@
+Basic MTP Structure testing.
+All tests passed.
diff --git a/tests/sccp/Makefile.am b/tests/sccp/Makefile.am
new file mode 100644
index 0000000..8cce20c
--- /dev/null
+++ b/tests/sccp/Makefile.am
@@ -0,0 +1,10 @@
+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS)
+
+EXTRA_DIST = sccp_test.ok
+
+noinst_PROGRAMS = sccp_test
+
+sccp_test_SOURCES = sccp_test.c $(top_srcdir)/src/sccp.c
+sccp_test_LDADD = $(LIBOSMOCORE_LIBS)
+
diff --git a/tests/sccp/sccp_test.c b/tests/sccp/sccp_test.c
new file mode 100644
index 0000000..6043cff
--- /dev/null
+++ b/tests/sccp/sccp_test.c
@@ -0,0 +1,1027 @@
+/*
+ * SCCP testing code
+ *
+ * (C) 2009,2011 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009,2011 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 <stdio.h>
+#include <string.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+
+#include <sccp/sccp.h>
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+/* BSC -> MSC */
+static const uint8_t bssmap_reset[] = {
+ 0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe,
+ 0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04,
+ 0x01, 0x20,
+};
+
+/* MSC -> BSC reset ack */
+static const uint8_t bssmap_reset_ack[] = {
+ 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01,
+ 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03,
+ 0x00, 0x01, 0x31,
+};
+
+/* MSC -> BSC paging, connection less */
+static const uint8_t bssmap_paging[] = {
+ 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01,
+ 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x10,
+ 0x00, 0x0e, 0x52, 0x08, 0x08, 0x29, 0x47, 0x10,
+ 0x02, 0x01, 0x31, 0x97, 0x61, 0x1a, 0x01, 0x06,
+};
+
+/* MSC -> BSC paging, UDT without PC */
+static const uint8_t bssmap_udt[] = {
+ 0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe,
+ 0x02, 0x42, 0xfe, 0x10, 0x00, 0x0e, 0x52, 0x08,
+ 0x08, 0x29, 0x47, 0x10, 0x02, 0x01, 0x31, 0x97,
+ 0x61, 0x1a, 0x01, 0x06,
+};
+
+/* BSC -> MSC connection open */
+static const uint8_t bssmap_cr[] = {
+ 0x01, 0x01, 0x02, 0x03, 0x02, 0x02, 0x04, 0x02,
+ 0x42, 0xfe, 0x0f, 0x1f, 0x00, 0x1d, 0x57, 0x05,
+ 0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x12, 0xc3,
+ 0x50, 0x17, 0x10, 0x05, 0x24, 0x11, 0x03, 0x33,
+ 0x19, 0xa2, 0x08, 0x29, 0x47, 0x10, 0x02, 0x01,
+ 0x31, 0x97, 0x61, 0x00
+};
+
+/* MSC -> BSC connection confirm */
+static const uint8_t bssmap_cc[] = {
+ 0x02, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00,
+};
+
+/* MSC -> BSC DTAP
+ *
+ * we fake a bit and make it BSC -> MSC... so the
+ * payload does not make any sense..
+ */
+static const uint8_t bssmap_dtap[] = {
+ 0x06, 0x00, 0x00, 0x03, 0x00, 0x01, 0x0f, 0x01, 0x00, 0x0c,
+ 0x03, 0x05, 0x5c, 0x08, 0x11, 0x81, 0x33, 0x66, 0x02, 0x13,
+ 0x45, 0xf4,
+};
+
+/* MSC -> BSC clear command */
+static const uint8_t bssmap_clear[] = {
+ 0x06, 0x00, 0x00, 0x03, 0x00, 0x01, 0x06, 0x00, 0x04, 0x20,
+ 0x04, 0x01, 0x09,
+};
+
+/* MSC -> BSC released */
+static const uint8_t bssmap_released[] = {
+ 0x04, 0x00, 0x00, 0x03, 0x01, 0x02, 0x03, 0x00, 0x01, 0x0f,
+ 0x02, 0x23, 0x42, 0x00,
+};
+
+/* BSC -> MSC released */
+static const uint8_t bssmap_release_complete[] = {
+ 0x05, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03
+};
+
+/* message with a SCCP global title */
+static const uint8_t tcap_global_title[] = {
+ 0x09,
+ 0x81, 0x03, 0x0d, 0x18, 0x0a, 0x12, 0x07, 0x00,
+ 0x12, 0x04, 0x53, 0x84, 0x09, 0x00, 0x17, 0x0b,
+ 0x12, 0x06, 0x00, 0x12, 0x04, 0x44, 0x87, 0x20,
+ 0x00, 0x20, 0x65, 0x9a, 0x65, 0x81, 0x97, 0x48,
+ 0x04, 0x26, 0x00, 0x01, 0x98, 0x49, 0x04, 0x51,
+ 0x01, 0x03, 0xdf, 0x6c, 0x81, 0x88, 0xa1, 0x81,
+ 0x85, 0x02, 0x01, 0x44, 0x02, 0x01, 0x07, 0x30,
+ 0x80, 0xa7, 0x80, 0xa0, 0x80, 0x04, 0x01, 0x2b,
+ 0x30, 0x80, 0x30, 0x12, 0x83, 0x01, 0x10, 0x84,
+ 0x01, 0x07, 0x85, 0x07, 0x91, 0x44, 0x57, 0x76,
+ 0x67, 0x16, 0x97, 0x86, 0x01, 0x20, 0x30, 0x06,
+ 0x82, 0x01, 0x18, 0x84, 0x01, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0xa3, 0x06, 0x04, 0x01, 0x42, 0x84,
+ 0x01, 0x05, 0xa3, 0x06, 0x04, 0x01, 0x51, 0x84,
+ 0x01, 0x05, 0xa3, 0x06, 0x04, 0x01, 0x31, 0x84,
+ 0x01, 0x05, 0xa3, 0x09, 0x04, 0x01, 0x12, 0x84,
+ 0x01, 0x05, 0x82, 0x01, 0x02, 0xa3, 0x09, 0x04,
+ 0x01, 0x11, 0x84, 0x01, 0x05, 0x81, 0x01, 0x01,
+ 0xa3, 0x06, 0x04, 0x01, 0x14, 0x84, 0x01, 0x00,
+ 0xa3, 0x0b, 0x04, 0x01, 0x41, 0x84, 0x01, 0x04,
+ 0x30, 0x03, 0x83, 0x01, 0x10, 0xa3, 0x0b, 0x04,
+ 0x01, 0x41, 0x84, 0x01, 0x04, 0x30, 0x03, 0x82,
+ 0x01, 0x18, 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t tcap_global_dst_gti[] = {
+ 0x00, 0x12, 0x04, 0x53, 0x84, 0x09, 0x00, 0x17,
+};
+
+static const uint8_t tcap_global_src_gti[] = {
+ 0x00, 0x12, 0x04, 0x44, 0x87, 0x20, 0x00, 0x20, 0x65,
+};
+
+
+struct test_data {
+ int length;
+ const uint8_t *data;
+ int payload_start;
+ int payload_length;
+ uint8_t first_byte;
+
+ /* in case it should trigger a sccp response */
+ int write;
+ const uint8_t *response;
+ int response_length;
+};
+
+static const struct test_data test_data[] = {
+ {
+ .length = ARRAY_SIZE(bssmap_reset),
+ .data = &bssmap_reset[0],
+ .payload_start = 12,
+ .payload_length = ARRAY_SIZE(bssmap_reset) - 12,
+ .first_byte = 0x0,
+ },
+ {
+ .length = ARRAY_SIZE(bssmap_reset_ack),
+ .data = &bssmap_reset_ack[0],
+ .payload_start = 16,
+ .payload_length = ARRAY_SIZE(bssmap_reset_ack) - 16,
+ .first_byte = 0x0,
+ },
+ {
+ .length = ARRAY_SIZE(bssmap_paging),
+ .data = &bssmap_paging[0],
+ .payload_start = 16,
+ .payload_length = ARRAY_SIZE(bssmap_paging) - 16,
+ .first_byte = 0x0,
+ },
+ {
+ .length = ARRAY_SIZE(bssmap_cr),
+ .data = &bssmap_cr[0],
+ .payload_start = 12,
+ /* 0x00 is end of optional data, subtract this byte */
+ .payload_length = 31,
+ .first_byte = 0x0,
+
+ /* the connection request should trigger a connection confirm */
+ .write = 1,
+ .response = &bssmap_cc[0],
+ .response_length= ARRAY_SIZE(bssmap_cc),
+ },
+ {
+ .length = ARRAY_SIZE(bssmap_dtap),
+ .data = &bssmap_dtap[0],
+ .payload_start = 7,
+ .payload_length = 15,
+ .first_byte = 0x01,
+ },
+ {
+ .length = ARRAY_SIZE(bssmap_clear),
+ .data = &bssmap_clear[0],
+ .payload_start = 7,
+ .payload_length = 6,
+ .first_byte = 0x00,
+ },
+ {
+ .length = ARRAY_SIZE(bssmap_released),
+ .data = &bssmap_released[0],
+ .payload_length = 2,
+ .payload_start = 11,
+ .first_byte = 0x23,
+
+ .write = 1,
+ .response = &bssmap_release_complete[0],
+ .response_length= ARRAY_SIZE(bssmap_release_complete),
+ },
+};
+
+/* we will send UDTs and verify they look like this */
+static const struct test_data send_data[] = {
+ {
+ .length = ARRAY_SIZE(bssmap_udt),
+ .data = &bssmap_udt[0],
+ .payload_start = 12,
+ .payload_length = ARRAY_SIZE(bssmap_udt) - 12,
+ .first_byte = 0x0,
+ },
+ {
+ .length = ARRAY_SIZE(bssmap_reset),
+ .data = &bssmap_reset[0],
+ .payload_start = 12,
+ .payload_length = ARRAY_SIZE(bssmap_reset) - 12,
+ .first_byte = 0x0,
+ },
+};
+
+struct connection_test {
+ /* should the connection be refused? */
+ int refuse;
+
+ int with_data;
+
+ /* on which side to close the connection? */
+ int close_side;
+ int close_cause;
+};
+
+/* sccp connection handling we want to test */
+static const struct connection_test connection_tests[] = {
+ {
+ .refuse = 1,
+ },
+ {
+ .refuse = 1,
+ .with_data = 1,
+ },
+ {
+ .refuse = 0,
+ .close_side = 0,
+ .close_cause = 5,
+ },
+ {
+ .refuse = 0,
+ .close_side = 0,
+ .close_cause = 5,
+ .with_data = 1,
+ },
+ {
+ .refuse = 0,
+ .close_side = 1,
+ .close_cause = 5,
+ },
+ {
+ .refuse = 0,
+ .close_side = 1,
+ .close_cause = 5,
+ .with_data = 1,
+ },
+};
+
+struct sccp_parse_header_result {
+ /* results */
+ int msg_type;
+ int wanted_len;
+ int src_ssn;
+ int dst_ssn;
+
+ int has_src_ref, has_dst_ref;
+ struct sccp_source_reference src_ref;
+ struct sccp_source_reference dst_ref;
+
+ /* global title len */
+ int src_gti_len;
+ const uint8_t *src_gti_data;
+ int dst_gti_len;
+ const uint8_t *dst_gti_data;
+
+ /* the input */
+ const uint8_t *input;
+ int input_len;
+};
+
+static const uint8_t it_test[] = {
+0x10, 0x01, 0x07,
+0x94, 0x01, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00 };
+
+static const uint8_t proto_err[] = {
+0x0f, 0x0c, 0x04, 0x00, 0x00,
+};
+
+static const struct sccp_parse_header_result parse_result[] = {
+ {
+ .msg_type = SCCP_MSG_TYPE_IT,
+ .wanted_len = 0,
+ .src_ssn = -1,
+ .dst_ssn = -1,
+ .has_src_ref = 1,
+ .has_dst_ref = 1,
+
+ .src_ref = {
+ .octet1 = 0x01,
+ .octet2 = 0x04,
+ .octet3 = 0x00
+ },
+ .dst_ref = {
+ .octet1 = 0x01,
+ .octet2 = 0x07,
+ .octet3 = 0x94,
+ },
+
+ .input = it_test,
+ .input_len = sizeof(it_test),
+ },
+ {
+ .msg_type = SCCP_MSG_TYPE_ERR,
+ .wanted_len = 0,
+ .src_ssn = -1,
+ .dst_ssn = -1,
+ .has_src_ref = 0,
+ .has_dst_ref = 1,
+ .dst_ref = {
+ .octet1 = 0x0c,
+ .octet2 = 0x04,
+ .octet3 = 0x00,
+ },
+ .input = proto_err,
+ .input_len = sizeof(proto_err),
+ },
+ {
+ .msg_type = SCCP_MSG_TYPE_UDT,
+ .input = tcap_global_title,
+ .input_len = sizeof(tcap_global_title),
+ .wanted_len = 154,
+ .dst_ssn = SCCP_SSN_VLR,
+ .dst_gti_data = tcap_global_dst_gti,
+ .dst_gti_len = 8,
+ .src_ssn = SCCP_SSN_HLR,
+ .src_gti_data = tcap_global_src_gti,
+ .src_gti_len = 9,
+ },
+};
+
+
+/* testing procedure:
+ * - we will use sccp_write and see what will be set in the
+ * outgoing callback
+ * - we will call sccp_system_incoming and see which calls
+ * are made. And then compare it to the ones we expect. We
+ * want the payload to arrive, or callbacks to be called.
+ * - we will use sccp_connection_socket and sccp_connection_write
+ * and verify state handling of connections
+ */
+
+static int current_test;
+
+/*
+ * test state...
+ */
+static int called = 0;
+static int matched = 0;
+static int write_called = 0;
+
+#define FAIL(x, args...) do { \
+ printf("FAILURE in %s:%d: " x, __FILE__, __LINE__, ## args); \
+ abort(); } while (0)
+
+/*
+ * writing these packets and expecting a result
+ */
+int sccp_read_cb(struct msgb *data, unsigned len, void *gctx)
+{
+ uint16_t payload_length = test_data[current_test].payload_length;
+ const uint8_t *got, *wanted;
+ int i;
+
+ called = 1;
+
+ if (msgb_l3len(data) < len) {
+ /* this should never be reached */
+ FAIL("Something horrible happened.. invalid packet..\n");
+ }
+
+ if (len == 0 || len != payload_length) {
+ FAIL("length mismatch: got: %d wanted: %d\n", msgb_l3len(data), payload_length);
+ }
+
+ if (data->l3h[0] != test_data[current_test].first_byte) {
+ FAIL("The first bytes of l3 do not match: 0x%x 0x%x\n",
+ data->l3h[0], test_data[current_test].first_byte);
+ }
+
+ got = &data->l3h[0];
+ wanted = test_data[current_test].data + test_data[current_test].payload_start;
+
+ for (i = 0; i < len; ++i) {
+ if (got[i] != wanted[i]) {
+ FAIL("Failed to compare byte. Got: 0x%x Wanted: 0x%x at %d\n",
+ got[i], wanted[i], i);
+ }
+ }
+
+ matched = 1;
+ return 0;
+}
+
+void sccp_write_cb(struct sccp_connection *conn, struct msgb *data, void *gctx, void *ctx)
+{
+ int i = 0;
+ const uint8_t *got, *wanted;
+
+ if (test_data[current_test].response == NULL) {
+ FAIL("Didn't expect write callback\n");
+ } else if (test_data[current_test].response_length != msgb_l2len(data)) {
+ FAIL("Size does not match. Got: %d Wanted: %d\n",
+ msgb_l2len(data), test_data[current_test].response_length);
+ }
+
+ got = &data->l2h[0];
+ wanted = test_data[current_test].response;
+
+ for (i = 0; i < msgb_l2len(data); ++i) {
+ if (got[i] != wanted[i]) {
+ FAIL("Failed to compare byte. Got: 0x%x Wanted: 0x%x at %d\n",
+ got[i], wanted[i], i);
+ }
+ }
+
+ write_called = 1;
+}
+
+void sccp_c_read(struct sccp_connection *connection, struct msgb *msgb, unsigned int len)
+{
+ sccp_read_cb(msgb, len, connection->data_ctx);
+}
+
+void sccp_c_state(struct sccp_connection *connection, int old_state)
+{
+ if (connection->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE)
+ sccp_connection_free(connection);
+}
+
+int sccp_accept_cb(struct sccp_connection *connection, void *user_data)
+{
+ called = 1;
+ unsigned int ref = 0;
+ ref |= connection->destination_local_reference.octet1 << 24;
+ ref |= connection->destination_local_reference.octet2 << 16;
+ ref |= connection->destination_local_reference.octet3 << 8;
+ ref = ntohl(ref);
+
+ connection->data_cb = sccp_c_read;
+ connection->state_cb = sccp_c_state;
+
+ /* accept this */
+ return 0;
+}
+
+static void sccp_udt_write_cb(struct sccp_connection *conn, struct msgb *data, void *gtx, void *ctx)
+{
+ const uint8_t *got, *wanted;
+ int i;
+
+ write_called = 1;
+
+ if (send_data[current_test].length != msgb_l2len(data)) {
+ FAIL("Size does not match. Got: %d Wanted: %d\n",
+ msgb_l2len(data), send_data[current_test].length);
+ }
+
+ got = &data->l2h[0];
+ wanted = send_data[current_test].data;
+
+ for (i = 0; i < msgb_l2len(data); ++i) {
+ if (got[i] != wanted[i]) {
+ FAIL("Failed to compare byte. Got: 0x%x Wanted: 0x%x at %d\n",
+ got[i], wanted[i], i);
+ }
+ }
+
+ matched = 1;
+}
+
+static void test_sccp_system(void)
+{
+ printf("Testing SCCP System\n");
+
+ sccp_system_init(sccp_write_cb, NULL);
+ sccp_set_read(&sccp_ssn_bssap, sccp_read_cb, NULL);
+ sccp_connection_set_incoming(&sccp_ssn_bssap, sccp_accept_cb, NULL);
+
+ for (current_test = 0; current_test < ARRAY_SIZE(test_data); ++current_test) {
+ unsigned int length = test_data[current_test].length;
+ struct msgb *msg = msgb_alloc_headroom(length + 2, 2, __func__);
+ msg->l2h = msgb_put(msg, length);
+ memcpy(msg->l2h, test_data[current_test].data, length);
+
+ called = matched = write_called = 0;
+ printf("Testing packet: %d\n", current_test);
+ sccp_system_incoming(msg);
+
+ if (!called || !matched || (test_data[current_test].write != write_called))
+ FAIL("current test: %d called: %d matched: %d write: %d\n",
+ current_test, called, matched, write_called);
+
+ msgb_free(msg);
+ }
+}
+
+/* test sending of udt */
+static void test_sccp_send_udt(void)
+{
+ printf("Testing send UDT\n");
+
+ sccp_system_init(sccp_udt_write_cb, NULL);
+ sccp_set_read(NULL, NULL, NULL);
+ sccp_connection_set_incoming(NULL, NULL, NULL);
+
+ for (current_test = 0; current_test < ARRAY_SIZE(send_data); ++current_test) {
+ const struct test_data *test = &send_data[current_test];
+
+ struct msgb *msg = msgb_alloc(test->payload_length, __func__);
+ msg->l3h = msgb_put(msg, test->payload_length);
+ memcpy(msg->l3h, test->data + test->payload_start, test->payload_length);
+
+ matched = write_called = 0;
+ printf("Testing packet: %d\n", current_test);
+ sccp_write(msg, &sccp_ssn_bssap, &sccp_ssn_bssap, 0, NULL);
+
+ if (!matched || !write_called)
+ FAIL("current test: %d matched: %d write: %d\n",
+ current_test, matched, write_called);
+
+ msgb_free(msg);
+ }
+}
+
+/* send udt from one end to another */
+static unsigned int test_value = 0x2442;
+static int sccp_udt_read(struct msgb *data, unsigned int len, void *gctx)
+{
+ unsigned int *val;
+
+ if (len != 4) {
+ FAIL("Wrong size: %d\n", msgb_l3len(data));
+ }
+
+ val = (unsigned int*)data->l3h;
+ matched = test_value == *val;
+
+ return 0;
+}
+
+static void sccp_write_loop(struct sccp_connection *conn, struct msgb *data, void *gctx, void *ctx)
+{
+ /* send it back to us */
+ sccp_system_incoming(data);
+ msgb_free(data);
+}
+
+static void test_sccp_udt_communication(void)
+{
+ struct msgb *data;
+ unsigned int *val;
+
+ printf("Testing UDT Communication.\n");
+
+ sccp_system_init(sccp_write_loop, NULL);
+ sccp_set_read(&sccp_ssn_bssap, sccp_udt_read, NULL);
+ sccp_connection_set_incoming(NULL, NULL, NULL);
+
+
+ data = msgb_alloc(4, "test data");
+ data->l3h = &data->data[0];
+ val = (unsigned int *)msgb_put(data, 4);
+ *val = test_value;
+
+ matched = 0;
+ sccp_write(data, &sccp_ssn_bssap, &sccp_ssn_bssap, 0, NULL);
+
+ if (!matched)
+ FAIL("Talking with us didn't work\n");
+
+ msgb_free(data);
+}
+
+
+/* connection testing... open, send, close */
+static const struct connection_test *current_con_test;
+static struct sccp_connection *outgoing_con;
+static struct sccp_connection *incoming_con;
+static int outgoing_data, incoming_data, incoming_state, outgoing_state;
+
+static struct msgb *test_data1, *test_data2, *test_data3;
+
+static void sccp_conn_in_state(struct sccp_connection *conn, int old_state)
+{
+ printf("\tincome: %d -> %d\n", old_state, conn->connection_state);
+ if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
+ if (conn == incoming_con) {
+ sccp_connection_free(conn);
+ incoming_con = NULL;
+ }
+ }
+}
+
+static void sccp_conn_in_data(struct sccp_connection *conn, struct msgb *msg, unsigned int len)
+{
+ /* compare the data */
+ ++incoming_data;
+ printf("\tincoming data: %d\n", len);
+
+ /* compare the data */
+ if (len != 4) {
+ FAIL("Length of packet is wrong: %u %u\n", msgb_l3len(msg), len);
+ }
+
+ if (incoming_data == 1) {
+ if (memcmp(msg->l3h, test_data1->l3h, len) != 0) {
+ FAIL("Comparing the data failed: %d\n", incoming_data);
+
+ }
+ } else if (incoming_data == 2) {
+ if (memcmp(msg->l3h, test_data2->l3h, len) != 0) {
+ FAIL("Comparing the data failed: %d\n", incoming_data);
+ }
+ }
+
+ /* sending out data */
+ if (incoming_data == 2) {
+ printf("\tReturning data3\n");
+ sccp_connection_write(conn, test_data3);
+ }
+}
+
+static int sccp_conn_accept(struct sccp_connection *conn, void *ctx)
+{
+ printf("\taccept: srcref(%u)\n",
+ sccp_src_ref_to_int(&conn->source_local_reference));
+ conn->state_cb = sccp_conn_in_state;
+ conn->data_cb = sccp_conn_in_data;
+
+ if (current_con_test->refuse)
+ return -1;
+
+ incoming_con = conn;
+ return 0;
+}
+
+/* callbacks for the outgoing side */
+static void sccp_conn_out_state(struct sccp_connection *conn, int old_state)
+{
+ printf("\toutgoing: dstref(%u) %d -> %d\n",
+ sccp_src_ref_to_int(&conn->destination_local_reference),
+ old_state, conn->connection_state);
+
+ if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
+ if (conn == outgoing_con) {
+ sccp_connection_free(conn);
+ outgoing_con = NULL;
+ }
+ }
+}
+
+static void sccp_conn_out_data(struct sccp_connection *conn, struct msgb *msg, unsigned int len)
+{
+ ++outgoing_data;
+ printf("\toutgoing data: dstref(%u) %d\n",
+ sccp_src_ref_to_int(&conn->destination_local_reference), len);
+
+ if (len != 4)
+ FAIL("Length of packet is wrong: %u %u\n", msgb_l3len(msg), len);
+
+ if (outgoing_data == 1) {
+ if (memcmp(msg->l3h, test_data3->l3h, len) != 0) {
+ FAIL("Comparing the data failed\n");
+ }
+ }
+}
+
+static void do_test_sccp_connection(const struct connection_test *test)
+{
+ int ret;
+
+ current_con_test = test;
+ outgoing_con = incoming_con = 0;
+
+ outgoing_con = sccp_connection_socket();
+ if (!outgoing_con) {
+ FAIL("Connection is NULL\n");
+ }
+
+ outgoing_con->state_cb = sccp_conn_out_state;
+ outgoing_con->data_cb = sccp_conn_out_data;
+ outgoing_data = incoming_data = 0;
+ incoming_state = outgoing_state = 1;
+
+ /* start testing */
+ if (test->with_data) {
+ if (sccp_connection_connect(outgoing_con, &sccp_ssn_bssap, test_data1) != 0)
+ FAIL("Binding failed\n");
+ } else {
+ ++incoming_data;
+ if (sccp_connection_connect(outgoing_con, &sccp_ssn_bssap, NULL) != 0)
+ FAIL("Binding failed\n");
+ }
+
+ if (test->refuse) {
+ if (outgoing_con)
+ FAIL("Outgoing connection should have been refused.\n");
+ } else {
+ if (!incoming_con)
+ FAIL("Creating incoming didn't work.\n");
+
+ printf("\tWriting test data2\n");
+ sccp_connection_write(outgoing_con, test_data2);
+ sccp_connection_send_it(outgoing_con);
+
+ /* closing connection */
+ if (test->close_side == 0)
+ ret = sccp_connection_close(outgoing_con, 0);
+ else
+ ret = sccp_connection_close(incoming_con, 0);
+
+ if (ret != 0)
+ FAIL("Closing the connection failed\n");
+ }
+
+ /* outgoing should be gone now */
+ if (outgoing_con)
+ FAIL("Outgoing connection was not properly closed\n");
+
+ if (incoming_con)
+ FAIL("Incoming connection was not propery closed.\n");
+
+ if (test->refuse == 0) {
+ if (outgoing_data != 1 || incoming_data != 2) {
+ FAIL("Data sending failed: %d/%d %d/%d\n",
+ outgoing_data, 1,
+ incoming_data, 2);
+ }
+ }
+
+ if (!incoming_state || !outgoing_state)
+ FAIL("Failure with the state transition. %d %d\n",
+ outgoing_state, incoming_state);
+}
+
+static void test_sccp_connection(void)
+{
+ printf("Testing SCCP connection.\n");
+
+ sccp_system_init(sccp_write_loop, NULL);
+ sccp_set_read(NULL, NULL, NULL);
+ sccp_connection_set_incoming(&sccp_ssn_bssap, sccp_conn_accept, NULL);
+
+ test_data1 = msgb_alloc(4, "data1");
+ test_data1->l3h = msgb_put(test_data1, 4);
+ *((unsigned int*)test_data1->l3h) = 0x23421122;
+
+ test_data2 = msgb_alloc(4, "data2");
+ test_data2->l3h = msgb_put(test_data2, 4);
+ *((unsigned int*)test_data2->l3h) = 0x42232211;
+
+ test_data3 = msgb_alloc(4, "data3");
+ test_data3->l3h = msgb_put(test_data3, 4);
+ *((unsigned int*)test_data3->l3h) = 0x2323ff55;
+
+
+ for (current_test = 0; current_test < ARRAY_SIZE(connection_tests); ++current_test) {
+ printf("Testing %d refuse: %d with_data: %d\n",
+ current_test, connection_tests[current_test].refuse,
+ connection_tests[current_test].with_data);
+ do_test_sccp_connection(&connection_tests[current_test]);
+ }
+
+ msgb_free(test_data1);
+ msgb_free(test_data2);
+ msgb_free(test_data3);
+}
+
+/* invalid input */
+static void test_sccp_system_crash(void)
+{
+ printf("trying to provoke a crash with invalid input\n");
+ sccp_set_read(&sccp_ssn_bssap, sccp_read_cb, NULL);
+ sccp_connection_set_incoming(&sccp_ssn_bssap, sccp_accept_cb, NULL);
+
+ for (current_test = 0; current_test < ARRAY_SIZE(test_data); ++current_test) {
+ int original_length = test_data[current_test].length;
+ int length = original_length + 2;
+ int i;
+
+ printf("Testing packet: %d\n", current_test);
+
+ for (i = length; i >= 0; --i) {
+ unsigned int length = MIN(test_data[current_test].length, i);
+ struct msgb *msg = msgb_alloc_headroom(length + 2, 2, __func__);
+ msg->l2h = msgb_put(msg, length);
+ memcpy(msg->l2h, test_data[current_test].data, length);
+ sccp_system_incoming(msg);
+ msgb_free(msg);
+ }
+ }
+
+ printf("survived\n");
+}
+
+static void test_sccp_parsing(void)
+{
+ printf("Test SCCP Parsing.\n");
+
+ for (current_test = 0; current_test < ARRAY_SIZE(parse_result); ++current_test) {
+ struct msgb *msg;
+ struct sccp_parse_result result;
+
+ msg = msgb_alloc_headroom(1024, 128, "parse-test");
+ msgb_put(msg, 1);
+ msg->l2h = msgb_put(msg, parse_result[current_test].input_len);
+ memcpy(msg->l2h, parse_result[current_test].input, msgb_l2len(msg));
+
+ memset(&result, 0, sizeof(result));
+ if (sccp_parse_header(msg, &result) != 0) {
+ FAIL("Failed to sccp parse test: %d\n", current_test);
+ } else {
+ if (parse_result[current_test].wanted_len != result.data_len) {
+ FAIL("Unexpected data length. Got: %d\n", result.data_len);
+ }
+
+ if (parse_result[current_test].has_src_ref) {
+ if (memcmp(result.source_local_reference,
+ &parse_result[current_test].src_ref,
+ sizeof(struct sccp_source_reference)) != 0) {
+ FAIL("SRC REF did not match\n");
+ }
+ }
+
+ if (parse_result[current_test].has_dst_ref) {
+ if (memcmp(result.destination_local_reference,
+ &parse_result[current_test].dst_ref,
+ sizeof(struct sccp_source_reference)) != 0) {
+ FAIL("DST REF did not match\n");
+ }
+ }
+
+ if (parse_result[current_test].src_ssn != -1 &&
+ parse_result[current_test].src_ssn != result.calling.ssn) {
+ FAIL("Calling SSN is wrong..\n");
+ }
+
+ if (parse_result[current_test].dst_ssn != -1 &&
+ parse_result[current_test].dst_ssn != result.called.ssn) {
+ FAIL("Called SSN is wrong..\n");
+ }
+
+ if (parse_result[current_test].src_gti_len != result.calling.gti_len) {
+ FAIL("GTI length is wrong: %d\n", result.calling.gti_len);
+ }
+
+ if (parse_result[current_test].dst_gti_len != result.called.gti_len) {
+ FAIL("GTI length is wrong: %d\n", result.called.gti_len);
+ }
+
+ if (memcmp(&parse_result[current_test].dst_gti_data[0],
+ result.called.gti_data, result.called.gti_len) != 0) {
+ FAIL("GTI data is wrong: %d '%s'\n",
+ result.called.gti_len,
+ osmo_hexdump(result.called.gti_data, result.called.gti_len));
+ }
+
+ if (memcmp(&parse_result[current_test].src_gti_data[0],
+ result.calling.gti_data, result.calling.gti_len) != 0) {
+ FAIL("GTI data is wrong: %d\n", result.calling.gti_len);
+ }
+ }
+
+ msgb_free(msg);
+ }
+}
+
+/*
+ * Test the creation of SCCP addresses
+ */
+int sccp_create_sccp_addr(struct msgb *msg, const struct sockaddr_sccp *sock);
+
+struct sccp_addr_tst {
+ const struct sockaddr_sccp *addr;
+
+ const uint8_t *output;
+ const int output_len;
+};
+
+static uint8_t ssn_out[] = {
+ 0x02, 0x42, 0xfe,
+};
+
+const struct sockaddr_sccp sccp_poi_bssap = {
+ .sccp_family = 0,
+ .sccp_ssn = SCCP_SSN_BSSAP,
+ .poi = {0x01, 0x00},
+ .use_poi = 1,
+};
+
+static uint8_t poi_out[] = {
+ 0x04, 0x43, 0x01, 0x00, 0xfe,
+};
+
+static uint8_t gti_dat[] = {
+ 0x00, 0x12, 0x04, 0x53, 0x84, 0x09, 0x00, 0x17,
+};
+
+const struct sockaddr_sccp sccp_gti_bssap = {
+ .sccp_family = 0,
+ .sccp_ssn = 7,
+ .gti_ind = 4,
+ .gti_len = ARRAY_SIZE(gti_dat),
+ .gti = gti_dat,
+};
+
+static uint8_t gti_out[] = {
+ 0x0a, 0x12, 0x07, 0x00, 0x12, 0x04, 0x53, 0x84, 0x09, 0x00, 0x17,
+};
+
+static struct sccp_addr_tst sccp_addr_tst[] = {
+ {
+ .addr = &sccp_ssn_bssap,
+ .output = ssn_out,
+ .output_len = ARRAY_SIZE(ssn_out),
+ },
+ {
+ .addr = &sccp_poi_bssap,
+ .output = poi_out,
+ .output_len = ARRAY_SIZE(poi_out),
+ },
+ {
+ .addr = &sccp_gti_bssap,
+ .output = gti_out,
+ .output_len = ARRAY_SIZE(gti_out),
+ },
+};
+
+static void test_sccp_address(void)
+{
+ int i, ret;
+ struct msgb *msg = msgb_alloc(128, "sccp-addr");
+
+ printf("Test SCCP Address\n");
+
+ for (i = 0; i < ARRAY_SIZE(sccp_addr_tst); ++i) {
+ msgb_reset(msg);
+ ret = sccp_create_sccp_addr(msg, sccp_addr_tst[i].addr);
+ if (ret != sccp_addr_tst[i].output_len) {
+ FAIL("Length is from for %d\n", i);
+ }
+
+ if (memcmp(msg->data, sccp_addr_tst[i].output, ret) != 0) {
+ FAIL("Unexpected data for %d '%s'\n", i,
+ osmo_hexdump(msg->data, ret));
+ }
+ }
+}
+
+static const struct log_info_cat default_categories[] = {
+ [0] = {
+ .name = "DSCCP",
+ .description = "DSCP",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+};
+
+static int null_flt(const struct log_context *ctx, struct log_target *target)
+{
+ return 1;
+}
+
+const struct log_info log_info = {
+ .filter_fn = null_flt,
+ .cat = default_categories,
+ .num_cat = ARRAY_SIZE(default_categories),
+};
+
+int main(int argc, char **argv)
+{
+ struct log_target *stderr_target;
+ log_init(&log_info, NULL);
+ stderr_target = log_target_create_stderr();
+ log_add_target(stderr_target);
+
+ printf("Testing SCCP handling.\n");
+
+ sccp_set_log_area(0);
+
+ test_sccp_system();
+ test_sccp_send_udt();
+ test_sccp_udt_communication();
+ test_sccp_connection();
+ test_sccp_system_crash();
+ test_sccp_parsing();
+ test_sccp_address();
+ printf("All tests passed.\n");
+ return 0;
+}
+
+void db_store_counter(void) {}
diff --git a/tests/sccp/sccp_test.ok b/tests/sccp/sccp_test.ok
new file mode 100644
index 0000000..2b55a16
--- /dev/null
+++ b/tests/sccp/sccp_test.ok
@@ -0,0 +1,86 @@
+Testing SCCP handling.
+Testing SCCP System
+Testing packet: 0
+Testing packet: 1
+Testing packet: 2
+Testing packet: 3
+Testing packet: 4
+Testing packet: 5
+Testing packet: 6
+Testing send UDT
+Testing packet: 0
+Testing packet: 1
+Testing UDT Communication.
+Testing SCCP connection.
+Testing 0 refuse: 1 with_data: 0
+ outgoing: dstref(0) 0 -> 1
+ accept: srcref(0)
+ outgoing: dstref(0) 1 -> 6
+ income: 0 -> 6
+Testing 1 refuse: 1 with_data: 1
+ outgoing: dstref(0) 0 -> 1
+ accept: srcref(0)
+ outgoing: dstref(0) 1 -> 6
+ income: 0 -> 6
+Testing 2 refuse: 0 with_data: 0
+ outgoing: dstref(0) 0 -> 1
+ accept: srcref(0)
+ outgoing: dstref(196612) 1 -> 3
+ income: 0 -> 3
+ Writing test data2
+ incoming data: 4
+ Returning data3
+ outgoing data: dstref(196612) 4
+ outgoing: dstref(196612) 3 -> 4
+ outgoing: dstref(196612) 4 -> 5
+ income: 3 -> 5
+Testing 3 refuse: 0 with_data: 1
+ outgoing: dstref(0) 0 -> 1
+ accept: srcref(0)
+ outgoing: dstref(196614) 1 -> 3
+ income: 0 -> 3
+ incoming data: 4
+ Writing test data2
+ incoming data: 4
+ Returning data3
+ outgoing data: dstref(196614) 4
+ outgoing: dstref(196614) 3 -> 4
+ outgoing: dstref(196614) 4 -> 5
+ income: 3 -> 5
+Testing 4 refuse: 0 with_data: 0
+ outgoing: dstref(0) 0 -> 1
+ accept: srcref(0)
+ outgoing: dstref(196616) 1 -> 3
+ income: 0 -> 3
+ Writing test data2
+ incoming data: 4
+ Returning data3
+ outgoing data: dstref(196616) 4
+ income: 3 -> 4
+ income: 4 -> 5
+ outgoing: dstref(196616) 3 -> 5
+Testing 5 refuse: 0 with_data: 1
+ outgoing: dstref(0) 0 -> 1
+ accept: srcref(0)
+ outgoing: dstref(196618) 1 -> 3
+ income: 0 -> 3
+ incoming data: 4
+ Writing test data2
+ incoming data: 4
+ Returning data3
+ outgoing data: dstref(196618) 4
+ income: 3 -> 4
+ income: 4 -> 5
+ outgoing: dstref(196618) 3 -> 5
+trying to provoke a crash with invalid input
+Testing packet: 0
+Testing packet: 1
+Testing packet: 2
+Testing packet: 3
+Testing packet: 4
+Testing packet: 5
+Testing packet: 6
+survived
+Test SCCP Parsing.
+Test SCCP Address
+All tests passed.
diff --git a/tests/testsuite.at b/tests/testsuite.at
new file mode 100644
index 0000000..8907ffa
--- /dev/null
+++ b/tests/testsuite.at
@@ -0,0 +1,20 @@
+AT_INIT
+AT_BANNER([Regression tests.])
+
+AT_SETUP([m2ua])
+AT_KEYWORDS([m2ua])
+cat $abs_srcdir/m2ua/m2ua_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/m2ua/m2ua_test], [], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([mtp])
+AT_KEYWORDS([mtp])
+cat $abs_srcdir/mtp/mtp_parse_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/mtp/mtp_parse_test], [], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([sccp])
+AT_KEYWORDS([sccp])
+cat $abs_srcdir/sccp/sccp_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/sccp/sccp_test], [], [expout], [ignore])
+AT_CLEANUP