diff options
author | Ruben Undheim <ruben.undheim@gmail.com> | 2016-02-05 16:22:38 +0100 |
---|---|---|
committer | Ruben Undheim <ruben.undheim@gmail.com> | 2016-02-05 16:22:38 +0100 |
commit | e9e10edaf79a7919495e1efce810a54b167870de (patch) | |
tree | 9660d8de7d024e5c55833189945706c7c3f0f536 |
Imported Upstream version 0.7.0
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.* @@ -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 |