Compare commits
257 Commits
Author | SHA1 | Date |
---|---|---|
Oliver Smith | 705a48e8f7 | |
Oliver Smith | abfe4e1643 | |
Oliver Smith | f3cc0d9e57 | |
Max | ee75a07dfd | |
Pau Espin | da7bc78cb4 | |
Pau Espin | ef237e1876 | |
Pau Espin | 7bf18b43a1 | |
Vadim Yanitskiy | 513e937a11 | |
Harald Welte | 2c9ba16c2c | |
Harald Welte | 4fc2477a66 | |
Harald Welte | 4714dc23ee | |
Harald Welte | 236445d9cf | |
Harald Welte | 471f859d77 | |
Harald Welte | 8e12612e17 | |
Harald Welte | e099fb9d69 | |
Harald Welte | 9257cd896e | |
Harald Welte | c72c2d383c | |
Harald Welte | d4ec8e7f9f | |
Vadim Yanitskiy | 542027f051 | |
Harald Welte | dfa6e681d8 | |
Vadim Yanitskiy | 4d7e20193c | |
Vadim Yanitskiy | 22929b1c04 | |
Vadim Yanitskiy | 85055b5ca2 | |
Vadim Yanitskiy | 246f7cd881 | |
Vadim Yanitskiy | ad9af625e5 | |
Vadim Yanitskiy | 285800966f | |
Vadim Yanitskiy | 5951f1f6d5 | |
Vadim Yanitskiy | 1c155860ea | |
Vadim Yanitskiy | 5e80ca8083 | |
Harald Welte | 4120e0d2fc | |
Harald Welte | c934cadaeb | |
Harald Welte | eae5eb498a | |
Harald Welte | 8000b23106 | |
Vadim Yanitskiy | 02d664b1e4 | |
Pau Espin | 5744469021 | |
Pau Espin | 59bab0187c | |
Pau Espin | 00ec53c057 | |
Pau Espin | c86bdae6f5 | |
Pau Espin | 483894bfd1 | |
Pau Espin | a431b9230b | |
Pau Espin | bbecd572a0 | |
Pau Espin | 6ad714de98 | |
Pau Espin | 65741dca05 | |
Harald Welte | 04ca01eaeb | |
Pau Espin | 42ed1bf15d | |
Max | 427c462484 | |
Pau Espin | 87ed6e7b53 | |
Harald Welte | b24cdce2d9 | |
Pau Espin | 08001775c6 | |
Pau Espin | c5a4b772b1 | |
Pau Espin | 613e25305b | |
Pau Espin | f8aaa75852 | |
Pau Espin | 44cf7c8231 | |
Pau Espin | 3ed3b2c2ed | |
Pau Espin | 4a744e2234 | |
Pau Espin | e39a104727 | |
Pau Espin | adbf243e1b | |
Pau Espin | 07e788fc94 | |
Pau Espin | 785d160da8 | |
Harald Welte | 743deba334 | |
Pau Espin | 033072fc2d | |
Pau Espin | 945b252a48 | |
Pau Espin | 1dab088701 | |
Pau Espin | 88de6d403b | |
Pau Espin | 0d7f99fd59 | |
Pau Espin | c4c444ec7e | |
Pau Espin | 15efbbdba4 | |
Pau Espin | abf26ce1ad | |
Pau Espin | 13a341e813 | |
Pau Espin | 0937875983 | |
Pau Espin | 08cb70e158 | |
Pau Espin | d01c45918d | |
Harald Welte | 4c1751ddca | |
Harald Welte | b93e1d0120 | |
Pau Espin | 1edd02bb3b | |
Pau Espin | 8b8458d153 | |
Pau Espin | 58fd289443 | |
Pau Espin | f9852ae077 | |
Pau Espin | e798ebdf6d | |
Pau Espin | a18f907aaf | |
Pau Espin | 4ddaee9362 | |
Pau Espin | 5cf725b191 | |
Pau Espin | c086a8ed42 | |
Harald Welte | 1532791798 | |
Pau Espin | 7afbc23c2d | |
Pau Espin | a8aab23588 | |
Pau Espin | 1470379c27 | |
Pau Espin | af768c716a | |
Pau Espin | 5d30f648f4 | |
Pau Espin | 83db938859 | |
Pau Espin | 0aff5d101d | |
Harald Welte | 2a0ccafbf8 | |
Pau Espin | 8698f273d2 | |
Oliver Smith | 7e6bce7322 | |
Vadim Yanitskiy | 1f50d451a0 | |
Neels Hofmeyr | 083ea4b1fc | |
Neels Hofmeyr | 8bc881f31a | |
Neels Hofmeyr | d5139548f2 | |
Oliver Smith | 42ebd023b7 | |
Neels Hofmeyr | a0fbeeb4d2 | |
Harald Welte | 460ba631b5 | |
Harald Welte | a54c714df3 | |
Neels Hofmeyr | c3604fe9a9 | |
Neels Hofmeyr | 078622128a | |
Vadim Yanitskiy | 79c64ab555 | |
Vadim Yanitskiy | 463888c1dc | |
Vadim Yanitskiy | c5cae83315 | |
Neels Hofmeyr | 2e476c692b | |
Vadim Yanitskiy | 2790599e56 | |
Vadim Yanitskiy | 002b4d29f9 | |
Vadim Yanitskiy | 0e065be375 | |
Pau Espin | 1e288b9614 | |
Pau Espin | 253c45307e | |
Pau Espin | a19cf68c65 | |
Pau Espin | d66c70208f | |
Pau Espin | f031be9141 | |
arehbein | 41089524a3 | |
Vadim Yanitskiy | 36d3877483 | |
Neels Hofmeyr | f9b0746d7d | |
Max | 6e8241356b | |
Oliver Smith | e753f21047 | |
Neels Hofmeyr | 4c761ba304 | |
Neels Hofmeyr | ed4e7dbf86 | |
Pau Espin | dc9713a672 | |
Harald Welte | 8b671b073f | |
Harald Welte | 99b912f94a | |
Pau Espin | 2f648b2daf | |
Oliver Smith | bd92ec7256 | |
Oliver Smith | ef0d676741 | |
Harald Welte | 2ce7b758e3 | |
Max | 71fc322431 | |
Max | e5d615efdc | |
Max | 56b7700910 | |
Max | e7228dbdd2 | |
Max | bb760c5915 | |
Max | 768f36440f | |
Max | 196ddcbb05 | |
Max | 28a8e3e836 | |
Max | 43d7455089 | |
Max | f4301b7f45 | |
Max | d388dead51 | |
Max | 4f9c66dc0f | |
Max | 12e76b8859 | |
Pau Espin | e6cecee78c | |
Pau Espin | 1032df2d88 | |
Harald Welte | e9cc5c9376 | |
Harald Welte | 206583846c | |
Harald Welte | 70802f76d9 | |
Harald Welte | d51a8de505 | |
Vadim Yanitskiy | 7d8a674e88 | |
Pau Espin | 64dda770c3 | |
Harald Welte | 218789e833 | |
Harald Welte | a1bf0c96ec | |
Harald Welte | 3424c5ed99 | |
Harald Welte | d8c7a40a60 | |
Harald Welte | d0cd3157b7 | |
Pau Espin | 7b4aea4aa6 | |
Oliver Smith | ebccb824a0 | |
Oliver Smith | e3e79e2806 | |
Philipp Maier | 5cac653a98 | |
Pau Espin | d9e54d7a34 | |
Oliver Smith | 853ce2ec13 | |
Oliver Smith | 0aa3164b93 | |
Oliver Smith | 043275129c | |
Pau Espin | 77700b6eb5 | |
Pau Espin | 2d9e84b703 | |
Harald Welte | 484fc3875b | |
Harald Welte | e9727688cd | |
Harald Welte | f71096a2b0 | |
Harald Welte | ff7404c742 | |
Harald Welte | 5ad97995e9 | |
Harald Welte | 8aa64eb92a | |
Pau Espin | 8275949838 | |
Pau Espin | f594b7dfd2 | |
Pau Espin | c83f3a870d | |
Pau Espin | 4dfb938f9c | |
Oliver Smith | 76985a9aa2 | |
Oliver Smith | 1a75755f9b | |
Eric Wild | 387304c41d | |
Philipp Maier | b3cb0c14bb | |
Philipp Maier | b4f1b2cb8b | |
Pau Espin | ee10347957 | |
Pau Espin | e4e047bbec | |
Vadim Yanitskiy | ce72cdff6e | |
Harald Welte | 2f8b645ecb | |
Harald Welte | 3639af549c | |
Harald Welte | 0f769392a5 | |
Harald Welte | e9b21ff256 | |
Harald Welte | 2cf796a2a5 | |
Harald Welte | ab85cdf1ab | |
Harald Welte | a7c4f97348 | |
Harald Welte | c288852320 | |
Harald Welte | 03e3b04234 | |
Harald Welte | a25251a15b | |
Harald Welte | 9c0fae14d1 | |
Harald Welte | d9098703fa | |
Harald Welte | 40c4461c3a | |
Harald Welte | 5b0ec1ce67 | |
Harald Welte | 6b799aeb67 | |
Harald Welte | 37a3b1f07d | |
Harald Welte | 8a43eb4186 | |
Harald Welte | f414447bfd | |
Harald Welte | fe770ee0b1 | |
Harald Welte | 9db6213620 | |
Harald Welte | e5ffedf526 | |
Pau Espin | 6304393363 | |
Pau Espin | 685816f97f | |
Harald Welte | 2181d644bc | |
Harald Welte | 6d8d9b3ced | |
Harald Welte | 7eedb6b03c | |
Pau Espin | d7ecf7a150 | |
Pau Espin | 25234f9158 | |
Pau Espin | 5cac145d2c | |
Harald Welte | afbcc5d532 | |
Harald Welte | 7daae9bd67 | |
Harald Welte | 0d53666108 | |
Harald Welte | bf85d6f433 | |
Harald Welte | d2dc5ed987 | |
Harald Welte | 943affdd48 | |
Harald Welte | 6cb841b92b | |
Harald Welte | f5d90c46e1 | |
Harald Welte | a0228a74e0 | |
Harald Welte | aabb38a77b | |
Harald Welte | bdf5ae5f02 | |
Harald Welte | 1153675012 | |
Harald Welte | 2aebd133ac | |
Harald Welte | 3bd913398a | |
Harald Welte | 30c2edb099 | |
Harald Welte | c584d87d0c | |
Oliver Smith | 20642478f3 | |
Oliver Smith | ed11cc8385 | |
Oliver Smith | 1e2ff4a8b8 | |
Harald Welte | 48e2988539 | |
Pau Espin | e7c8067df0 | |
Philipp Maier | 320c71d45c | |
Vadim Yanitskiy | b4bac73245 | |
Neels Hofmeyr | 7a4f614ba9 | |
Neels Hofmeyr | 134e463230 | |
Pau Espin | c6a598d21c | |
Pau Espin | 2221ad87f4 | |
Pau Espin | 95e2ec698d | |
Neels Hofmeyr | b114b73e5d | |
Pau Espin | 44456920e5 | |
Neels Hofmeyr | ec3e7e43ab | |
Pau Espin | b35dc5fc81 | |
Pau Espin | d94d58c523 | |
Pau Espin | 2d5b4ef2be | |
Pau Espin | 96d348efbf | |
Pau Espin | 531ba5d729 | |
Pau Espin | fc3d9a963e | |
Neels Hofmeyr | ee81289297 | |
Pau Espin | ce88150c05 | |
Pau Espin | 0b39f2cf7b | |
Pau Espin | 67eddd6aa2 | |
Pau Espin | 002197560d | |
Pau Espin | 661c555729 | |
Vadim Yanitskiy | 2b5d9a4862 |
|
@ -0,0 +1 @@
|
|||
open_collective: osmocom
|
|
@ -13,6 +13,7 @@ bsc_msc_ip
|
|||
bsc_mgcp
|
||||
*.*~
|
||||
*.sw?
|
||||
*~
|
||||
|
||||
#configure
|
||||
aclocal.m4
|
||||
|
@ -66,6 +67,7 @@ stp/osmo-stp
|
|||
|
||||
*.pc
|
||||
config.*
|
||||
*.pyc
|
||||
|
||||
tags
|
||||
/Doxyfile
|
||||
|
|
|
@ -8,7 +8,7 @@ pkgconfig_DATA = libosmo-sccp.pc libosmo-mtp.pc libosmo-sigtran.pc libosmo-xua.p
|
|||
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
contrib/libosmo-sccp.spec.in \
|
||||
README.md \
|
||||
debian \
|
||||
git-version-gen \
|
||||
osmoappdesc.py \
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
libosmo-sccp - Osmocom SCCP, SIGTRAN and STP
|
||||
============================================
|
||||
|
||||
This repository contains
|
||||
|
||||
* *libosmo-sigtran*, a C-language library implementation of a variety of telecom signaling protocols, such as M3UA, SUA, SCCP
|
||||
(connection oriented and connectionless)
|
||||
* *OsmoSTP*, a SS7 Transfer Point that can be used to act as router and translator between M3UA, SUA and/or
|
||||
SCCPlite
|
||||
* *libosmo-sccp*, a legacy C-language [static] library that we used in prehistoric osmocom code before we had
|
||||
libosmo-sigtran.
|
||||
|
||||
Homepage
|
||||
--------
|
||||
|
||||
The official homepage of libosmo-sccp is at <https://osmocom.org/projects/libosmo-sccp/wiki>
|
||||
|
||||
The official homepage of osmo-stp is at <https://osmocom.org/projects/osmo-stp/wiki>
|
||||
|
||||
GIT Repository
|
||||
--------------
|
||||
|
||||
You can clone from the official git repository using
|
||||
|
||||
git clone https://gitea.osmocom.org/osmocom/libosmo-sccp
|
||||
|
||||
There is a web interface at <https://gitea.osmocom.org/osmocom/libosmo-sccp>
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
osmo-stp User Manuals and VTY reference manuals are [optionally] built in PDF form
|
||||
as part of the build process.
|
||||
|
||||
Pre-rendered PDF version of the current "master" can be found at
|
||||
[User Manual](https://ftp.osmocom.org/docs/latest/osmostp-usermanual.pdf)
|
||||
as well as the VTY reference manuals
|
||||
* [VTY Reference Manual for osmo-stp](https://ftp.osmocom.org/docs/latest/osmostp-vty-reference.pdf)
|
||||
|
||||
Forum
|
||||
-----
|
||||
|
||||
We welcome any libosmo-sigtran + osmo-stp related discussions in the
|
||||
[Cellular Network Infrastructure -> 2G/3G Core Network](https://discourse.osmocom.org/c/cni/2g-3g-cn/)
|
||||
section of the osmocom discourse (web based Forum).
|
||||
|
||||
Mailing List
|
||||
------------
|
||||
|
||||
Discussions related to osmo-stp are happening on the
|
||||
openbsc@lists.osmocom.org mailing list, please see
|
||||
https://lists.osmocom.org/mailman/listinfo/openbsc for subscription
|
||||
options and the list archive.
|
||||
|
||||
Please observe the [Osmocom Mailing List
|
||||
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
|
||||
when posting.
|
||||
|
||||
Issue Tracker
|
||||
-------------
|
||||
|
||||
We use the issue trackers of smocom.org for tracking the state of bug reports and feature requests. Feel free
|
||||
to submit any issues you may find, or help us out by resolving existing issues.
|
||||
|
||||
* [osmo-stp issue tracker](https://osmocom.org/projects/osmo-stp/issues)
|
||||
* [libosmo-sigtran issue tracker](https://osmocom.org/projects/libosmo-sccp/issues)
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Our coding standards are described at
|
||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
|
||||
|
||||
We us a gerrit based patch submission/review process for managing contributions. Please see
|
||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> for more details
|
||||
|
||||
The current patch queue can be seen at <https://gerrit.osmocom.org/#/q/project:libosmo-sccp+status:open>
|
11
TODO-RELEASE
11
TODO-RELEASE
|
@ -7,3 +7,14 @@
|
|||
# If any interfaces have been added since the last public release: c:r:a + 1.
|
||||
# If any interfaces have been removed or changed since the last public release: c:r:0.
|
||||
#library what description / commit summary line
|
||||
libosmocore >1.9.0 osmo_sock_multiaddr_{add,del}_local_addr()
|
||||
libosmo-netif >1.4.0 osmo_stream_{srv,cli}_get_fd()
|
||||
libosmocore >1.9.0 osmo_sock_multiaddr_get_ip_and_port(), osmo_multiaddr_ip_and_port_snprintf()
|
||||
libosmocore >1.9.0 osmo_sock_sctp_get_peer_addr_info()
|
||||
libosmo-netif >1.4.0 osmo_sctp_spinfo_state_str(), osmo_sctp_sstat_state_str()
|
||||
libosmo-sigtran ABI change struct osmo_ss7_asp: new field(s) at the end
|
||||
libosmo-sigtran ABI change struct osmo_xua_server: new field(s) at the end
|
||||
libosmo-sigtran API added osmo_ss7_asp_get_trans_proto()
|
||||
libosmo-sigtran API added osmo_ss7_asp_{find2,find_or_create2}()
|
||||
libosmo-sigtran API added osmo_ss7_xua_server_{find2,create2}()
|
||||
libosmo-netif >1.4.0 osmo_io SCTP support
|
||||
|
|
16
configure.ac
16
configure.ac
|
@ -9,6 +9,8 @@ AC_CONFIG_AUX_DIR([.])
|
|||
AM_INIT_AUTOMAKE([dist-bzip2])
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
|
||||
CFLAGS="$CFLAGS -std=gnu11"
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
|
@ -22,7 +24,7 @@ AC_PROG_CC
|
|||
AC_PROG_INSTALL
|
||||
LT_INIT
|
||||
|
||||
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
|
||||
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
|
||||
AS_CASE(["$LD"],[*clang*],
|
||||
[AS_CASE(["${host_os}"],
|
||||
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
|
||||
|
@ -34,10 +36,10 @@ if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
|||
fi
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.7.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.4.0)
|
||||
|
||||
old_LIBS=$LIBS
|
||||
AC_SEARCH_LIBS([sctp_recvmsg], [sctp], [
|
||||
|
@ -75,6 +77,7 @@ if test x"$werror" = x"yes"
|
|||
then
|
||||
WERROR_FLAGS="-Werror"
|
||||
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
|
||||
WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
|
||||
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
|
||||
CFLAGS="$CFLAGS $WERROR_FLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
|
||||
|
@ -168,7 +171,7 @@ if test "x$enable_ext_tests" = "xyes" ; then
|
|||
AM_PATH_PYTHON
|
||||
AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmo_verify_transcript_vty.py,yes)
|
||||
if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
|
||||
AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
|
||||
AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
|
||||
fi
|
||||
fi
|
||||
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
|
||||
|
@ -203,6 +206,5 @@ AC_OUTPUT(
|
|||
doc/manuals/Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
contrib/libosmo-sccp.spec
|
||||
Doxyfile
|
||||
Makefile)
|
||||
|
|
|
@ -36,7 +36,6 @@ osmo-build-dep.sh libosmo-netif
|
|||
# Additional configure options and depends
|
||||
CONFIG=""
|
||||
if [ "$WITH_MANUALS" = "1" ]; then
|
||||
osmo-build-dep.sh osmo-gsm-manuals
|
||||
CONFIG="--enable-manuals"
|
||||
fi
|
||||
|
||||
|
@ -51,13 +50,14 @@ set -x
|
|||
autoreconf --install --force
|
||||
./configure --enable-sanitize --enable-werror --enable-external-tests $CONFIG
|
||||
$MAKE $PARALLEL_MAKE
|
||||
$MAKE $PARALLEL_MAKE check || cat-testlogs.sh
|
||||
DISTCHECK_CONFIGURE_FLAGS="--enable-external-tests $CONFIG" \
|
||||
$MAKE distcheck \
|
||||
$MAKE $PARALLEL_MAKE distcheck \
|
||||
|| cat-testlogs.sh
|
||||
|
||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
|
||||
make -C "$base/doc/manuals" publish
|
||||
fi
|
||||
|
||||
$MAKE maintainer-clean
|
||||
$MAKE $PARALLEL_MAKE maintainer-clean
|
||||
osmo-clean-workspace.sh
|
||||
|
|
|
@ -1,188 +0,0 @@
|
|||
#
|
||||
# spec file for package libosmo-sccp
|
||||
#
|
||||
# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
# upon. The license for this file, and modifications and additions to the
|
||||
# file, is the same license as for the pristine package itself (unless the
|
||||
# license for the pristine package is not an Open Source License, in which
|
||||
# case the license is the MIT License). An "Open Source License" is a
|
||||
# license that conforms to the Open Source Definition (Version 1.9)
|
||||
# published by the Open Source Initiative.
|
||||
|
||||
# Avoid "E: lto-no-text-in-archive"
|
||||
# https://en.opensuse.org/openSUSE:LTO#Static_libraries
|
||||
%if 0%{?suse_version}
|
||||
%global _lto_cflags %{?_lto_cflags} -ffat-lto-objects
|
||||
%endif
|
||||
|
||||
Name: libosmo-sccp
|
||||
Version: @VERSION@
|
||||
Release: 0
|
||||
Summary: Osmocom library for the A-bis interface between BTS and BSC
|
||||
License: AGPL-3.0-or-later AND GPL-2.0-or-later
|
||||
Group: Hardware/Mobile
|
||||
URL: https://osmocom.org/projects/libosmo-sccp
|
||||
Source: %{name}-%{version}.tar.xz
|
||||
BuildRequires: automake >= 1.6
|
||||
BuildRequires: libtool >= 2
|
||||
BuildRequires: lksctp-tools-devel
|
||||
BuildRequires: pkgconfig >= 0.20
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
BuildRequires: xz
|
||||
BuildRequires: pkgconfig(libosmo-netif) >= 0.4.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.0.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.0.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.0.0
|
||||
%{?systemd_requires}
|
||||
|
||||
%description
|
||||
SCCP is a network layer protocol that provides extended routing, flow
|
||||
control, segmentation, connection-orientation, and error correction
|
||||
facilities in Signaling System 7 telecommunications networks. SCCP is
|
||||
heavily used in cellular networks such as GSM.
|
||||
|
||||
%package -n libosmo-mtp-devel
|
||||
Summary: Development files for the Osmocom MTP library
|
||||
License: GPL-2.0-or-later
|
||||
Group: Development/Libraries/C and C++
|
||||
|
||||
%description -n libosmo-mtp-devel
|
||||
MTP is part of SS7 used for communication in Public Switched
|
||||
Telephone Networks.
|
||||
|
||||
This subpackage contains the development files for the Osmocom MTP
|
||||
library.
|
||||
|
||||
%package -n libosmo-sccp-devel
|
||||
Summary: Development files for the Osmocom SCCP library
|
||||
License: GPL-2.0-or-later
|
||||
Group: Development/Libraries/C and C++
|
||||
|
||||
%description -n libosmo-sccp-devel
|
||||
SCCP is a network layer protocol that provides routing, flow control,
|
||||
segmentation, connection-orientation, and error correction facilities
|
||||
in SS7 telecommunications networks.
|
||||
|
||||
This subpackage contains the development files for the Osmocom SCCP
|
||||
library.
|
||||
|
||||
%package -n libosmo-sigtran5
|
||||
Summary: Osmocom SIGTRAN library
|
||||
License: GPL-2.0-or-later
|
||||
Group: System/Libraries
|
||||
|
||||
%description -n libosmo-sigtran5
|
||||
Osmocom implementation of (parts of) SIGTRAN.
|
||||
|
||||
%package -n libosmo-sigtran-devel
|
||||
Summary: Development files for the Osmocom sigtran library
|
||||
License: GPL-2.0-or-later
|
||||
Group: Development/Libraries/C and C++
|
||||
Requires: libosmo-sigtran5 = %{version}
|
||||
|
||||
%description -n libosmo-sigtran-devel
|
||||
Osmocom implementation of (parts of) SIGTRAN.
|
||||
|
||||
This subpackage contains the development files for the Osmocom
|
||||
SIGTRAN library.
|
||||
|
||||
%package -n libosmo-xua-devel
|
||||
Summary: Development files for the Osmocom M2UA library
|
||||
License: GPL-2.0-or-later
|
||||
Group: Development/Libraries/C and C++
|
||||
Requires: libosmo-sigtran-devel = %{version}
|
||||
|
||||
%description -n libosmo-xua-devel
|
||||
M2UA provides an SCTP adaptation layer for MTP level 2 user messages
|
||||
and service interface across an IP network.
|
||||
|
||||
This subpackage contains the development files for the Osmocom M2UA
|
||||
library.
|
||||
|
||||
%package -n osmo-stp
|
||||
Summary: Osmocom SIGTRAN STP (Signaling Transfer Point)
|
||||
License: GPL-2.0-or-later
|
||||
Group: Productivity/Telephony/Servers
|
||||
|
||||
%description -n osmo-stp
|
||||
This is the Osmocom (Open Source Mobile Communications) implementation
|
||||
of a Signaling Transfer Point (STP) for SS7/SIGTRAN telecommunication
|
||||
networks. At this point it is a very minimal implementation, missing
|
||||
lots of the functionality usually present in a STP, such as Global Title
|
||||
Routing, Global Title Translation.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
echo "%{version}" >.tarball-version
|
||||
autoreconf -fiv
|
||||
%configure \
|
||||
--enable-shared \
|
||||
--includedir="%{_includedir}/%{name}" \
|
||||
--docdir="%{_docdir}/%{name}" \
|
||||
--with-systemdsystemunitdir=%{_unitdir}
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
%make_install
|
||||
find %{buildroot} -type f -name "*.la" -delete -print
|
||||
|
||||
%check
|
||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
|
||||
%post -n libosmo-sigtran5 -p /sbin/ldconfig
|
||||
%postun -n libosmo-sigtran5 -p /sbin/ldconfig
|
||||
%if 0%{?suse_version}
|
||||
%preun -n osmo-stp %service_del_preun osmo-stp.service
|
||||
%postun -n osmo-stp %service_del_postun osmo-stp.service
|
||||
%pre -n osmo-stp %service_add_pre osmo-stp.service
|
||||
%post -n osmo-stp %service_add_post osmo-stp.service
|
||||
%endif
|
||||
|
||||
%files -n libosmo-mtp-devel
|
||||
%dir %{_includedir}/%{name}
|
||||
%dir %{_includedir}/%{name}/osmocom
|
||||
%{_includedir}/%{name}/osmocom/mtp/
|
||||
%{_libdir}/libmtp.a
|
||||
%{_libdir}/pkgconfig/libosmo-mtp.pc
|
||||
|
||||
%files -n libosmo-sccp-devel
|
||||
%dir %{_includedir}/%{name}
|
||||
%dir %{_includedir}/%{name}/osmocom
|
||||
%{_includedir}/%{name}/osmocom/sccp/
|
||||
%{_libdir}/libsccp.a
|
||||
%{_libdir}/pkgconfig/libosmo-sccp.pc
|
||||
|
||||
%files -n libosmo-sigtran5
|
||||
%{_libdir}/libosmo-sigtran.so.5*
|
||||
|
||||
%files -n libosmo-sigtran-devel
|
||||
%dir %{_includedir}/%{name}
|
||||
%dir %{_includedir}/%{name}/osmocom
|
||||
%{_includedir}/%{name}/osmocom/sigtran
|
||||
%{_libdir}/libosmo-sigtran.so
|
||||
%{_libdir}/libosmo-sigtran.a
|
||||
%{_libdir}/pkgconfig/libosmo-sigtran.pc
|
||||
|
||||
%files -n libosmo-xua-devel
|
||||
%{_libdir}/libxua.a
|
||||
%{_libdir}/pkgconfig/libosmo-xua.pc
|
||||
|
||||
%files -n osmo-stp
|
||||
%{_bindir}/osmo-stp
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-stp.cfg
|
||||
%{_unitdir}/osmo-stp.service
|
||||
%dir %{_docdir}/%{name}
|
||||
%dir %{_docdir}/%{name}/examples
|
||||
%dir %{_docdir}/%{name}/examples/osmo-stp
|
||||
%{_docdir}/%{name}/examples/osmo-stp/osmo-stp.cfg
|
||||
%{_docdir}/%{name}/examples/osmo-stp/osmo-stp-multihome.cfg
|
||||
|
||||
%changelog
|
|
@ -1,10 +1,16 @@
|
|||
[Unit]
|
||||
Description=Osmocom STP (Signal Transfer Point)
|
||||
Documentation=https://osmocom.org/projects/osmo-stp/wiki
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
StateDirectory=osmocom
|
||||
WorkingDirectory=%S/osmocom
|
||||
User=osmocom
|
||||
Group=osmocom
|
||||
ExecStart=/usr/bin/osmo-stp -c /etc/osmocom/osmo-stp.cfg
|
||||
RestartSec=2
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# this script executes m3ua-testtool against osmo-stp. It assumes that
|
||||
# it is called from within libosmo-sccp/contrib/test and also assumes
|
||||
# that adjacent to the libosmo-sccp, there's a check-out of
|
||||
# git://git.osmocom.org/nplab/m3ua-testtool
|
||||
# https://gitea.osmocom.org/nplab/m3ua-testtool
|
||||
|
||||
# the top of the libosmo-sccp git repository
|
||||
TOPDIR=../../
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# this script executes m3ua-testtool against osmo-stp. It assumes that
|
||||
# it is called from within libosmo-sccp/contrib/test and also assumes
|
||||
# that adjacent to the libosmo-sccp, there's a check-out of
|
||||
# git://git.osmocom.org/nplab/m3ua-testtool
|
||||
# https://gitea.osmocom.org/nplab/sua-testtool
|
||||
|
||||
# the top of the libosmo-sccp git repository
|
||||
TOPDIR=../../
|
||||
|
|
|
@ -1,3 +1,267 @@
|
|||
libosmo-sccp (1.8.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* sccp_scmg: ignore PCSTATE.indication
|
||||
* add osmo_scu_prim_name_c() / _buf()
|
||||
* fixup for: add osmo_scu_prim_name_c() / _buf()
|
||||
* fix sccp_scoc state S_WAIT_CONN_CONF out_state_mask
|
||||
* add two value_string[]s related to N-PCSTATE
|
||||
* comment: fix typo 'OSMO_ASCU_'
|
||||
* SCCP: implement variable limit on Optional Data (CR,CC,CREF,RLSD)
|
||||
* add osmo_sccp_set_max_optional_data()
|
||||
* sccp_scoc.c: fix infinite loop on conn ID exhaustion
|
||||
* add public API: osmo_sccp_instance_next_conn_id()
|
||||
|
||||
[ Oliver Smith ]
|
||||
* Run struct_endianness.py
|
||||
* debian: set compat level to 10
|
||||
* systemd: depend on networking-online.target
|
||||
|
||||
[ Max ]
|
||||
* SS7: do not attempt transfer if AS is down
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* mtp_pcap: remove unused static_assert() macro definition
|
||||
* {examples,stp}/Makefile.am: fix typo in $(COVERAGE_[C]FLAGS)
|
||||
* tests/*/Makefile.am: -Wall is not a preprocessor flag
|
||||
* tests: use -no-install libtool flag to avoid ./lt-* scripts
|
||||
* tests: $(BUILT_SOURCES) is not defined, depend on osmo-stp
|
||||
* tests: execute osmotest{vty,config}.py against osmo-stp
|
||||
* osmoappdesc.py: also test doc/examples/osmo-stp-multihome.cfg
|
||||
* copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
|
||||
|
||||
[ arehbein ]
|
||||
* Transition to use of 'telnet_init_default'
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* Avoid allocating conn_id 0x00FFFFFF
|
||||
* sccp_scpc.c: Simplify conn_create()
|
||||
* cosmetic: sccp.h: Fix trailing whitespace
|
||||
* Implement sccp_instance->connections as rbtree
|
||||
* sccp: Introduce APIs to get/set osmo_sccp_instance priv data pointer
|
||||
* .gitignore: Ignore *.pyc
|
||||
* osmo_sccp_simple_client_on_ss7_id(): Support ASP explicitly configured as sctp server
|
||||
* osmo_sccp_simple_client_on_ss7_id(): Always follow VTY config of ASP if it was explicitly defined in VTY
|
||||
* Forbid partial VTY configurations of ASPs with name asp-clnt-*
|
||||
* Add osmo_ss7_asp getters for name and proto
|
||||
* Set stream_cli/srv name to contain ASP + sockname
|
||||
* vty: Improve output of show cs7 instance <0-15> asp
|
||||
* xua_default_lm_fsm.c: Log rx ASP_UP timeout with NOTICE level
|
||||
* sccp: Use tdef to implement osmo_sccp_timers
|
||||
* ss7: Refactor osmo_ss7_asp_find_or_create()
|
||||
* asp: Make ASP default LM timers VTY configurable
|
||||
* cosmetic: Fix pointer location format
|
||||
* tests/vty: test multiple local/remote IP addresses
|
||||
* ss7: Log addr and event state for SCTP_PEER_ADDR_CHANGE
|
||||
* ss7: Use libosmo-netif's osmo_stream_{cli,srv}_recv() APIs
|
||||
* asp: Allow setting IP address as SCTP primary upon conn establishment
|
||||
* asp: Monitor SCTP_PEER_ADDR_CHANGE events to re-apply configured Primary Address
|
||||
* ss7: Rename internal APIs to avoid exporting them in so file
|
||||
* ss7: Drop unneeded else branch after early return
|
||||
* ss7: Split asp and xua_server into their own files
|
||||
* asp,xua_srv: Use new osmo_stream API to request sockopt SCTP AUTH/ASCONF SUPPORTED
|
||||
* Allow configuring per-ASP SCTP INIT parameters
|
||||
|
||||
[ Harald Welte ]
|
||||
* cosmetic: Fix log message in routing table add error case
|
||||
* ASPAC/ASPIA: Don't add routing context IE in ASP-role if routing context 0
|
||||
* cosmetic: fix various typos in comments, log messages and VTY strings
|
||||
* M3UA/SUA: Don't add empty routing context IE in DUNA/DAVA/DUPU
|
||||
* ipa: use LSBs of file descriptor as SLS in IPA->M3UA direction
|
||||
* m3ua: Add some TODO comments on where we fall short of our potential
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 14:03:24 +0200
|
||||
|
||||
libosmo-sccp (1.7.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* xua_snm: assert to guard against NULL ptr
|
||||
* sccp: Validate local references > 0x00fffffe are not used
|
||||
|
||||
[ Max ]
|
||||
* cosmetic: improve usage help of example code
|
||||
* SCCP: enforce optional data length limits
|
||||
* SCCP: Log more data on CR size error
|
||||
* [cosmetic] sccp_scoc.c: constify function parameters
|
||||
* Add basic readme for example code
|
||||
* SIGTRAN: don't advance FSM on failed connection request
|
||||
* SIGTRAN: add function to check connection existence
|
||||
* SIGTRAN: error if attempting to send exceedingly big data
|
||||
* SIGTRAN: add osmo_sccp_tx_disconn_data() helper
|
||||
* examples: update vty code
|
||||
* SIGTRAN: arrange the comments in the encoder to match the spec
|
||||
* SIGTRAN: cache Optional Data for SCCP CR/CC/RLSD
|
||||
* Set working directory in systemd service file
|
||||
|
||||
[ Harald Welte ]
|
||||
* Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition
|
||||
* SCOC: dump SUA header when logging "mismatching remote pc"
|
||||
* cosmetic: fix typo in comment
|
||||
|
||||
[ Oliver Smith ]
|
||||
* tests: fix old-style function definition
|
||||
* contrib/jenkins.sh: add missing 'make check'
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 14:37:23 +0100
|
||||
|
||||
libosmo-sccp (1.6.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* ss7: Use sctp value_string from libosmo-netif
|
||||
* xua_default_lm_fsm.c: Fix typo in comment
|
||||
* xua_asp_fsm: Fix gcc false positive warning
|
||||
* Add ss7 instances to osmo_ss7_instances in creation order
|
||||
|
||||
[ Oliver Smith ]
|
||||
* treewide: remove FSF address
|
||||
* src/sccp_scmg: fix typo
|
||||
* osmo_ss7_instance_destroy: use for_each_entry_safe
|
||||
* include/osmocom/sigtran/sccp_sap.h: cosmetic fixes
|
||||
* osmo_ss7_vty: add osmo_sccp_addr_by_name_local
|
||||
|
||||
[ Philipp Maier ]
|
||||
* sccp_user: do not force the role ASP when configured differently
|
||||
|
||||
[ Harald Welte ]
|
||||
* sccp_demo_user: Add command line argument to specify protocol (m3ua/ipa)
|
||||
* sccp_demo_user: Add 'show stats' to VTY
|
||||
* sccp_demo_user: make sure the command-line specified point codes are used
|
||||
* sccp_demo_user: Allow user to specify logmask on command line
|
||||
* sccp_demo_user: enable stats / rate_counter timers
|
||||
* M3UA/SUA: Implement handling of SCON (signaling congestion)
|
||||
* [cosmetic] sccp_scoc.c: fix comments about corresponding SCCP msg types
|
||||
* [cosmetic] sccp_scoc: Fix typo in comment
|
||||
* update git URLs (git -> https; gitea)
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 28 Jun 2022 18:22:13 +0200
|
||||
|
||||
libosmo-sccp (1.5.0) unstable; urgency=medium
|
||||
|
||||
[ Harald Welte ]
|
||||
* Add README.md describing what this repo is about
|
||||
* ipa: Use VTY-configured default network indicator
|
||||
* ipa_asp_fsm_del_route(): Log what happens if we bail out early
|
||||
* don't log "SCTP connection close" if an IPA/TCP connection closes
|
||||
* osmo_ss7: Log route destruction (like we log route creation)
|
||||
* ipa_asp_fsm: Fix AS lookup from IPA ASP
|
||||
* Don't create duplicate routes in osmo_ss7_route_create()
|
||||
* ipa: Move automatic route add/del from ASP to AS level
|
||||
* osmo_ss7: Truncate route 'mask' to point code bit length
|
||||
* xua_as_fsm: Only delete a route for an IPA AS if we created one earlier
|
||||
* xua_asp_fsm: Fix IPA client role
|
||||
* don't do explicit NULL checks before calling talloc_free()
|
||||
* osmo_ss7: free the sock_name string once an ASP socket is closed
|
||||
* Revert "osmo_ss7: free the sock_name string once an ASP socket is closed"
|
||||
* ss7_vty: Print actual connected port number in case of IPA server
|
||||
* Constrain connection ID allocation to 24 bits
|
||||
* ss7_asp_test.vty: Allow more asp node commands after shutdown
|
||||
* osmo_ss7: introduce notion of configurable 'quirks'
|
||||
* m3ua/sua: Add quirk for allowing inbound DAUD from SG in ASP role.
|
||||
* m3ua/sua: Add new snm_inactive quirk
|
||||
* sccp_scrc: Ensure we have not just SSN but at least OPC+SSN in CallingParty
|
||||
* sccp: Add minimalistic support for XUDT / XUDTS
|
||||
* sccp_scmg: Add missing line eneding to log statement
|
||||
* stp: Add basic RX/TX rate counters on AS and ASP level
|
||||
* spelling fixes in comments
|
||||
* rate_ctr: Use legal counter group prefixes / names (no '.' allowed)
|
||||
* osmo_ss7: properly unlink rate_counter on AS/ASP free
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* VTY: write_one_asp(): fix 1 << 31 cannot be represented by 'int'
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* cosmetic: xua_asp_fsm.c: reorder functions in file
|
||||
* ipa_asp_fsm: Support server starting handshake with ID_GET or ID_ACK
|
||||
* cosmetic: Fix extra empty line
|
||||
* cosmetic: Fix typos in documentation
|
||||
* osmo_sccp_simple_client_on_ss7_id(): Allow set internally proper IPv4/v6 default hosts
|
||||
* osmo_ss7.c: Fix typo in doc for osmo_ss7_asp_peer_add_host()
|
||||
|
||||
[ Philipp Maier ]
|
||||
* osmo_ss7_vty: automatically create routes for routing key
|
||||
* osmo_ss7_vty: allow users to inspect routes also in ASP role
|
||||
|
||||
[ Eric ]
|
||||
* m3ua: fix leaky usage of m3ua_tx_xua_asp
|
||||
|
||||
[ Oliver Smith ]
|
||||
* m3ua: reject deregistration of static routing keys
|
||||
* m3ua: fix m3ua_encode_dupu
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 14:44:42 +0100
|
||||
|
||||
libosmo-sccp (1.4.0) unstable; urgency=medium
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* debian/control: change maintainer to the Osmocom team / mailing list
|
||||
* vty: use install_lib_element() and install_lib_element_ve()
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* doc: Update VTY reference xml file
|
||||
* Support setting rt-prio and cpu-affinity mask through VTY
|
||||
* configure.ac: Fix trailing whitespace
|
||||
* Introduce SS7 IPv6 support
|
||||
* Fix finding ASP on IPv6 connections
|
||||
* vty: log and return warning if adding ss7 host to set fails
|
||||
* Revert "Revert "Introduce SS7 IPv6 support""
|
||||
* Fix change in ss7 server default listen addr, keeps backward-compatibility behavior
|
||||
* Validate correctly against IPv6 addresses in osmo_ss7_asp_peer_set_hosts()
|
||||
* Fix matching IPv6 anyaddr :: in osmo_ss7_asp_find_by_socket_addr
|
||||
* Fix (2) change in ss7 server default listen addr, keeps backward-compatibility behavior
|
||||
* Fix default ASP local addr if remote addr contains only IPv4
|
||||
* sua: Support SUA_IEI_IPv6
|
||||
* xua_msg: Get Rid of confusing double network byte order swap
|
||||
* sccp_helpers: Support printing IPV6 SCCP addresses
|
||||
* contrib/jenkins: Enable parallel make in make distcheck
|
||||
* tests: Explicitly drop category from log
|
||||
* gitignore: Ignore autofoo *~ churn
|
||||
* tests: Replace deprecated API log_set_print_filename
|
||||
* stp: generate coredump and exit upon SIGABRT received
|
||||
|
||||
[ neels ]
|
||||
* Revert "Introduce SS7 IPv6 support"
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* enum osmo_sccp_ssn: rename to OSMO_SCCP_SSN_SMLC_BSSAP_LE
|
||||
* add OSMO_SCCP_ADDR_T_MASK for osmo_sccp_addr_cmp()
|
||||
* add osmo_sccp_addr_to_str_c osmo_sccp_inst_addr_to_str_c
|
||||
* add osmo_sccp_addr_to_id_c()
|
||||
|
||||
[ Philipp Maier ]
|
||||
* vty: add attributes to VTY commands indicating when they apply
|
||||
|
||||
[ Harald Welte ]
|
||||
* update VTY / copyright message
|
||||
* xua_msg_add_data() Mark input argument as 'const'
|
||||
* xua_msg: const-ify input arguments to xua_*_get_u32()
|
||||
* osmo_ss7: const-ify input arguments to osmo_ss7_as_has_asp()
|
||||
* sccp_sap: Fix string for N-PCSTATE primitive
|
||||
* xua_msg: Add xua_msg*_get_str() to obtain string IE
|
||||
* Introduce osmo_ss7_asp_get_log_subsys()
|
||||
* m3ua: re-factor m3ua_rx_xfer(): Externalize AS for ASP lookup
|
||||
* m3ua: Move find_as_for_asp() to shared xua_find_as_for_asp()
|
||||
* SUA: Verify routing context IE of incoming CL and CO messages
|
||||
* xua: Implement SNM availability/unavailability messaging
|
||||
* sccp: Notify users of point code available/unavailable
|
||||
* sccp: Add minimalistic SCMG implementation
|
||||
* xua: introduce xua_msg_get_u32p() helper
|
||||
* sua: per-ssn DUNA/DAVA notification
|
||||
* sccp: more SCMG handling: SSA and SSP message handling
|
||||
* xua_snm: Implement handling of DUPU messages
|
||||
* Add osmo_ss7_pointcode_print_buf() to API
|
||||
* main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
|
||||
* manuals: generate vty reference xml at build time
|
||||
|
||||
[ Oliver Smith ]
|
||||
* contrib/jenkins: don't build osmo-gsm-manuals
|
||||
* configure.ac: set -std=gnu11
|
||||
* xua_test: fix gcc 4 + -std=gnu11 error
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 17:57:33 +0100
|
||||
|
||||
libosmo-sccp (1.3.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
|
|
|
@ -1 +1 @@
|
|||
9
|
||||
10
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
Source: libosmo-sccp
|
||||
Maintainer: Harald Welte <laforge@gnumonks.org>
|
||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
||||
Section: libs
|
||||
Priority: optional
|
||||
Build-Depends: debhelper (>= 9),
|
||||
Build-Depends: debhelper (>= 10),
|
||||
autotools-dev,
|
||||
pkg-config,
|
||||
libosmocore-dev (>= 1.3.0),
|
||||
libosmocore-dev (>= 1.9.0),
|
||||
autoconf,
|
||||
automake,
|
||||
libtool,
|
||||
|
@ -13,12 +13,12 @@ Build-Depends: debhelper (>= 9),
|
|||
git,
|
||||
doxygen,
|
||||
libdpkg-perl,
|
||||
libosmo-netif-dev (>= 0.7.0),
|
||||
libosmo-netif-dev (>= 1.4.0),
|
||||
libsctp-dev,
|
||||
osmo-gsm-manuals-dev
|
||||
osmo-gsm-manuals-dev (>= 1.5.0)
|
||||
Standards-Version: 3.9.7
|
||||
Vcs-Git: git://git.osmocom.org/libosmo-sccp.git
|
||||
Vcs-Browser: http://git.osmocom.org/libosmo-sccp/
|
||||
Vcs-Git: https://gitea.osmocom.org/osmocom/libosmo-sccp
|
||||
Vcs-Browser: https://gitea.osmocom.org/osmocom/libosmo-sccp
|
||||
Homepage: https://projects.osmocom.org/projects/libosmo-sccp
|
||||
|
||||
Package: libosmo-sccp-dev
|
||||
|
@ -34,7 +34,7 @@ Description: Development files for libsccp, libmtp and libxua
|
|||
.
|
||||
This package contains the development files for the library.
|
||||
|
||||
Package: libosmo-sigtran5
|
||||
Package: libosmo-sigtran9
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
|
@ -53,7 +53,7 @@ Package: libosmo-sigtran-dbg
|
|||
Architecture: any
|
||||
Section: debug
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-sigtran5 (= ${binary:Version}),
|
||||
Depends: libosmo-sigtran9 (= ${binary:Version}),
|
||||
${misc:Depends}
|
||||
Description: Documentation for the Osmocom SIGTRAN library
|
||||
libosmo-sigtran is a shared library containing SS7/SIGTRAN related functionality,
|
||||
|
@ -68,7 +68,7 @@ Package: libosmo-sigtran-doc
|
|||
Architecture: all
|
||||
Section: doc
|
||||
Depends: ${misc:Depends},
|
||||
libosmo-sigtran5 (= ${binary:Version}),
|
||||
libosmo-sigtran9 (= ${binary:Version}),
|
||||
libjs-jquery
|
||||
Description: Documentation for the Osmocom SIGTRAN library
|
||||
libosmo-sigtran is a shared library containing SS7/SIGTRAN related functionality,
|
||||
|
@ -83,7 +83,7 @@ Architecture: any
|
|||
Multi-Arch: same
|
||||
Section: libdevel
|
||||
Depends: ${misc:Depends},
|
||||
libosmo-sigtran5 (= ${binary:Version}),
|
||||
libosmo-sigtran9 (= ${binary:Version}),
|
||||
libosmocore-dev,
|
||||
libosmo-netif-dev
|
||||
Description: Development headers for the Osmocom SIGTRAN library
|
||||
|
@ -98,7 +98,7 @@ Package: osmo-stp
|
|||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Section: net
|
||||
Depends: libosmo-sigtran5,
|
||||
Depends: libosmo-sigtran9,
|
||||
${shlibs:Depends},
|
||||
${misc:Depends}
|
||||
Description: Osmocom SIGTRAN STP (Signaling Transfer Point)
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
# Create the osmocom group and user (if it doesn't exist yet)
|
||||
if ! getent group osmocom >/dev/null; then
|
||||
groupadd --system osmocom
|
||||
fi
|
||||
if ! getent passwd osmocom >/dev/null; then
|
||||
useradd \
|
||||
--system \
|
||||
--gid osmocom \
|
||||
--home-dir /var/lib/osmocom \
|
||||
--shell /sbin/nologin \
|
||||
--comment "Open Source Mobile Communications" \
|
||||
osmocom
|
||||
fi
|
||||
|
||||
# Fix permissions of previous (root-owned) install (OS#4107)
|
||||
if dpkg --compare-versions "$2" le "1.9.0"; then
|
||||
if [ -e /etc/osmocom/osmo-stp.cfg ]; then
|
||||
chown -v osmocom:osmocom /etc/osmocom/osmo-stp.cfg
|
||||
chmod -v 0660 /etc/osmocom/osmo-stp.cfg
|
||||
fi
|
||||
|
||||
if [ -d /etc/osmocom ]; then
|
||||
chown -v root:osmocom /etc/osmocom
|
||||
chmod -v 2775 /etc/osmocom
|
||||
fi
|
||||
|
||||
mkdir -p /var/lib/osmocom
|
||||
chown -R -v osmocom:osmocom /var/lib/osmocom
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# dh_installdeb(1) will replace this with shell code automatically
|
||||
# generated by other debhelper scripts.
|
||||
#DEBHELPER#
|
|
@ -12,7 +12,7 @@ VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g')
|
|||
dh $@ --with autoreconf
|
||||
|
||||
override_dh_strip:
|
||||
dh_strip -plibosmo-sigtran5 --dbg-package=libosmo-sigtran-dbg
|
||||
dh_strip -plibosmo-sigtran9 --dbg-package=libosmo-sigtran-dbg
|
||||
dh_strip -posmo-stp --dbg-package=osmo-stp-dbg
|
||||
|
||||
override_dh_install:
|
||||
|
|
|
@ -20,3 +20,4 @@ cs7 instance 0
|
|||
accept-asp-connections dynamic-permitted
|
||||
local-ip 127.0.0.2
|
||||
local-ip 127.0.0.1
|
||||
local-ip ::1
|
||||
|
|
|
@ -12,6 +12,12 @@ if BUILD_MANUALS
|
|||
aoip-mgw-options.pdf: $(srcdir)/aoip-mgw-options.adoc $(srcdir)/mgw/*.msc
|
||||
|
||||
VTY_REFERENCE = osmostp-vty-reference.xml
|
||||
|
||||
BUILT_REFERENCE_XML = $(builddir)/vty/osmo-stp_vty_reference.xml
|
||||
$(builddir)/vty/osmo-stp_vty_reference.xml: $(top_builddir)/stp/osmo-stp
|
||||
mkdir -p $(builddir)/vty
|
||||
$(top_builddir)/stp/osmo-stp --vty-ref-xml > $@
|
||||
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
|
||||
|
||||
OSMO_REPOSITORY=osmo-stp
|
||||
|
|
|
@ -18,6 +18,8 @@ include::./common/chapters/vty.adoc[]
|
|||
|
||||
include::./common/chapters/logging.adoc[]
|
||||
|
||||
include::./common/chapters/vty_cpu_sched.adoc[]
|
||||
|
||||
//include::./common/chapters/bts.adoc[]
|
||||
|
||||
//include::../OsmoNITB/chapters/bts-examples.adoc[]
|
||||
|
@ -31,4 +33,3 @@ include::./common/chapters/bibliography.adoc[]
|
|||
include::./common/chapters/glossary.adoc[]
|
||||
|
||||
include::./common/chapters/gfdl.adoc[]
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
|
||||
</vtydoc>
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
|||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(COVERAGE_FLAGS)
|
||||
AM_CFLAGS=-Wall -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS)
|
||||
AM_LDFLAGS=$(COVERAGE_LDFLAGS)
|
||||
|
||||
noinst_HEADERS = internal.h
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
This example code is handy in illustrating libosmo-sigtran library use as well as experimenting with SCCP protocol.
|
||||
|
||||
Run it as follows:
|
||||
Server:
|
||||
./sccp_demo_user -d DLINP,5:DLSS7,5:DLSCCP,1:DSCCP,1:DMAIN,1
|
||||
Client:
|
||||
./sccp_demo_user -d DLINP,5:DLSS7,5:DLSCCP,1:DSCCP,1:DMAIN,1 -c
|
||||
Server's vty:
|
||||
telnet 127.0.0.1 2324
|
||||
Client's vty:
|
||||
telnet 127.0.0.2 2325
|
||||
|
||||
On the client side, after entering privileged mode by typing 'enable', you can switch to demo user mode with 'sccp-user' command.
|
||||
|
||||
This gives several additional commands, for example:
|
||||
'called-addr-ssn 202' - selecting "echo" application (201 is "refuser", 203 is "callback")
|
||||
'connect-req 10 aaaaaaaaaaaaaaaaaaa' - sending N-CONNECT message with ID=12 and optional data
|
||||
|
||||
See vty's help for further details.
|
||||
|
||||
Calling 'show cs7 instance 0 sccp connections' in privileged mode on the server will show currently active SCCP connections.
|
|
@ -12,6 +12,9 @@ enum {
|
|||
|
||||
struct osmo_sccp_user;
|
||||
|
||||
extern struct osmo_sccp_addr g_calling_addr;
|
||||
extern struct osmo_sccp_addr g_called_addr;
|
||||
|
||||
int sccp_test_user_vty_install(struct osmo_sccp_instance *inst, int ssn);
|
||||
|
||||
int sccp_test_server_init(struct osmo_sccp_instance *sccp);
|
||||
|
|
|
@ -11,9 +11,12 @@
|
|||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/stats.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
|
@ -28,16 +31,17 @@ static const char *config_file;
|
|||
|
||||
static struct osmo_sccp_instance *g_sccp;
|
||||
|
||||
static struct osmo_sccp_instance *sua_server_helper(int local_port, const char *local_address, int local_pc,
|
||||
static struct osmo_sccp_instance *sua_server_helper(enum osmo_ss7_asp_protocol protocol,
|
||||
int local_port, const char *local_address, int local_pc,
|
||||
int remote_port, const char *remote_address, int remote_pc)
|
||||
{
|
||||
struct osmo_sccp_instance *sccp;
|
||||
|
||||
sccp = osmo_sccp_simple_server(NULL, local_pc, OSMO_SS7_ASP_PROT_M3UA, local_port, local_address);
|
||||
sccp = osmo_sccp_simple_server(NULL, local_pc, protocol, local_port, local_address);
|
||||
if (sccp == NULL)
|
||||
return NULL;
|
||||
|
||||
osmo_sccp_simple_server_add_clnt(sccp, OSMO_SS7_ASP_PROT_M3UA, "client", remote_pc, local_port,
|
||||
osmo_sccp_simple_server_add_clnt(sccp, protocol, "client", remote_pc, local_port,
|
||||
remote_port, remote_address);
|
||||
|
||||
return sccp;
|
||||
|
@ -101,7 +105,6 @@ static struct vty_app_info vty_info = {
|
|||
#define DEFAULT_LOCAL_PORT_CLIENT M3UA_PORT
|
||||
#define DEFAULT_REMOTE_PORT_CLIENT DEFAULT_LOCAL_PORT_SERVER
|
||||
#define DEFAULT_REMOTE_PORT_SERVER DEFAULT_LOCAL_PORT_CLIENT
|
||||
#define DEFAULT_REMOTE_PORT_SERVER_STR DEFAULT_LOCAL_PORT_CLIENT_STR
|
||||
#define DEFAULT_PC_SERVER 1
|
||||
#define DEFAULT_PC_CLIENT 23
|
||||
|
||||
|
@ -110,13 +113,16 @@ static void usage(void) {
|
|||
" [-r REMOTE_ADDRESS[:REMOTE_PORT]]\n"
|
||||
" [-L LOCAL_POINT_CODE] [-R REMOTE_POINT_CODE]\n"
|
||||
"Options:\n"
|
||||
" -p: protocol to use (m3ua, sua, ipa; default is m3ua)\n"
|
||||
" -c: Run in client mode (default is server mode)\n"
|
||||
" -C filename The config file to use\n"
|
||||
" -l: local IP address and SCTP port (default is %s:%d in server mode,\n"
|
||||
" %s:%d in client mode)\n"
|
||||
" -r: remote IP address and SCTP port (default is %s:%d in server mode,\n"
|
||||
" %s:%d in client mode)\n"
|
||||
" -L: local point code (default is %d in server mode, %d in client mode)\n"
|
||||
" -R: remote point code (default is %d in server mode, %d in client mode)\n",
|
||||
" -R: remote point code (default is %d in server mode, %d in client mode)\n"
|
||||
" -d: LOGMASK (libosmocore log mask string, e.g. -d DLINP,1:DLSS7,2)\n",
|
||||
DEFAULT_LOCAL_ADDRESS_SERVER, DEFAULT_LOCAL_PORT_SERVER,
|
||||
DEFAULT_LOCAL_ADDRESS_CLIENT, DEFAULT_LOCAL_PORT_CLIENT,
|
||||
DEFAULT_REMOTE_ADDRESS_SERVER, DEFAULT_REMOTE_PORT_SERVER,
|
||||
|
@ -179,9 +185,19 @@ int main(int argc, char **argv)
|
|||
int remote_port = DEFAULT_REMOTE_PORT_SERVER;
|
||||
int remote_pc = DEFAULT_PC_CLIENT;
|
||||
bool lflag = false, rflag = false, Lflag = false, Rflag = false;
|
||||
enum osmo_ss7_asp_protocol protocol = OSMO_SS7_ASP_PROT_M3UA;
|
||||
|
||||
while ((ch = getopt(argc, argv, "cl:r:L:R:C:")) != -1) {
|
||||
void *tall_ctx = talloc_named_const(NULL, 1, "sccp_demo_user");
|
||||
init_logging(tall_ctx);
|
||||
|
||||
while ((ch = getopt(argc, argv, "p:cl:r:L:R:C:d:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'p':
|
||||
rc = get_string_value(osmo_ss7_asp_protocol_vals, optarg);
|
||||
if (rc < 0)
|
||||
exit(1);
|
||||
protocol = rc;
|
||||
break;
|
||||
case 'c':
|
||||
client = true;
|
||||
|
||||
|
@ -228,6 +244,9 @@ int main(int argc, char **argv)
|
|||
case 'C':
|
||||
config_file = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
log_parse_category_mask(osmo_stderr_target, optarg);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
|
@ -243,14 +262,15 @@ int main(int argc, char **argv)
|
|||
signal(SIGUSR1, &signal_handler);
|
||||
signal(SIGUSR2, &signal_handler);
|
||||
|
||||
void *tall_ctx = talloc_named_const(NULL, 1, "sccp_demo_user");
|
||||
init_logging(tall_ctx);
|
||||
osmo_stats_init(tall_ctx);
|
||||
rate_ctr_init(tall_ctx);
|
||||
OSMO_ASSERT(osmo_ss7_init() == 0);
|
||||
osmo_fsm_log_addr(false);
|
||||
vty_info.tall_ctx = tall_ctx;
|
||||
vty_init(&vty_info);
|
||||
logging_vty_add_cmds();
|
||||
osmo_talloc_vty_add_cmds();
|
||||
osmo_stats_vty_add_cmds();
|
||||
osmo_fsm_vty_add_cmds();
|
||||
osmo_ss7_vty_init_asp(NULL);
|
||||
osmo_sccp_vty_init();
|
||||
|
@ -265,15 +285,14 @@ int main(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
rc = telnet_init_dynif(NULL, NULL, config_file ? vty_get_bind_addr() : local_address,
|
||||
2324+client);
|
||||
rc = telnet_init_default(NULL, NULL, 2324 + client);
|
||||
if (rc < 0) {
|
||||
perror("Error binding VTY port");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (client) {
|
||||
g_sccp = osmo_sccp_simple_client(NULL, "client", local_pc, OSMO_SS7_ASP_PROT_M3UA,
|
||||
g_sccp = osmo_sccp_simple_client(NULL, "client", local_pc, protocol,
|
||||
local_port, local_address, remote_port, remote_address);
|
||||
if (g_sccp == NULL) {
|
||||
perror("Could not create SCCP client");
|
||||
|
@ -281,14 +300,17 @@ int main(int argc, char **argv)
|
|||
}
|
||||
sccp_test_user_vty_install(g_sccp, OSMO_SCCP_SSN_BSSAP);
|
||||
} else {
|
||||
g_sccp = sua_server_helper(local_port, local_address, local_pc,
|
||||
g_sccp = sua_server_helper(protocol, local_port, local_address, local_pc,
|
||||
remote_port, remote_address, remote_pc);
|
||||
if (g_sccp == NULL) {
|
||||
perror("Could not create SCCP server");
|
||||
exit(1);
|
||||
}
|
||||
sccp_test_server_init(g_sccp);
|
||||
sccp_test_user_vty_install(g_sccp, OSMO_SCCP_SSN_BSSAP);
|
||||
}
|
||||
g_calling_addr.pc = local_pc;
|
||||
g_called_addr.pc = remote_pc;
|
||||
|
||||
while (1) {
|
||||
osmo_select_main(0);
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
|
||||
static struct osmo_sccp_user *g_scu;
|
||||
|
||||
static struct osmo_sccp_addr g_calling_addr = {
|
||||
struct osmo_sccp_addr g_calling_addr = {
|
||||
.presence = OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC,
|
||||
.ri = OSMO_SCCP_RI_SSN_PC,
|
||||
.pc = 23,
|
||||
};
|
||||
|
||||
static struct osmo_sccp_addr g_called_addr = {
|
||||
struct osmo_sccp_addr g_called_addr = {
|
||||
.presence = OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC,
|
||||
.ssn = 1,
|
||||
.ri = OSMO_SCCP_RI_SSN_PC,
|
||||
|
@ -38,35 +38,42 @@ DEFUN(scu_called_ssn, scu_called_ssn_cmd,
|
|||
DEFUN(scu_conn_req, scu_conn_req_cmd,
|
||||
"connect-req <0-16777216> [DATA]",
|
||||
"N-CONNECT.req\n"
|
||||
"Connection ID\n")
|
||||
"Connection ID\n"
|
||||
"Optional Data\n")
|
||||
{
|
||||
struct osmo_sccp_user *scu = vty->index;
|
||||
int conn_id = atoi(argv[0]);
|
||||
const char *data = argv[1];
|
||||
int rc, conn_id = atoi(argv[0]);
|
||||
|
||||
rc = osmo_sccp_tx_conn_req(scu, conn_id, &g_calling_addr, &g_called_addr,
|
||||
(const uint8_t *)argv[1], (argc > 1) ? strlen(argv[1]) + 1 : 0);
|
||||
|
||||
if (rc < 0) {
|
||||
vty_out(vty, "Error while sending N-CONNECT.req: %s%s", strerror(-rc), VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
osmo_sccp_tx_conn_req(scu, conn_id, &g_calling_addr, &g_called_addr,
|
||||
(const uint8_t *)data, data ? strlen(data)+1 : 0);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(scu_conn_resp, scu_conn_resp_cmd,
|
||||
"connect-resp <0-16777216> [DATA]",
|
||||
"N-CONNET.resp\n"
|
||||
"Connection ID\n")
|
||||
"Connection ID\n"
|
||||
"Optional Data\n")
|
||||
{
|
||||
struct osmo_sccp_user *scu = vty->index;
|
||||
int conn_id = atoi(argv[0]);
|
||||
const char *data = argv[1];
|
||||
|
||||
osmo_sccp_tx_conn_resp(scu, conn_id, NULL,
|
||||
(const uint8_t *)data, data ? strlen(data)+1 : 0);
|
||||
osmo_sccp_tx_conn_resp(scu, conn_id, NULL, (const uint8_t *)argv[1], (argc > 1) ? strlen(argv[1]) + 1 : 0);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(scu_data_req, scu_data_req_cmd,
|
||||
"data-req <0-16777216> DATA",
|
||||
"N-DATA.req\n"
|
||||
"Connection ID\n")
|
||||
"N-DATA.req\n"
|
||||
"Connection ID\n"
|
||||
"Data\n")
|
||||
{
|
||||
struct osmo_sccp_user *scu = vty->index;
|
||||
int conn_id = atoi(argv[0]);
|
||||
|
@ -89,14 +96,16 @@ DEFUN(scu_unitdata_req, scu_unitdata_req_cmd,
|
|||
}
|
||||
|
||||
DEFUN(scu_disc_req, scu_disc_req_cmd,
|
||||
"disconnect-req <0-16777216>",
|
||||
"disconnect-req <0-16777216> [DATA]",
|
||||
"N-DISCONNT.req\n"
|
||||
"Connection ID\n")
|
||||
"Connection ID\n"
|
||||
"Optional Data\n")
|
||||
{
|
||||
struct osmo_sccp_user *scu = vty->index;
|
||||
int conn_id = atoi(argv[0]);
|
||||
|
||||
osmo_sccp_tx_disconn(scu, conn_id, NULL, 42);
|
||||
osmo_sccp_tx_disconn_data(scu, conn_id, NULL, 42, (const uint8_t *)argv[1], (argc > 1) ? strlen(argv[1]) + 1 : 0);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,10 +14,6 @@
|
|||
* 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
|
||||
|
@ -131,7 +127,7 @@ struct mtp_level_3_hdr {
|
|||
spare : 2,
|
||||
ni : 2;
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
||||
uint8_t ni:2, spare:2, ser_ind:4;
|
||||
#endif
|
||||
uint32_t addr;
|
||||
|
@ -143,7 +139,7 @@ struct mtp_level_3_cmn {
|
|||
uint8_t h0 : 4,
|
||||
h1 : 4;
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
||||
uint8_t h1:4, h0:4;
|
||||
#endif
|
||||
} __attribute__((packed));
|
||||
|
@ -154,7 +150,7 @@ struct mtp_level_3_mng {
|
|||
uint8_t spare : 4,
|
||||
length : 4;
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
||||
uint8_t length:4, spare:4;
|
||||
#endif
|
||||
uint8_t data[0];
|
||||
|
@ -174,7 +170,7 @@ struct sccp_con_ctrl_prt_mgt {
|
|||
uint8_t mul_ind : 2,
|
||||
spare : 6;
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
||||
uint8_t spare:6, mul_ind:2;
|
||||
#endif
|
||||
} __attribute__((packed));
|
||||
|
|
|
@ -13,10 +13,6 @@
|
|||
* 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
|
||||
|
|
|
@ -16,10 +16,6 @@
|
|||
* 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
|
||||
|
@ -124,7 +120,7 @@ int sccp_connection_close(struct sccp_connection *connection, int cause);
|
|||
int sccp_connection_free(struct sccp_connection *connection);
|
||||
|
||||
/**
|
||||
* internal..
|
||||
* internal..
|
||||
*/
|
||||
int sccp_connection_force_free(struct sccp_connection *conn);
|
||||
|
||||
|
|
|
@ -15,10 +15,6 @@
|
|||
* 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
|
||||
|
@ -28,6 +24,9 @@
|
|||
#include <osmocom/core/endian.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#define SCCP_MAX_OPTIONAL_DATA 130
|
||||
#define SCCP_MAX_DATA 256
|
||||
|
||||
/* Table 1/Q.713 - SCCP message types */
|
||||
enum sccp_message_types {
|
||||
SCCP_MSG_TYPE_CR = 1,
|
||||
|
@ -106,7 +105,7 @@ struct sccp_called_party_address {
|
|||
routing_indicator : 1,
|
||||
reserved : 1;
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
||||
uint8_t reserved:1, routing_indicator:1, global_title_indicator:4, ssn_indicator:1, point_code_indicator:1;
|
||||
#endif
|
||||
uint8_t data[0];
|
||||
|
@ -121,7 +120,7 @@ struct sccp_signalling_point_code {
|
|||
uint8_t msb : 6,
|
||||
reserved : 2;
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
||||
uint8_t reserved:2, msb:6;
|
||||
#endif
|
||||
} __attribute__((packed));
|
||||
|
@ -163,7 +162,7 @@ struct sccp_global_title {
|
|||
uint8_t nature_of_addr_ind : 7,
|
||||
odd_even : 1;
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
||||
uint8_t odd_even:1, nature_of_addr_ind:7;
|
||||
#endif
|
||||
uint8_t data[0];
|
||||
|
@ -446,6 +445,9 @@ struct sccp_data_ext_unitdata {
|
|||
data
|
||||
#endif
|
||||
|
||||
/* optional */
|
||||
uint8_t optional_start;
|
||||
|
||||
#if OPTIONAL
|
||||
segmentation
|
||||
importance
|
||||
|
@ -475,9 +477,70 @@ struct sccp_data_ext_unitdata_service {
|
|||
data
|
||||
#endif
|
||||
|
||||
/* optional */
|
||||
uint8_t optional_start;
|
||||
|
||||
#if OPTIONAL
|
||||
segmentation
|
||||
importancd
|
||||
importance
|
||||
#endif
|
||||
|
||||
uint8_t data[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Long unitdata (LUDT), ITU-T Q.713 4.20 */
|
||||
struct sccp_data_long_unitdata {
|
||||
/* mandatory */
|
||||
uint8_t type;
|
||||
uint8_t proto_class;
|
||||
uint8_t hop_counter;
|
||||
|
||||
/* variable */
|
||||
uint16_t variable_called;
|
||||
uint16_t variable_calling;
|
||||
uint16_t variable_data;
|
||||
/* optional */
|
||||
uint16_t optional_start;
|
||||
|
||||
#if VARIABLE
|
||||
called party address
|
||||
calling party address
|
||||
data
|
||||
#endif
|
||||
|
||||
#if OPTIONAL
|
||||
segmentation
|
||||
importance
|
||||
#endif
|
||||
|
||||
uint8_t data[0];
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Long unitdata service (LUDTS), ITU-T Q.713 4.21 */
|
||||
struct sccp_data_long_unitdata_service {
|
||||
/* mandantory */
|
||||
uint8_t type;
|
||||
uint8_t return_cause;
|
||||
uint8_t hop_counter;
|
||||
|
||||
|
||||
/* variable */
|
||||
uint16_t variable_called;
|
||||
uint16_t variable_calling;
|
||||
uint16_t variable_data;
|
||||
/* optional */
|
||||
uint16_t optional_start;
|
||||
|
||||
#if VARIABLE
|
||||
called party address
|
||||
calling party address
|
||||
data
|
||||
#endif
|
||||
|
||||
#if OPTIONAL
|
||||
segmentation
|
||||
importance
|
||||
#endif
|
||||
|
||||
uint8_t data[0];
|
||||
|
|
|
@ -3,5 +3,6 @@ sigtran_HEADERS = xua_types.h xua_msg.h m2ua_types.h sccp_sap.h \
|
|||
|
||||
sigtrandir = $(includedir)/osmocom/sigtran
|
||||
|
||||
sigtran_prot_HEADERS = protocol/sua.h protocol/m3ua.h protocol/mtp.h
|
||||
sigtran_prot_HEADERS = protocol/sua.h protocol/m3ua.h protocol/mtp.h \
|
||||
protocol/sccp_scmg.h
|
||||
sigtran_protdir = $(includedir)/osmocom/sigtran/protocol
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/prim.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
|
||||
extern struct llist_head osmo_ss7_instances;
|
||||
|
||||
|
@ -24,6 +25,7 @@ int osmo_ss7_find_free_rctx(struct osmo_ss7_instance *inst);
|
|||
bool osmo_ss7_pc_is_local(struct osmo_ss7_instance *inst, uint32_t pc);
|
||||
int osmo_ss7_pointcode_parse(struct osmo_ss7_instance *inst, const char *str);
|
||||
int osmo_ss7_pointcode_parse_mask_or_len(struct osmo_ss7_instance *inst, const char *in);
|
||||
const char *osmo_ss7_pointcode_print_buf(char *buf, size_t buf_len, const struct osmo_ss7_instance *inst, uint32_t pc);
|
||||
const char *osmo_ss7_pointcode_print(const struct osmo_ss7_instance *inst, uint32_t pc);
|
||||
const char *osmo_ss7_pointcode_print2(const struct osmo_ss7_instance *inst, uint32_t pc);
|
||||
|
||||
|
@ -113,6 +115,7 @@ int osmo_ss7_instance_set_pc_fmt(struct osmo_ss7_instance *inst,
|
|||
struct osmo_sccp_instance *osmo_ss7_ensure_sccp(struct osmo_ss7_instance *inst);
|
||||
|
||||
uint8_t osmo_ss7_pc_width(const struct osmo_ss7_pc_fmt *pc_fmt);
|
||||
uint32_t osmo_ss7_pc_normalize(const struct osmo_ss7_pc_fmt *pc_fmt, uint32_t pc);
|
||||
|
||||
/***********************************************************************
|
||||
* MTP Users (Users of MTP, such as SCCP or ISUP)
|
||||
|
@ -308,6 +311,9 @@ struct osmo_ss7_as {
|
|||
/*! Were we allocated by "simple client" support? */
|
||||
bool simple_client_allocated;
|
||||
|
||||
/*! Rate Counter Group */
|
||||
struct rate_ctr_group *ctrg;
|
||||
|
||||
struct {
|
||||
char *name;
|
||||
char *description;
|
||||
|
@ -344,8 +350,9 @@ osmo_ss7_as_find_or_create(struct osmo_ss7_instance *inst, const char *name,
|
|||
int osmo_ss7_as_add_asp(struct osmo_ss7_as *as, const char *asp_name);
|
||||
int osmo_ss7_as_del_asp(struct osmo_ss7_as *as, const char *asp_name);
|
||||
void osmo_ss7_as_destroy(struct osmo_ss7_as *as);
|
||||
bool osmo_ss7_as_has_asp(struct osmo_ss7_as *as,
|
||||
struct osmo_ss7_asp *asp);
|
||||
bool osmo_ss7_as_has_asp(const struct osmo_ss7_as *as,
|
||||
const struct osmo_ss7_asp *asp);
|
||||
bool osmo_ss7_as_down(const struct osmo_ss7_as *as);
|
||||
bool osmo_ss7_as_active(const struct osmo_ss7_as *as);
|
||||
bool osmo_ss7_as_tmode_compatible_xua(struct osmo_ss7_as *as, uint32_t m3ua_tmt);
|
||||
void osmo_ss7_asp_disconnect(struct osmo_ss7_asp *asp);
|
||||
|
@ -359,6 +366,8 @@ struct osmo_ss7_asp_peer {
|
|||
char *host[OSMO_SOCK_MAX_ADDRS];
|
||||
size_t host_cnt;
|
||||
uint16_t port;
|
||||
/* index in "hosts" array marking the SCTP Primary Address, -1 if no explicit Primary Address set */
|
||||
int idx_primary;
|
||||
};
|
||||
|
||||
enum osmo_ss7_asp_admin_state {
|
||||
|
@ -410,6 +419,9 @@ struct osmo_ss7_asp {
|
|||
/*! Were we allocated by "simple client" support? */
|
||||
bool simple_client_allocated;
|
||||
|
||||
/*! Rate Counter Group */
|
||||
struct rate_ctr_group *ctrg;
|
||||
|
||||
/*! Pending message for non-blocking IPA read */
|
||||
struct msgb *pending_msg;
|
||||
|
||||
|
@ -421,36 +433,82 @@ struct osmo_ss7_asp {
|
|||
bool is_server;
|
||||
enum osmo_ss7_asp_role role;
|
||||
bool role_set_by_vty;
|
||||
bool trans_role_set_by_vty;
|
||||
|
||||
struct osmo_ss7_asp_peer local;
|
||||
struct osmo_ss7_asp_peer remote;
|
||||
uint8_t qos_class;
|
||||
uint32_t quirks;
|
||||
|
||||
/* T_defs used by the default_lm: */
|
||||
struct osmo_tdef *T_defs_lm;
|
||||
|
||||
struct {
|
||||
bool num_ostreams_present;
|
||||
bool max_instreams_present;
|
||||
bool max_attempts_present;
|
||||
bool max_init_timeo_present;
|
||||
uint16_t num_ostreams_value;
|
||||
uint16_t max_instreams_value;
|
||||
uint16_t max_attempts_value;
|
||||
uint16_t max_init_timeo_value; /* ms */
|
||||
} sctp_init;
|
||||
|
||||
/*! The underlaying transport protocol (one of IPPROTO_*) */
|
||||
int trans_proto;
|
||||
} cfg;
|
||||
};
|
||||
|
||||
/*! Peer SG doesn't send NTFY(AS-INACTIVE) after ASP-UP procedure */
|
||||
#define OSMO_SS7_ASP_QUIRK_NO_NOTIFY 0x00000001
|
||||
/*! Accept DAUD in ASP role (RFC states only permitted in ASP->SG role) */
|
||||
#define OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP 0x00000002
|
||||
/*! Accept SSNM even if ASP is in AS-INACTIVE state */
|
||||
#define OSMO_SS7_ASP_QUIRK_SNM_INACTIVE 0x00000004
|
||||
|
||||
int osmo_ss7_asp_peer_snprintf(char* buf, size_t buf_len, struct osmo_ss7_asp_peer *peer);
|
||||
void osmo_ss7_asp_peer_init(struct osmo_ss7_asp_peer *peer);
|
||||
int osmo_ss7_asp_peer_set_hosts(struct osmo_ss7_asp_peer *peer, void *talloc_ctx,
|
||||
const char* const* hosts, size_t host_cnt);
|
||||
const char *const*hosts, size_t host_cnt);
|
||||
int osmo_ss7_asp_peer_set_hosts2(struct osmo_ss7_asp_peer *peer, void *talloc_ctx,
|
||||
const char *const*hosts, size_t host_cnt, int idx_primary);
|
||||
int osmo_ss7_asp_peer_add_host(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, const char *host);
|
||||
int osmo_ss7_asp_peer_add_host2(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, const char *host, bool is_primary_addr);
|
||||
int osmo_ss7_asp_peer_del_host(struct osmo_ss7_asp_peer *peer, const char *host);
|
||||
|
||||
|
||||
struct osmo_ss7_asp *
|
||||
osmo_ss7_asp_find_by_name(struct osmo_ss7_instance *inst, const char *name);
|
||||
struct osmo_ss7_asp
|
||||
*osmo_ss7_asp_find_by_proto(struct osmo_ss7_as *as,
|
||||
enum osmo_ss7_asp_protocol proto);
|
||||
struct osmo_ss7_asp *
|
||||
osmo_ss7_asp_find_by_proto(struct osmo_ss7_as *as,
|
||||
enum osmo_ss7_asp_protocol proto);
|
||||
struct osmo_ss7_asp *
|
||||
osmo_ss7_asp_find(struct osmo_ss7_instance *inst, const char *name,
|
||||
uint16_t remote_port, uint16_t local_port,
|
||||
enum osmo_ss7_asp_protocol proto);
|
||||
enum osmo_ss7_asp_protocol proto)
|
||||
OSMO_DEPRECATED("Use osmo_ss7_asp_find2() instead");
|
||||
struct osmo_ss7_asp *
|
||||
osmo_ss7_asp_find2(struct osmo_ss7_instance *inst, const char *name,
|
||||
uint16_t remote_port, uint16_t local_port,
|
||||
int trans_proto, enum osmo_ss7_asp_protocol proto);
|
||||
struct osmo_ss7_asp *
|
||||
osmo_ss7_asp_find_or_create(struct osmo_ss7_instance *inst, const char *name,
|
||||
uint16_t remote_port, uint16_t local_port,
|
||||
enum osmo_ss7_asp_protocol proto);
|
||||
enum osmo_ss7_asp_protocol proto)
|
||||
OSMO_DEPRECATED("Use osmo_ss7_asp_find_or_create2() instead");
|
||||
struct osmo_ss7_asp *
|
||||
osmo_ss7_asp_find_or_create2(struct osmo_ss7_instance *inst, const char *name,
|
||||
uint16_t remote_port, uint16_t local_port,
|
||||
int trans_proto, enum osmo_ss7_asp_protocol proto);
|
||||
void osmo_ss7_asp_destroy(struct osmo_ss7_asp *asp);
|
||||
int osmo_ss7_asp_send(struct osmo_ss7_asp *asp, struct msgb *msg);
|
||||
int osmo_ss7_asp_restart(struct osmo_ss7_asp *asp);
|
||||
int osmo_ss7_asp_use_default_lm(struct osmo_ss7_asp *asp, int log_level);
|
||||
bool osmo_ss7_asp_active(const struct osmo_ss7_asp *asp);
|
||||
int osmo_ss7_asp_get_log_subsys(const struct osmo_ss7_asp *asp);
|
||||
const char *osmo_ss7_asp_get_name(const struct osmo_ss7_asp *asp);
|
||||
enum osmo_ss7_asp_protocol osmo_ss7_asp_get_proto(const struct osmo_ss7_asp *asp);
|
||||
int osmo_ss7_asp_get_trans_proto(const struct osmo_ss7_asp *asp);
|
||||
|
||||
/*! Weak function to handle payload for unknown/unsupported PPID or IPA StreamID.
|
||||
* This function can be overridden by application code to implement whatever handling
|
||||
|
@ -493,16 +551,38 @@ struct osmo_xua_server {
|
|||
bool accept_dyn_reg;
|
||||
struct osmo_ss7_asp_peer local;
|
||||
enum osmo_ss7_asp_protocol proto;
|
||||
struct {
|
||||
bool num_ostreams_present;
|
||||
bool max_instreams_present;
|
||||
uint16_t num_ostreams_value;
|
||||
uint16_t max_instreams_value;
|
||||
} sctp_init;
|
||||
|
||||
/*! The underlaying transport protocol (one of IPPROTO_*) */
|
||||
int trans_proto;
|
||||
} cfg;
|
||||
};
|
||||
|
||||
struct osmo_xua_server *
|
||||
osmo_ss7_xua_server_find(struct osmo_ss7_instance *inst, enum osmo_ss7_asp_protocol proto,
|
||||
uint16_t local_port);
|
||||
osmo_ss7_xua_server_find(struct osmo_ss7_instance *inst,
|
||||
enum osmo_ss7_asp_protocol proto,
|
||||
uint16_t local_port)
|
||||
OSMO_DEPRECATED("Use osmo_ss7_xua_server_find2() instead");
|
||||
struct osmo_xua_server *
|
||||
osmo_ss7_xua_server_find2(struct osmo_ss7_instance *inst,
|
||||
int trans_proto,
|
||||
enum osmo_ss7_asp_protocol proto,
|
||||
uint16_t local_port);
|
||||
|
||||
struct osmo_xua_server *
|
||||
osmo_ss7_xua_server_create(struct osmo_ss7_instance *inst, enum osmo_ss7_asp_protocol proto,
|
||||
uint16_t local_port, const char *local_host);
|
||||
osmo_ss7_xua_server_create(struct osmo_ss7_instance *inst,
|
||||
enum osmo_ss7_asp_protocol proto,
|
||||
uint16_t local_port, const char *local_host)
|
||||
OSMO_DEPRECATED("Use osmo_ss7_xua_server_create2() instead");
|
||||
struct osmo_xua_server *
|
||||
osmo_ss7_xua_server_create2(struct osmo_ss7_instance *inst,
|
||||
int trans_proto, enum osmo_ss7_asp_protocol proto,
|
||||
uint16_t local_port, const char *local_host);
|
||||
|
||||
int
|
||||
osmo_ss7_xua_server_bind(struct osmo_xua_server *xs);
|
||||
|
@ -512,6 +592,7 @@ osmo_ss7_xua_server_set_local_host(struct osmo_xua_server *xs, const char *local
|
|||
int
|
||||
osmo_ss7_xua_server_set_local_hosts(struct osmo_xua_server *xs, const char **local_hosts, size_t local_host_cnt);
|
||||
int osmo_ss7_xua_server_add_local_host(struct osmo_xua_server *xs, const char *local_host);
|
||||
int osmo_ss7_xua_server_del_local_host(struct osmo_xua_server *xs, const char *local_host);
|
||||
void osmo_ss7_xua_server_destroy(struct osmo_xua_server *xs);
|
||||
|
||||
struct osmo_sccp_instance *
|
||||
|
@ -546,6 +627,8 @@ osmo_sccp_simple_server_add_clnt(struct osmo_sccp_instance *inst,
|
|||
int local_port, int remote_port,
|
||||
const char *remote_ip);
|
||||
|
||||
void osmo_sccp_set_max_optional_data(struct osmo_sccp_instance *inst, int val);
|
||||
|
||||
enum osmo_ss7_as_traffic_mode osmo_ss7_tmode_from_xua(uint32_t in);
|
||||
int osmo_ss7_tmode_to_xua(enum osmo_ss7_as_traffic_mode tmod);
|
||||
|
||||
|
|
|
@ -22,3 +22,18 @@ enum mtp_si_ni00 {
|
|||
};
|
||||
|
||||
extern const struct value_string mtp_si_vals[];
|
||||
|
||||
|
||||
/* Chapter 15.17.5 of Q.705 */
|
||||
enum mtp_unavail_cause {
|
||||
MTP_UNAVAIL_C_UNKNOWN = 0x0,
|
||||
MTP_UNAVAIL_C_UNEQUIP_REM_USER = 0x1,
|
||||
MTP_UNAVAIL_C_INACC_REM_USER = 0x2,
|
||||
/* reserved */
|
||||
};
|
||||
|
||||
extern const struct value_string mtp_unavail_cause_vals[];
|
||||
|
||||
static inline const char *mtp_unavail_cause_str(enum mtp_unavail_cause cs) {
|
||||
return get_value_string(mtp_unavail_cause_vals, cs);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
/* SCCP Management as per Section 5.3 of ITU-T Q.713 */
|
||||
|
||||
enum sccp_scmg_msg_type {
|
||||
SCCP_SCMG_MSGT_SSA = 0x01, /* Subsystem Allowed */
|
||||
SCCP_SCMG_MSGT_SSP = 0x02, /* Subsystem Prohibited */
|
||||
SCCP_SCMG_MSGT_SST = 0x03, /* Subsystem Status Test */
|
||||
SCCP_SCMG_MSGT_SOR = 0x04, /* Subsystem Out-of-service Request */
|
||||
SCCP_SCMG_MSGT_SOG = 0x05, /* Subsystem Out-of-service Grant */
|
||||
SCCP_SCMG_MSGT_SSC = 0x06, /* Subsystem Congested */
|
||||
};
|
||||
|
||||
struct sccp_scmg_msg {
|
||||
uint8_t msg_type; /* enum sccp_scmg_msg_type */
|
||||
uint8_t affected_ssn;
|
||||
uint16_t affected_pc;
|
||||
uint8_t smi;
|
||||
/* one octet, only in case of SSC */
|
||||
uint8_t ssc_congestion_lvl[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
extern const struct value_string sccp_scmg_msgt_names[];
|
||||
static inline const char *sccp_scmg_msgt_name(enum sccp_scmg_msg_type msgt)
|
||||
{ return get_value_string(sccp_scmg_msgt_names, msgt); }
|
|
@ -44,6 +44,10 @@ int osmo_sccp_tx_disconn(struct osmo_sccp_user *scu, uint32_t conn_id,
|
|||
const struct osmo_sccp_addr *resp_addr,
|
||||
uint32_t cause);
|
||||
|
||||
int osmo_sccp_tx_disconn_data(struct osmo_sccp_user *scu, uint32_t conn_id,
|
||||
const struct osmo_sccp_addr *resp_addr,
|
||||
uint32_t cause, const uint8_t *data, size_t len);
|
||||
|
||||
int osmo_sccp_tx_conn_resp_msg(struct osmo_sccp_user *scu, uint32_t conn_id,
|
||||
const struct osmo_sccp_addr *resp_addr,
|
||||
struct msgb *msg);
|
||||
|
@ -54,5 +58,20 @@ int osmo_sccp_tx_conn_resp(struct osmo_sccp_user *scu, uint32_t conn_id,
|
|||
|
||||
char *osmo_sccp_gt_dump(const struct osmo_sccp_gt *gt);
|
||||
char *osmo_sccp_addr_dump(const struct osmo_sccp_addr *addr);
|
||||
|
||||
int osmo_sccp_inst_addr_to_str_buf(char *buf, size_t buf_len, const struct osmo_sccp_instance *sccp,
|
||||
const struct osmo_sccp_addr *addr);
|
||||
char *osmo_sccp_inst_addr_to_str_c(void *ctx, const struct osmo_sccp_instance *sccp,
|
||||
const struct osmo_sccp_addr *addr);
|
||||
int osmo_sccp_addr_to_str_buf(char *buf, size_t buf_len, const struct osmo_ss7_instance *ss7,
|
||||
const struct osmo_sccp_addr *addr);
|
||||
char *osmo_sccp_addr_to_str_c(void *ctx, const struct osmo_ss7_instance *ss7, const struct osmo_sccp_addr *addr);
|
||||
|
||||
int osmo_sccp_addr_to_id_buf(char *buf, size_t buf_len, const struct osmo_ss7_instance *ss7,
|
||||
const struct osmo_sccp_addr *addr);
|
||||
char *osmo_sccp_addr_to_id_c(void *ctx, const struct osmo_ss7_instance *ss7, const struct osmo_sccp_addr *addr);
|
||||
|
||||
bool osmo_sccp_conn_id_exists(const struct osmo_sccp_instance *inst, uint32_t id);
|
||||
|
||||
char *osmo_sccp_addr_name(const struct osmo_ss7_instance *ss7, const struct osmo_sccp_addr *addr);
|
||||
char *osmo_sccp_inst_addr_name(const struct osmo_sccp_instance *sccp, const struct osmo_sccp_addr *addr);
|
||||
|
|
|
@ -51,6 +51,7 @@ enum osmo_scu_prim_type {
|
|||
#define OSMO_SCCP_ADDR_T_SSN 0x0004 /* subsystem number */
|
||||
#define OSMO_SCCP_ADDR_T_IPv4 0x0008
|
||||
#define OSMO_SCCP_ADDR_T_IPv6 0x0010
|
||||
#define OSMO_SCCP_ADDR_T_MASK 0x001f
|
||||
|
||||
/* Q.713 3.4.1 + RFC 3868 3.10.2.3 */
|
||||
enum osmo_sccp_routing_ind {
|
||||
|
@ -131,11 +132,42 @@ enum osmo_sccp_ssn {
|
|||
OSMO_SCCP_SSN_PCAP = 249,
|
||||
OSMO_SCCP_SSN_BSC_BSSAP_LE = 250,
|
||||
OSMO_SCCP_SSN_MSC_BSSAP_LE = 251,
|
||||
OSMO_SCCP_SSN_SMLC_BSSAP = 252,
|
||||
OSMO_SCCP_SSN_SMLC_BSSAP_LE = 252,
|
||||
OSMO_SCCP_SSN_BSS_OAM = 253,
|
||||
OSMO_SCCP_SSN_BSSAP = 254,
|
||||
};
|
||||
|
||||
/* Q.711 6.3.2.2.5 Signalling point status */
|
||||
enum osmo_sccp_sp_status {
|
||||
OSMO_SCCP_SP_S_INACCESSIBLE = 1,
|
||||
OSMO_SCCP_SP_S_CONGESTED = 2,
|
||||
OSMO_SCCP_SP_S_ACCESSIBLE = 3,
|
||||
};
|
||||
|
||||
extern const struct value_string osmo_sccp_sp_status_names[];
|
||||
static inline const char *osmo_sccp_sp_status_name(enum osmo_sccp_sp_status val)
|
||||
{
|
||||
return get_value_string(osmo_sccp_sp_status_names, val);
|
||||
}
|
||||
|
||||
/* Q.711 6.3.2.2.6 Remote SCCP status */
|
||||
enum osmo_sccp_rem_sccp_status {
|
||||
OSMO_SCCP_REM_SCCP_S_AVAILABLE = 1,
|
||||
OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN = 2,
|
||||
OSMO_SCCP_REM_SCCP_S_UNEQUIPPED = 3,
|
||||
OSMO_SCCP_REM_SCCP_S_INACCESSIBLE = 4,
|
||||
OSMO_SCCP_REM_SCCP_S_CONGESTED = 5,
|
||||
};
|
||||
|
||||
extern const struct value_string osmo_sccp_rem_sccp_status_names[];
|
||||
static inline const char *osmo_sccp_rem_sccp_status_name(enum osmo_sccp_rem_sccp_status val)
|
||||
{
|
||||
return get_value_string(osmo_sccp_rem_sccp_status_names, val);
|
||||
}
|
||||
|
||||
/* legacy shim for name change */
|
||||
#define OSMO_SCCP_SSN_SMLC_BSSAP OSMO_SCCP_SSN_SMLC_BSSAP_LE
|
||||
|
||||
extern const struct value_string osmo_sccp_ssn_names[];
|
||||
static inline const char *osmo_sccp_ssn_name(enum osmo_sccp_ssn val)
|
||||
{ return get_value_string(osmo_sccp_ssn_names, val); }
|
||||
|
@ -222,6 +254,22 @@ struct osmo_scu_notice_param {
|
|||
/* user data */
|
||||
};
|
||||
|
||||
/* OSMO_SCU_PRIM_N_STATE */
|
||||
struct osmo_scu_state_param {
|
||||
uint32_t affected_pc;
|
||||
uint32_t affected_ssn;
|
||||
bool user_in_service; /* true: UIS; false: UOS */
|
||||
uint32_t ssn_multiplicity_ind;
|
||||
};
|
||||
|
||||
/* OSMO_SCU_PRIM_N_PCSTATE */
|
||||
struct osmo_scu_pcstate_param {
|
||||
uint32_t affected_pc;
|
||||
uint32_t restricted_importance_level;
|
||||
enum osmo_sccp_sp_status sp_status;
|
||||
enum osmo_sccp_rem_sccp_status remote_sccp_status;
|
||||
};
|
||||
|
||||
struct osmo_scu_prim {
|
||||
struct osmo_prim_hdr oph;
|
||||
union {
|
||||
|
@ -231,12 +279,22 @@ struct osmo_scu_prim {
|
|||
struct osmo_scu_reset_param reset;
|
||||
struct osmo_scu_unitdata_param unitdata;
|
||||
struct osmo_scu_notice_param notice;
|
||||
struct osmo_scu_state_param state;
|
||||
struct osmo_scu_pcstate_param pcstate;
|
||||
} u;
|
||||
};
|
||||
|
||||
#define msgb_scu_prim(msg) ((struct osmo_scu_prim *)(msg)->l1h)
|
||||
|
||||
char *osmo_scu_prim_name(struct osmo_prim_hdr *oph);
|
||||
extern const struct value_string osmo_scu_prim_type_names[];
|
||||
static inline const char *osmo_scu_prim_type_name(enum osmo_scu_prim_type val)
|
||||
{
|
||||
return get_value_string(osmo_scu_prim_type_names, val);
|
||||
}
|
||||
|
||||
int osmo_scu_prim_hdr_name_buf(char *buf, size_t buflen, const struct osmo_prim_hdr *oph);
|
||||
char *osmo_scu_prim_hdr_name_c(void *ctx, const struct osmo_prim_hdr *oph);
|
||||
char *osmo_scu_prim_name(const struct osmo_prim_hdr *oph);
|
||||
|
||||
struct osmo_ss7_instance;
|
||||
struct osmo_sccp_instance;
|
||||
|
@ -244,32 +302,30 @@ struct osmo_sccp_user;
|
|||
|
||||
void osmo_sccp_vty_init(void);
|
||||
|
||||
struct osmo_sccp_instance *
|
||||
osmo_sccp_instance_create(struct osmo_ss7_instance *ss7, void *priv);
|
||||
struct osmo_sccp_instance *osmo_sccp_instance_create(struct osmo_ss7_instance *ss7, void *priv);
|
||||
void osmo_sccp_instance_destroy(struct osmo_sccp_instance *inst);
|
||||
struct osmo_ss7_instance *osmo_sccp_get_ss7(const struct osmo_sccp_instance *sccp);
|
||||
struct osmo_sccp_instance *osmo_sccp_get_sccp(const struct osmo_sccp_user *scu);
|
||||
void osmo_sccp_set_priv(struct osmo_sccp_instance *sccp, void *priv);
|
||||
void *osmo_sccp_get_priv(struct osmo_sccp_instance *sccp);
|
||||
|
||||
void osmo_sccp_user_unbind(struct osmo_sccp_user *scu);
|
||||
void osmo_sccp_user_set_priv(struct osmo_sccp_user *scu, void *priv);
|
||||
void *osmo_sccp_user_get_priv(struct osmo_sccp_user *scu);
|
||||
|
||||
struct osmo_sccp_user *
|
||||
osmo_sccp_user_bind_pc(struct osmo_sccp_instance *inst, const char *name,
|
||||
osmo_prim_cb prim_cb, uint16_t ssn, uint32_t pc);
|
||||
struct osmo_sccp_user *osmo_sccp_user_bind_pc(struct osmo_sccp_instance *inst, const char *name,
|
||||
osmo_prim_cb prim_cb, uint16_t ssn, uint32_t pc);
|
||||
|
||||
struct osmo_sccp_user *
|
||||
osmo_sccp_user_bind(struct osmo_sccp_instance *inst, const char *name,
|
||||
osmo_prim_cb prim_cb, uint16_t ssn);
|
||||
struct osmo_sccp_user *
|
||||
osmo_sccp_user_find(struct osmo_sccp_instance *inst, uint16_t ssn, uint32_t pc);
|
||||
struct osmo_sccp_user *osmo_sccp_user_bind(struct osmo_sccp_instance *inst, const char *name,
|
||||
osmo_prim_cb prim_cb, uint16_t ssn);
|
||||
struct osmo_sccp_user *osmo_sccp_user_find(struct osmo_sccp_instance *inst, uint16_t ssn, uint32_t pc);
|
||||
|
||||
int osmo_sccp_user_sap_down(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph);
|
||||
int osmo_sccp_user_sap_down_nofree(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph);
|
||||
|
||||
struct osmo_ss7_instance *
|
||||
osmo_sccp_addr_by_name(struct osmo_sccp_addr *dest_addr,
|
||||
const char *name);
|
||||
struct osmo_ss7_instance *osmo_sccp_addr_by_name(struct osmo_sccp_addr *dest_addr, const char *name);
|
||||
int osmo_sccp_addr_by_name_local(struct osmo_sccp_addr *dest_addr, const char *name,
|
||||
const struct osmo_ss7_instance *inst);
|
||||
|
||||
const char *osmo_sccp_name_by_addr(const struct osmo_sccp_addr *addr);
|
||||
|
||||
|
@ -283,3 +339,5 @@ int osmo_sccp_addr_ri_cmp(const struct osmo_sccp_addr *a, const struct osmo_sccp
|
|||
int osmo_sccp_gt_cmp(const struct osmo_sccp_gt *a, const struct osmo_sccp_gt *b);
|
||||
|
||||
const char *osmo_sccp_user_name(struct osmo_sccp_user *scu);
|
||||
|
||||
int osmo_sccp_instance_next_conn_id(struct osmo_sccp_instance *sccp);
|
||||
|
|
|
@ -74,7 +74,7 @@ void osmo_xua_msg_tall_ctx_init(void *ctx);
|
|||
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);
|
||||
int xua_msg_add_data(struct xua_msg *msg, uint16_t tag, uint16_t len, const uint8_t *dat);
|
||||
|
||||
struct xua_msg_part *xua_msg_find_tag(const struct xua_msg *msg, uint16_t tag);
|
||||
int xua_msg_free_tag(struct xua_msg *xua, uint16_t tag);
|
||||
|
@ -89,8 +89,12 @@ struct xua_msg *xua_from_nested(struct xua_msg_part *outer);
|
|||
int msgb_t16l16vp_put(struct msgb *msg, uint16_t tag, uint16_t len, const uint8_t *data);
|
||||
int msgb_t16l16vp_put_u32(struct msgb *msg, uint16_t tag, uint32_t val);
|
||||
int xua_msg_add_u32(struct xua_msg *xua, uint16_t iei, uint32_t val);
|
||||
uint32_t xua_msg_part_get_u32(struct xua_msg_part *part);
|
||||
uint32_t xua_msg_get_u32(struct xua_msg *xua, uint16_t iei);
|
||||
uint32_t xua_msg_part_get_u32(const struct xua_msg_part *part);
|
||||
uint32_t xua_msg_get_u32(const struct xua_msg *xua, uint16_t iei);
|
||||
const uint32_t *xua_msg_get_u32p(const struct xua_msg *xua, uint16_t iei, uint32_t *out);
|
||||
const char *xua_msg_part_get_str(const struct xua_msg_part *part);
|
||||
const char *xua_msg_get_str(const struct xua_msg *xua, uint16_t iei);
|
||||
int xua_msg_get_len(const struct xua_msg *xua, uint16_t iei);
|
||||
void xua_part_add_gt(struct msgb *msg, const struct osmo_sccp_gt *gt);
|
||||
int xua_msg_add_sccp_addr(struct xua_msg *xua, uint16_t iei, const struct osmo_sccp_addr *addr);
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
app_configs = {
|
||||
"osmo-stp": ["doc/examples/osmo-stp.cfg"],
|
||||
"osmo-stp": ["doc/examples/osmo-stp.cfg",
|
||||
"doc/examples/osmo-stp-multihome.cfg"],
|
||||
}
|
||||
|
||||
apps = [(4239, "stp/osmo-stp", "OsmoSTP", "osmo-stp"),
|
||||
|
|
|
@ -2,7 +2,13 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
|
|||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS)
|
||||
|
||||
noinst_HEADERS = sccp_internal.h xua_asp_fsm.h xua_as_fsm.h xua_internal.h
|
||||
noinst_HEADERS = \
|
||||
sccp_internal.h \
|
||||
ss7_internal.h \
|
||||
xua_asp_fsm.h \
|
||||
xua_as_fsm.h \
|
||||
xua_internal.h \
|
||||
$(NULL)
|
||||
|
||||
# Legacy static libs
|
||||
|
||||
|
@ -25,13 +31,44 @@ lib_LTLIBRARIES = libosmo-sigtran.la
|
|||
# This is _NOT_ the library release version, it's an API version.
|
||||
# Please read Chapter 6 "Library interface versions" of the libtool
|
||||
# documentation before making any modification
|
||||
LIBVERSION=5:0:0
|
||||
LIBVERSION=9:0:0
|
||||
|
||||
libosmo_sigtran_la_SOURCES = \
|
||||
ipa.c \
|
||||
m3ua.c \
|
||||
osmo_ss7.c \
|
||||
osmo_ss7_as.c \
|
||||
osmo_ss7_asp.c \
|
||||
osmo_ss7_asp_peer.c \
|
||||
osmo_ss7_hmrt.c \
|
||||
osmo_ss7_vty.c \
|
||||
osmo_ss7_xua_srv.c \
|
||||
sccp2sua.c \
|
||||
sccp_helpers.c \
|
||||
sccp_lbcs.c \
|
||||
sccp_sap.c \
|
||||
sccp_sclc.c \
|
||||
sccp_scmg.c \
|
||||
sccp_scrc.c \
|
||||
sccp_scoc.c \
|
||||
sccp_types.c \
|
||||
sccp_user.c \
|
||||
sccp_vty.c \
|
||||
sua.c \
|
||||
xua_asp_fsm.c \
|
||||
xua_as_fsm.c \
|
||||
xua_default_lm_fsm.c \
|
||||
xua_msg.c \
|
||||
xua_rkm.c \
|
||||
xua_shared.c \
|
||||
xua_snm.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_sigtran_la_SOURCES = sccp_sap.c sua.c m3ua.c xua_msg.c sccp_helpers.c \
|
||||
sccp2sua.c sccp_scrc.c sccp_sclc.c sccp_scoc.c \
|
||||
sccp_user.c sccp_types.c xua_rkm.c xua_default_lm_fsm.c \
|
||||
osmo_ss7.c osmo_ss7_hmrt.c xua_asp_fsm.c xua_as_fsm.c \
|
||||
osmo_ss7_vty.c sccp_vty.c ipa.c
|
||||
libosmo_sigtran_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined -export-symbols-regex '^osmo_'
|
||||
libosmo_sigtran_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) $(LIBSCTP_LIBS)
|
||||
libosmo_sigtran_la_LIBADD = \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBSCTP_LIBS) \
|
||||
$(NULL)
|
||||
|
|
20
src/ipa.c
20
src/ipa.c
|
@ -47,6 +47,7 @@
|
|||
#include <osmocom/sigtran/protocol/mtp.h>
|
||||
|
||||
#include "xua_internal.h"
|
||||
#include "ss7_internal.h"
|
||||
#include "xua_asp_fsm.h"
|
||||
|
||||
|
||||
|
@ -130,11 +131,11 @@ static int ipa_rx_msg_ccm(struct osmo_ss7_asp *asp, struct msgb *msg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct osmo_ss7_as *find_as_for_asp(struct osmo_ss7_asp *asp)
|
||||
struct osmo_ss7_as *ipa_find_as_for_asp(struct osmo_ss7_asp *asp)
|
||||
{
|
||||
struct osmo_ss7_as *as;
|
||||
|
||||
/* in the IPA case, weassume there is a 1:1 mapping between the
|
||||
/* in the IPA case, we assume there is a 1:1 mapping between the
|
||||
* ASP and the AS. An AS without ASP means there is no
|
||||
* connection, and an ASP without AS means that we don't (yet?)
|
||||
* know the identity of the peer */
|
||||
|
@ -210,12 +211,12 @@ static struct msgb *patch_sccp_with_pc(struct osmo_ss7_asp *asp, struct msgb *sc
|
|||
return sccp_msg_out;
|
||||
}
|
||||
|
||||
static int ipa_rx_msg_sccp(struct osmo_ss7_asp *asp, struct msgb *msg)
|
||||
static int ipa_rx_msg_sccp(struct osmo_ss7_asp *asp, struct msgb *msg, uint8_t sls)
|
||||
{
|
||||
int rc;
|
||||
struct m3ua_data_hdr data_hdr;
|
||||
struct xua_msg *xua;
|
||||
struct osmo_ss7_as *as = find_as_for_asp(asp);
|
||||
struct osmo_ss7_as *as = ipa_find_as_for_asp(asp);
|
||||
uint32_t opc, dpc;
|
||||
|
||||
if (!as) {
|
||||
|
@ -224,6 +225,8 @@ static int ipa_rx_msg_sccp(struct osmo_ss7_asp *asp, struct msgb *msg)
|
|||
return -1;
|
||||
}
|
||||
|
||||
rate_ctr_inc2(as->ctrg, SS7_AS_CTR_RX_MSU_TOTAL);
|
||||
|
||||
/* pull the IPA header */
|
||||
msgb_pull_to_l2(msg);
|
||||
|
||||
|
@ -273,6 +276,8 @@ static int ipa_rx_msg_sccp(struct osmo_ss7_asp *asp, struct msgb *msg)
|
|||
data_hdr.si = MTP_SI_SCCP;
|
||||
data_hdr.opc = osmo_htonl(opc);
|
||||
data_hdr.dpc = osmo_htonl(dpc);
|
||||
data_hdr.sls = sls;
|
||||
data_hdr.ni = as->inst->cfg.network_indicator;
|
||||
/* Create M3UA message in XUA structure */
|
||||
xua = m3ua_xfer_from_data(&data_hdr, msgb_l2(msg), msgb_l2len(msg));
|
||||
msgb_free(msg);
|
||||
|
@ -288,8 +293,9 @@ static int ipa_rx_msg_sccp(struct osmo_ss7_asp *asp, struct msgb *msg)
|
|||
/*! \brief process M3UA message received from socket
|
||||
* \param[in] asp Application Server Process receiving \a msg
|
||||
* \param[in] msg received message buffer. Callee takes ownership!
|
||||
* \param[in] sls The SLS (signaling link selector) field to use in the generated M3UA header
|
||||
* \returns 0 on success; negative on error */
|
||||
int ipa_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
|
||||
int ipa_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg, uint8_t sls)
|
||||
{
|
||||
struct ipaccess_head *hh;
|
||||
int rc;
|
||||
|
@ -297,7 +303,7 @@ int ipa_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
|
|||
OSMO_ASSERT(asp->cfg.proto == OSMO_SS7_ASP_PROT_IPA);
|
||||
|
||||
/* osmo_ipa_process_msg() will already have verified length
|
||||
* consistency and set up l2h poiter */
|
||||
* consistency and set up l2h pointer */
|
||||
hh = (struct ipaccess_head *) msg->l1h;
|
||||
|
||||
switch (hh->proto) {
|
||||
|
@ -305,7 +311,7 @@ int ipa_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
|
|||
rc = ipa_rx_msg_ccm(asp, msg);
|
||||
break;
|
||||
case IPAC_PROTO_SCCP:
|
||||
rc = ipa_rx_msg_sccp(asp, msg);
|
||||
rc = ipa_rx_msg_sccp(asp, msg, sls);
|
||||
break;
|
||||
default:
|
||||
rc = ss7_asp_rx_unknown(asp, hh->proto, msg);
|
||||
|
|
315
src/m3ua.c
315
src/m3ua.c
|
@ -44,6 +44,7 @@
|
|||
#include "xua_as_fsm.h"
|
||||
#include "xua_asp_fsm.h"
|
||||
#include "xua_internal.h"
|
||||
#include "ss7_internal.h"
|
||||
|
||||
#define M3UA_MSGB_SIZE 1500
|
||||
|
||||
|
@ -309,9 +310,6 @@ void m3ua_dh_to_xfer_param(struct osmo_mtp_transfer_param *param,
|
|||
(mdh->ni & 0x3 << 6);
|
||||
}
|
||||
|
||||
#define M3UA_MSG_SIZE 2048
|
||||
#define M3UA_MSG_HEADROOM 512
|
||||
|
||||
struct msgb *m3ua_msgb_alloc(const char *name)
|
||||
{
|
||||
if (!name)
|
||||
|
@ -448,7 +446,7 @@ int m3ua_decode_notify(struct osmo_xlm_prim_notify *npar, void *ctx,
|
|||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Transmitting M3UA messsages to SCTP
|
||||
* Transmitting M3UA messages to SCTP
|
||||
***********************************************************************/
|
||||
|
||||
/* Convert M3UA from xua_msg to msgb and set PPID/stream */
|
||||
|
@ -461,9 +459,11 @@ static struct msgb *m3ua_to_msg(struct xua_msg *xua)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (xua->hdr.msg_class == M3UA_MSGC_XFER)
|
||||
if (xua->hdr.msg_class == M3UA_MSGC_XFER) {
|
||||
/* TODO: M3UA RFC says that multiple different streams within the SCTP association
|
||||
* *may* be used, for example, by using the SLS value. Not required but makes sense. */
|
||||
msgb_sctp_stream(msg) = 1;
|
||||
else
|
||||
} else
|
||||
msgb_sctp_stream(msg) = 0;
|
||||
msgb_sctp_ppid(msg) = M3UA_PPID;
|
||||
|
||||
|
@ -503,7 +503,7 @@ int m3ua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua)
|
|||
return -1;
|
||||
|
||||
/* send the msg to the AS for transmission. The AS FSM might
|
||||
* (depending on its state) enqueue it before trnsmission */
|
||||
* (depending on its state) enqueue it before transmission */
|
||||
rc = osmo_fsm_inst_dispatch(as->fi, XUA_AS_E_TRANSFER_REQ, msg);
|
||||
if (rc < 0)
|
||||
msgb_free(msg);
|
||||
|
@ -511,7 +511,7 @@ int m3ua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua)
|
|||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Receiving M3UA messsages from SCTP
|
||||
* Receiving M3UA messages from SCTP
|
||||
***********************************************************************/
|
||||
|
||||
/* obtain the destination point code from a M3UA message in XUA fmt * */
|
||||
|
@ -532,28 +532,12 @@ struct m3ua_data_hdr *data_hdr_from_m3ua(struct xua_msg *xua)
|
|||
return data_hdr;
|
||||
}
|
||||
|
||||
/* if given ASP only has one AS, return that AS */
|
||||
static struct osmo_ss7_as *find_single_as_for_asp(struct osmo_ss7_asp *asp)
|
||||
{
|
||||
struct osmo_ss7_as *as, *as_found = NULL;
|
||||
|
||||
llist_for_each_entry(as, &asp->inst->as_list, list) {
|
||||
if (!osmo_ss7_as_has_asp(as, asp))
|
||||
continue;
|
||||
/* check if we already had found another AS within this ASP -> not unique */
|
||||
if (as_found)
|
||||
return NULL;
|
||||
as_found = as;
|
||||
}
|
||||
|
||||
return as_found;
|
||||
}
|
||||
|
||||
static int m3ua_rx_xfer(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
||||
{
|
||||
struct xua_msg_part *rctx_ie = xua_msg_find_tag(xua, M3UA_IEI_ROUTE_CTX);
|
||||
struct m3ua_data_hdr *dh;
|
||||
struct osmo_ss7_as *as;
|
||||
int rc;
|
||||
|
||||
LOGPASP(asp, DLM3UA, LOGL_DEBUG, "m3ua_rx_xfer\n");
|
||||
|
||||
|
@ -565,34 +549,11 @@ static int m3ua_rx_xfer(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
|||
return M3UA_ERR_UNSUPP_MSG_TYPE;
|
||||
}
|
||||
|
||||
if (rctx_ie) {
|
||||
uint32_t rctx = xua_msg_part_get_u32(rctx_ie);
|
||||
/* Use routing context IE to look up the AS for which the
|
||||
* message was received. */
|
||||
as = osmo_ss7_as_find_by_rctx(asp->inst, rctx);
|
||||
if (!as) {
|
||||
LOGPASP(asp, DLM3UA, LOGL_ERROR, "%s(): invalid routing context: %u\n",
|
||||
__func__, rctx);
|
||||
return M3UA_ERR_INVAL_ROUT_CTX;
|
||||
}
|
||||
rc = xua_find_as_for_asp(&as, asp, rctx_ie);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Verify that this ASP is part of the AS. */
|
||||
if (!osmo_ss7_as_has_asp(as, asp)) {
|
||||
LOGPASP(asp, DLM3UA, LOGL_ERROR,
|
||||
"%s(): This Application Server Process is not part of the AS %s "
|
||||
"resolved by routing context %u\n", __func__, (as)->cfg.name, rctx);
|
||||
return M3UA_ERR_NO_CONFGD_AS_FOR_ASP;
|
||||
}
|
||||
} else {
|
||||
/* no explicit routing context; this only works if there is only one AS in the ASP */
|
||||
as = find_single_as_for_asp(asp);
|
||||
if (!as) {
|
||||
LOGPASP(asp, DLM3UA, LOGL_ERROR,
|
||||
"%s(): ASP sent M3UA without Routing Context IE but unable to uniquely "
|
||||
"identify the AS for this message\n", __func__);
|
||||
return M3UA_ERR_INVAL_ROUT_CTX;
|
||||
}
|
||||
}
|
||||
rate_ctr_inc2(as->ctrg, SS7_AS_CTR_RX_MSU_TOTAL);
|
||||
|
||||
/* FIXME: check for AS state == ACTIVE */
|
||||
|
||||
|
@ -718,6 +679,8 @@ static int m3ua_rx_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int m3ua_rx_snm(struct osmo_ss7_asp *asp, struct xua_msg *xua);
|
||||
|
||||
/*! \brief process M3UA message received from socket
|
||||
* \param[in] asp Application Server Process receiving \ref msg
|
||||
* \param[in] msg received message buffer
|
||||
|
@ -730,7 +693,7 @@ int m3ua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
|
|||
OSMO_ASSERT(asp->cfg.proto == OSMO_SS7_ASP_PROT_M3UA);
|
||||
|
||||
/* caller owns msg memory, we shall neither free it here nor
|
||||
* keep references beyond the executin of this function and its
|
||||
* keep references beyond the execution of this function and its
|
||||
* callees */
|
||||
|
||||
xua = xua_from_msg(M3UA_VERSION, msgb_length(msg), msgb_data(msg));
|
||||
|
@ -778,10 +741,7 @@ int m3ua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
|
|||
rc = m3ua_rx_rkm(asp, xua);
|
||||
break;
|
||||
case M3UA_MSGC_SNM:
|
||||
/* FIXME */
|
||||
LOGPASP(asp, DLM3UA, LOGL_NOTICE, "Received unsupported M3UA "
|
||||
"Message Class %u\n", xua->hdr.msg_class);
|
||||
err = m3ua_gen_error_msg(M3UA_ERR_UNSUPP_MSG_CLASS, msg);
|
||||
rc = m3ua_rx_snm(asp, xua);
|
||||
break;
|
||||
default:
|
||||
LOGPASP(asp, DLM3UA, LOGL_NOTICE, "Received unknown M3UA "
|
||||
|
@ -794,10 +754,249 @@ int m3ua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
|
|||
err = m3ua_gen_error_msg(rc, msg);
|
||||
|
||||
out:
|
||||
if (err)
|
||||
if (err) {
|
||||
m3ua_tx_xua_asp(asp, err);
|
||||
xua_msg_free(err);
|
||||
}
|
||||
|
||||
xua_msg_free(xua);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* SSNM msg generation
|
||||
***********************************************************************/
|
||||
|
||||
/* 3.4.1 Destination Unavailable (DUNA) */
|
||||
static struct xua_msg *m3ua_encode_duna(const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc,
|
||||
const char *info_string)
|
||||
{
|
||||
struct xua_msg *xua = xua_msg_alloc();
|
||||
|
||||
xua->hdr = XUA_HDR(M3UA_MSGC_SNM, M3UA_SNM_DUNA);
|
||||
xua->hdr.version = M3UA_VERSION;
|
||||
if (rctx && num_rctx)
|
||||
xua_msg_add_data(xua, M3UA_IEI_ROUTE_CTX, num_rctx * sizeof(*rctx), (const uint8_t *)rctx);
|
||||
|
||||
xua_msg_add_data(xua, M3UA_IEI_AFFECTED_PC, num_aff_pc * sizeof(*aff_pc), (const uint8_t *) aff_pc);
|
||||
|
||||
if (info_string) {
|
||||
xua_msg_add_data(xua, M3UA_IEI_INFO_STRING,
|
||||
strlen(info_string)+1,
|
||||
(const uint8_t *) info_string);
|
||||
}
|
||||
return xua;
|
||||
}
|
||||
|
||||
/* 3.4.2 Destination Available (DAVA) */
|
||||
static struct xua_msg *m3ua_encode_dava(const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc,
|
||||
const char *info_string)
|
||||
{
|
||||
/* encoding is exactly identical to DUNA */
|
||||
struct xua_msg *xua = m3ua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, info_string);
|
||||
if (xua)
|
||||
xua->hdr.msg_type = M3UA_SNM_DAVA;
|
||||
return xua;
|
||||
}
|
||||
|
||||
#if 0 /* not used so far */
|
||||
/* 3.4.3 Destination Available (DAUD) */
|
||||
static struct xua_msg *m3ua_encode_daud(const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc,
|
||||
const char *info_string)
|
||||
{
|
||||
/* encoding is exactly identical to DUNA */
|
||||
struct xua_msg *xua = m3ua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, info_string);
|
||||
if (xua)
|
||||
xua->hdr.msg_type = M3UA_SNM_DAUD;
|
||||
return xua;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 3.4.5 Destination User Part Unavailable (DUPU) */
|
||||
static struct xua_msg *m3ua_encode_dupu(const uint32_t *rctx, unsigned int num_rctx,
|
||||
uint32_t dpc, uint16_t user, uint16_t cause,
|
||||
const char *info_string)
|
||||
{
|
||||
struct xua_msg *xua = xua_msg_alloc();
|
||||
uint32_t user_cause = (cause << 16) | user;
|
||||
|
||||
xua->hdr = XUA_HDR(M3UA_MSGC_SNM, M3UA_SNM_DUPU);
|
||||
xua->hdr.version = M3UA_VERSION;
|
||||
if (rctx && num_rctx)
|
||||
xua_msg_add_data(xua, M3UA_IEI_ROUTE_CTX, num_rctx * sizeof(*rctx), (const uint8_t *)rctx);
|
||||
|
||||
xua_msg_add_u32(xua, M3UA_IEI_AFFECTED_PC, dpc);
|
||||
xua_msg_add_u32(xua, M3UA_IEI_USER_CAUSE, user_cause);
|
||||
|
||||
if (info_string) {
|
||||
xua_msg_add_data(xua, M3UA_IEI_INFO_STRING,
|
||||
strlen(info_string)+1,
|
||||
(const uint8_t *) info_string);
|
||||
}
|
||||
return xua;
|
||||
}
|
||||
|
||||
/*! Transmit SSNM DUNA/DAVA message indicating [un]availability of certain point code[s]
|
||||
* \param[in] asp ASP through which to transmit message. Must be ACTIVE.
|
||||
* \param[in] rctx array of Routing Contexts in network byte order.
|
||||
* \param[in] num_rctx number of rctx
|
||||
* \param[in] aff_pc array of 'Affected Point Code' in network byte order.
|
||||
* \param[in] num_aff_pc number of aff_pc
|
||||
* \param[in] info_string optional information string (can be NULL).
|
||||
* \param[in] available are aff_pc now available (true) or unavailable (false) */
|
||||
void m3ua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc,
|
||||
const char *info_string, bool available)
|
||||
{
|
||||
struct xua_msg *xua;
|
||||
|
||||
if (available)
|
||||
xua = m3ua_encode_dava(rctx, num_rctx, aff_pc, num_aff_pc, info_string);
|
||||
else
|
||||
xua = m3ua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, info_string);
|
||||
|
||||
m3ua_tx_xua_asp(asp, xua);
|
||||
xua_msg_free(xua);
|
||||
}
|
||||
|
||||
/*! Transmit SSNM SCON message indicating congestion
|
||||
* \param[in] asp ASP through which to transmit message. Must be ACTIVE.
|
||||
* \param[in] rctx array of Routing Contexts in network byte order.
|
||||
* \param[in] num_rctx number of rctx
|
||||
* \param[in] aff_pc array of 'Affected Point Code' in network byte order.
|
||||
* \param[in] num_aff_pc number of aff_pc
|
||||
* \param[in] concerned_dpc optional concerned DPC (can be NULL)
|
||||
* \param[in] cong_level optional congestion level (can be NULL)
|
||||
* \param[in] info_string optional information string (can be NULL). */
|
||||
void m3ua_tx_snm_congestion(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc,
|
||||
const uint32_t *concerned_dpc, const uint8_t *cong_level,
|
||||
const char *info_string)
|
||||
{
|
||||
struct xua_msg *xua = xua_msg_alloc();
|
||||
|
||||
xua->hdr = XUA_HDR(M3UA_MSGC_SNM, M3UA_SNM_SCON);
|
||||
xua->hdr.version = M3UA_VERSION;
|
||||
if (rctx && num_rctx)
|
||||
xua_msg_add_data(xua, M3UA_IEI_ROUTE_CTX, num_rctx * sizeof(*rctx), (const uint8_t *)rctx);
|
||||
|
||||
xua_msg_add_data(xua, M3UA_IEI_AFFECTED_PC, num_aff_pc * sizeof(*aff_pc), (const uint8_t *) aff_pc);
|
||||
|
||||
if (concerned_dpc)
|
||||
xua_msg_add_u32(xua, M3UA_IEI_CONC_DEST, *concerned_dpc & 0xffffff);
|
||||
if (cong_level)
|
||||
xua_msg_add_u32(xua, M3UA_IEI_CONG_IND, *cong_level & 0xff);
|
||||
if (info_string)
|
||||
xua_msg_add_data(xua, M3UA_IEI_INFO_STRING, strlen(info_string)+1, (const uint8_t *) info_string);
|
||||
|
||||
m3ua_tx_xua_asp(asp, xua);
|
||||
xua_msg_free(xua);
|
||||
}
|
||||
|
||||
/*! Transmit SSNM DUPU message indicating user unavailability.
|
||||
* \param[in] asp ASP through which to transmit message. Must be ACTIVE.
|
||||
* \param[in] rctx array of Routing Contexts in network byte order.
|
||||
* \param[in] num_rctx number of rctx
|
||||
* \param[in] dpc affected point code
|
||||
* \param[in] user the user (SI) that is unavailable
|
||||
* \param[in] cause the cause of the user unavailability
|
||||
* \param[in] info_string optional information string (can be NULL). */
|
||||
void m3ua_tx_dupu(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
uint32_t dpc, uint16_t user, uint16_t cause, const char *info_str)
|
||||
{
|
||||
struct xua_msg *xua = m3ua_encode_dupu(rctx, num_rctx, dpc, user, cause, info_str);
|
||||
m3ua_tx_xua_asp(asp, xua);
|
||||
xua_msg_free(xua);
|
||||
}
|
||||
|
||||
/* received SNM message on ASP side */
|
||||
static int m3ua_rx_snm_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
||||
{
|
||||
struct osmo_ss7_as *as = NULL;
|
||||
struct xua_msg_part *rctx_ie = xua_msg_find_tag(xua, M3UA_IEI_ROUTE_CTX);
|
||||
int rc;
|
||||
|
||||
rc = xua_find_as_for_asp(&as, asp, rctx_ie);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* report those up the stack so both other ASPs and local SCCP users can be notified */
|
||||
switch (xua->hdr.msg_type) {
|
||||
case M3UA_SNM_DUNA:
|
||||
xua_snm_rx_duna(asp, as, xua);
|
||||
break;
|
||||
case M3UA_SNM_DAVA:
|
||||
xua_snm_rx_dava(asp, as, xua);
|
||||
break;
|
||||
case M3UA_SNM_DUPU:
|
||||
xua_snm_rx_dupu(asp, as, xua);
|
||||
break;
|
||||
case M3UA_SNM_SCON:
|
||||
xua_snm_rx_scon(asp, as, xua);
|
||||
break;
|
||||
case M3UA_SNM_DRST:
|
||||
LOGPASP(asp, DLM3UA, LOGL_NOTICE, "Received unsupported M3UA SNM message type %u\n",
|
||||
xua->hdr.msg_type);
|
||||
/* silently ignore those to not confuse the sender */
|
||||
break;
|
||||
case M3UA_SNM_DAUD:
|
||||
/* RFC states only permitted in ASP->SG direction, not reverse. But some
|
||||
* equipment still sends it to us as ASP ?!? */
|
||||
if (asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP) {
|
||||
LOGPASP(asp, DLM3UA, LOGL_NOTICE, "quirk daud_in_asp active: Accepting DAUD "
|
||||
"despite being in ASP role\n");
|
||||
xua_snm_rx_daud(asp, xua);
|
||||
} else {
|
||||
LOGPASP(asp, DLM3UA, LOGL_ERROR, "DAUD not permitted in ASP role\n");
|
||||
return M3UA_ERR_UNSUPP_MSG_TYPE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return M3UA_ERR_UNSUPP_MSG_TYPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* received SNM message on SG side */
|
||||
static int m3ua_rx_snm_sg(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
||||
{
|
||||
switch (xua->hdr.msg_type) {
|
||||
case M3UA_SNM_DAUD: /* Audit: ASP inquires about availability of Point Codes */
|
||||
xua_snm_rx_daud(asp, xua);
|
||||
break;
|
||||
default:
|
||||
return M3UA_ERR_UNSUPP_MSG_TYPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int m3ua_rx_snm(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
||||
{
|
||||
/* SNM only permitted in ACTIVE state */
|
||||
if (asp->fi->state != XUA_ASP_S_ACTIVE) {
|
||||
if (asp->fi->state == XUA_ASP_S_INACTIVE &&
|
||||
asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_SNM_INACTIVE) {
|
||||
LOGPASP(asp, DLM3UA, LOGL_NOTICE, "quirk snm_inactive active: "
|
||||
"Accepting SNM in state %s\n", osmo_fsm_inst_state_name(asp->fi));
|
||||
} else {
|
||||
LOGPASP(asp, DLM3UA, LOGL_ERROR, "Rx M3UA SNM not permitted "
|
||||
"while ASP in state %s\n", osmo_fsm_inst_state_name(asp->fi));
|
||||
return M3UA_ERR_UNEXPECTED_MSG;
|
||||
}
|
||||
}
|
||||
|
||||
switch (asp->cfg.role) {
|
||||
case OSMO_SS7_ASP_ROLE_SG:
|
||||
return m3ua_rx_snm_sg(asp, xua);
|
||||
case OSMO_SS7_ASP_ROLE_ASP:
|
||||
return m3ua_rx_snm_asp(asp, xua);
|
||||
default:
|
||||
return M3UA_ERR_UNSUPP_MSG_CLASS;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,6 @@
|
|||
* 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 <osmocom/mtp/mtp_pcap.h>
|
||||
|
@ -28,8 +24,6 @@
|
|||
|
||||
#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
|
||||
|
|
1323
src/osmo_ss7.c
1323
src/osmo_ss7.c
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,213 @@
|
|||
/* Core SS7 AS Handling */
|
||||
|
||||
/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include "ss7_internal.h"
|
||||
#include "xua_as_fsm.h"
|
||||
#include "xua_asp_fsm.h"
|
||||
|
||||
/***********************************************************************
|
||||
* SS7 Application Server
|
||||
***********************************************************************/
|
||||
|
||||
struct value_string osmo_ss7_as_traffic_mode_vals[] = {
|
||||
{ OSMO_SS7_AS_TMOD_BCAST, "broadcast" },
|
||||
{ OSMO_SS7_AS_TMOD_LOADSHARE, "loadshare" },
|
||||
{ OSMO_SS7_AS_TMOD_ROUNDROBIN, "round-robin" },
|
||||
{ OSMO_SS7_AS_TMOD_OVERRIDE, "override" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc ss7_as_rcd[] = {
|
||||
[SS7_AS_CTR_RX_MSU_TOTAL] = { "rx:msu:total", "Total number of MSU received" },
|
||||
[SS7_AS_CTR_TX_MSU_TOTAL] = { "tx:msu:total", "Total number of MSU transmitted" },
|
||||
};
|
||||
|
||||
static const struct rate_ctr_group_desc ss7_as_rcgd = {
|
||||
.group_name_prefix = "sigtran_as",
|
||||
.group_description = "SIGTRAN Application Server",
|
||||
.num_ctr = ARRAY_SIZE(ss7_as_rcd),
|
||||
.ctr_desc = ss7_as_rcd,
|
||||
};
|
||||
static unsigned int g_ss7_as_rcg_idx;
|
||||
|
||||
/*! \brief Allocate an Application Server
|
||||
* \param[in] inst SS7 Instance on which we operate
|
||||
* \param[in] name Name of Application Server
|
||||
* \param[in] proto Protocol of Application Server
|
||||
* \returns pointer to Application Server on success; NULL otherwise */
|
||||
struct osmo_ss7_as *ss7_as_alloc(struct osmo_ss7_instance *inst, const char *name,
|
||||
enum osmo_ss7_asp_protocol proto)
|
||||
{
|
||||
struct osmo_ss7_as *as;
|
||||
|
||||
as = talloc_zero(inst, struct osmo_ss7_as);
|
||||
if (!as)
|
||||
return NULL;
|
||||
as->ctrg = rate_ctr_group_alloc(as, &ss7_as_rcgd, g_ss7_as_rcg_idx++);
|
||||
if (!as->ctrg) {
|
||||
talloc_free(as);
|
||||
return NULL;
|
||||
}
|
||||
rate_ctr_group_set_name(as->ctrg, name);
|
||||
as->inst = inst;
|
||||
as->cfg.name = talloc_strdup(as, name);
|
||||
as->cfg.proto = proto;
|
||||
as->cfg.mode = OSMO_SS7_AS_TMOD_OVERRIDE;
|
||||
as->cfg.recovery_timeout_msec = 2000;
|
||||
as->cfg.routing_key.l_rk_id = ss7_find_free_l_rk_id(inst);
|
||||
as->fi = xua_as_fsm_start(as, LOGL_DEBUG);
|
||||
llist_add_tail(&as->list, &inst->as_list);
|
||||
|
||||
return as;
|
||||
}
|
||||
|
||||
/*! \brief Add given ASP to given AS
|
||||
* \param[in] as Application Server to which \ref asp is added
|
||||
* \param[in] asp Application Server Process to be added to \ref as
|
||||
* \returns 0 on success; negative in case of error */
|
||||
int osmo_ss7_as_add_asp(struct osmo_ss7_as *as, const char *asp_name)
|
||||
{
|
||||
struct osmo_ss7_asp *asp;
|
||||
unsigned int i;
|
||||
|
||||
OSMO_ASSERT(ss7_initialized);
|
||||
asp = osmo_ss7_asp_find_by_name(as->inst, asp_name);
|
||||
if (!asp)
|
||||
return -ENODEV;
|
||||
|
||||
LOGPAS(as, DLSS7, LOGL_INFO, "Adding ASP %s to AS\n", asp->cfg.name);
|
||||
|
||||
if (osmo_ss7_as_has_asp(as, asp))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
|
||||
if (!as->cfg.asps[i]) {
|
||||
as->cfg.asps[i] = asp;
|
||||
osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_AS_ASSIGNED, as);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/*! \brief Delete given ASP from given AS
|
||||
* \param[in] as Application Server from which \ref asp is deleted
|
||||
* \param[in] asp Application Server Process to delete from \ref as
|
||||
* \returns 0 on success; negative in case of error */
|
||||
int osmo_ss7_as_del_asp(struct osmo_ss7_as *as, const char *asp_name)
|
||||
{
|
||||
struct osmo_ss7_asp *asp;
|
||||
unsigned int i;
|
||||
|
||||
OSMO_ASSERT(ss7_initialized);
|
||||
asp = osmo_ss7_asp_find_by_name(as->inst, asp_name);
|
||||
if (!asp)
|
||||
return -ENODEV;
|
||||
|
||||
LOGPAS(as, DLSS7, LOGL_INFO, "Removing ASP %s from AS\n", asp->cfg.name);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
|
||||
if (as->cfg.asps[i] == asp) {
|
||||
as->cfg.asps[i] = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! \brief Destroy given Application Server
|
||||
* \param[in] as Application Server to destroy */
|
||||
void osmo_ss7_as_destroy(struct osmo_ss7_as *as)
|
||||
{
|
||||
struct osmo_ss7_route *rt, *rt2;
|
||||
|
||||
OSMO_ASSERT(ss7_initialized);
|
||||
LOGPAS(as, DLSS7, LOGL_INFO, "Destroying AS\n");
|
||||
|
||||
if (as->fi)
|
||||
osmo_fsm_inst_term(as->fi, OSMO_FSM_TERM_REQUEST, NULL);
|
||||
|
||||
/* find any routes pointing to this AS and remove them */
|
||||
llist_for_each_entry_safe(rt, rt2, &as->inst->rtable_system->routes, list) {
|
||||
if (rt->dest.as == as)
|
||||
osmo_ss7_route_destroy(rt);
|
||||
}
|
||||
|
||||
as->inst = NULL;
|
||||
llist_del(&as->list);
|
||||
rate_ctr_group_free(as->ctrg);
|
||||
talloc_free(as);
|
||||
}
|
||||
|
||||
/*! \brief Determine if given AS contains ASP
|
||||
* \param[in] as Application Server in which to look for \ref asp
|
||||
* \param[in] asp Application Server Process to look for in \ref as
|
||||
* \returns true in case \ref asp is part of \ref as; false otherwise */
|
||||
bool osmo_ss7_as_has_asp(const struct osmo_ss7_as *as,
|
||||
const struct osmo_ss7_asp *asp)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
OSMO_ASSERT(ss7_initialized);
|
||||
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
|
||||
if (as->cfg.asps[i] == asp)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! Determine if given AS is in the active state.
|
||||
* \param[in] as Application Server.
|
||||
* \returns true in case as is active; false otherwise. */
|
||||
bool osmo_ss7_as_active(const struct osmo_ss7_as *as)
|
||||
{
|
||||
if (!as->fi)
|
||||
return false;
|
||||
return as->fi->state == XUA_AS_S_ACTIVE;
|
||||
}
|
||||
|
||||
/*! Determine if given AS is in the down state.
|
||||
* \param[in] as Application Server.
|
||||
* \returns true in case as is down; false otherwise. */
|
||||
bool osmo_ss7_as_down(const struct osmo_ss7_as *as)
|
||||
{
|
||||
OSMO_ASSERT(as);
|
||||
|
||||
if (!as->fi)
|
||||
return true;
|
||||
return as->fi->state == XUA_AS_S_DOWN;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,294 @@
|
|||
/* SS7 ASP Peer (one endpoint of a conn) */
|
||||
|
||||
/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/sctp.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
|
||||
#include "ss7_internal.h"
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* SS7 Application Server Process peer
|
||||
***********************************************************************/
|
||||
|
||||
void osmo_ss7_asp_peer_init(struct osmo_ss7_asp_peer *peer)
|
||||
{
|
||||
memset(peer, 0, sizeof(*peer));
|
||||
peer->idx_primary = -1;
|
||||
}
|
||||
|
||||
int osmo_ss7_asp_peer_snprintf(char *buf, size_t buf_len, struct osmo_ss7_asp_peer *peer)
|
||||
{
|
||||
int len = 0, offset = 0, rem = buf_len;
|
||||
int ret, i;
|
||||
char *after;
|
||||
char *primary;
|
||||
|
||||
if (buf_len < 3)
|
||||
return -EINVAL;
|
||||
|
||||
if (peer->host_cnt > 1) {
|
||||
ret = snprintf(buf, rem, "(");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
OSMO_SNPRINTF_RET(ret, rem, offset, len);
|
||||
}
|
||||
for (i = 0; i < peer->host_cnt; i++) {
|
||||
primary = (peer->idx_primary == i) ? "*" : "";
|
||||
if (peer->host_cnt == 1)
|
||||
after = "";
|
||||
else
|
||||
after = (i == (peer->host_cnt - 1)) ? ")" : "|";
|
||||
ret = snprintf(buf + offset, rem, "%s%s%s", peer->host[i] ? : "0.0.0.0", primary, after);
|
||||
OSMO_SNPRINTF_RET(ret, rem, offset, len);
|
||||
}
|
||||
ret = snprintf(buf + offset, rem, ":%u", peer->port);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
OSMO_SNPRINTF_RET(ret, rem, offset, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*! \brief Set (copy) addresses for a given ASP peer. Previous addresses are freed.
|
||||
* \param[in] peer Application Server Process peer whose addresses are to be set.
|
||||
* \param[in] talloc_ctx talloc context used to allocate new addresses.
|
||||
* \param[in] hosts Array of strings containing IP addresses.
|
||||
* \param[in] host_cnt Number of strings in hosts
|
||||
* \param[in] idx_primary Index in "hosts" array marking the SCTP Primary Address, -1 if no explicit Primary Address set
|
||||
* \returns 0 on success; negative otherwise */
|
||||
int osmo_ss7_asp_peer_set_hosts2(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, const char *const*hosts, size_t host_cnt, int idx_primary)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (idx_primary >= (int)host_cnt || idx_primary < -1)
|
||||
return -EINVAL;
|
||||
|
||||
if (host_cnt > ARRAY_SIZE(peer->host))
|
||||
return -EINVAL;
|
||||
|
||||
for (; i < host_cnt; i++)
|
||||
osmo_talloc_replace_string(talloc_ctx, &peer->host[i], hosts[i]);
|
||||
for (; i < peer->host_cnt; i++) {
|
||||
talloc_free(peer->host[i]);
|
||||
peer->host[i] = NULL;
|
||||
}
|
||||
|
||||
peer->host_cnt = host_cnt;
|
||||
peer->idx_primary = idx_primary;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Set (copy) addresses for a given ASP peer. Previous addresses are freed.
|
||||
* \param[in] peer Application Server Process peer whose addresses are to be set.
|
||||
* \param[in] talloc_ctx talloc context used to allocate new addresses.
|
||||
* \param[in] hosts Array of strings containing IP addresses.
|
||||
* \param[in] host_cnt Number of strings in hosts
|
||||
* \returns 0 on success; negative otherwise */
|
||||
int osmo_ss7_asp_peer_set_hosts(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, const char *const*hosts, size_t host_cnt)
|
||||
{
|
||||
return osmo_ss7_asp_peer_set_hosts2(peer, talloc_ctx, hosts, host_cnt, -1);
|
||||
}
|
||||
|
||||
/* Is string formatted IPv4/v6 addr considered IN(6)ADDR_ANY? */
|
||||
static inline bool host_is_ip_anyaddr(const char *host, bool is_v6)
|
||||
{
|
||||
/* NULL addr is resolved as 0.0.0.0 (IPv4) by getaddrinfo(), most
|
||||
* probably for backward-compatibility reasons.
|
||||
*/
|
||||
return is_v6 ? (host && !strcmp(host, "::"))
|
||||
: (!host || !strcmp(host, "0.0.0.0"));
|
||||
}
|
||||
|
||||
/*! \brief Append (copy) address to a given ASP peer. Previous addresses are kept.
|
||||
* \param[in] peer Application Server Process peer the address is appended to.
|
||||
* \param[in] talloc_ctx talloc context used to allocate new address.
|
||||
* \param[in] host string containing an IP address.
|
||||
* \param[in] is_primary_addr whether this IP address is to be added as SCTP Primary Address
|
||||
* \returns 0 on success; negative otherwise */
|
||||
int osmo_ss7_asp_peer_add_host2(struct osmo_ss7_asp_peer *peer, void *talloc_ctx,
|
||||
const char *host, bool is_primary_addr)
|
||||
{
|
||||
int i;
|
||||
bool new_is_v6 = osmo_ip_str_type(host) == AF_INET6;
|
||||
bool new_is_any = host_is_ip_anyaddr(host, new_is_v6);
|
||||
struct osmo_sockaddr_str addr_str;
|
||||
|
||||
if (osmo_sockaddr_str_from_str(&addr_str, host, 0) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (new_is_any) {
|
||||
/* Makes no sense to have INET(6)_ANY many times, or INET(6)_ANY
|
||||
* together with specific addresses, be it of same or different
|
||||
* IP version: */
|
||||
if (peer->host_cnt != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Makes no sense to have INET(6)_ANY as primary: */
|
||||
if (is_primary_addr)
|
||||
return -EINVAL;
|
||||
|
||||
if (peer->host_cnt >= ARRAY_SIZE(peer->host))
|
||||
return -EINVAL;
|
||||
osmo_talloc_replace_string(talloc_ctx, &peer->host[peer->host_cnt], host);
|
||||
peer->host_cnt++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Makes no sense to add specific address to set if INET(6)_ANY
|
||||
* is already set, be it from same or different IP version: */
|
||||
for (i = 0; i < peer->host_cnt; i++) {
|
||||
bool iter_is_v6 = osmo_ip_str_type(peer->host[i]) == AF_INET6;
|
||||
if (host_is_ip_anyaddr(peer->host[i], iter_is_v6))
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Reached this point, no INET(6)_ANY address is set nor we are adding an INET(6)_ANY address. */
|
||||
|
||||
/* Check if address already exists, and then if primary flags need to be changed: */
|
||||
for (i = 0; i < peer->host_cnt; i++) {
|
||||
struct osmo_sockaddr_str it_addr_str;
|
||||
bool it_is_primary;
|
||||
osmo_sockaddr_str_from_str(&it_addr_str, peer->host[i], 0);
|
||||
|
||||
if (osmo_sockaddr_str_cmp(&addr_str, &it_addr_str) != 0)
|
||||
continue;
|
||||
it_is_primary = (peer->idx_primary == i);
|
||||
if (is_primary_addr == it_is_primary) {
|
||||
/* Nothing to do, return below */
|
||||
} else if (is_primary_addr && !it_is_primary) {
|
||||
/* Mark it as primary: */
|
||||
peer->idx_primary = i;
|
||||
} else { /* if (!is_primary_addr && it_is_primary) { */
|
||||
/* mark it as non-primary: */
|
||||
peer->idx_primary = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (peer->host_cnt >= ARRAY_SIZE(peer->host))
|
||||
return -EINVAL;
|
||||
|
||||
osmo_talloc_replace_string(talloc_ctx, &peer->host[peer->host_cnt], host);
|
||||
if (is_primary_addr)
|
||||
peer->idx_primary = peer->host_cnt;
|
||||
peer->host_cnt++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Remove address from a given ASP peer.
|
||||
* \param[in] peer Application Server Process peer the address is removed from.
|
||||
* \param[in] host string containing an IP address.
|
||||
* \returns 0 on success; negative otherwise */
|
||||
int osmo_ss7_asp_peer_del_host(struct osmo_ss7_asp_peer *peer, const char *host)
|
||||
{
|
||||
int i;
|
||||
struct osmo_sockaddr_str addr_str;
|
||||
bool found = false;
|
||||
|
||||
if (osmo_sockaddr_str_from_str(&addr_str, host, 0) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < peer->host_cnt; i++) {
|
||||
if (strcmp(host, peer->host[i]) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return -ENOENT;
|
||||
|
||||
/* If current primary points to addr being removed, unset it: */
|
||||
if (peer->idx_primary == i)
|
||||
peer->idx_primary = -1;
|
||||
/* If it's after it, update it together with sliding done further below: */
|
||||
else if (peer->idx_primary > i)
|
||||
peer->idx_primary--;
|
||||
|
||||
/* Free addr to remove: */
|
||||
TALLOC_FREE(peer->host[i]);
|
||||
|
||||
/* Move the rest of the array: */
|
||||
for (; i < peer->host_cnt - 1; i++)
|
||||
peer->host[i] = peer->host[i + 1];
|
||||
peer->host[i] = NULL;
|
||||
|
||||
/* Update array size: */
|
||||
peer->host_cnt--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Append (copy) address to a given ASP peer. Previous addresses are kept.
|
||||
* \param[in] peer Application Server Process peer the address is appended to.
|
||||
* \param[in] talloc_ctx talloc context used to allocate new address.
|
||||
* \param[in] host string containing an IP address.
|
||||
* \returns 0 on success; negative otherwise */
|
||||
int osmo_ss7_asp_peer_add_host(struct osmo_ss7_asp_peer *peer, void *talloc_ctx,
|
||||
const char *host)
|
||||
{
|
||||
return osmo_ss7_asp_peer_add_host2(peer, talloc_ctx, host, false);
|
||||
}
|
||||
|
||||
bool ss7_asp_peer_match_host(const struct osmo_ss7_asp_peer *peer, const char *host, bool host_is_v6)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; i < peer->host_cnt; i++) {
|
||||
bool iter_is_v6 = osmo_ip_str_type(peer->host[i]) == AF_INET6;
|
||||
bool iter_is_anyaddr = host_is_ip_anyaddr(peer->host[i], iter_is_v6);
|
||||
/* "::" (v6) covers "0.0.0.0" (v4), but not otherwise */
|
||||
if ((iter_is_v6 != host_is_v6) && !(iter_is_v6 && iter_is_anyaddr))
|
||||
continue;
|
||||
if (iter_is_anyaddr || !strcmp(peer->host[i], host))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! \brief Find the exact IP address match and return its index in the array
|
||||
* \param[in] peer Application Server Process peer where the address is looked up.
|
||||
* \param[in] host string containing an IP address.
|
||||
* \returns >=0 on success containing the index of the host; negative otherwise */
|
||||
int ss7_asp_peer_find_host(const struct osmo_ss7_asp_peer *peer, const char *host)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < peer->host_cnt; i++) {
|
||||
if (strcmp(host, peer->host[i]) == 0)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
|
@ -35,6 +35,7 @@
|
|||
#include <osmocom/sigtran/protocol/m3ua.h>
|
||||
|
||||
#include "xua_internal.h"
|
||||
#include "ss7_internal.h"
|
||||
|
||||
/* convert from M3UA message to MTP-TRANSFER.ind osmo_mtp_prim */
|
||||
struct osmo_mtp_prim *m3ua_to_xfer_ind(struct xua_msg *xua)
|
||||
|
@ -227,6 +228,14 @@ static int hmrt_message_for_routing(struct osmo_ss7_instance *inst,
|
|||
dpc, osmo_ss7_pointcode_print(inst, dpc), rt_name);
|
||||
}
|
||||
|
||||
if (osmo_ss7_as_down(as)) {
|
||||
LOGP(DLSS7, LOGL_ERROR, "Unable to route HMRT message: the AS %s is down\n",
|
||||
as->cfg.name);
|
||||
return -ENETDOWN;
|
||||
}
|
||||
|
||||
rate_ctr_inc2(as->ctrg, SS7_AS_CTR_TX_MSU_TOTAL);
|
||||
|
||||
switch (as->cfg.proto) {
|
||||
case OSMO_SS7_ASP_PROT_M3UA:
|
||||
DEBUGP(DLSS7, "rt->dest.as proto is M3UA for dpc=%u=%s\n",
|
||||
|
|
1795
src/osmo_ss7_vty.c
1795
src/osmo_ss7_vty.c
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,374 @@
|
|||
/* Core SS7 xUA Server */
|
||||
|
||||
/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/sctp.h>
|
||||
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
#include <osmocom/sigtran/mtp_sap.h>
|
||||
#include <osmocom/sigtran/protocol/mtp.h>
|
||||
#include <osmocom/sigtran/protocol/sua.h>
|
||||
#include <osmocom/sigtran/protocol/m3ua.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/core/osmo_io.h>
|
||||
|
||||
#include <osmocom/netif/stream.h>
|
||||
#include <osmocom/netif/ipa.h>
|
||||
#include <osmocom/netif/sctp.h>
|
||||
|
||||
#include "sccp_internal.h"
|
||||
#include "xua_internal.h"
|
||||
#include "ss7_internal.h"
|
||||
#include "xua_asp_fsm.h"
|
||||
#include "xua_as_fsm.h"
|
||||
|
||||
/***********************************************************************
|
||||
* SS7 xUA Server
|
||||
***********************************************************************/
|
||||
|
||||
/* server has accept()ed a new SCTP association, let's find the ASP for
|
||||
* it (if any) */
|
||||
static int xua_accept_cb(struct osmo_stream_srv_link *link, int fd)
|
||||
{
|
||||
struct osmo_xua_server *oxs = osmo_stream_srv_link_get_data(link);
|
||||
struct osmo_stream_srv *srv;
|
||||
struct osmo_ss7_asp *asp;
|
||||
char *sock_name = osmo_sock_get_name(link, fd);
|
||||
const char *proto_name = get_value_string(osmo_ss7_asp_protocol_vals, oxs->cfg.proto);
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DLSS7, LOGL_INFO, "%s: New %s connection accepted\n", sock_name, proto_name);
|
||||
|
||||
srv = osmo_stream_srv_create2(oxs, link, fd, NULL);
|
||||
if (!srv) {
|
||||
LOGP(DLSS7, LOGL_ERROR, "%s: Unable to create stream server "
|
||||
"for connection\n", sock_name);
|
||||
close(fd);
|
||||
talloc_free(sock_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
osmo_iofd_set_alloc_info(osmo_stream_srv_get_iofd(srv), M3UA_MSG_SIZE, M3UA_MSG_HEADROOM);
|
||||
|
||||
switch (oxs->cfg.proto) {
|
||||
case OSMO_SS7_ASP_PROT_IPA:
|
||||
osmo_stream_srv_set_read_cb(srv, ss7_asp_ipa_srv_conn_rx_cb);
|
||||
osmo_stream_srv_set_segmentation_cb(srv, osmo_ipa_segmentation_cb);
|
||||
break;
|
||||
case OSMO_SS7_ASP_PROT_M3UA:
|
||||
if (oxs->cfg.trans_proto == IPPROTO_SCTP)
|
||||
osmo_stream_srv_set_read_cb(srv, &ss7_asp_xua_srv_conn_rx_cb);
|
||||
else if (oxs->cfg.trans_proto == IPPROTO_TCP) {
|
||||
osmo_stream_srv_set_read_cb(srv, &ss7_asp_m3ua_tcp_srv_conn_rx_cb);
|
||||
osmo_stream_srv_set_segmentation_cb(srv, xua_tcp_segmentation_cb);
|
||||
} else
|
||||
OSMO_ASSERT(0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(oxs->cfg.trans_proto == IPPROTO_SCTP);
|
||||
osmo_stream_srv_set_read_cb(srv, &ss7_asp_xua_srv_conn_rx_cb);
|
||||
osmo_stream_srv_set_segmentation_cb(srv, NULL);
|
||||
break;
|
||||
}
|
||||
osmo_stream_srv_set_closed_cb(srv, ss7_asp_xua_srv_conn_closed_cb);
|
||||
|
||||
asp = ss7_asp_find_by_socket_addr(fd, oxs->cfg.trans_proto);
|
||||
if (asp) {
|
||||
LOGP(DLSS7, LOGL_INFO, "%s: matched connection to ASP %s\n",
|
||||
sock_name, asp->cfg.name);
|
||||
/* we need to check if we already have a socket associated, and close it. Otherwise it might
|
||||
* happen that both the listen-fd for this accept() and the old socket are marked 'readable'
|
||||
* during the same scheduling interval, and we're processing them in the "wrong" order, i.e.
|
||||
* we first see the accept of the new fd before we see the close on the old fd */
|
||||
if (asp->server) {
|
||||
LOGPASP(asp, DLSS7, LOGL_FATAL, "accept of new connection from %s before old was closed "
|
||||
"-> close old one\n", sock_name);
|
||||
osmo_stream_srv_set_data(asp->server, NULL);
|
||||
osmo_stream_srv_destroy(asp->server);
|
||||
asp->server = NULL;
|
||||
}
|
||||
} else {
|
||||
if (!oxs->cfg.accept_dyn_reg) {
|
||||
LOGP(DLSS7, LOGL_NOTICE, "%s: %s connection without matching "
|
||||
"ASP definition and no dynamic registration enabled, terminating\n",
|
||||
sock_name, proto_name);
|
||||
} else {
|
||||
char namebuf[32];
|
||||
static uint32_t dyn_asp_num = 0;
|
||||
snprintf(namebuf, sizeof(namebuf), "asp-dyn-%u", dyn_asp_num++);
|
||||
asp = osmo_ss7_asp_find_or_create2(oxs->inst, namebuf, 0, 0,
|
||||
oxs->cfg.trans_proto,
|
||||
oxs->cfg.proto);
|
||||
if (asp) {
|
||||
char hostbuf[INET6_ADDRSTRLEN];
|
||||
const char *hostbuf_ptr = &hostbuf[0];
|
||||
char portbuf[16];
|
||||
|
||||
osmo_sock_get_ip_and_port(fd, hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), false);
|
||||
LOGP(DLSS7, LOGL_INFO, "%s: created dynamic ASP %s\n",
|
||||
sock_name, asp->cfg.name);
|
||||
asp->cfg.is_server = true;
|
||||
asp->cfg.role = OSMO_SS7_ASP_ROLE_SG;
|
||||
asp->cfg.local.port = oxs->cfg.local.port;
|
||||
asp->cfg.remote.port = atoi(portbuf);
|
||||
asp->dyn_allocated = true;
|
||||
asp->server = srv;
|
||||
osmo_ss7_asp_peer_set_hosts(&asp->cfg.local, asp,
|
||||
(const char * const*)oxs->cfg.local.host,
|
||||
oxs->cfg.local.host_cnt);
|
||||
osmo_ss7_asp_peer_set_hosts(&asp->cfg.remote, asp,
|
||||
&hostbuf_ptr, 1);
|
||||
osmo_ss7_asp_restart(asp);
|
||||
}
|
||||
}
|
||||
if (!asp) {
|
||||
osmo_stream_srv_destroy(srv);
|
||||
talloc_free(sock_name);
|
||||
return -1;
|
||||
}
|
||||
llist_add_tail(&asp->siblings, &oxs->asp_list);
|
||||
}
|
||||
|
||||
/* update the ASP reference back to the server over which the
|
||||
* connection came in */
|
||||
asp->server = srv;
|
||||
asp->xua_server = oxs;
|
||||
|
||||
/* update the ASP socket name */
|
||||
talloc_free(asp->sock_name);
|
||||
asp->sock_name = talloc_reparent(link, asp, sock_name);
|
||||
osmo_stream_srv_set_name(asp->server, asp->cfg.name);
|
||||
/* make sure the conn_cb() is called with the asp as private
|
||||
* data */
|
||||
osmo_stream_srv_set_data(srv, asp);
|
||||
|
||||
if (oxs->cfg.trans_proto == IPPROTO_SCTP) {
|
||||
rc = ss7_asp_apply_peer_primary_address(asp);
|
||||
rc = ss7_asp_apply_primary_address(asp);
|
||||
}
|
||||
|
||||
/* send M-SCTP_ESTABLISH.ind to Layer Manager */
|
||||
osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_SCTP_EST_IND, 0);
|
||||
xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_ESTABLISH, PRIM_OP_INDICATION);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! \brief create a new xUA server configured with given ip/port
|
||||
* \param[in] inst SS7 Instance on which we operate
|
||||
* \param[in] trans_proto transport protocol to use (one of IPPROTO_*)
|
||||
* \param[in] proto protocol (xUA variant) to use
|
||||
* \param[in] local_port local SCTP port to bind/listen to
|
||||
* \param[in] local_host local IP address to bind/listen to (optional)
|
||||
* \returns callee-allocated \ref osmo_xua_server in case of success
|
||||
*/
|
||||
struct osmo_xua_server *
|
||||
osmo_ss7_xua_server_create2(struct osmo_ss7_instance *inst,
|
||||
int trans_proto, enum osmo_ss7_asp_protocol proto,
|
||||
uint16_t local_port, const char *local_host)
|
||||
{
|
||||
struct osmo_xua_server *oxs;
|
||||
|
||||
if (!ss7_asp_protocol_check_trans_proto(proto, trans_proto)) {
|
||||
LOGP(DLSCCP, LOGL_ERROR,
|
||||
"ASP protocol '%s' with transport protocol %d is not supported",
|
||||
osmo_ss7_asp_protocol_name(proto), trans_proto);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(ss7_initialized);
|
||||
oxs = talloc_zero(inst, struct osmo_xua_server);
|
||||
if (!oxs)
|
||||
return NULL;
|
||||
|
||||
LOGP(DLSS7, LOGL_INFO, "Creating %s Server %s:%u\n",
|
||||
get_value_string(osmo_ss7_asp_protocol_vals, proto), local_host, local_port);
|
||||
|
||||
INIT_LLIST_HEAD(&oxs->asp_list);
|
||||
|
||||
oxs->cfg.trans_proto = trans_proto;
|
||||
oxs->cfg.proto = proto;
|
||||
oxs->cfg.local.port = local_port;
|
||||
|
||||
oxs->server = osmo_stream_srv_link_create(oxs);
|
||||
osmo_stream_srv_link_set_name(oxs->server, osmo_ss7_asp_protocol_name(proto));
|
||||
osmo_stream_srv_link_set_data(oxs->server, oxs);
|
||||
osmo_stream_srv_link_set_accept_cb(oxs->server, xua_accept_cb);
|
||||
|
||||
osmo_stream_srv_link_set_nodelay(oxs->server, true);
|
||||
osmo_stream_srv_link_set_port(oxs->server, oxs->cfg.local.port);
|
||||
osmo_stream_srv_link_set_proto(oxs->server, trans_proto);
|
||||
|
||||
osmo_ss7_xua_server_set_local_host(oxs, local_host);
|
||||
|
||||
LOGP(DLSS7, LOGL_INFO, "Created %s server on %s:%" PRIu16 "\n",
|
||||
get_value_string(osmo_ss7_asp_protocol_vals, proto), local_host, local_port);
|
||||
|
||||
oxs->inst = inst;
|
||||
llist_add_tail(&oxs->list, &inst->xua_servers);
|
||||
|
||||
/* The SUA code internally needs SCCP to work */
|
||||
if (proto == OSMO_SS7_ASP_PROT_SUA)
|
||||
osmo_ss7_ensure_sccp(inst);
|
||||
|
||||
return oxs;
|
||||
}
|
||||
|
||||
/*! \brief create a new xUA server configured with given ip/port
|
||||
* \param[in] ctx talloc allocation context
|
||||
* \param[in] proto protocol (xUA variant) to use
|
||||
* \param[in] local_port local SCTP port to bind/listen to
|
||||
* \param[in] local_host local IP address to bind/listen to (optional)
|
||||
* \returns callee-allocated \ref osmo_xua_server in case of success
|
||||
*/
|
||||
struct osmo_xua_server *
|
||||
osmo_ss7_xua_server_create(struct osmo_ss7_instance *inst,
|
||||
enum osmo_ss7_asp_protocol proto,
|
||||
uint16_t local_port, const char *local_host)
|
||||
{
|
||||
const int trans_proto = ss7_default_trans_proto_for_asp_proto(proto);
|
||||
|
||||
return osmo_ss7_xua_server_create2(inst, trans_proto, proto,
|
||||
local_port, local_host);
|
||||
}
|
||||
|
||||
/*! \brief Set the xUA server to bind/listen to the currently configured ip/port
|
||||
* \param[in] xs xUA server to operate
|
||||
* \returns 0 on success, negative value on error.
|
||||
*/
|
||||
int
|
||||
osmo_ss7_xua_server_bind(struct osmo_xua_server *xs)
|
||||
{
|
||||
char buf[512];
|
||||
int rc;
|
||||
uint8_t byte;
|
||||
const char *proto = get_value_string(osmo_ss7_asp_protocol_vals, xs->cfg.proto);
|
||||
|
||||
rc = osmo_ss7_asp_peer_snprintf(buf, sizeof(buf), &xs->cfg.local);
|
||||
if (rc < 0) {
|
||||
LOGP(DLSS7, LOGL_INFO, "Failed parsing %s Server osmo_ss7_asp_peer\n", proto);
|
||||
} else {
|
||||
LOGP(DLSS7, LOGL_INFO, "(Re)binding %s Server to %s\n",
|
||||
proto, buf);
|
||||
}
|
||||
|
||||
/* Applying xUA Server config which may have changed through VTY on the srv_link before opening it: */
|
||||
byte = 1; /*AUTH is needed by ASCONF. enable, don't abort socket creation if AUTH can't be enabled */
|
||||
osmo_stream_srv_link_set_param(xs->server, OSMO_STREAM_SRV_LINK_PAR_SCTP_SOCKOPT_AUTH_SUPPORTED, &byte, sizeof(byte));
|
||||
byte = 1; /* enable, don't abort socket creation if ASCONF can't be enabled */
|
||||
osmo_stream_srv_link_set_param(xs->server, OSMO_STREAM_SRV_LINK_PAR_SCTP_SOCKOPT_ASCONF_SUPPORTED, &byte, sizeof(byte));
|
||||
if (xs->cfg.sctp_init.num_ostreams_present)
|
||||
osmo_stream_srv_link_set_param(xs->server, OSMO_STREAM_SRV_LINK_PAR_SCTP_INIT_NUM_OSTREAMS,
|
||||
&xs->cfg.sctp_init.num_ostreams_value,
|
||||
sizeof(xs->cfg.sctp_init.num_ostreams_value));
|
||||
if (xs->cfg.sctp_init.max_instreams_present)
|
||||
osmo_stream_srv_link_set_param(xs->server, OSMO_STREAM_SRV_LINK_PAR_SCTP_INIT_MAX_INSTREAMS,
|
||||
&xs->cfg.sctp_init.max_instreams_value,
|
||||
sizeof(xs->cfg.sctp_init.max_instreams_value));
|
||||
|
||||
return osmo_stream_srv_link_open(xs->server);
|
||||
}
|
||||
|
||||
int
|
||||
osmo_ss7_xua_server_set_local_host(struct osmo_xua_server *xs, const char *local_host)
|
||||
{
|
||||
return osmo_ss7_xua_server_set_local_hosts(xs, &local_host, 1);
|
||||
}
|
||||
|
||||
int
|
||||
osmo_ss7_xua_server_set_local_hosts(struct osmo_xua_server *xs, const char **local_hosts, size_t local_host_cnt)
|
||||
{
|
||||
int rc;
|
||||
OSMO_ASSERT(ss7_initialized);
|
||||
|
||||
rc = osmo_ss7_asp_peer_set_hosts(&xs->cfg.local, xs, local_hosts, local_host_cnt);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
return osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt);
|
||||
}
|
||||
|
||||
int
|
||||
osmo_ss7_xua_server_add_local_host(struct osmo_xua_server *xs, const char *local_host)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = osmo_ss7_asp_peer_add_host(&xs->cfg.local, xs, local_host);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
return osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt);
|
||||
}
|
||||
|
||||
int
|
||||
osmo_ss7_xua_server_del_local_host(struct osmo_xua_server *xs, const char *local_host)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = osmo_ss7_asp_peer_del_host(&xs->cfg.local, local_host);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
return osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt);
|
||||
}
|
||||
|
||||
bool ss7_xua_server_set_default_local_hosts(struct osmo_xua_server *oxs)
|
||||
{
|
||||
/* If no local addr was set, or erased after _create(): */
|
||||
if (!oxs->cfg.local.host_cnt) {
|
||||
/* "::" Covers both IPv4 and IPv6 */
|
||||
if (ss7_ipv6_sctp_supported("::", true))
|
||||
osmo_ss7_xua_server_set_local_host(oxs, "::");
|
||||
else
|
||||
osmo_ss7_xua_server_set_local_host(oxs, "0.0.0.0");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void osmo_ss7_xua_server_destroy(struct osmo_xua_server *xs)
|
||||
{
|
||||
struct osmo_ss7_asp *asp, *asp2;
|
||||
|
||||
if (xs->server) {
|
||||
osmo_stream_srv_link_close(xs->server);
|
||||
osmo_stream_srv_link_destroy(xs->server);
|
||||
}
|
||||
/* iterate and close all connections established in relation
|
||||
* with this server */
|
||||
llist_for_each_entry_safe(asp, asp2, &xs->asp_list, siblings)
|
||||
osmo_ss7_asp_destroy(asp);
|
||||
|
||||
llist_del(&xs->list);
|
||||
talloc_free(xs);
|
||||
}
|
42
src/sccp.c
42
src/sccp.c
|
@ -18,12 +18,9 @@
|
|||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
@ -31,7 +28,7 @@
|
|||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/endian.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
#include <osmocom/sccp/sccp.h>
|
||||
|
||||
// Unassigned debug area
|
||||
|
@ -230,6 +227,12 @@ int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result *
|
|||
}
|
||||
|
||||
if (optional_data.data_len != 0) {
|
||||
if (optional_data.data_len > SCCP_MAX_OPTIONAL_DATA) {
|
||||
LOGP(DSCCP, LOGL_ERROR,
|
||||
"optional data has length %u exceeding max of %u according to ITU-T Rec. Q.713 §4.2\n",
|
||||
optional_data.data_len, SCCP_MAX_OPTIONAL_DATA);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
msgb->l3h = &msgb->l2h[optional_data.data_start];
|
||||
result->data_len = optional_data.data_len;
|
||||
} else {
|
||||
|
@ -264,6 +267,12 @@ int _sccp_parse_connection_released(struct msgb *msgb, struct sccp_parse_result
|
|||
result->destination_local_reference = &rls->destination_local_reference;
|
||||
|
||||
if (optional_data.data_len != 0) {
|
||||
if (optional_data.data_len > SCCP_MAX_OPTIONAL_DATA) {
|
||||
LOGP(DSCCP, LOGL_ERROR,
|
||||
"optional data has length %u exceeding max of %u according to ITU-T Rec. Q.713 §4.5\n",
|
||||
optional_data.data_len, SCCP_MAX_OPTIONAL_DATA);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
msgb->l3h = &msgb->l2h[optional_data.data_start];
|
||||
result->data_len = optional_data.data_len;
|
||||
} else {
|
||||
|
@ -301,6 +310,12 @@ int _sccp_parse_connection_refused(struct msgb *msgb, struct sccp_parse_result *
|
|||
|
||||
/* optional data */
|
||||
if (optional_data.data_len != 0) {
|
||||
if (optional_data.data_len > SCCP_MAX_OPTIONAL_DATA) {
|
||||
LOGP(DSCCP, LOGL_ERROR,
|
||||
"optional data has length %u exceeding max of %u according to ITU-T Rec. Q.713 §4.4\n",
|
||||
optional_data.data_len, SCCP_MAX_OPTIONAL_DATA);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
msgb->l3h = &msgb->l2h[optional_data.data_start];
|
||||
result->data_len = optional_data.data_len;
|
||||
} else {
|
||||
|
@ -338,6 +353,12 @@ int _sccp_parse_connection_confirm(struct msgb *msgb, struct sccp_parse_result *
|
|||
}
|
||||
|
||||
if (optional_data.data_len != 0) {
|
||||
if (optional_data.data_len > SCCP_MAX_OPTIONAL_DATA) {
|
||||
LOGP(DSCCP, LOGL_ERROR,
|
||||
"optional data has length %u exceeding max of %u according to ITU-T Rec. Q.713 §4.3\n",
|
||||
optional_data.data_len, SCCP_MAX_OPTIONAL_DATA);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
msgb->l3h = &msgb->l2h[optional_data.data_start];
|
||||
result->data_len = optional_data.data_len;
|
||||
} else {
|
||||
|
@ -819,16 +840,15 @@ struct msgb *sccp_create_cr(const struct sccp_source_reference *src_ref,
|
|||
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) {
|
||||
if (l3_length < 3 || l3_length > SCCP_MAX_OPTIONAL_DATA) {
|
||||
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];
|
||||
|
|
560
src/sccp2sua.c
560
src/sccp2sua.c
|
@ -26,6 +26,7 @@
|
|||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/sccp/sccp.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
@ -337,10 +338,17 @@ static int sccp_addr_to_sua(struct xua_msg *xua, uint16_t iei, const uint8_t *ad
|
|||
}
|
||||
|
||||
/*! \brief convenience wrapper around sccp_addr_to_sua() for variable mandatory addresses */
|
||||
static int sccp_addr_to_sua_ptr(struct xua_msg *xua, uint16_t iei, struct msgb *msg, uint8_t *ptr_addr)
|
||||
static int sccp_addr_to_sua_ptr(struct xua_msg *xua, uint16_t iei, const uint8_t *ptr_addr, bool ptr_addr_is_long)
|
||||
{
|
||||
uint8_t *addr = ptr_addr + *ptr_addr + 1;
|
||||
unsigned int addrlen = *(ptr_addr + *ptr_addr);
|
||||
const uint8_t *addr;
|
||||
unsigned int addrlen;
|
||||
|
||||
if (ptr_addr_is_long) /* +1: Distance from MSB of pointer */
|
||||
addr = ptr_addr + 1 + osmo_load16le(ptr_addr);
|
||||
else
|
||||
addr = ptr_addr + *ptr_addr;
|
||||
addrlen = *addr;
|
||||
addr++;
|
||||
|
||||
return sccp_addr_to_sua(xua, iei, addr, addrlen);
|
||||
}
|
||||
|
@ -349,7 +357,7 @@ static int sccp_addr_to_sua_ptr(struct xua_msg *xua, uint16_t iei, struct msgb *
|
|||
* \param msg user-provided message buffer to which address shall be * appended
|
||||
* \param[in] part SUA wire format binary address
|
||||
* \returns 0 in case of success; negative on error */
|
||||
static int sua_addr_to_sccp(struct msgb *msg, struct xua_msg_part *part)
|
||||
static int sua_addr_to_sccp(struct msgb *msg, const struct xua_msg_part *part)
|
||||
{
|
||||
struct osmo_sccp_addr osa;
|
||||
int rc;
|
||||
|
@ -367,9 +375,10 @@ static int sua_addr_to_sccp(struct msgb *msg, struct xua_msg_part *part)
|
|||
/*! \brief Add a "SCCP Variable Mandatory Part" (Address format) to the given msgb
|
||||
* \param msg Message buffer to which part shall be added
|
||||
* \param[out] var_ptr pointer to relative pointer in SCCP header
|
||||
* \param[in] var_ptr_is_long Whether the var_ptr field is 2 bytes long (network order)
|
||||
* \param[in] xua xUA message from which to use address
|
||||
* \param[in] iei xUA information element identifier of address */
|
||||
static int sccp_add_var_addr(struct msgb *msg, uint8_t *var_ptr, struct xua_msg *xua, uint16_t iei)
|
||||
static int sccp_add_var_addr(struct msgb *msg, uint8_t *var_ptr, bool var_ptr_is_long, const struct xua_msg *xua, uint16_t iei)
|
||||
{
|
||||
struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
|
||||
uint8_t *lenbyte;
|
||||
|
@ -382,7 +391,10 @@ static int sccp_add_var_addr(struct msgb *msg, uint8_t *var_ptr, struct xua_msg
|
|||
/* first allocate one byte for the length */
|
||||
lenbyte = msgb_put(msg, 1);
|
||||
/* update the relative pointer to the length byte */
|
||||
*var_ptr = lenbyte - var_ptr;
|
||||
if (var_ptr_is_long) /* -1: Distance from MSB of pointer */
|
||||
osmo_store16le(lenbyte - var_ptr - 1, var_ptr);
|
||||
else
|
||||
*var_ptr = lenbyte - var_ptr;
|
||||
|
||||
/* then append the encoded SCCP address */
|
||||
rc = sua_addr_to_sccp(msg, part);
|
||||
|
@ -400,7 +412,7 @@ static int sccp_add_var_addr(struct msgb *msg, uint8_t *var_ptr, struct xua_msg
|
|||
* \param[out] var_ptr pointer to relative pointer in SCCP header
|
||||
* \param[in] xua xUA message from which to use source data
|
||||
* \param[in] iei xUA information element identifier of source data */
|
||||
static int sccp_add_variable_part(struct msgb *msg, uint8_t *var_ptr, struct xua_msg *xua, uint16_t iei)
|
||||
static int sccp_add_variable_part(struct msgb *msg, uint8_t *var_ptr, const struct xua_msg *xua, uint16_t iei)
|
||||
{
|
||||
struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
|
||||
uint8_t *lenbyte;
|
||||
|
@ -425,14 +437,45 @@ static int sccp_add_variable_part(struct msgb *msg, uint8_t *var_ptr, struct xua
|
|||
return part->len;
|
||||
}
|
||||
|
||||
/*! \brief Add a "SCCP Long Variable Mandatory Part" to the given msgb
|
||||
* \param msg Message buffer to which part shall be added
|
||||
* \param[out] var_ptr pointer to relative pointer in SCCP header
|
||||
* \param[in] xua xUA message from which to use source data
|
||||
* \param[in] iei xUA information element identifier of source data */
|
||||
static int sccp_add_long_variable_part(struct msgb *msg, uint8_t *var_ptr, const struct xua_msg *xua, uint16_t iei)
|
||||
{
|
||||
struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
|
||||
uint8_t *lenbyte;
|
||||
uint8_t *cur;
|
||||
if (!part) {
|
||||
LOGP(DLSUA, LOGL_ERROR, "Cannot find IEI %u in SUA message\n", iei);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* first allocate 2 bytes for the length */
|
||||
lenbyte = msgb_put(msg, 2);
|
||||
/* update the relative pointer to the length byte.
|
||||
* This is only used in LUDT and LUDTS, so the pointer is always 2 bytes.
|
||||
* -1: Distance from MSB of pointer */
|
||||
osmo_store16le(lenbyte - var_ptr - 1, var_ptr);
|
||||
|
||||
/* then append the encoded SCCP address */
|
||||
cur = msgb_put(msg, part->len);
|
||||
memcpy(cur, part->dat, part->len);
|
||||
|
||||
/* store the encoded length of the address, LSB first */
|
||||
osmo_store16le(part->len, lenbyte);
|
||||
|
||||
return part->len;
|
||||
}
|
||||
|
||||
/*! \brief validate that SCCP part with pointer + length doesn't exceed msg tail
|
||||
* \param[in] msg Message containing SCCP address
|
||||
* \param[in] ptr_addr pointer to byte with relative SCCP pointer
|
||||
* \returns true if OK; false if message inconsistent */
|
||||
static bool sccp_ptr_part_consistent(struct msgb *msg, uint8_t *ptr_addr)
|
||||
static bool sccp_ptr_part_consistent(const struct msgb *msg, const uint8_t *ptr_addr)
|
||||
{
|
||||
uint8_t *ptr;
|
||||
const uint8_t *ptr;
|
||||
|
||||
/* check the address of the relative pointer is within msg */
|
||||
if (ptr_addr < msg->data || ptr_addr > msg->tail) {
|
||||
|
@ -454,15 +497,68 @@ static bool sccp_ptr_part_consistent(struct msgb *msg, uint8_t *ptr_addr)
|
|||
return true;
|
||||
}
|
||||
|
||||
/*! \brief convenience wrapper around xua_msg_add_data() for variable mandatory data */
|
||||
static int sccp_data_to_sua_ptr(struct xua_msg *xua, uint16_t iei, struct msgb *msg, uint8_t *ptr_addr)
|
||||
/*! \brief validate that SCCP part with long pointer (2 bytes) + length doesn't exceed msg tail
|
||||
* \param[in] msg Message containing SCCP address (LUDT or LUDTS)
|
||||
* \param[in] ptr_addr pointer to byte with relative SCCP long pointer (uint16_t, 2 bytes in network order)
|
||||
* \param[in] len_is_long whether the length field at the starting of the value field pointer to by ptr_addr is 2 bytes long.
|
||||
* \returns true if OK; false if message inconsistent */
|
||||
static bool sccp_longptr_part_consistent(const struct msgb *msg, const uint8_t *ptr_addr, bool len_is_long)
|
||||
{
|
||||
uint8_t *addr = ptr_addr + *ptr_addr + 1;
|
||||
const uint8_t *ptr;
|
||||
uint8_t offs;
|
||||
uint16_t len;
|
||||
|
||||
/* check the address of the relative pointer is within msg */
|
||||
if (ptr_addr < msg->data || ptr_addr > msg->tail) {
|
||||
LOGP(DLSUA, LOGL_ERROR, "ptr_addr outside msg boundary\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* +1: Distance from MSB of pointer */
|
||||
ptr = ptr_addr + 1 + osmo_load16le(ptr_addr);
|
||||
if (ptr > msg->tail) {
|
||||
LOGP(DLSUA, LOGL_ERROR, "ptr %p points outside msg boundary %p\n", ptr, msg->tail);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* at destination of relative pointer is the length */
|
||||
if (len_is_long) {
|
||||
offs = 2;
|
||||
len = osmo_load16le(ptr);
|
||||
} else {
|
||||
offs = 1;
|
||||
len = *ptr;
|
||||
}
|
||||
if (ptr + offs + len > msg->tail) {
|
||||
LOGP(DLSUA, LOGL_ERROR, "ptr + len points outside msg boundary\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*! \brief convenience wrapper around xua_msg_add_data() for variable mandatory data */
|
||||
static int sccp_data_to_sua_ptr(struct xua_msg *xua, uint16_t iei, const uint8_t *ptr_addr)
|
||||
{
|
||||
const uint8_t *addr = ptr_addr + *ptr_addr + 1;
|
||||
unsigned int addrlen = *(ptr_addr + *ptr_addr);
|
||||
|
||||
return xua_msg_add_data(xua, iei, addrlen, addr);
|
||||
}
|
||||
|
||||
/*! \brief convenience wrapper around xua_msg_add_data() for variable mandatory data */
|
||||
static int sccp_longdata_to_sua_ptr(struct xua_msg *xua, uint16_t iei, const uint8_t *ptr_addr)
|
||||
{
|
||||
const uint16_t ptr_value = osmo_load16le(ptr_addr);
|
||||
/* +1: Distance from MSB of pointer */
|
||||
const uint8_t *addrlen_ptr = ptr_addr + 1 + ptr_value;
|
||||
/* +2: size of length field */
|
||||
const uint8_t *addr = addrlen_ptr + 2;
|
||||
unsigned int addrlen = osmo_load16le(addrlen_ptr);
|
||||
|
||||
return xua_msg_add_data(xua, iei, addrlen, addr);
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Convert a given SCCP option to SUA and add it to given xua_msg
|
||||
* \param xua caller-provided xUA message to which option is to be added
|
||||
* \param[in] sccp_opt_type SCCP option type (PNC)
|
||||
|
@ -470,7 +566,7 @@ static int sccp_data_to_sua_ptr(struct xua_msg *xua, uint16_t iei, struct msgb *
|
|||
* \param[in] opt pointer to wire-format encoded SCCP option data
|
||||
* \returns 0 in case of success; negative on error */
|
||||
static int xua_msg_add_sccp_opt(struct xua_msg *xua, uint8_t sccp_opt_type,
|
||||
uint16_t opt_len, uint8_t *opt)
|
||||
uint16_t opt_len, const uint8_t *opt)
|
||||
{
|
||||
switch (sccp_opt_type) {
|
||||
case SCCP_PNC_DESTINATION_LOCAL_REFERENCE:
|
||||
|
@ -586,7 +682,7 @@ static void msgb_put_sccp_opt(struct msgb *msg, uint8_t pnc, uint8_t len, const
|
|||
* \param msg caller-provided message buffer to which option is to be appended
|
||||
* \param[in] opt xUA option/IE (messge part) to be converted+added
|
||||
* \returns 0 in case of success; negative on error */
|
||||
static int sccp_msg_add_sua_opt(enum sccp_message_types type, struct msgb *msg, struct xua_msg_part *opt)
|
||||
static int sccp_msg_add_sua_opt(enum sccp_message_types type, struct msgb *msg, const struct xua_msg_part *opt)
|
||||
{
|
||||
uint32_t tmp32;
|
||||
uint8_t pnc, *lenptr;
|
||||
|
@ -696,22 +792,34 @@ static int sccp_msg_add_sua_opt(enum sccp_message_types type, struct msgb *msg,
|
|||
/*! \brief convert SCCP optional part to list of SUA options
|
||||
* \param[in] msg Message buffer holding SCCP message
|
||||
* \param[in] ptr_opt address of relative pointer to optional part
|
||||
* \param[in] ptr_opt_is_long whether ptr_opt is a long pointer (2 bytes, network order)
|
||||
* \param xua caller-provided xUA message to which options are added
|
||||
* \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_opt(struct msgb *msg, uint8_t *ptr_opt, struct xua_msg *xua)
|
||||
static struct xua_msg *sccp_to_xua_opt(const struct msgb *msg, const uint8_t *ptr_opt, bool ptr_opt_is_long, struct xua_msg *xua)
|
||||
{
|
||||
uint8_t *opt_start, *oneopt;
|
||||
const uint8_t *opt_start, *oneopt;
|
||||
uint16_t ptr_opt_value;
|
||||
|
||||
/* some bounds checking */
|
||||
if (ptr_opt < msg->data || ptr_opt > msg->tail)
|
||||
if (ptr_opt < msg->data)
|
||||
return NULL;
|
||||
if (ptr_opt > msg->tail - (ptr_opt_is_long ? 2 : 1))
|
||||
return NULL;
|
||||
|
||||
if (ptr_opt_is_long)
|
||||
ptr_opt_value = osmo_load16le(ptr_opt);
|
||||
else
|
||||
ptr_opt_value = *ptr_opt;
|
||||
|
||||
/* Q.713 section 2.3 "Coding of pointers": pointer value all zeros used
|
||||
to indicate that no optional param is present. */
|
||||
if (*ptr_opt == 0)
|
||||
if (ptr_opt_value == 0)
|
||||
return xua;
|
||||
|
||||
opt_start = ptr_opt + *ptr_opt;
|
||||
if (ptr_opt_is_long) /* +1: Distance from MSB of pointer */
|
||||
opt_start = ptr_opt + 1 + ptr_opt_value;
|
||||
else
|
||||
opt_start = ptr_opt + ptr_opt_value;
|
||||
if (opt_start > msg->tail)
|
||||
return NULL;
|
||||
|
||||
|
@ -982,37 +1090,55 @@ static bool sccp_option_permitted(enum sccp_message_types type, const struct xua
|
|||
return false;
|
||||
}
|
||||
|
||||
static int xua_ies_to_sccp_opts(struct msgb *msg, uint8_t *ptr_opt,
|
||||
enum sccp_message_types type, struct xua_msg *xua)
|
||||
static int xua_ies_to_sccp_opts(struct msgb *msg, uint8_t *ptr_opt, bool ptr_opt_is_long,
|
||||
enum sccp_message_types type, const struct xua_msg *xua)
|
||||
{
|
||||
struct xua_msg_part *part;
|
||||
|
||||
/* store relative pointer to start of optional part */
|
||||
*ptr_opt = msg->tail - ptr_opt;
|
||||
const struct xua_msg_part *part;
|
||||
bool any_added = false;
|
||||
uint8_t *old_tail = msg->tail;
|
||||
|
||||
llist_for_each_entry(part, &xua->headers, entry) {
|
||||
/* make sure we don't add a SCCP option for information
|
||||
* that is already present in mandatory fixed or
|
||||
* mandatory variable parts of the header */
|
||||
if (!sccp_is_mandatory(type, part) && sccp_option_permitted(type, part))
|
||||
if (!sccp_is_mandatory(type, part) && sccp_option_permitted(type, part)) {
|
||||
sccp_msg_add_sua_opt(type, msg, part);
|
||||
any_added = true;
|
||||
}
|
||||
}
|
||||
if (any_added) {
|
||||
msgb_put_u8(msg, SCCP_PNC_END_OF_OPTIONAL);
|
||||
/* store relative pointer to start of optional part */
|
||||
if (ptr_opt_is_long) /* -1: Distance from MSB of pointer */
|
||||
osmo_store16le(old_tail - ptr_opt - 1, ptr_opt);
|
||||
else
|
||||
*ptr_opt = old_tail - ptr_opt;
|
||||
} else {
|
||||
/* If nothing was added, simply update the pointer to 0 to signal the optional part is omitted. */
|
||||
ptr_opt[0] = 0;
|
||||
if (ptr_opt_is_long)
|
||||
ptr_opt[1] = 0;
|
||||
}
|
||||
msgb_put_u8(msg, SCCP_PNC_END_OF_OPTIONAL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* store a 'local reference' as big-eidian 24bit value at local_ref */
|
||||
static void store_local_ref(struct sccp_source_reference *local_ref, struct xua_msg *xua, uint16_t iei)
|
||||
/* store a 'local reference' as big-endian 24bit value at local_ref */
|
||||
static int store_local_ref(struct sccp_source_reference *local_ref, const struct xua_msg *xua, uint16_t iei)
|
||||
{
|
||||
uint32_t tmp32 = xua_msg_get_u32(xua, iei);
|
||||
if (tmp32 > 0x00fffffe) {
|
||||
LOGP(DLSUA, LOGL_ERROR, "SUA->SCCP: Local Reference value 0x%" PRIx32 " > 0x00fffffe\n", tmp32);
|
||||
return -1;
|
||||
}
|
||||
local_ref->octet1 = (tmp32 >> 16) & 0xff;
|
||||
local_ref->octet2 = (tmp32 >> 8) & 0xff;
|
||||
local_ref->octet3 = tmp32 & 0xff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_cr(struct msgb *msg, struct xua_msg *xua)
|
||||
static struct xua_msg *sccp_to_xua_cr(const struct msgb *msg, struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_connection_request *req = (struct sccp_connection_request *)msg->l2h;
|
||||
|
||||
|
@ -1022,30 +1148,33 @@ static struct xua_msg *sccp_to_xua_cr(struct msgb *msg, struct xua_msg *xua)
|
|||
/* Variable Part */
|
||||
if (!sccp_ptr_part_consistent(msg, &req->variable_called))
|
||||
return NULL;
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, msg, &req->variable_called);
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &req->variable_called, false);
|
||||
/* Optional Part */
|
||||
return sccp_to_xua_opt(msg, &req->optional_start, xua);
|
||||
return sccp_to_xua_opt(msg, &req->optional_start, false, xua);
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static int sua_to_sccp_cr(struct msgb *msg, struct xua_msg *xua)
|
||||
static int sua_to_sccp_cr(struct msgb *msg, const struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_connection_request *req;
|
||||
int rc;
|
||||
req = (struct sccp_connection_request *) msgb_put(msg, sizeof(*req));
|
||||
|
||||
/* Fixed Part */
|
||||
req->type = SCCP_MSG_TYPE_CR;
|
||||
req->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
|
||||
store_local_ref(&req->source_local_reference, xua, SUA_IEI_SRC_REF);
|
||||
rc = store_local_ref(&req->source_local_reference, xua, SUA_IEI_SRC_REF);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
/* Variable Part */
|
||||
sccp_add_var_addr(msg, &req->variable_called, xua, SUA_IEI_DEST_ADDR);
|
||||
sccp_add_var_addr(msg, &req->variable_called, false, xua, SUA_IEI_DEST_ADDR);
|
||||
|
||||
/* Optional Part */
|
||||
return xua_ies_to_sccp_opts(msg, &req->optional_start, req->type, xua);
|
||||
return xua_ies_to_sccp_opts(msg, &req->optional_start, false, req->type, xua);
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_cc(struct msgb *msg, struct xua_msg *xua)
|
||||
static struct xua_msg *sccp_to_xua_cc(const struct msgb *msg, struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_connection_confirm *cnf = (struct sccp_connection_confirm *)msg->l2h;
|
||||
|
||||
|
@ -1054,83 +1183,96 @@ static struct xua_msg *sccp_to_xua_cc(struct msgb *msg, struct xua_msg *xua)
|
|||
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&cnf->destination_local_reference));
|
||||
xua_msg_add_u32(xua, SUA_IEI_SRC_REF, load_24be(&cnf->source_local_reference));
|
||||
/* Optional Part */
|
||||
return sccp_to_xua_opt(msg, &cnf->optional_start, xua);
|
||||
return sccp_to_xua_opt(msg, &cnf->optional_start, false, xua);
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static int sua_to_sccp_cc(struct msgb *msg, struct xua_msg *xua)
|
||||
static int sua_to_sccp_cc(struct msgb *msg, const struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_connection_confirm *cnf;
|
||||
int rc;
|
||||
cnf = (struct sccp_connection_confirm *) msgb_put(msg, sizeof(*cnf));
|
||||
|
||||
/* Fixed Part */
|
||||
cnf->type = SCCP_MSG_TYPE_CC;
|
||||
cnf->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
|
||||
store_local_ref(&cnf->destination_local_reference, xua, SUA_IEI_DEST_REF);
|
||||
store_local_ref(&cnf->source_local_reference, xua, SUA_IEI_SRC_REF);
|
||||
rc = store_local_ref(&cnf->destination_local_reference, xua, SUA_IEI_DEST_REF);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
rc = store_local_ref(&cnf->source_local_reference, xua, SUA_IEI_SRC_REF);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
/* Optional Part */
|
||||
return xua_ies_to_sccp_opts(msg, &cnf->optional_start, cnf->type, xua);
|
||||
return xua_ies_to_sccp_opts(msg, &cnf->optional_start, false, cnf->type, xua);
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_cref(struct msgb *msg, struct xua_msg *xua)
|
||||
static struct xua_msg *sccp_to_xua_cref(const struct msgb *msg, struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_connection_refused *ref = (struct sccp_connection_refused *)msg->l2h;
|
||||
const struct sccp_connection_refused *ref = (const struct sccp_connection_refused *)msg->l2h;
|
||||
|
||||
/* Fixed Part */
|
||||
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&ref->destination_local_reference));
|
||||
xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_REFUSAL | ref->cause);
|
||||
/* Optional Part */
|
||||
return sccp_to_xua_opt(msg, &ref->optional_start, xua);
|
||||
return sccp_to_xua_opt(msg, &ref->optional_start, false, xua);
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static int sua_to_sccp_cref(struct msgb *msg, struct xua_msg *xua)
|
||||
static int sua_to_sccp_cref(struct msgb *msg, const struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_connection_refused *ref;
|
||||
int rc;
|
||||
ref = (struct sccp_connection_refused *) msgb_put(msg, sizeof(*ref));
|
||||
|
||||
/* Fixed Part */
|
||||
ref->type = SCCP_MSG_TYPE_CREF;
|
||||
store_local_ref(&ref->destination_local_reference, xua, SUA_IEI_DEST_REF);
|
||||
rc = store_local_ref(&ref->destination_local_reference, xua, SUA_IEI_DEST_REF);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
ref->cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff;
|
||||
/* Optional Part */
|
||||
return xua_ies_to_sccp_opts(msg, &ref->optional_start, ref->type, xua);
|
||||
return xua_ies_to_sccp_opts(msg, &ref->optional_start, false, ref->type, xua);
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_rlsd(struct msgb *msg, struct xua_msg *xua)
|
||||
static struct xua_msg *sccp_to_xua_rlsd(const struct msgb *msg, struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_connection_released *rlsd = (struct sccp_connection_released *)msg->l2h;
|
||||
const struct sccp_connection_released *rlsd = (const struct sccp_connection_released *)msg->l2h;
|
||||
|
||||
/* Fixed Part */
|
||||
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&rlsd->destination_local_reference));
|
||||
xua_msg_add_u32(xua, SUA_IEI_SRC_REF, load_24be(&rlsd->source_local_reference));
|
||||
xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RELEASE | rlsd->release_cause);
|
||||
/* Optional Part */
|
||||
return sccp_to_xua_opt(msg, &rlsd->optional_start, xua);
|
||||
return sccp_to_xua_opt(msg, &rlsd->optional_start, false, xua);
|
||||
}
|
||||
|
||||
static int sua_to_sccp_rlsd(struct msgb *msg, struct xua_msg *xua)
|
||||
static int sua_to_sccp_rlsd(struct msgb *msg, const struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_connection_released *rlsd;
|
||||
int rc;
|
||||
rlsd =(struct sccp_connection_released *) msgb_put(msg, sizeof(*rlsd));
|
||||
|
||||
/* Fixed Part */
|
||||
rlsd->type = SCCP_MSG_TYPE_RLSD;
|
||||
store_local_ref(&rlsd->destination_local_reference, xua, SUA_IEI_DEST_REF);
|
||||
store_local_ref(&rlsd->source_local_reference, xua, SUA_IEI_SRC_REF);
|
||||
rc = store_local_ref(&rlsd->destination_local_reference, xua, SUA_IEI_DEST_REF);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
rc = store_local_ref(&rlsd->source_local_reference, xua, SUA_IEI_SRC_REF);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
rlsd->release_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff;
|
||||
|
||||
/* Optional Part */
|
||||
return xua_ies_to_sccp_opts(msg, &rlsd->optional_start, rlsd->type, xua);
|
||||
return xua_ies_to_sccp_opts(msg, &rlsd->optional_start, false, rlsd->type, xua);
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_rlc(struct msgb *msg, struct xua_msg *xua)
|
||||
static struct xua_msg *sccp_to_xua_rlc(const struct msgb *msg, struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_connection_release_complete *rlc;
|
||||
rlc = (struct sccp_connection_release_complete *) msg->l2h;
|
||||
const struct sccp_connection_release_complete *rlc;
|
||||
rlc = (const struct sccp_connection_release_complete *) msg->l2h;
|
||||
|
||||
/* Fixed Part */
|
||||
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&rlc->destination_local_reference));
|
||||
|
@ -1138,22 +1280,27 @@ static struct xua_msg *sccp_to_xua_rlc(struct msgb *msg, struct xua_msg *xua)
|
|||
return xua;
|
||||
}
|
||||
|
||||
static int sua_to_sccp_rlc(struct msgb *msg, struct xua_msg *xua)
|
||||
static int sua_to_sccp_rlc(struct msgb *msg, const struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_connection_release_complete *rlc;
|
||||
int rc;
|
||||
rlc = (struct sccp_connection_release_complete *) msgb_put(msg, sizeof(*rlc));
|
||||
|
||||
/* Fixed Part */
|
||||
rlc->type = SCCP_MSG_TYPE_RLC;
|
||||
store_local_ref(&rlc->destination_local_reference, xua, SUA_IEI_DEST_REF);
|
||||
store_local_ref(&rlc->source_local_reference, xua, SUA_IEI_SRC_REF);
|
||||
rc = store_local_ref(&rlc->destination_local_reference, xua, SUA_IEI_DEST_REF);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
rc = store_local_ref(&rlc->source_local_reference, xua, SUA_IEI_SRC_REF);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_dt1(struct msgb *msg, struct xua_msg *xua)
|
||||
static struct xua_msg *sccp_to_xua_dt1(const struct msgb *msg, struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_data_form1 *dt1 = (struct sccp_data_form1 *) msg->l2h;
|
||||
const struct sccp_data_form1 *dt1 = (const struct sccp_data_form1 *) msg->l2h;
|
||||
|
||||
/* Fixed Part */
|
||||
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&dt1->destination_local_reference));
|
||||
|
@ -1161,18 +1308,21 @@ static struct xua_msg *sccp_to_xua_dt1(struct msgb *msg, struct xua_msg *xua)
|
|||
/* Variable Part */
|
||||
if (!sccp_ptr_part_consistent(msg, &dt1->variable_start))
|
||||
return NULL;
|
||||
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, msg, &dt1->variable_start);
|
||||
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, &dt1->variable_start);
|
||||
return xua;
|
||||
}
|
||||
|
||||
static int sua_to_sccp_dt1(struct msgb *msg, struct xua_msg *xua)
|
||||
static int sua_to_sccp_dt1(struct msgb *msg, const struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_data_form1 *dt1;
|
||||
int rc;
|
||||
dt1 = (struct sccp_data_form1 *) msgb_put(msg, sizeof(*dt1));
|
||||
|
||||
/* Fixed Part */
|
||||
dt1->type = SCCP_MSG_TYPE_DT1;
|
||||
store_local_ref(&dt1->destination_local_reference, xua, SUA_IEI_DEST_REF);
|
||||
rc = store_local_ref(&dt1->destination_local_reference, xua, SUA_IEI_DEST_REF);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
dt1->segmenting = xua_msg_get_u32(xua, SUA_IEI_SEGMENTATION);
|
||||
/* Variable Part */
|
||||
sccp_add_variable_part(msg, &dt1->variable_start, xua, SUA_IEI_DATA);
|
||||
|
@ -1180,82 +1330,276 @@ static int sua_to_sccp_dt1(struct msgb *msg, struct xua_msg *xua)
|
|||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_udt(struct msgb *msg, struct xua_msg *xua)
|
||||
static struct xua_msg *sccp_to_xua_udt(const struct msgb *msg, struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msg->l2h;
|
||||
const struct sccp_data_unitdata *udt = (const struct sccp_data_unitdata *)msg->l2h;
|
||||
|
||||
/* Fixed Part */
|
||||
xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, udt->proto_class);
|
||||
/* Variable Part */
|
||||
if (!sccp_ptr_part_consistent(msg, &udt->variable_called))
|
||||
return NULL;
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, msg, &udt->variable_called);
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &udt->variable_called, false);
|
||||
if (!sccp_ptr_part_consistent(msg, &udt->variable_calling))
|
||||
return NULL;
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, msg, &udt->variable_calling);
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &udt->variable_calling, false);
|
||||
if (!sccp_ptr_part_consistent(msg, &udt->variable_data))
|
||||
return NULL;
|
||||
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, msg, &udt->variable_data);
|
||||
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, &udt->variable_data);
|
||||
return xua;
|
||||
|
||||
}
|
||||
|
||||
static int sua_to_sccp_udt(struct msgb *msg, struct xua_msg *xua)
|
||||
static int sua_to_sccp_xudt(struct msgb *msg, const struct xua_msg *xua);
|
||||
static int sua_to_sccp_ludt(struct msgb *msg, const struct xua_msg *xua);
|
||||
|
||||
static int sua_to_sccp_udt(struct msgb *msg, const struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_data_unitdata *udt;
|
||||
|
||||
/* Use LUDT if length exceeds 255 (single byte length field) */
|
||||
/* TODO: start using LUDT sooner if called/calling party contain GT or if
|
||||
* segmentation and/or importance present, see Q.715 Section 8.3.2 */
|
||||
if (xua_msg_get_len(xua, SUA_IEI_DATA) > 255)
|
||||
return sua_to_sccp_ludt(msg, xua);
|
||||
|
||||
/* Use XUDT if we have a hop counter on the SUA side */
|
||||
if (xua_msg_find_tag(xua, SUA_IEI_S7_HOP_CTR))
|
||||
return sua_to_sccp_xudt(msg, xua);
|
||||
|
||||
udt = (struct sccp_data_unitdata *) msgb_put(msg, sizeof(*udt));
|
||||
|
||||
/* Fixed Part */
|
||||
udt->type = SCCP_MSG_TYPE_UDT;
|
||||
udt->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
|
||||
/* Variable Part */
|
||||
sccp_add_var_addr(msg, &udt->variable_called, xua, SUA_IEI_DEST_ADDR);
|
||||
sccp_add_var_addr(msg, &udt->variable_calling, xua, SUA_IEI_SRC_ADDR);
|
||||
sccp_add_var_addr(msg, &udt->variable_called, false, xua, SUA_IEI_DEST_ADDR);
|
||||
sccp_add_var_addr(msg, &udt->variable_calling, false, xua, SUA_IEI_SRC_ADDR);
|
||||
sccp_add_variable_part(msg, &udt->variable_data, xua, SUA_IEI_DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_udts(struct msgb *msg, struct xua_msg *xua)
|
||||
static struct xua_msg *sccp_to_xua_xudt(const struct msgb *msg, struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_data_unitdata_service *udts;
|
||||
udts =(struct sccp_data_unitdata_service *)msg->l2h;
|
||||
const struct sccp_data_ext_unitdata *xudt = (const struct sccp_data_ext_unitdata *)msg->l2h;
|
||||
|
||||
/* Fixed Part */
|
||||
xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, xudt->proto_class);
|
||||
xua_msg_add_u32(xua, SUA_IEI_S7_HOP_CTR, xudt->hop_counter);
|
||||
/* Variable Part */
|
||||
if (!sccp_ptr_part_consistent(msg, &xudt->variable_called))
|
||||
return NULL;
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &xudt->variable_called, false);
|
||||
if (!sccp_ptr_part_consistent(msg, &xudt->variable_calling))
|
||||
return NULL;
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &xudt->variable_calling, false);
|
||||
if (!sccp_ptr_part_consistent(msg, &xudt->variable_data))
|
||||
return NULL;
|
||||
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, &xudt->variable_data);
|
||||
/* Optional Part */
|
||||
return sccp_to_xua_opt(msg, &xudt->optional_start, false, xua);
|
||||
|
||||
}
|
||||
|
||||
static int sua_to_sccp_xudt(struct msgb *msg, const struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_data_ext_unitdata *xudt;
|
||||
|
||||
/* Use LUDT if length exceeds 255 (single byte length field) */
|
||||
/* TODO: start using LUDTS sooner if called/calling party contain GT or if
|
||||
* segmentation and/or importance present, see Q.715 Section 8.3.2 */
|
||||
if (xua_msg_get_len(xua, SUA_IEI_DATA) > 255)
|
||||
return sua_to_sccp_ludt(msg, xua);
|
||||
|
||||
xudt = (struct sccp_data_ext_unitdata *) msgb_put(msg, sizeof(*xudt));
|
||||
|
||||
/* Fixed Part */
|
||||
xudt->type = SCCP_MSG_TYPE_XUDT;
|
||||
xudt->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
|
||||
xudt->hop_counter = xua_msg_get_u32(xua, SUA_IEI_S7_HOP_CTR);
|
||||
/* Variable Part */
|
||||
sccp_add_var_addr(msg, &xudt->variable_called, false, xua, SUA_IEI_DEST_ADDR);
|
||||
sccp_add_var_addr(msg, &xudt->variable_calling, false, xua, SUA_IEI_SRC_ADDR);
|
||||
sccp_add_variable_part(msg, &xudt->variable_data, xua, SUA_IEI_DATA);
|
||||
/* Optional Part */
|
||||
return xua_ies_to_sccp_opts(msg, &xudt->optional_start, false, xudt->type, xua);
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_ludt(struct msgb *msg, struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_data_long_unitdata *ludt = (struct sccp_data_long_unitdata *)msg->l2h;
|
||||
|
||||
/* Fixed Part */
|
||||
xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, ludt->proto_class);
|
||||
xua_msg_add_u32(xua, SUA_IEI_S7_HOP_CTR, ludt->hop_counter);
|
||||
/* Variable Part */
|
||||
if (!sccp_longptr_part_consistent(msg, (uint8_t *)&ludt->variable_called, false))
|
||||
return NULL;
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, (uint8_t *)&ludt->variable_called, true);
|
||||
if (!sccp_longptr_part_consistent(msg, (uint8_t *)&ludt->variable_calling, false))
|
||||
return NULL;
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, (uint8_t *)&ludt->variable_calling, true);
|
||||
if (!sccp_longptr_part_consistent(msg, (uint8_t *)&ludt->variable_data, true))
|
||||
return NULL;
|
||||
sccp_longdata_to_sua_ptr(xua, SUA_IEI_DATA, (uint8_t *)&ludt->variable_data);
|
||||
/* Optional Part */
|
||||
return sccp_to_xua_opt(msg, (uint8_t *)&ludt->optional_start, true, xua);
|
||||
}
|
||||
|
||||
static int sua_to_sccp_ludt(struct msgb *msg, const struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_data_long_unitdata *ludt;
|
||||
ludt = (struct sccp_data_long_unitdata *) msgb_put(msg, sizeof(*ludt));
|
||||
|
||||
/* Fixed Part */
|
||||
ludt->type = SCCP_MSG_TYPE_LUDT;
|
||||
ludt->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
|
||||
ludt->hop_counter = xua_msg_get_u32(xua, SUA_IEI_S7_HOP_CTR);
|
||||
/* Variable Part */
|
||||
sccp_add_var_addr(msg, (uint8_t *)&ludt->variable_called, true, xua, SUA_IEI_DEST_ADDR);
|
||||
sccp_add_var_addr(msg, (uint8_t *)&ludt->variable_calling, true, xua, SUA_IEI_SRC_ADDR);
|
||||
sccp_add_long_variable_part(msg, (uint8_t *)&ludt->variable_data, xua, SUA_IEI_DATA);
|
||||
/* Optional Part */
|
||||
return xua_ies_to_sccp_opts(msg, (uint8_t *)&ludt->optional_start, true, ludt->type, xua);
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_udts(const struct msgb *msg, struct xua_msg *xua)
|
||||
{
|
||||
const struct sccp_data_unitdata_service *udts;
|
||||
udts = (const struct sccp_data_unitdata_service *)msg->l2h;
|
||||
|
||||
/* Fixed Part */
|
||||
xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RETURN | udts->return_cause);
|
||||
/* Variable Part */
|
||||
if (!sccp_ptr_part_consistent(msg, &udts->variable_called))
|
||||
return NULL;
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, msg, &udts->variable_called);
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &udts->variable_called, false);
|
||||
if (!sccp_ptr_part_consistent(msg, &udts->variable_calling))
|
||||
return NULL;
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, msg, &udts->variable_calling);
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &udts->variable_calling, false);
|
||||
if (!sccp_ptr_part_consistent(msg, &udts->variable_data))
|
||||
return NULL;
|
||||
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, msg, &udts->variable_data);
|
||||
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, &udts->variable_data);
|
||||
return xua;
|
||||
|
||||
}
|
||||
|
||||
static int sua_to_sccp_udts(struct msgb *msg, struct xua_msg *xua)
|
||||
static int sua_to_sccp_xudts(struct msgb *msg, const struct xua_msg *xua);
|
||||
static int sua_to_sccp_ludts(struct msgb *msg, const struct xua_msg *xua);
|
||||
|
||||
static int sua_to_sccp_udts(struct msgb *msg, const struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_data_unitdata_service *udts;
|
||||
|
||||
/* Use LUDTS if length exceeds 255 (single byte length field) */
|
||||
/* TODO: start using LUDTS sooner if called/calling party contain GT,
|
||||
* see Q.715 Section 8.3.2 */
|
||||
if (xua_msg_get_len(xua, SUA_IEI_DATA) > 255)
|
||||
return sua_to_sccp_ludts(msg, xua);
|
||||
|
||||
/* Use XUDTS if we have a hop counter */
|
||||
if (xua_msg_find_tag(xua, SUA_IEI_S7_HOP_CTR))
|
||||
return sua_to_sccp_xudts(msg, xua);
|
||||
|
||||
udts = (struct sccp_data_unitdata_service *) msgb_put(msg, sizeof(*udts));
|
||||
|
||||
/* Fixed Part */
|
||||
udts->type = SCCP_MSG_TYPE_UDTS;
|
||||
udts->return_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff;
|
||||
/* Variable Part */
|
||||
sccp_add_var_addr(msg, &udts->variable_called, xua, SUA_IEI_DEST_ADDR);
|
||||
sccp_add_var_addr(msg, &udts->variable_calling, xua, SUA_IEI_SRC_ADDR);
|
||||
sccp_add_var_addr(msg, &udts->variable_called, false, xua, SUA_IEI_DEST_ADDR);
|
||||
sccp_add_var_addr(msg, &udts->variable_calling, false, xua, SUA_IEI_SRC_ADDR);
|
||||
sccp_add_variable_part(msg, &udts->variable_data, xua, SUA_IEI_DATA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_it(struct msgb *msg, struct xua_msg *xua)
|
||||
static struct xua_msg *sccp_to_xua_xudts(const struct msgb *msg, struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_data_it *it = (struct sccp_data_it *)msg->l2h;
|
||||
const struct sccp_data_ext_unitdata_service *xudts;
|
||||
xudts = (const struct sccp_data_ext_unitdata_service *)msg->l2h;
|
||||
|
||||
/* Fixed Part */
|
||||
xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RETURN | xudts->return_cause);
|
||||
xua_msg_add_u32(xua, SUA_IEI_S7_HOP_CTR, xudts->hop_counter);
|
||||
/* Variable Part */
|
||||
if (!sccp_ptr_part_consistent(msg, (uint8_t *)&xudts->variable_called))
|
||||
return NULL;
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, &xudts->variable_called, false);
|
||||
if (!sccp_ptr_part_consistent(msg, (uint8_t *)&xudts->variable_calling))
|
||||
return NULL;
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, &xudts->variable_calling, false);
|
||||
if (!sccp_ptr_part_consistent(msg, (uint8_t *)&xudts->variable_data))
|
||||
return NULL;
|
||||
sccp_data_to_sua_ptr(xua, SUA_IEI_DATA, &xudts->variable_data);
|
||||
/* Optional Part */
|
||||
return sccp_to_xua_opt(msg, &xudts->optional_start, false, xua);
|
||||
}
|
||||
|
||||
static int sua_to_sccp_xudts(struct msgb *msg, const struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_data_ext_unitdata_service *xudts;
|
||||
xudts = (struct sccp_data_ext_unitdata_service *) msgb_put(msg, sizeof(*xudts));
|
||||
|
||||
/* Fixed Part */
|
||||
xudts->type = SCCP_MSG_TYPE_XUDTS;
|
||||
xudts->return_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff;
|
||||
xudts->hop_counter = xua_msg_get_u32(xua, SUA_IEI_S7_HOP_CTR);
|
||||
/* Variable Part */
|
||||
sccp_add_var_addr(msg, &xudts->variable_called, false, xua, SUA_IEI_DEST_ADDR);
|
||||
sccp_add_var_addr(msg, &xudts->variable_calling, false, xua, SUA_IEI_SRC_ADDR);
|
||||
sccp_add_variable_part(msg, &xudts->variable_data, xua, SUA_IEI_DATA);
|
||||
/* Optional Part */
|
||||
return xua_ies_to_sccp_opts(msg, &xudts->optional_start, false, xudts->type, xua);
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_ludts(const struct msgb *msg, struct xua_msg *xua)
|
||||
{
|
||||
const struct sccp_data_long_unitdata_service *ludts;
|
||||
ludts = (const struct sccp_data_long_unitdata_service *)msg->l2h;
|
||||
|
||||
/* Fixed Part */
|
||||
xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RETURN | ludts->return_cause);
|
||||
xua_msg_add_u32(xua, SUA_IEI_S7_HOP_CTR, ludts->hop_counter);
|
||||
/* Variable Part */
|
||||
if (!sccp_longptr_part_consistent(msg, (uint8_t *)&ludts->variable_called, false))
|
||||
return NULL;
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_DEST_ADDR, (uint8_t *)&ludts->variable_called, true);
|
||||
if (!sccp_longptr_part_consistent(msg, (uint8_t *)&ludts->variable_calling, false))
|
||||
return NULL;
|
||||
sccp_addr_to_sua_ptr(xua, SUA_IEI_SRC_ADDR, (uint8_t *)&ludts->variable_calling, true);
|
||||
if (!sccp_longptr_part_consistent(msg, (uint8_t *)&ludts->variable_data, true))
|
||||
return NULL;
|
||||
sccp_longdata_to_sua_ptr(xua, SUA_IEI_DATA, (uint8_t *)&ludts->variable_data);
|
||||
/* Optional Part */
|
||||
return sccp_to_xua_opt(msg, (uint8_t *)&ludts->optional_start, true, xua);
|
||||
}
|
||||
|
||||
static int sua_to_sccp_ludts(struct msgb *msg, const struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_data_long_unitdata_service *ludts;
|
||||
ludts = (struct sccp_data_long_unitdata_service *) msgb_put(msg, sizeof(*ludts));
|
||||
|
||||
/* Fixed Part */
|
||||
ludts->type = SCCP_MSG_TYPE_LUDTS;
|
||||
ludts->return_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff;
|
||||
ludts->hop_counter = xua_msg_get_u32(xua, SUA_IEI_S7_HOP_CTR);
|
||||
/* Variable Part */
|
||||
sccp_add_var_addr(msg, (uint8_t *)&ludts->variable_called, true, xua, SUA_IEI_DEST_ADDR);
|
||||
sccp_add_var_addr(msg, (uint8_t *)&ludts->variable_calling, true, xua, SUA_IEI_SRC_ADDR);
|
||||
sccp_add_long_variable_part(msg, (uint8_t *)&ludts->variable_data, xua, SUA_IEI_DATA);
|
||||
/* Optional Part */
|
||||
return xua_ies_to_sccp_opts(msg, (uint8_t *)&ludts->optional_start, true, ludts->type, xua);
|
||||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_it(const struct msgb *msg, struct xua_msg *xua)
|
||||
{
|
||||
const struct sccp_data_it *it = (const struct sccp_data_it *)msg->l2h;
|
||||
|
||||
/* Fixed Part */
|
||||
xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, it->proto_class);
|
||||
|
@ -1268,16 +1612,21 @@ static struct xua_msg *sccp_to_xua_it(struct msgb *msg, struct xua_msg *xua)
|
|||
return xua;
|
||||
}
|
||||
|
||||
static int sua_to_sccp_it(struct msgb *msg, struct xua_msg *xua)
|
||||
static int sua_to_sccp_it(struct msgb *msg, const struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_data_it *it;
|
||||
int rc;
|
||||
it = (struct sccp_data_it *) msgb_put(msg, sizeof(*it));
|
||||
|
||||
/* Fixed Part */
|
||||
it->type = SCCP_MSG_TYPE_IT;
|
||||
it->proto_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
|
||||
store_local_ref(&it->destination_local_reference, xua, SUA_IEI_DEST_REF);
|
||||
store_local_ref(&it->source_local_reference, xua, SUA_IEI_SRC_REF);
|
||||
rc = store_local_ref(&it->destination_local_reference, xua, SUA_IEI_DEST_REF);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
rc = store_local_ref(&it->source_local_reference, xua, SUA_IEI_SRC_REF);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if ((it->proto_class & 0xF) == 3) {
|
||||
//it->sequencing
|
||||
it->credit = xua_msg_get_u32(xua, SUA_IEI_CREDIT);
|
||||
|
@ -1287,9 +1636,9 @@ static int sua_to_sccp_it(struct msgb *msg, struct xua_msg *xua)
|
|||
}
|
||||
|
||||
/*! \returns \ref xua in case of success, NULL on error (xua not freed!) */
|
||||
static struct xua_msg *sccp_to_xua_err(struct msgb *msg, struct xua_msg *xua)
|
||||
static struct xua_msg *sccp_to_xua_err(const struct msgb *msg, struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_proto_err *err = (struct sccp_proto_err *)msg->l2h;
|
||||
const struct sccp_proto_err *err = (const struct sccp_proto_err *)msg->l2h;
|
||||
|
||||
/* Fixed Part */
|
||||
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, load_24be(&err->destination_local_reference));
|
||||
|
@ -1297,14 +1646,17 @@ static struct xua_msg *sccp_to_xua_err(struct msgb *msg, struct xua_msg *xua)
|
|||
return xua;
|
||||
}
|
||||
|
||||
static int sua_to_sccp_err(struct msgb *msg, struct xua_msg *xua)
|
||||
static int sua_to_sccp_err(struct msgb *msg, const struct xua_msg *xua)
|
||||
{
|
||||
struct sccp_proto_err *err;
|
||||
int rc;
|
||||
err = (struct sccp_proto_err *) msgb_put(msg, sizeof(*err));
|
||||
|
||||
/* Fixed Part */
|
||||
err->type = SCCP_MSG_TYPE_ERR;
|
||||
store_local_ref(&err->destination_local_reference, xua, SUA_IEI_DEST_REF);
|
||||
rc = store_local_ref(&err->destination_local_reference, xua, SUA_IEI_DEST_REF);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
err->error_cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE) & 0xff;
|
||||
return 0;
|
||||
}
|
||||
|
@ -1376,6 +1728,26 @@ struct xua_msg *osmo_sccp_to_xua(struct msgb *msg)
|
|||
if (!sccp_to_xua_err(msg, xua))
|
||||
goto malformed;
|
||||
return xua;
|
||||
case SCCP_MSG_TYPE_XUDT:
|
||||
xua->hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT);
|
||||
if (!sccp_to_xua_xudt(msg, xua))
|
||||
goto malformed;
|
||||
return xua;
|
||||
case SCCP_MSG_TYPE_XUDTS:
|
||||
xua->hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDR);
|
||||
if (!sccp_to_xua_xudts(msg, xua))
|
||||
goto malformed;
|
||||
return xua;
|
||||
case SCCP_MSG_TYPE_LUDT:
|
||||
xua->hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT);
|
||||
if (!sccp_to_xua_ludt(msg, xua))
|
||||
goto malformed;
|
||||
return xua;
|
||||
case SCCP_MSG_TYPE_LUDTS:
|
||||
xua->hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDR);
|
||||
if (!sccp_to_xua_ludts(msg, xua))
|
||||
goto malformed;
|
||||
return xua;
|
||||
/* Unsupported Message Types */
|
||||
case SCCP_MSG_TYPE_DT2:
|
||||
case SCCP_MSG_TYPE_AK:
|
||||
|
@ -1383,10 +1755,6 @@ struct xua_msg *osmo_sccp_to_xua(struct msgb *msg)
|
|||
case SCCP_MSG_TYPE_EA:
|
||||
case SCCP_MSG_TYPE_RSR:
|
||||
case SCCP_MSG_TYPE_RSC:
|
||||
case SCCP_MSG_TYPE_XUDT:
|
||||
case SCCP_MSG_TYPE_XUDTS:
|
||||
case SCCP_MSG_TYPE_LUDT:
|
||||
case SCCP_MSG_TYPE_LUDTS:
|
||||
LOGP(DLSUA, LOGL_ERROR, "Unsupported SCCP message %s\n",
|
||||
osmo_sccp_msg_type_name(msg->l2h[0]));
|
||||
xua_msg_free(xua);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* SCCP User SAP helper functions */
|
||||
|
||||
/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
|
||||
* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
|
@ -21,6 +21,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
@ -28,6 +29,7 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
|
@ -152,9 +154,21 @@ int osmo_sccp_tx_conn_req_msg(struct osmo_sccp_user *scu, uint32_t conn_id,
|
|||
int osmo_sccp_tx_data(struct osmo_sccp_user *scu, uint32_t conn_id,
|
||||
const uint8_t *data, unsigned int len)
|
||||
{
|
||||
struct msgb *msg = scu_msgb_alloc(__func__);
|
||||
struct msgb *msg;
|
||||
struct osmo_scu_prim *prim;
|
||||
|
||||
if (!osmo_sccp_conn_id_exists(scu->inst, conn_id)) {
|
||||
LOGP(DLSCCP, LOGL_ERROR, "N-DATA.req TX error: unable to find connection ID (local_ref) %u\n", conn_id);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
if (len > SCCP_MAX_DATA) {
|
||||
LOGP(DLSCCP, LOGL_ERROR, "N-DATA.req TX error: amount of data %u > %u - ITU-T Rec. Q.713 §4.7 limit\n",
|
||||
len, SCCP_MAX_DATA);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
msg = scu_msgb_alloc(__func__);
|
||||
prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER,
|
||||
OSMO_SCU_PRIM_N_DATA,
|
||||
|
@ -179,14 +193,20 @@ int osmo_sccp_tx_data_msg(struct osmo_sccp_user *scu, uint32_t conn_id,
|
|||
}
|
||||
|
||||
/* N-DISCONNECT.req */
|
||||
int osmo_sccp_tx_disconn(struct osmo_sccp_user *scu, uint32_t conn_id,
|
||||
const struct osmo_sccp_addr *resp_addr,
|
||||
uint32_t cause)
|
||||
int osmo_sccp_tx_disconn_data(struct osmo_sccp_user *scu, uint32_t conn_id,
|
||||
const struct osmo_sccp_addr *resp_addr,
|
||||
uint32_t cause, const uint8_t *data, size_t len)
|
||||
{
|
||||
struct msgb *msg = scu_msgb_alloc(__func__);
|
||||
struct msgb *msg;
|
||||
struct osmo_scu_prim *prim;
|
||||
struct osmo_scu_disconn_param *param;
|
||||
|
||||
if (!osmo_sccp_conn_id_exists(scu->inst, conn_id)) {
|
||||
LOGP(DLSCCP, LOGL_ERROR, "N-DISCONNECT.req TX error: unable to find connection ID (local_ref) %u\n", conn_id);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
msg = scu_msgb_alloc(__func__);
|
||||
prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER,
|
||||
OSMO_SCU_PRIM_N_DISCONNECT,
|
||||
|
@ -199,9 +219,21 @@ int osmo_sccp_tx_disconn(struct osmo_sccp_user *scu, uint32_t conn_id,
|
|||
param->conn_id = conn_id;
|
||||
param->cause = cause;
|
||||
|
||||
if (data && len) {
|
||||
msg->l2h = msgb_put(msg, len);
|
||||
memcpy(msg->l2h, data, len);
|
||||
}
|
||||
|
||||
return osmo_sccp_user_sap_down(scu, &prim->oph);
|
||||
}
|
||||
|
||||
int osmo_sccp_tx_disconn(struct osmo_sccp_user *scu, uint32_t conn_id,
|
||||
const struct osmo_sccp_addr *resp_addr,
|
||||
uint32_t cause)
|
||||
{
|
||||
return osmo_sccp_tx_disconn_data(scu, conn_id, resp_addr, cause, NULL, 0);
|
||||
}
|
||||
|
||||
/* N-CONNECT.resp */
|
||||
int osmo_sccp_tx_conn_resp_msg(struct osmo_sccp_user *scu, uint32_t conn_id,
|
||||
const struct osmo_sccp_addr *resp_addr,
|
||||
|
@ -210,6 +242,11 @@ int osmo_sccp_tx_conn_resp_msg(struct osmo_sccp_user *scu, uint32_t conn_id,
|
|||
struct osmo_scu_prim *prim;
|
||||
struct osmo_scu_connect_param *param;
|
||||
|
||||
if (!osmo_sccp_conn_id_exists(scu->inst, conn_id)) {
|
||||
LOGP(DLSCCP, LOGL_ERROR, "N-CONNECT.resp TX error: unable to find connection ID (local_ref) %u\n", conn_id);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
msg->l2h = msg->data;
|
||||
|
||||
prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim));
|
||||
|
@ -289,6 +326,7 @@ char *osmo_sccp_addr_dump(const struct osmo_sccp_addr *addr)
|
|||
{
|
||||
static char buf[256];
|
||||
bool comma = false;
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
|
||||
buf[0] = '\0';
|
||||
|
||||
|
@ -299,7 +337,9 @@ char *osmo_sccp_addr_dump(const struct osmo_sccp_addr *addr)
|
|||
if (addr->presence & OSMO_SCCP_ADDR_T_SSN)
|
||||
append_to_buf(buf, sizeof(buf), &comma, "SSN=%u", addr->ssn);
|
||||
if (addr->presence & OSMO_SCCP_ADDR_T_IPv4)
|
||||
append_to_buf(buf, sizeof(buf), &comma, "IP=%s", inet_ntoa(addr->ip.v4));
|
||||
append_to_buf(buf, sizeof(buf), &comma, "IP=%s", inet_ntop(AF_INET, &addr->ip.v4, ipbuf, sizeof(ipbuf)));
|
||||
else if (addr->presence & OSMO_SCCP_ADDR_T_IPv6)
|
||||
append_to_buf(buf, sizeof(buf), &comma, "IP=%s", inet_ntop(AF_INET6, &addr->ip.v6, ipbuf, sizeof(ipbuf)));
|
||||
if (addr->gt.gti != OSMO_SCCP_GTI_NO_GT || addr->presence & OSMO_SCCP_ADDR_T_GT)
|
||||
append_to_buf(buf, sizeof(buf), &comma, "GTI=%u", addr->gt.gti);
|
||||
if (addr->presence & OSMO_SCCP_ADDR_T_GT)
|
||||
|
@ -308,31 +348,84 @@ char *osmo_sccp_addr_dump(const struct osmo_sccp_addr *addr)
|
|||
return buf;
|
||||
}
|
||||
|
||||
/* Like osmo_sccp_addr_dump() but print human readable representations instead of raw values. */
|
||||
static int sccp_addr_to_str_buf(char *buf, size_t buf_len, const struct osmo_ss7_instance *ss7,
|
||||
const struct osmo_sccp_addr *addr, char sep_char)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buf_len };
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "RI=%s", osmo_sccp_routing_ind_name(addr->ri));
|
||||
|
||||
if (addr->presence & OSMO_SCCP_ADDR_T_PC)
|
||||
OSMO_STRBUF_PRINTF(sb, "%cPC=%s", sep_char, osmo_ss7_pointcode_print(ss7, addr->pc));
|
||||
if (addr->presence & OSMO_SCCP_ADDR_T_SSN)
|
||||
OSMO_STRBUF_PRINTF(sb, "%cSSN=%s", sep_char, osmo_sccp_ssn_name(addr->ssn));
|
||||
if (addr->presence & OSMO_SCCP_ADDR_T_IPv4)
|
||||
OSMO_STRBUF_PRINTF(sb, "%cIP=%s", sep_char, inet_ntop(AF_INET, &addr->ip.v4, ipbuf, sizeof(ipbuf)));
|
||||
else if (addr->presence & OSMO_SCCP_ADDR_T_IPv6)
|
||||
OSMO_STRBUF_PRINTF(sb, "%cIP=%s", sep_char, inet_ntop(AF_INET6, &addr->ip.v6, ipbuf, sizeof(ipbuf)));
|
||||
if (addr->gt.gti != OSMO_SCCP_GTI_NO_GT || addr->presence & OSMO_SCCP_ADDR_T_GT)
|
||||
OSMO_STRBUF_PRINTF(sb, "%cGTI=%s", sep_char, osmo_sccp_gti_name(addr->gt.gti));
|
||||
if (addr->presence & OSMO_SCCP_ADDR_T_GT)
|
||||
OSMO_STRBUF_PRINTF(sb, "%cGT=(%s)", sep_char, osmo_sccp_gt_dump(&addr->gt));
|
||||
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
int osmo_sccp_addr_to_str_buf(char *buf, size_t buf_len, const struct osmo_ss7_instance *ss7,
|
||||
const struct osmo_sccp_addr *addr)
|
||||
{
|
||||
return sccp_addr_to_str_buf(buf, buf_len, ss7, addr, ',');
|
||||
}
|
||||
|
||||
char *osmo_sccp_addr_to_str_c(void *ctx, const struct osmo_ss7_instance *ss7, const struct osmo_sccp_addr *addr)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_sccp_addr_to_str_buf, ss7, addr)
|
||||
}
|
||||
|
||||
/*! like osmo_sccp_addr_to_str_buf, but using only characters passing osmo_identifier_valid(). Useful for FSM and CTRL
|
||||
* IDs.
|
||||
*
|
||||
* The advantage over using osmo_sccp_addr_to_str_buf() followed by osmo_identifier_sanitize_buf() is that here, the
|
||||
* address elements are separated by ':', while osmo_identifier_sanitize_buf() would replace all characters with the
|
||||
* same, e.g. '-'.
|
||||
*/
|
||||
int osmo_sccp_addr_to_id_buf(char *buf, size_t buf_len, const struct osmo_ss7_instance *ss7,
|
||||
const struct osmo_sccp_addr *addr)
|
||||
{
|
||||
int rc = sccp_addr_to_str_buf(buf, buf_len, ss7, addr, ':');
|
||||
/* inet_ntop() and osmo_sccp_gt_dump() may have written non-id chars. */
|
||||
osmo_identifier_sanitize_buf(buf, "", '-');
|
||||
return rc;
|
||||
}
|
||||
|
||||
char *osmo_sccp_addr_to_id_c(void *ctx, const struct osmo_ss7_instance *ss7, const struct osmo_sccp_addr *addr)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_sccp_addr_to_id_buf, ss7, addr)
|
||||
}
|
||||
|
||||
/* Rather use osmo_sccp_addr_to_str_buf() or osmo_sccp_addr_to_str_c() to not use a static buffer */
|
||||
char *osmo_sccp_addr_name(const struct osmo_ss7_instance *ss7, const struct osmo_sccp_addr *addr)
|
||||
{
|
||||
static char buf[256];
|
||||
bool comma = false;
|
||||
|
||||
buf[0] = '\0';
|
||||
|
||||
append_to_buf(buf, sizeof(buf), &comma, "RI=%s", osmo_sccp_routing_ind_name(addr->ri));
|
||||
|
||||
if (addr->presence & OSMO_SCCP_ADDR_T_PC)
|
||||
append_to_buf(buf, sizeof(buf), &comma, "PC=%s", osmo_ss7_pointcode_print(ss7, addr->pc));
|
||||
if (addr->presence & OSMO_SCCP_ADDR_T_SSN)
|
||||
append_to_buf(buf, sizeof(buf), &comma, "SSN=%s", osmo_sccp_ssn_name(addr->ssn));
|
||||
if (addr->presence & OSMO_SCCP_ADDR_T_IPv4)
|
||||
append_to_buf(buf, sizeof(buf), &comma, "IP=%s", inet_ntoa(addr->ip.v4));
|
||||
if (addr->gt.gti != OSMO_SCCP_GTI_NO_GT || addr->presence & OSMO_SCCP_ADDR_T_GT)
|
||||
append_to_buf(buf, sizeof(buf), &comma, "GTI=%s", osmo_sccp_gti_name(addr->gt.gti));
|
||||
if (addr->presence & OSMO_SCCP_ADDR_T_GT)
|
||||
append_to_buf(buf, sizeof(buf), &comma, "GT=(%s)", osmo_sccp_gt_dump(&addr->gt));
|
||||
|
||||
osmo_sccp_addr_to_str_buf(buf, sizeof(buf), ss7, addr);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Derive ss7 from the sccp instance and call osmo_sccp_addr_name() with that.
|
||||
int osmo_sccp_inst_addr_to_str_buf(char *buf, size_t buf_len, const struct osmo_sccp_instance *sccp,
|
||||
const struct osmo_sccp_addr *addr)
|
||||
{
|
||||
return osmo_sccp_addr_to_str_buf(buf, buf_len, sccp? sccp->ss7 : NULL, addr);
|
||||
}
|
||||
|
||||
char *osmo_sccp_inst_addr_to_str_c(void *ctx, const struct osmo_sccp_instance *sccp,
|
||||
const struct osmo_sccp_addr *addr)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_sccp_inst_addr_to_str_buf, sccp, addr);
|
||||
}
|
||||
|
||||
/* Rather use osmo_sccp_inst_addr_to_str_buf() or osmo_sccp_inst_addr_to_str_c() to not use a static buffer.
|
||||
* Derive ss7 from the sccp instance and call osmo_sccp_addr_name() with that.
|
||||
* If sccp is passed as NULL, simply use the default point code format. */
|
||||
char *osmo_sccp_inst_addr_name(const struct osmo_sccp_instance *sccp, const struct osmo_sccp_addr *addr)
|
||||
{
|
||||
|
|
|
@ -2,14 +2,19 @@
|
|||
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/prim.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/linuxrbtree.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
#include <osmocom/sigtran/protocol/mtp.h>
|
||||
|
||||
#define SCCP_STR "Signalling Connection Control Part\n"
|
||||
|
||||
/* Appendix C.4 of Q.714 */
|
||||
enum osmo_sccp_timer {
|
||||
OSMO_SCCP_TIMER_CONN_EST,
|
||||
/* 0 kept unused on purpose since it's handled specially by osmo_fsm */
|
||||
OSMO_SCCP_TIMER_CONN_EST = 1,
|
||||
OSMO_SCCP_TIMER_IAS,
|
||||
OSMO_SCCP_TIMER_IAR,
|
||||
OSMO_SCCP_TIMER_REL,
|
||||
|
@ -19,30 +24,21 @@ enum osmo_sccp_timer {
|
|||
OSMO_SCCP_TIMER_RESET,
|
||||
OSMO_SCCP_TIMER_REASSEMBLY,
|
||||
/* This must remain the last item: */
|
||||
OSMO_SCCP_TIMERS_COUNT
|
||||
OSMO_SCCP_TIMERS_LEN
|
||||
};
|
||||
|
||||
struct osmo_sccp_timer_val {
|
||||
uint32_t s;
|
||||
uint32_t us;
|
||||
};
|
||||
|
||||
extern const struct osmo_sccp_timer_val osmo_sccp_timer_defaults[];
|
||||
extern const struct osmo_tdef osmo_sccp_timer_defaults[OSMO_SCCP_TIMERS_LEN];
|
||||
|
||||
extern const struct value_string osmo_sccp_timer_names[];
|
||||
static inline const char *osmo_sccp_timer_name(enum osmo_sccp_timer val)
|
||||
{ return get_value_string(osmo_sccp_timer_names, val); }
|
||||
|
||||
extern const struct value_string osmo_sccp_timer_descriptions[];
|
||||
static inline const char *osmo_sccp_timer_description(enum osmo_sccp_timer val)
|
||||
{ return get_value_string(osmo_sccp_timer_descriptions, val); }
|
||||
|
||||
/* an instance of the SCCP stack */
|
||||
struct osmo_sccp_instance {
|
||||
/* entry in global list of ss7 instances */
|
||||
struct llist_head list;
|
||||
/* list of 'struct sccp_connection' in this instance */
|
||||
struct llist_head connections;
|
||||
/* rbtree root of 'struct sccp_connection' in this instance */
|
||||
struct rb_root connections;
|
||||
/* list of SCCP users in this instance */
|
||||
struct llist_head users;
|
||||
/* routing context to be used in all outbound messages */
|
||||
|
@ -54,7 +50,9 @@ struct osmo_sccp_instance {
|
|||
|
||||
struct osmo_ss7_user ss7_user;
|
||||
|
||||
struct osmo_sccp_timer_val timers[OSMO_SCCP_TIMERS_COUNT];
|
||||
struct osmo_tdef *tdefs;
|
||||
|
||||
uint32_t max_optional_data;
|
||||
};
|
||||
|
||||
struct osmo_sccp_user {
|
||||
|
@ -124,8 +122,18 @@ extern struct osmo_fsm sccp_scoc_fsm;
|
|||
|
||||
void sccp_scoc_show_connections(struct vty *vty, struct osmo_sccp_instance *inst);
|
||||
|
||||
const struct osmo_sccp_timer_val *osmo_sccp_timer_get(const struct osmo_sccp_instance *inst,
|
||||
enum osmo_sccp_timer timer,
|
||||
bool default_if_unset);
|
||||
|
||||
void osmo_sccp_vty_write_cs7_node(struct vty *vty, const char *indent, struct osmo_sccp_instance *inst);
|
||||
|
||||
/* Local Broadcast (LBCS) */
|
||||
void sccp_lbcs_local_bcast_pcstate(struct osmo_sccp_instance *inst,
|
||||
const struct osmo_scu_pcstate_param *pcstate);
|
||||
void sccp_lbcs_local_bcast_state(struct osmo_sccp_instance *inst,
|
||||
const struct osmo_scu_state_param *state);
|
||||
|
||||
/* SCCP Management (SCMG) */
|
||||
void sccp_scmg_rx_ssn_allowed(struct osmo_sccp_instance *inst, uint32_t dpc, uint32_t ssn, uint32_t smi);
|
||||
void sccp_scmg_rx_ssn_prohibited(struct osmo_sccp_instance *inst, uint32_t dpc, uint32_t ssn, uint32_t smi);
|
||||
void sccp_scmg_rx_mtp_pause(struct osmo_sccp_instance *inst, uint32_t dpc);
|
||||
void sccp_scmg_rx_mtp_resume(struct osmo_sccp_instance *inst, uint32_t dpc);
|
||||
void sccp_scmg_rx_mtp_status(struct osmo_sccp_instance *inst, uint32_t dpc, enum mtp_unavail_cause cause);
|
||||
int sccp_scmg_init(struct osmo_sccp_instance *inst);
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/* SCCP Local Broadcast (LBCS) according to ITU-T Q.713/Q.714 */
|
||||
|
||||
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/sigtran/protocol/sua.h>
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
|
||||
#include "xua_internal.h"
|
||||
#include "sccp_internal.h"
|
||||
|
||||
/* perform a "local broadcast" of a N-PCSTATE.ind */
|
||||
void sccp_lbcs_local_bcast_pcstate(struct osmo_sccp_instance *inst,
|
||||
const struct osmo_scu_pcstate_param *pcstate)
|
||||
{
|
||||
struct osmo_sccp_user *scu;
|
||||
|
||||
llist_for_each_entry(scu, &inst->users, list) {
|
||||
struct msgb *upmsg = sccp_msgb_alloc(__func__);
|
||||
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_PCSTATE,
|
||||
PRIM_OP_INDICATION, upmsg);
|
||||
prim->u.pcstate = *pcstate;
|
||||
sccp_user_prim_up(scu, prim);
|
||||
}
|
||||
}
|
||||
|
||||
/* perform a "local broadcast" of a N-STATE.ind */
|
||||
void sccp_lbcs_local_bcast_state(struct osmo_sccp_instance *inst,
|
||||
const struct osmo_scu_state_param *state)
|
||||
{
|
||||
struct osmo_sccp_user *scu;
|
||||
|
||||
llist_for_each_entry(scu, &inst->users, list) {
|
||||
struct msgb *upmsg = sccp_msgb_alloc(__func__);
|
||||
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_STATE,
|
||||
PRIM_OP_INDICATION, upmsg);
|
||||
prim->u.state = *state;
|
||||
sccp_user_prim_up(scu, prim);
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
const struct value_string osmo_scu_prim_names[] = {
|
||||
const struct value_string osmo_scu_prim_type_names[] = {
|
||||
{ OSMO_SCU_PRIM_N_CONNECT, "N-CONNECT" },
|
||||
{ OSMO_SCU_PRIM_N_DATA, "N-DATA" },
|
||||
{ OSMO_SCU_PRIM_N_EXPEDITED_DATA, "N-EXPEDITED-DATA" },
|
||||
|
@ -37,22 +37,37 @@ const struct value_string osmo_scu_prim_names[] = {
|
|||
/* management */
|
||||
{ OSMO_SCU_PRIM_N_COORD, "N-COORD" },
|
||||
{ OSMO_SCU_PRIM_N_STATE, "N-STATE" },
|
||||
{ OSMO_SCU_PRIM_N_PCSTATE, "N-PCSATE" },
|
||||
{ OSMO_SCU_PRIM_N_PCSTATE, "N-PCSTATE" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static char prim_name_buf[128];
|
||||
|
||||
char *osmo_scu_prim_name(struct osmo_prim_hdr *oph)
|
||||
char *osmo_scu_prim_name(const struct osmo_prim_hdr *oph)
|
||||
{
|
||||
const char *name = get_value_string(osmo_scu_prim_names, oph->primitive);
|
||||
|
||||
snprintf(prim_name_buf, sizeof(prim_name_buf), "%s.%s", name,
|
||||
get_value_string(osmo_prim_op_names, oph->operation));
|
||||
|
||||
osmo_scu_prim_hdr_name_buf(prim_name_buf, sizeof(prim_name_buf), oph);
|
||||
return prim_name_buf;
|
||||
}
|
||||
|
||||
int osmo_scu_prim_hdr_name_buf(char *buf, size_t buflen, const struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
|
||||
if (!oph) {
|
||||
OSMO_STRBUF_PRINTF(sb, "null");
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "%s.%s",
|
||||
osmo_scu_prim_type_name(oph->primitive),
|
||||
osmo_prim_operation_name(oph->operation));
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *osmo_scu_prim_hdr_name_c(void *ctx, const struct osmo_prim_hdr *oph)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 32, "ERROR", osmo_scu_prim_hdr_name_buf, oph)
|
||||
}
|
||||
|
||||
#include <osmocom/sigtran/sigtran_sap.h>
|
||||
|
||||
|
@ -105,6 +120,22 @@ const struct value_string osmo_sccp_gti_names[] = {
|
|||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string osmo_sccp_sp_status_names[] = {
|
||||
{ OSMO_SCCP_SP_S_INACCESSIBLE, "INACCESSIBLE" },
|
||||
{ OSMO_SCCP_SP_S_CONGESTED, "CONGESTED" },
|
||||
{ OSMO_SCCP_SP_S_ACCESSIBLE, "ACCESSIBLE" },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct value_string osmo_sccp_rem_sccp_status_names[] = {
|
||||
{ OSMO_SCCP_REM_SCCP_S_AVAILABLE, "AVAILABLE" },
|
||||
{ OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN, "UNAVAILABLE_UNKNOWN" },
|
||||
{ OSMO_SCCP_REM_SCCP_S_UNEQUIPPED, "UNEQUIPPED" },
|
||||
{ OSMO_SCCP_REM_SCCP_S_INACCESSIBLE, "INACCESSIBLE" },
|
||||
{ OSMO_SCCP_REM_SCCP_S_CONGESTED, "CONGESTED" },
|
||||
{}
|
||||
};
|
||||
|
||||
const struct value_string osmo_sccp_ssn_names[] = {
|
||||
{ OSMO_SCCP_SSN_MGMT, "MGMT" },
|
||||
{ OSMO_SCCP_SSN_ISUP, "ISUP" },
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
* However, all SCCP features can be expressed in SUA.
|
||||
*
|
||||
* The code only supports Class 2. No support for Class 3 is intended,
|
||||
* but patches are of course alwys welcome.
|
||||
* but patches are of course always welcome.
|
||||
*
|
||||
* Missing other features:
|
||||
* * Segmentation/Reassembly support
|
||||
|
@ -119,7 +119,7 @@ static int xua_gen_encode_and_send(struct osmo_sccp_user *scu, uint32_t event,
|
|||
* The caller is required to free oph->msg, otherwise the same as sccp_sclc_user_sap_down().
|
||||
* \param[in] scu SCCP User who is sending the primitive
|
||||
* \param[on] oph Osmocom primitive header of the primitive
|
||||
* \returns 0 on success; negtive in case of error */
|
||||
* \returns 0 on success; negative in case of error */
|
||||
int sccp_sclc_user_sap_down_nofree(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
|
||||
|
@ -143,7 +143,7 @@ int sccp_sclc_user_sap_down_nofree(struct osmo_sccp_user *scu, struct osmo_prim_
|
|||
* Implies a msgb_free(oph->msg), otherwise the same as sccp_sclc_user_sap_down_nofree().
|
||||
* \param[in] scu SCCP User who is sending the primitive
|
||||
* \param[on] oph Osmocom primitive header of the primitive
|
||||
* \returns 0 on success; negtive in case of error */
|
||||
* \returns 0 on success; negative in case of error */
|
||||
int sccp_sclc_user_sap_down(struct osmo_sccp_user *scu, struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
|
||||
|
|
|
@ -0,0 +1,318 @@
|
|||
/* SCCP Management (SCMG) according to ITU-T Q.713/Q.714 */
|
||||
|
||||
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/sigtran/protocol/sua.h>
|
||||
#include <osmocom/sigtran/protocol/sccp_scmg.h>
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
|
||||
#include "xua_internal.h"
|
||||
#include "sccp_internal.h"
|
||||
|
||||
/* ITU-T Q.714 5.3.3 Subsystem allowed */
|
||||
void sccp_scmg_rx_ssn_allowed(struct osmo_sccp_instance *inst, uint32_t dpc, uint32_t ssn, uint32_t smi)
|
||||
{
|
||||
struct osmo_scu_state_param state;
|
||||
/* 1) Instruct the translation function to update the translation tables */
|
||||
/* 2) Mark as "allowed" the status of that subsystem. */
|
||||
/* 3) Initiate a local broadcast of "User-in-service" information for the allowed subsystem */
|
||||
state = (struct osmo_scu_state_param) {
|
||||
.affected_pc = dpc,
|
||||
.affected_ssn = ssn,
|
||||
.user_in_service = true,
|
||||
.ssn_multiplicity_ind = smi,
|
||||
};
|
||||
sccp_lbcs_local_bcast_state(inst, &state);
|
||||
/* 4) Discontinue the subsystem status test if such a test was in progress */
|
||||
/* 5) Initiate a broadcast of Subsystem-Allowed messages to concerned signalling points. */
|
||||
}
|
||||
|
||||
/* ITU-T Q.714 5.3.2 Subsystem prohibited */
|
||||
void sccp_scmg_rx_ssn_prohibited(struct osmo_sccp_instance *inst, uint32_t dpc, uint32_t ssn, uint32_t smi)
|
||||
{
|
||||
struct osmo_scu_state_param state;
|
||||
/* 1) instruct the translation function to update the translation tables */
|
||||
/* 2) mark as "prohibited" the status of that subsystem */
|
||||
/* 3) initiate a local broadcast of "User-out-of-service" information */
|
||||
state = (struct osmo_scu_state_param) {
|
||||
.affected_pc = dpc,
|
||||
.affected_ssn = ssn,
|
||||
.user_in_service = false,
|
||||
.ssn_multiplicity_ind = smi,
|
||||
};
|
||||
sccp_lbcs_local_bcast_state(inst, &state);
|
||||
|
||||
/* 4) initiate the subsystem status test procedure if the prohibited subsystem is not local */
|
||||
/* 5) initiate a broadcast of Subsystem-Prohibited messages to concerned SP */
|
||||
/* 6) cancel "ignore subsystem status test" and the associated timer if in progress and if
|
||||
* the newly prohibited subsystem resides at the local node. */
|
||||
}
|
||||
|
||||
/*! brief MTP -> SNM (MTP-PAUSE.ind) - inability to providing MTP service Q.714 5.2.2 */
|
||||
void sccp_scmg_rx_mtp_pause(struct osmo_sccp_instance *inst, uint32_t dpc)
|
||||
{
|
||||
struct osmo_scu_pcstate_param pcstate;
|
||||
/* 1) Informs the translation function to update the translation tables. */
|
||||
/* 2) SCCP management marks as "prohibited" the status of the remote signalling point, the
|
||||
remote SCCP and each subsystem at the remote signalling point. */
|
||||
/* 3) Discontinues all subsystem status tests (including SSN = 1) */
|
||||
|
||||
/* 4) local broadcast of "user-out-of-service" for each SSN at that dest
|
||||
* [this would require us to track SSNs at each PC, which we don't] */
|
||||
|
||||
/* 5) local broadcast of "signaling point inaccessible" */
|
||||
/* 6) local broadcast of "remote SCCP unavailable" */
|
||||
pcstate = (struct osmo_scu_pcstate_param) {
|
||||
.affected_pc = dpc,
|
||||
.restricted_importance_level = 0,
|
||||
.sp_status = OSMO_SCCP_SP_S_INACCESSIBLE,
|
||||
.remote_sccp_status = OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN,
|
||||
};
|
||||
sccp_lbcs_local_bcast_pcstate(inst, &pcstate);
|
||||
}
|
||||
|
||||
/*! brief MTP -> SNM (MTP-RESUME.ind) - ability of providing the MTP service Q.714 5.2.3 */
|
||||
void sccp_scmg_rx_mtp_resume(struct osmo_sccp_instance *inst, uint32_t dpc)
|
||||
{
|
||||
struct osmo_scu_pcstate_param pcstate;
|
||||
/* 1) Sets the congestion state of that signalling point */
|
||||
/* 2) Instructs the translation function to update the translation tables. */
|
||||
/* 3) Marks as "allowed" the status of that destination, and the SCCP */
|
||||
/* 4) - not applicable */
|
||||
/* 5) Marks as "allowed" the status of remote subsystems */
|
||||
|
||||
/* 6) local broadcast of "signalling point accessible" */
|
||||
/* 7) local broadcast of "remote SCCP accessible" */
|
||||
pcstate = (struct osmo_scu_pcstate_param) {
|
||||
.affected_pc = dpc,
|
||||
.restricted_importance_level = 0,
|
||||
.sp_status = OSMO_SCCP_SP_S_ACCESSIBLE,
|
||||
.remote_sccp_status = OSMO_SCCP_REM_SCCP_S_AVAILABLE,
|
||||
};
|
||||
sccp_lbcs_local_bcast_pcstate(inst, &pcstate);
|
||||
|
||||
/* 8) local broadcast of "user-in-service"
|
||||
* [this would require us to track SSNs at each PC, which we don't] */
|
||||
}
|
||||
|
||||
void sccp_scmg_rx_mtp_status(struct osmo_sccp_instance *inst, uint32_t dpc, enum mtp_unavail_cause cause)
|
||||
{
|
||||
struct osmo_scu_pcstate_param pcstate;
|
||||
/* 1) Informs the translation function to update the translation tables. */
|
||||
/* 2) In the case where the SCCP has received an MTP-STATUS indication primitive relating to
|
||||
Mark the status of the SCCP and each SSN for the relevant destination to "prohibited"
|
||||
and initiates a subsystem status test with SSN = 1. If the cause in the MTP-STATUS
|
||||
indication primitive indicates "unequipped user", then no subsystem status test is
|
||||
initiated. */
|
||||
/* 3) Discontinues all subsystem status tests (including SSN = 1) if an MTP-STATUS
|
||||
indication primitive is received with a cause of "unequipped SCCP". The SCCP
|
||||
discontinues all subsystem status tests, except for SSN = 1, if an MTP-STATUS
|
||||
indication primitive is received with a cause of either "unknown" or "inaccessible" */
|
||||
switch (cause) {
|
||||
case MTP_UNAVAIL_C_UNKNOWN:
|
||||
case MTP_UNAVAIL_C_UNEQUIP_REM_USER:
|
||||
case MTP_UNAVAIL_C_INACC_REM_USER:
|
||||
break;
|
||||
}
|
||||
|
||||
/* 4) local broadcast of "user-out-of-service" for each SSN at that dest
|
||||
* [this would require us to track SSNs at each PC, which we don't] */
|
||||
|
||||
/* 6) local broadcast of "remote SCCP unavailable" */
|
||||
pcstate = (struct osmo_scu_pcstate_param) {
|
||||
.affected_pc = dpc,
|
||||
.restricted_importance_level = 0,
|
||||
.sp_status = OSMO_SCCP_SP_S_ACCESSIBLE,
|
||||
.remote_sccp_status = OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN,
|
||||
};
|
||||
sccp_lbcs_local_bcast_pcstate(inst, &pcstate);
|
||||
}
|
||||
|
||||
const struct value_string sccp_scmg_msgt_names[] = {
|
||||
{ SCCP_SCMG_MSGT_SSA, "SSA (Subsystem Allowed)" },
|
||||
{ SCCP_SCMG_MSGT_SSP, "SSP (Subsystem Prohibited)" },
|
||||
{ SCCP_SCMG_MSGT_SST, "SST (Subsystem Status Test)" },
|
||||
{ SCCP_SCMG_MSGT_SOR, "SOR (Subsystem Out-of-service Request)" },
|
||||
{ SCCP_SCMG_MSGT_SOG, "SOG (Subsystem Out-of-service Grant)" },
|
||||
{ SCCP_SCMG_MSGT_SSC, "SSC (Subsystem Congested)" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static int sccp_scmg_tx(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
|
||||
const struct osmo_sccp_addr *called_addr,
|
||||
uint8_t msg_type, uint8_t ssn, uint16_t pc, uint8_t smi, uint8_t *ssc_cong_lvl)
|
||||
{
|
||||
struct msgb *msg = sccp_msgb_alloc(__func__);
|
||||
struct osmo_scu_prim *prim;
|
||||
struct osmo_scu_unitdata_param *param;
|
||||
struct sccp_scmg_msg *scmg;
|
||||
|
||||
/* fill primitive header */
|
||||
prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
|
||||
param = &prim->u.unitdata;
|
||||
memcpy(¶m->calling_addr, calling_addr, sizeof(*calling_addr));
|
||||
memcpy(¶m->called_addr, called_addr, sizeof(*called_addr));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_REQUEST, msg);
|
||||
|
||||
/* Fill the actual SCMG message */
|
||||
msg->l2h = msgb_put(msg, sizeof(*scmg));
|
||||
scmg = (struct sccp_scmg_msg *) msg->l2h;
|
||||
scmg->msg_type = msg_type;
|
||||
scmg->affected_ssn = ssn;
|
||||
scmg->affected_pc = pc;
|
||||
scmg->smi = smi;
|
||||
|
||||
/* add congestion level in case of SSC message */
|
||||
if (msg_type == SCCP_SCMG_MSGT_SSC) {
|
||||
msgb_put(msg, 1);
|
||||
OSMO_ASSERT(ssc_cong_lvl);
|
||||
scmg->ssc_congestion_lvl[1] = *ssc_cong_lvl;
|
||||
}
|
||||
|
||||
return osmo_sccp_user_sap_down(scu, &prim->oph);
|
||||
}
|
||||
|
||||
|
||||
/* Subsystem Allowed received */
|
||||
static int scmg_rx_ssa(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
|
||||
const struct osmo_sccp_addr *called_addr, const struct sccp_scmg_msg *ssa)
|
||||
{
|
||||
/* Q.714 5.3.3 */
|
||||
if (ssa->affected_ssn == SCCP_SSN_MANAGEMENT)
|
||||
return 0;
|
||||
|
||||
/* if the SSN is not marked as prohibited, ignore */
|
||||
|
||||
/* Q.714 5.3.2.2 a) */
|
||||
sccp_scmg_rx_ssn_allowed(scu->inst, ssa->affected_pc, ssa->affected_ssn, ssa->smi);
|
||||
|
||||
/* If the remote SCCP, at which the subsystem reported in the SSA message resides, is marked
|
||||
* inaccessible, then the message is treated as an implicit indication of SCCP restart */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Subsystem Prohibited received */
|
||||
static int scmg_rx_ssp(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
|
||||
const struct osmo_sccp_addr *called_addr, const struct sccp_scmg_msg *ssp)
|
||||
{
|
||||
/* Q.714 5.3.2.2 a) */
|
||||
sccp_scmg_rx_ssn_prohibited(scu->inst, ssp->affected_pc, ssp->affected_ssn, ssp->smi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Subsystem Test received */
|
||||
static int scmg_rx_sst(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
|
||||
const struct osmo_sccp_addr *called_addr, const struct sccp_scmg_msg *sst)
|
||||
{
|
||||
/* Q.714 5.3.4.3 Actions at the receiving side (of SST) */
|
||||
|
||||
/* check "ignore subsystem status test" and bail out */
|
||||
/* check if SSN in question is available. If yes, return SSA. If not, ignore */
|
||||
scu = sccp_user_find(scu->inst, sst->affected_ssn, sst->affected_pc);
|
||||
if (!scu)
|
||||
return 0;
|
||||
|
||||
/* is subsystem available? */
|
||||
if (0 /* !subsys_available(scu) */)
|
||||
return 0;
|
||||
|
||||
return sccp_scmg_tx(scu, called_addr, calling_addr, SCCP_SCMG_MSGT_SSA,
|
||||
sst->affected_ssn, sst->affected_pc, 0, NULL);
|
||||
}
|
||||
|
||||
static int scmg_rx(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
|
||||
const struct osmo_sccp_addr *called_addr, const struct sccp_scmg_msg *scmg)
|
||||
{
|
||||
switch (scmg->msg_type) {
|
||||
case SCCP_SCMG_MSGT_SSA:
|
||||
return scmg_rx_ssa(scu, calling_addr, called_addr, scmg);
|
||||
case SCCP_SCMG_MSGT_SSP:
|
||||
return scmg_rx_ssp(scu, calling_addr, called_addr, scmg);
|
||||
case SCCP_SCMG_MSGT_SST:
|
||||
return scmg_rx_sst(scu, calling_addr, called_addr, scmg);
|
||||
case SCCP_SCMG_MSGT_SOR:
|
||||
case SCCP_SCMG_MSGT_SOG:
|
||||
case SCCP_SCMG_MSGT_SSC:
|
||||
default:
|
||||
LOGP(DLSCCP, LOGL_NOTICE, "Rx unsupported SCCP SCMG %s, ignoring\n",
|
||||
sccp_scmg_msgt_name(scmg->msg_type));
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* main entry point for SCCP user primitives from SCRC/SCOC */
|
||||
static int scmg_prim_cb(struct osmo_prim_hdr *oph, void *_scu)
|
||||
{
|
||||
struct osmo_sccp_user *scu = _scu;
|
||||
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
|
||||
struct osmo_scu_unitdata_param *param;
|
||||
struct sccp_scmg_msg *scmg;
|
||||
int rc = 0;
|
||||
|
||||
switch (OSMO_PRIM_HDR(oph)) {
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
|
||||
param = &prim->u.unitdata;
|
||||
scmg = msgb_l2(oph->msg);
|
||||
/* ensure minimum length based on message type */
|
||||
if (msgb_l2len(oph->msg) < sizeof(*scmg)) {
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
if (scmg->msg_type == SCCP_SCMG_MSGT_SSC && msgb_l2len(oph->msg) < sizeof(*scmg)+1) {
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
/* interestingly, PC is specified to be encoded in little endian ?!? */
|
||||
scmg->affected_pc = osmo_load16le(&scmg->affected_pc);
|
||||
rc = scmg_rx(scu, ¶m->calling_addr, ¶m->called_addr, scmg);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION):
|
||||
LOGP(DLSCCP, LOGL_DEBUG, "Ignoring SCCP user primitive %s\n", osmo_scu_prim_name(oph));
|
||||
break;
|
||||
default:
|
||||
LOGP(DLSCCP, LOGL_ERROR, "unsupported SCCP user primitive %s\n",
|
||||
osmo_scu_prim_name(oph));
|
||||
break;
|
||||
}
|
||||
|
||||
msgb_free(oph->msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* register SCMG as SCCP user for SSN=1 */
|
||||
int sccp_scmg_init(struct osmo_sccp_instance *inst)
|
||||
{
|
||||
struct osmo_sccp_user *scu;
|
||||
scu = osmo_sccp_user_bind(inst, "SCCP Management", scmg_prim_cb, SCCP_SSN_MANAGEMENT);
|
||||
if (!scu)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
433
src/sccp_scoc.c
433
src/sccp_scoc.c
|
@ -36,7 +36,7 @@
|
|||
* However, all SCCP features can be expressed in SUA.
|
||||
*
|
||||
* The code only supports Class 2. No support for Class 3 is intended,
|
||||
* but patches are of course alwys welcome.
|
||||
* but patches are of course always welcome.
|
||||
*
|
||||
* Missing other features:
|
||||
* * Segmentation/Reassembly support
|
||||
|
@ -46,14 +46,17 @@
|
|||
* * use of multiple Routing Contexts in SUA case
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/linuxrbtree.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/sigtran/protocol/sua.h>
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
|
@ -70,8 +73,8 @@
|
|||
|
||||
/* a logical connection within the SCCP instance */
|
||||
struct sccp_connection {
|
||||
/* part of osmo_sccp_instance.list */
|
||||
struct llist_head list;
|
||||
/* entry in (struct sccp_instance)->connections */
|
||||
struct rb_node node;
|
||||
/* which instance are we part of? */
|
||||
struct osmo_sccp_instance *inst;
|
||||
/* which user owns us? */
|
||||
|
@ -80,7 +83,7 @@ struct sccp_connection {
|
|||
/* remote point code */
|
||||
uint32_t remote_pc;
|
||||
|
||||
/* local/remote addresses and identiies */
|
||||
/* local/remote addresses and identities */
|
||||
struct osmo_sccp_addr calling_addr;
|
||||
struct osmo_sccp_addr called_addr;
|
||||
/* SCCP connection identifier. Only relevant across the SCCP User SAP,
|
||||
|
@ -90,7 +93,7 @@ struct sccp_connection {
|
|||
/* SCCP Remote Connection Reference. Allocated by the remote
|
||||
* SCCP stack to uniquely identify a SCCP connection on its end.
|
||||
* We don't interpret it, but simply cache it here so we can use
|
||||
* it whever sending data to the peer. Only relevant over the
|
||||
* it whenever sending data to the peer. Only relevant over the
|
||||
* wire, not to be used across the SCCP user SAP */
|
||||
uint32_t remote_ref;
|
||||
|
||||
|
@ -98,6 +101,8 @@ struct sccp_connection {
|
|||
uint32_t sccp_class;
|
||||
uint32_t release_cause; /* WAIT_CONN_CONF */
|
||||
|
||||
struct msgb *opt_data_cache;
|
||||
|
||||
/* incoming (true) or outgoing (false) */
|
||||
bool incoming;
|
||||
|
||||
|
@ -229,6 +234,31 @@ static const struct osmo_prim_event_map scu_scoc_event_map[] = {
|
|||
* Timer Handling
|
||||
***********************************************************************/
|
||||
|
||||
/* Mostly pasted from Appendix C.4 of ITU-T Q.714 (05/2001) -- some of their descriptions are quite
|
||||
* unintelligible out of context, for which we have our own description here. */
|
||||
const struct osmo_tdef osmo_sccp_timer_defaults[OSMO_SCCP_TIMERS_LEN] = {
|
||||
{ .T = OSMO_SCCP_TIMER_CONN_EST, .default_val = 1*60, .unit = OSMO_TDEF_S,
|
||||
.desc = "Waiting for connection confirm message, 1 to 2 minutes" },
|
||||
{ .T = OSMO_SCCP_TIMER_IAS, .default_val = 7*60, .unit = OSMO_TDEF_S,
|
||||
.desc = "Send keep-alive: on an idle connection, delay before sending an Idle Timer message, 5 to 10 minutes" }, /* RFC 3868 Ch. 8. */
|
||||
{ .T = OSMO_SCCP_TIMER_IAR, .default_val = 15*60, .unit = OSMO_TDEF_S,
|
||||
.desc = "Receive keep-alive: on an idle connection, delay until considering a connection as stale, 11 to 21 minutes" }, /* RFC 3868 Ch. 8. */
|
||||
{ .T = OSMO_SCCP_TIMER_REL, .default_val = 10, .unit = OSMO_TDEF_S,
|
||||
.desc = "Waiting for release complete message, 10 to 20 seconds" },
|
||||
{ .T = OSMO_SCCP_TIMER_REPEAT_REL, .default_val = 10, .unit = OSMO_TDEF_S,
|
||||
.desc = "Waiting for release complete message; or to repeat sending released message after the initial expiry, 10 to 20 seconds" },
|
||||
{ .T = OSMO_SCCP_TIMER_INT, .default_val = 1*60, .unit = OSMO_TDEF_S,
|
||||
.desc = "Waiting for release complete message; or to release connection resources, freeze the LRN and "
|
||||
"alert a maintenance function after the initial expiry, extending to 1 minute" },
|
||||
{ .T = OSMO_SCCP_TIMER_GUARD, .default_val = 23*60, .unit = OSMO_TDEF_S,
|
||||
.desc = "Waiting to resume normal procedure for temporary connection sections during the restart procedure, 23 to 25 minutes" },
|
||||
{ .T = OSMO_SCCP_TIMER_RESET, .default_val = 10, .unit = OSMO_TDEF_S,
|
||||
.desc = "Waiting to release temporary connection section or alert maintenance function after reset request message is sent, 10 to 20 seconds" },
|
||||
{ .T = OSMO_SCCP_TIMER_REASSEMBLY, .default_val = 10, .unit = OSMO_TDEF_S,
|
||||
.desc = "Waiting to receive all the segments of the remaining segments, single segmented message after receiving the first segment, 10 to 20 seconds" },
|
||||
{}
|
||||
};
|
||||
|
||||
/* Appendix C.4 of ITU-T Q.714 */
|
||||
const struct value_string osmo_sccp_timer_names[] = {
|
||||
{ OSMO_SCCP_TIMER_CONN_EST, "conn_est" },
|
||||
|
@ -243,93 +273,16 @@ const struct value_string osmo_sccp_timer_names[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
/* Mostly pasted from Appendix C.4 of ITU-T Q.714 (05/2001) -- some of their descriptions are quite
|
||||
* unintelligible out of context, for which we have our own description here. */
|
||||
const struct value_string osmo_sccp_timer_descriptions[] = {
|
||||
{ OSMO_SCCP_TIMER_CONN_EST,
|
||||
"Waiting for connection confirm message, 1 to 2 minutes" },
|
||||
{ OSMO_SCCP_TIMER_IAS,
|
||||
"Send keep-alive: on an idle connection, delay before sending an Idle Timer message,"
|
||||
" 5 to 10 minutes" },
|
||||
{ OSMO_SCCP_TIMER_IAR,
|
||||
"Receive keep-alive: on an idle connection, delay until considering a connection as stale,"
|
||||
" 11 to 21 minutes" },
|
||||
{ OSMO_SCCP_TIMER_REL,
|
||||
"Waiting for release complete message, 10 to 20 seconds" },
|
||||
{ OSMO_SCCP_TIMER_REPEAT_REL,
|
||||
"Waiting for release complete message; or to repeat sending released message after the initial"
|
||||
" expiry, 10 to 20 seconds" },
|
||||
{ OSMO_SCCP_TIMER_INT,
|
||||
"Waiting for release complete message; or to release connection resources, freeze the LRN and"
|
||||
" alert a maintenance function after the initial expiry, extending to 1 minute" },
|
||||
{ OSMO_SCCP_TIMER_GUARD,
|
||||
"Waiting to resume normal procedure for temporary connection sections during the restart"
|
||||
" procedure, 23 to 25 minutes" },
|
||||
{ OSMO_SCCP_TIMER_RESET,
|
||||
"Waiting to release temporary connection section or alert maintenance function after reset"
|
||||
" request message is sent, 10 to 20 seconds" },
|
||||
{ OSMO_SCCP_TIMER_REASSEMBLY,
|
||||
"Waiting to receive all the segments of the remaining segments, single segmented message after"
|
||||
" receiving the first segment, 10 to 20 seconds" },
|
||||
{}
|
||||
};
|
||||
|
||||
/* Appendix C.4 of ITU-T Q.714 */
|
||||
const struct osmo_sccp_timer_val osmo_sccp_timer_defaults[] = {
|
||||
[OSMO_SCCP_TIMER_CONN_EST] = { .s = 1 * 60, },
|
||||
[OSMO_SCCP_TIMER_IAS] = { .s = 7 * 60, }, /* RFC 3868 Ch. 8. */
|
||||
[OSMO_SCCP_TIMER_IAR] = { .s = 15 * 60, }, /* RFC 3868 Ch. 8. */
|
||||
[OSMO_SCCP_TIMER_REL] = { .s = 10, },
|
||||
[OSMO_SCCP_TIMER_REPEAT_REL] = { .s = 10, },
|
||||
[OSMO_SCCP_TIMER_INT] = { .s = 1 * 60, },
|
||||
[OSMO_SCCP_TIMER_GUARD] = { .s = 23 * 60, },
|
||||
[OSMO_SCCP_TIMER_RESET] = { .s = 10, },
|
||||
[OSMO_SCCP_TIMER_REASSEMBLY] = { .s = 10, },
|
||||
};
|
||||
|
||||
osmo_static_assert(ARRAY_SIZE(osmo_sccp_timer_defaults) == OSMO_SCCP_TIMERS_COUNT,
|
||||
osmo_static_assert(ARRAY_SIZE(osmo_sccp_timer_defaults) == (OSMO_SCCP_TIMERS_LEN) &&
|
||||
ARRAY_SIZE(osmo_sccp_timer_names) == (OSMO_SCCP_TIMERS_LEN),
|
||||
assert_osmo_sccp_timers_count);
|
||||
|
||||
/* Look up an SCCP timer value as configured in the osmo_ss7_instance.
|
||||
* If no user defined value is set, return the global default from osmo_sccp_timer_defaults instead.
|
||||
* However, if default_if_unset is passed false, return NULL in case there is no user defined setting, or
|
||||
* in case it is identical to the global default. */
|
||||
const struct osmo_sccp_timer_val *osmo_sccp_timer_get(const struct osmo_sccp_instance *inst,
|
||||
enum osmo_sccp_timer timer,
|
||||
bool default_if_unset)
|
||||
{
|
||||
const struct osmo_sccp_timer_val *val;
|
||||
const struct osmo_sccp_timer_val *def;
|
||||
|
||||
OSMO_ASSERT(timer >= 0
|
||||
&& timer < ARRAY_SIZE(inst->timers)
|
||||
&& timer < ARRAY_SIZE(osmo_sccp_timer_defaults));
|
||||
|
||||
val = &inst->timers[timer];
|
||||
def = &osmo_sccp_timer_defaults[timer];
|
||||
|
||||
/* Assert that all timer definitions have a sane global default */
|
||||
OSMO_ASSERT(def->s || def->us);
|
||||
|
||||
if (val->s || val->us) {
|
||||
if (!default_if_unset && val->s == def->s && val->us == def->us)
|
||||
return NULL;
|
||||
return val;
|
||||
}
|
||||
|
||||
if (!default_if_unset)
|
||||
return NULL;
|
||||
|
||||
/* If unset, use the global default. */
|
||||
return def;
|
||||
}
|
||||
|
||||
static void sccp_timer_schedule(const struct sccp_connection *conn,
|
||||
struct osmo_timer_list *timer,
|
||||
enum osmo_sccp_timer timer_name)
|
||||
{
|
||||
const struct osmo_sccp_timer_val *val = osmo_sccp_timer_get(conn->inst, timer_name, true);
|
||||
osmo_timer_schedule(timer, val->s, val->us);
|
||||
const unsigned long val_sec = osmo_tdef_get(conn->inst->tdefs, timer_name, OSMO_TDEF_S, -1);
|
||||
osmo_timer_schedule(timer, val_sec, 0);
|
||||
}
|
||||
|
||||
/* T(ias) has expired, send a COIT message to the peer */
|
||||
|
@ -445,17 +398,55 @@ static void conn_stop_connect_timer(struct sccp_connection *conn)
|
|||
|
||||
static void conn_destroy(struct sccp_connection *conn);
|
||||
|
||||
static struct sccp_connection *conn_find_by_id(struct osmo_sccp_instance *inst, uint32_t id)
|
||||
static struct sccp_connection *conn_find_by_id(const struct osmo_sccp_instance *inst, uint32_t id)
|
||||
{
|
||||
struct sccp_connection *conn;
|
||||
const struct rb_node *node = inst->connections.rb_node;
|
||||
|
||||
llist_for_each_entry(conn, &inst->connections, list) {
|
||||
if (conn->conn_id == id)
|
||||
while (node) {
|
||||
conn = container_of(node, struct sccp_connection, node);
|
||||
if (id < conn->conn_id)
|
||||
node = node->rb_left;
|
||||
else if (id > conn->conn_id)
|
||||
node = node->rb_right;
|
||||
else
|
||||
return conn;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int conn_add_node(struct osmo_sccp_instance *inst, struct sccp_connection *conn)
|
||||
{
|
||||
struct rb_node **n = &(inst->connections.rb_node);
|
||||
struct rb_node *parent = NULL;
|
||||
|
||||
while (*n) {
|
||||
struct sccp_connection *it;
|
||||
|
||||
it = container_of(*n, struct sccp_connection, node);
|
||||
|
||||
parent = *n;
|
||||
if (conn->conn_id < it->conn_id) {
|
||||
n = &((*n)->rb_left);
|
||||
} else if (conn->conn_id > it->conn_id) {
|
||||
n = &((*n)->rb_right);
|
||||
} else {
|
||||
LOGP(DLSCCP, LOGL_ERROR,
|
||||
"Trying to reserve already reserved conn_id %u\n", conn->conn_id);
|
||||
return -EEXIST;
|
||||
}
|
||||
}
|
||||
|
||||
rb_link_node(&conn->node, parent, n);
|
||||
rb_insert_color(&conn->node, &inst->connections);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool osmo_sccp_conn_id_exists(const struct osmo_sccp_instance *inst, uint32_t id)
|
||||
{
|
||||
return conn_find_by_id(inst, id) ? true : false;
|
||||
}
|
||||
|
||||
#define INIT_TIMER(x, fn, priv) do { (x)->cb = fn; (x)->data = priv; } while (0)
|
||||
|
||||
/* allocate + init a SCCP Connection with given ID */
|
||||
|
@ -468,7 +459,10 @@ static struct sccp_connection *conn_create_id(struct osmo_sccp_user *user, uint3
|
|||
conn->inst = user->inst;
|
||||
conn->user = user;
|
||||
|
||||
llist_add_tail(&conn->list, &user->inst->connections);
|
||||
if (conn_add_node(user->inst, conn) < 0) {
|
||||
talloc_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_TIMER(&conn->t_conn, conn_tmr_cb, conn);
|
||||
INIT_TIMER(&conn->t_ias, tx_inact_tmr_cb, conn);
|
||||
|
@ -485,7 +479,7 @@ static struct sccp_connection *conn_create_id(struct osmo_sccp_user *user, uint3
|
|||
conn->fi = osmo_fsm_inst_alloc(&sccp_scoc_fsm, conn, conn,
|
||||
LOGL_DEBUG, name);
|
||||
if (!conn->fi) {
|
||||
llist_del(&conn->list);
|
||||
rb_erase(&conn->node, &user->inst->connections);
|
||||
talloc_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -493,26 +487,66 @@ static struct sccp_connection *conn_create_id(struct osmo_sccp_user *user, uint3
|
|||
return conn;
|
||||
}
|
||||
|
||||
/* Return an unused SCCP connection ID.
|
||||
* Callers should check the returned value: on negative return value, there are no unused IDs available.
|
||||
* \param[in] sccp The SCCP instance to determine a new connection ID for.
|
||||
* \return unused ID on success (range [0x0, 0x00fffffe]) or negative on elapsed max_attempts without an unused id (<0).
|
||||
*/
|
||||
int osmo_sccp_instance_next_conn_id(struct osmo_sccp_instance *sccp)
|
||||
{
|
||||
int max_attempts = 0x00FFFFFE;
|
||||
|
||||
/* SUA: RFC3868 sec 3.10.4:
|
||||
* The source reference number is a 4 octet long integer.
|
||||
* This is allocated by the source SUA instance.
|
||||
* M3UA/SCCP: ITU-T Q.713 sec 3.3:
|
||||
* The "source local reference" parameter field is a three-octet field containing a
|
||||
* reference number which is generated and used by the local node to identify the
|
||||
* connection section after the connection section is set up.
|
||||
* The coding "all ones" is reserved for future use.
|
||||
* Hence, as we currently use the connection ID also as local reference,
|
||||
* let's simply use 24 bit ids to fit all link types (excluding 0x00ffffff).
|
||||
*/
|
||||
while (OSMO_LIKELY((max_attempts--) > 0)) {
|
||||
/* Optimized modulo operation (% 0x00FFFFFE) using bitwise AND plus CMP: */
|
||||
sccp->next_id = (sccp->next_id + 1) & 0x00FFFFFF;
|
||||
if (OSMO_UNLIKELY(sccp->next_id == 0x00FFFFFF))
|
||||
sccp->next_id = 0;
|
||||
|
||||
if (!conn_find_by_id(sccp, sccp->next_id))
|
||||
return sccp->next_id;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Search for next free connection ID and allocate conn */
|
||||
static struct sccp_connection *conn_create(struct osmo_sccp_user *user)
|
||||
{
|
||||
uint32_t conn_id;
|
||||
|
||||
do {
|
||||
conn_id = user->inst->next_id++;
|
||||
} while (conn_find_by_id(user->inst, conn_id));
|
||||
|
||||
int conn_id = osmo_sccp_instance_next_conn_id(user->inst);
|
||||
if (conn_id < 0)
|
||||
return NULL;
|
||||
return conn_create_id(user, conn_id);
|
||||
}
|
||||
|
||||
static void conn_opt_data_clear_cache(struct sccp_connection *conn)
|
||||
{
|
||||
if (conn->opt_data_cache) {
|
||||
msgb_free(conn->opt_data_cache);
|
||||
conn->opt_data_cache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* destroy a SCCP connection state, releasing all timers, terminating
|
||||
* FSM and releasing associated memory */
|
||||
static void conn_destroy(struct sccp_connection *conn)
|
||||
{
|
||||
conn_opt_data_clear_cache(conn);
|
||||
|
||||
conn_stop_connect_timer(conn);
|
||||
conn_stop_inact_timers(conn);
|
||||
conn_stop_release_timers(conn);
|
||||
llist_del(&conn->list);
|
||||
rb_erase(&conn->node, &conn->inst->connections);
|
||||
|
||||
osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_REQUEST, NULL);
|
||||
|
||||
|
@ -566,11 +600,115 @@ static int xua_gen_relre_and_send(struct sccp_connection *conn, uint32_t cause,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Send cached optional data (if any) from expected message type and clear cache */
|
||||
static void xua_opt_data_send_cache(struct sccp_connection *conn, int exp_type, uint8_t msg_class)
|
||||
{
|
||||
const struct xua_dialect *dialect = &xua_dialect_sua;
|
||||
const struct xua_msg_class *xmc = dialect->class[msg_class];
|
||||
|
||||
if (!conn->opt_data_cache)
|
||||
return;
|
||||
|
||||
if (conn->opt_data_cache->cb[0] != exp_type) {
|
||||
/* Caller (from the FSM) knows what was the source of Optional Data we're sending.
|
||||
* Compare this information with source of Optional Data recorded while caching
|
||||
* to make sure we're on the same page.
|
||||
*/
|
||||
LOGP(DLSCCP, LOGL_ERROR, "unexpected message type %s != cache source %s\n",
|
||||
xua_class_msg_name(xmc, exp_type), xua_class_msg_name(xmc, conn->opt_data_cache->cb[0]));
|
||||
} else {
|
||||
osmo_sccp_tx_data(conn->user, conn->conn_id, msgb_data(conn->opt_data_cache), msgb_length(conn->opt_data_cache));
|
||||
}
|
||||
|
||||
conn_opt_data_clear_cache(conn);
|
||||
}
|
||||
|
||||
/* Check if optional data should be dropped, log given error message if so */
|
||||
static bool xua_drop_data_check_drop(const struct osmo_scu_prim *prim, unsigned lim, const char *message)
|
||||
{
|
||||
if (msgb_l2len(prim->oph.msg) > lim) {
|
||||
LOGP(DLSCCP, LOGL_ERROR,
|
||||
"%s: dropping optional data with length %u > %u - %s\n",
|
||||
osmo_scu_prim_name(&prim->oph), msgb_l2len(prim->oph.msg), lim, message);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Cache the optional data (if necessary)
|
||||
* returns true if Optional Data should be kept while encoding the message */
|
||||
static bool xua_opt_data_cache_keep(struct sccp_connection *conn, const struct osmo_scu_prim *prim, int msg_type)
|
||||
{
|
||||
uint8_t *buf;
|
||||
uint32_t max_optional_data = conn->inst->max_optional_data;
|
||||
|
||||
if (xua_drop_data_check_drop(prim, SCCP_MAX_DATA, "cache overrun"))
|
||||
return false;
|
||||
|
||||
if (msgb_l2len(prim->oph.msg) > max_optional_data) {
|
||||
if (conn->opt_data_cache) {
|
||||
/* Caching optional data, but there already is optional data occupying the cache: */
|
||||
LOGP(DLSCCP, LOGL_ERROR, "replacing unsent %u bytes of optional data cache with %s optional data\n",
|
||||
msgb_length(conn->opt_data_cache), osmo_scu_prim_name(&prim->oph));
|
||||
msgb_trim(conn->opt_data_cache, 0);
|
||||
} else {
|
||||
conn->opt_data_cache = msgb_alloc_c(conn, SCCP_MAX_DATA, "SCCP optional data cache for CR/CC/RLSD");
|
||||
}
|
||||
|
||||
buf = msgb_put(conn->opt_data_cache, msgb_l2len(prim->oph.msg));
|
||||
memcpy(buf, msgb_l2(prim->oph.msg), msgb_l2len(prim->oph.msg));
|
||||
|
||||
conn->opt_data_cache->cb[0] = msg_type;
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check optional Data size limit, cache if necessary, return indication whether original opt data should be sent */
|
||||
static bool xua_opt_data_length_lim(struct sccp_connection *conn, const struct osmo_scu_prim *prim, int msg_type)
|
||||
{
|
||||
uint32_t max_optional_data = conn->inst->max_optional_data;
|
||||
|
||||
if (!(prim && msgb_l2(prim->oph.msg) && msgb_l2len(prim->oph.msg)))
|
||||
return false;
|
||||
|
||||
switch (msg_type) {
|
||||
case SUA_CO_CORE: /* §4.2 Connection request (CR) */
|
||||
case SUA_CO_COAK: /* §4.3 Connection confirm (CC) */
|
||||
return xua_opt_data_cache_keep(conn, prim, msg_type);
|
||||
case SUA_CO_COREF: /* §4.4 Connection refused (CREF) */
|
||||
if (xua_drop_data_check_drop(prim, max_optional_data, "over ITU-T Rec. Q.713 §4.4 limit")) {
|
||||
/* From the state diagrams in ITU-T Rec Q.714, there's no way to send DT1 neither before nor after CREF
|
||||
* at this point, so the only option we have is to drop optional data:
|
||||
* see Figure C.3 / Q.714 (sheet 2 of 6) */
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case SUA_CO_RELRE: /* §4.5 Released (RLSD) */
|
||||
if (msgb_l2len(prim->oph.msg) > max_optional_data) {
|
||||
if (xua_drop_data_check_drop(prim, SCCP_MAX_DATA, "protocol error"))
|
||||
return false;
|
||||
/* There's no need to cache the optional data since the connection is still active at this point:
|
||||
* Send the Optional Data in a DT1 ahead of the RLSD, because it is too large to be sent in one message.
|
||||
*/
|
||||
osmo_sccp_tx_data(conn->user, conn->conn_id, msgb_l2(prim->oph.msg), msgb_l2len(prim->oph.msg));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* generate a 'struct xua_msg' of requested type from connection +
|
||||
* primitive data */
|
||||
static struct xua_msg *xua_gen_msg_co(struct sccp_connection *conn, uint32_t event,
|
||||
struct osmo_scu_prim *prim, int msg_type)
|
||||
const struct osmo_scu_prim *prim, int msg_type)
|
||||
{
|
||||
bool encode_opt_data = xua_opt_data_length_lim(conn, prim, msg_type);
|
||||
struct xua_msg *xua = xua_msg_alloc();
|
||||
|
||||
if (!xua)
|
||||
|
@ -587,10 +725,11 @@ static struct xua_msg *xua_gen_msg_co(struct sccp_connection *conn, uint32_t eve
|
|||
/* optional: sequence number (class 3 only) */
|
||||
if (conn->calling_addr.presence)
|
||||
xua_msg_add_sccp_addr(xua, SUA_IEI_SRC_ADDR, &conn->calling_addr);
|
||||
/* optional: hop count; importance; priority; credit */
|
||||
if (prim && msgb_l2(prim->oph.msg) && msgb_l2len(prim->oph.msg))
|
||||
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
|
||||
msgb_l2(prim->oph.msg));
|
||||
/* optional: data */
|
||||
if (encode_opt_data)
|
||||
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg), msgb_l2(prim->oph.msg));
|
||||
/* optional: hop count */
|
||||
/* optional: importance */
|
||||
break;
|
||||
case SUA_CO_COAK: /* Connect Acknowledge == SCCP CC */
|
||||
xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_COAK);
|
||||
|
@ -608,11 +747,12 @@ static struct xua_msg *xua_gen_msg_co(struct sccp_connection *conn, uint32_t eve
|
|||
* parameter */
|
||||
if (conn->calling_addr.presence)
|
||||
xua_msg_add_sccp_addr(xua, SUA_IEI_DEST_ADDR, &conn->calling_addr);
|
||||
if (prim && msgb_l2(prim->oph.msg) && msgb_l2len(prim->oph.msg))
|
||||
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
|
||||
msgb_l2(prim->oph.msg));
|
||||
/* optional: data */
|
||||
if (encode_opt_data)
|
||||
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg), msgb_l2(prim->oph.msg));
|
||||
/* optional: importance */
|
||||
break;
|
||||
case SUA_CO_RELRE: /* Release Request == SCCP REL */
|
||||
case SUA_CO_RELRE: /* Release Request == SCCP RLSD */
|
||||
if (!prim)
|
||||
goto prim_needed;
|
||||
xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_RELRE);
|
||||
|
@ -620,17 +760,16 @@ static struct xua_msg *xua_gen_msg_co(struct sccp_connection *conn, uint32_t eve
|
|||
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, conn->remote_ref);
|
||||
xua_msg_add_u32(xua, SUA_IEI_SRC_REF, conn->conn_id);
|
||||
xua_msg_add_u32(xua, SUA_IEI_CAUSE, SUA_CAUSE_T_RELEASE | prim->u.disconnect.cause);
|
||||
/* optional: data */
|
||||
if (encode_opt_data)
|
||||
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg), msgb_l2(prim->oph.msg));
|
||||
/* optional: importance */
|
||||
if (prim && msgb_l2(prim->oph.msg) && msgb_l2len(prim->oph.msg))
|
||||
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
|
||||
msgb_l2(prim->oph.msg));
|
||||
break;
|
||||
case SUA_CO_RELCO: /* Release Confirm == SCCP RLSD */
|
||||
case SUA_CO_RELCO: /* Release Confirm == SCCP RLC */
|
||||
xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_RELCO);
|
||||
xua_msg_add_u32(xua, SUA_IEI_ROUTE_CTX, conn->inst->route_ctx);
|
||||
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, conn->remote_ref);
|
||||
xua_msg_add_u32(xua, SUA_IEI_SRC_REF, conn->conn_id);
|
||||
/* optional: importance */
|
||||
break;
|
||||
case SUA_CO_CODT: /* Connection Oriented Data Transfer == SCCP DT1 */
|
||||
if (!prim)
|
||||
|
@ -663,11 +802,10 @@ static struct xua_msg *xua_gen_msg_co(struct sccp_connection *conn, uint32_t eve
|
|||
/* conditional: dest addr */
|
||||
if (conn->calling_addr.presence)
|
||||
xua_msg_add_sccp_addr(xua, SUA_IEI_DEST_ADDR, &conn->calling_addr);
|
||||
/* optional: importance */
|
||||
/* optional: data */
|
||||
if (prim && msgb_l2(prim->oph.msg) && msgb_l2len(prim->oph.msg))
|
||||
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
|
||||
msgb_l2(prim->oph.msg));
|
||||
if (encode_opt_data)
|
||||
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg), msgb_l2(prim->oph.msg));
|
||||
/* optional: importance */
|
||||
break;
|
||||
/* FIXME */
|
||||
default:
|
||||
|
@ -684,15 +822,17 @@ prim_needed:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* generate xua_msg, encode it and send it to SCRC */
|
||||
/* generate xua_msg, encode it and send it to SCRC
|
||||
* returns 0 on success, negative on error
|
||||
*/
|
||||
static int xua_gen_encode_and_send(struct sccp_connection *conn, uint32_t event,
|
||||
struct osmo_scu_prim *prim, int msg_type)
|
||||
const struct osmo_scu_prim *prim, int msg_type)
|
||||
{
|
||||
struct xua_msg *xua;
|
||||
|
||||
xua = xua_gen_msg_co(conn, event, prim, msg_type);
|
||||
if (!xua)
|
||||
return -1;
|
||||
return -ENOMEM;
|
||||
|
||||
/* amend this with point code information; Many CO msgs
|
||||
* includes neither called nor calling party address! */
|
||||
|
@ -804,7 +944,7 @@ static void scu_gen_encode_and_send(struct sccp_connection *conn, uint32_t event
|
|||
|
||||
|
||||
/***********************************************************************
|
||||
* Actual SCCP Connection Oriented Control (SCOC) Finite Stte Machine
|
||||
* Actual SCCP Connection Oriented Control (SCOC) Finite State Machine
|
||||
***********************************************************************/
|
||||
|
||||
/* Figure C.2/Q.714 (sheet 1 of 7) and C.3/Q.714 (sheet 1 of 6) */
|
||||
|
@ -814,6 +954,7 @@ static void scoc_fsm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|||
struct osmo_scu_prim *prim = NULL;
|
||||
struct osmo_scu_connect_param *uconp;
|
||||
struct xua_msg *xua = NULL;
|
||||
int rc;
|
||||
|
||||
switch (event) {
|
||||
case SCOC_E_SCU_N_CONN_REQ:
|
||||
|
@ -824,10 +965,14 @@ static void scoc_fsm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|||
conn->calling_addr = uconp->calling_addr;
|
||||
conn->sccp_class = uconp->sccp_class;
|
||||
/* generate + send CR PDU to SCRC */
|
||||
xua_gen_encode_and_send(conn, event, prim, SUA_CO_CORE);
|
||||
/* start connection timer */
|
||||
conn_start_connect_timer(conn);
|
||||
osmo_fsm_inst_state_chg(fi, S_CONN_PEND_OUT, 0, 0);
|
||||
rc = xua_gen_encode_and_send(conn, event, prim, SUA_CO_CORE);
|
||||
if (rc < 0)
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to initiate connection: %s\n", strerror(-rc));
|
||||
else {
|
||||
/* start connection timer */
|
||||
conn_start_connect_timer(conn);
|
||||
osmo_fsm_inst_state_chg(fi, S_CONN_PEND_OUT, 0, 0);
|
||||
}
|
||||
break;
|
||||
#if 0
|
||||
case SCOC_E_SCU_N_TYPE1_REQ:
|
||||
|
@ -898,11 +1043,13 @@ static void scoc_fsm_conn_pend_in(struct osmo_fsm_inst *fi, uint32_t event, void
|
|||
/* start inactivity timers */
|
||||
conn_start_inact_timers(conn);
|
||||
osmo_fsm_inst_state_chg(fi, S_ACTIVE, 0, 0);
|
||||
xua_opt_data_send_cache(conn, SUA_CO_COAK, SUA_MSGC_CO);
|
||||
break;
|
||||
case SCOC_E_SCU_N_DISC_REQ:
|
||||
prim = data;
|
||||
/* release resources: implicit */
|
||||
xua_gen_encode_and_send(conn, event, prim, SUA_CO_COREF);
|
||||
/* N. B: we've ignored CREF sending errors as there's no recovery option anyway */
|
||||
osmo_fsm_inst_state_chg(fi, S_IDLE, 0, 0);
|
||||
break;
|
||||
}
|
||||
|
@ -979,6 +1126,11 @@ static void scoc_fsm_conn_pend_out(struct osmo_fsm_inst *fi, uint32_t event, voi
|
|||
conn->remote_pc = xua->mtp.opc;
|
||||
|
||||
osmo_fsm_inst_state_chg(fi, S_ACTIVE, 0, 0);
|
||||
/* If CR which was used to initiate this connection had excessive Optional Data which we had to cache,
|
||||
* now is the time to send it: the connection is already active but we hadn't notified upper layers about it
|
||||
* so we have the connection all to ourselves and can use it to transmit "leftover" data via DT1 */
|
||||
xua_opt_data_send_cache(conn, SUA_CO_CORE, xua->hdr.msg_class);
|
||||
|
||||
/* N-CONNECT.conf to user */
|
||||
scu_gen_encode_and_send(conn, event, xua, OSMO_SCU_PRIM_N_CONNECT,
|
||||
PRIM_OP_CONFIRM);
|
||||
|
@ -1008,6 +1160,13 @@ static void scoc_fsm_wait_conn_conf(struct osmo_fsm_inst *fi, uint32_t event, vo
|
|||
conn_stop_connect_timer(conn);
|
||||
/* associate rem ref to conn */
|
||||
conn->remote_ref = xua_msg_get_u32(xua, SUA_IEI_SRC_REF);
|
||||
/* 3.1.4.2 The node sending the CC message (identified
|
||||
* by the parameter OPC contained in the
|
||||
* MTP-TRANSFER.indication primitive which conveyed the
|
||||
* CC message [plus the MTP-SAP instance]) is associated
|
||||
* with the connection section. */
|
||||
conn->remote_pc = xua->mtp.opc;
|
||||
|
||||
/* released to SCRC */
|
||||
xua_gen_relre_and_send(conn, conn->release_cause, NULL);
|
||||
/* start rel timer */
|
||||
|
@ -1283,6 +1442,8 @@ static const struct osmo_fsm_state sccp_scoc_states[] = {
|
|||
S(SCOC_E_CONN_TMR_EXP) |
|
||||
S(SCOC_E_RCOC_CREF_IND) |
|
||||
S(SCOC_E_RCOC_ROUT_FAIL_IND),
|
||||
.out_state_mask = S(S_IDLE) |
|
||||
S(S_DISCONN_PEND),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1518,7 +1679,7 @@ static void tx_rlsd_from_xua_twoway(struct sccp_connection *conn,
|
|||
xua_msg_free(xua);
|
||||
}
|
||||
|
||||
/* process received message for unasigned local reference */
|
||||
/* process received message for unassigned local reference */
|
||||
static void sccp_scoc_rx_unass_local_ref(struct osmo_sccp_instance *inst,
|
||||
struct xua_msg *xua)
|
||||
{
|
||||
|
@ -1589,11 +1750,12 @@ static void sccp_scoc_rx_inval_opc(struct sccp_connection *conn,
|
|||
struct xua_msg *xua)
|
||||
{
|
||||
LOGP(DLSCCP, LOGL_NOTICE,
|
||||
"Received message for opc=%u=%s on conn with mismatching remote pc=%u=%s\n",
|
||||
"Received message %s for opc=%u=%s on conn with mismatching remote pc=%u=%s\n",
|
||||
xua_hdr_dump(xua, &xua_dialect_sua),
|
||||
xua->mtp.opc, osmo_ss7_pointcode_print(conn->inst->ss7, xua->mtp.opc),
|
||||
conn->remote_pc, osmo_ss7_pointcode_print2(conn->inst->ss7, conn->remote_pc));
|
||||
/* we have received a message with invalid origin PC and thus
|
||||
* apply the action indiacted in Table B.2/Q.714 */
|
||||
* apply the action indicated in Table B.2/Q.714 */
|
||||
switch (xua->hdr.msg_type) {
|
||||
case SUA_CO_RELRE: /* RLSD */
|
||||
case SUA_CO_RESRE: /* RSR */
|
||||
|
@ -1628,7 +1790,7 @@ void sccp_scoc_rx_from_scrc(struct osmo_sccp_instance *inst,
|
|||
/* we basically try to convert the SUA message into an event,
|
||||
* and then dispatch the event to the connection-specific FSM.
|
||||
* If it is a CORE (Connect REquest), we create the connection
|
||||
* (and imlpicitly its FSM) first */
|
||||
* (and implicitly its FSM) first */
|
||||
|
||||
if (xua->hdr.msg_type == SUA_CO_CORE) {
|
||||
scu = sccp_find_user(inst, xua);
|
||||
|
@ -1785,10 +1947,12 @@ int osmo_sccp_user_sap_down(struct osmo_sccp_user *scu, struct osmo_prim_hdr *op
|
|||
|
||||
void sccp_scoc_flush_connections(struct osmo_sccp_instance *inst)
|
||||
{
|
||||
struct sccp_connection *conn, *conn2;
|
||||
|
||||
llist_for_each_entry_safe(conn, conn2, &inst->connections, list)
|
||||
struct rb_node *node;
|
||||
while ((node = rb_first(&inst->connections))) {
|
||||
struct sccp_connection *conn = container_of(node, struct sccp_connection, node);
|
||||
conn_destroy(conn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
|
@ -1821,11 +1985,14 @@ static void vty_show_connection(struct vty *vty, struct sccp_connection *conn)
|
|||
void sccp_scoc_show_connections(struct vty *vty, struct osmo_sccp_instance *inst)
|
||||
{
|
||||
struct sccp_connection *conn;
|
||||
struct rb_node *node;
|
||||
|
||||
vty_out(vty, "I Local Conn. Remote %s", VTY_NEWLINE);
|
||||
vty_out(vty, "O Ref SSN PC State Ref SSN PC %s", VTY_NEWLINE);
|
||||
vty_out(vty, "- ------ --- ------- ---------------- ------ --- -------%s", VTY_NEWLINE);
|
||||
|
||||
llist_for_each_entry(conn, &inst->connections, list)
|
||||
for (node = rb_first(&inst->connections); node; node = rb_next(node)) {
|
||||
conn = container_of(node, struct sccp_connection, node);
|
||||
vty_show_connection(vty, conn);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ static int sua2sccp_tx_m3ua(struct osmo_sccp_instance *inst,
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* 2) wrap into MTP-TRANSFER.req primtiive */
|
||||
/* 2) wrap into MTP-TRANSFER.req primitive */
|
||||
msg->l2h = msg->data;
|
||||
omp = (struct osmo_mtp_prim *) msgb_push(msg, sizeof(*omp));
|
||||
osmo_prim_init(&omp->oph, MTP_SAP_USER,
|
||||
|
@ -109,7 +109,7 @@ static int sua2sccp_tx_m3ua(struct osmo_sccp_instance *inst,
|
|||
return osmo_ss7_user_mtp_xfer_req(s7i, omp);
|
||||
}
|
||||
|
||||
/* Gererate MTP-TRANSFER.req from xUA message */
|
||||
/* Generate MTP-TRANSFER.req from xUA message */
|
||||
static int gen_mtp_transfer_req_xua(struct osmo_sccp_instance *inst,
|
||||
struct xua_msg *xua,
|
||||
const struct osmo_sccp_addr *called)
|
||||
|
@ -230,7 +230,7 @@ static int scrc_node_7(struct osmo_sccp_instance *inst,
|
|||
/* Connection Oriented? */
|
||||
if (sua_is_connectionless(xua)) {
|
||||
/* TODO: Perform Capability Test */
|
||||
/* TODO: Canges Needed? */
|
||||
/* TODO: Changes Needed? */
|
||||
if (0) {
|
||||
/* Changes Needed -> SCLC */
|
||||
return 0;
|
||||
|
@ -440,6 +440,26 @@ int sccp_scrc_rx_sclc_msg(struct osmo_sccp_instance *inst,
|
|||
return scrc_local_out_common(inst, xua, &called);
|
||||
}
|
||||
|
||||
/* ensure the CallingParty address doesn't just contain SSN, but at least SSN+PC */
|
||||
static void ensure_opc_in_calling_ssn(struct osmo_sccp_instance *inst,
|
||||
struct xua_msg *xua)
|
||||
{
|
||||
struct osmo_sccp_addr calling;
|
||||
|
||||
sua_addr_parse(&calling, xua, SUA_IEI_SRC_ADDR);
|
||||
|
||||
/* if we route on SSN and only have a SSN in the address... */
|
||||
if (calling.ri == OSMO_SCCP_RI_SSN_PC &&
|
||||
calling.presence == OSMO_SCCP_ADDR_T_SSN) {
|
||||
/* add the M3UA OPC to the address to ensure that the recipient
|
||||
* can actually respond back to the source */
|
||||
calling.presence |= OSMO_SCCP_ADDR_T_PC;
|
||||
calling.pc = xua->mtp.opc;
|
||||
xua_msg_free_tag(xua, SUA_IEI_SRC_ADDR);
|
||||
xua_msg_add_sccp_addr(xua, SUA_IEI_SRC_ADDR, &calling);
|
||||
}
|
||||
}
|
||||
|
||||
/* Figure C.1/Q.714 Sheet 1 of 12, after we converted the
|
||||
* MTP-TRANSFER.ind to SUA. */
|
||||
int scrc_rx_mtp_xfer_ind_xua(struct osmo_sccp_instance *inst,
|
||||
|
@ -461,6 +481,9 @@ int scrc_rx_mtp_xfer_ind_xua(struct osmo_sccp_instance *inst,
|
|||
}
|
||||
/* We only treat connectionless and CR below */
|
||||
|
||||
/* ensure we have at least OPC+SSN and not just SSN in CallingParty (OS#5146) */
|
||||
ensure_opc_in_calling_ssn(inst, xua);
|
||||
|
||||
sua_addr_parse(&called, xua, SUA_IEI_DEST_ADDR);
|
||||
|
||||
/* Route on GT? */
|
||||
|
|
143
src/sccp_user.c
143
src/sccp_user.c
|
@ -35,9 +35,11 @@
|
|||
#include <osmocom/sigtran/mtp_sap.h>
|
||||
#include <osmocom/sigtran/protocol/mtp.h>
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
|
||||
#include "sccp_internal.h"
|
||||
#include "xua_internal.h"
|
||||
#include "ss7_internal.h"
|
||||
|
||||
/*! \brief Find a SCCP User registered for given PC+SSN or SSN only
|
||||
* First search all users with a valid PC for a full PC+SSN match.
|
||||
|
@ -220,6 +222,7 @@ struct osmo_sccp_instance *
|
|||
osmo_sccp_instance_create(struct osmo_ss7_instance *ss7, void *priv)
|
||||
{
|
||||
struct osmo_sccp_instance *inst;
|
||||
int rc;
|
||||
|
||||
inst = talloc_zero(ss7, struct osmo_sccp_instance);
|
||||
if (!inst)
|
||||
|
@ -227,13 +230,23 @@ osmo_sccp_instance_create(struct osmo_ss7_instance *ss7, void *priv)
|
|||
|
||||
inst->ss7 = ss7;
|
||||
inst->priv = priv;
|
||||
INIT_LLIST_HEAD(&inst->connections);
|
||||
INIT_LLIST_HEAD(&inst->users);
|
||||
|
||||
inst->ss7_user.inst = ss7;
|
||||
inst->ss7_user.name = "SCCP";
|
||||
inst->ss7_user.prim_cb = mtp_user_prim_cb;
|
||||
inst->ss7_user.priv = inst;
|
||||
inst->max_optional_data = SCCP_MAX_OPTIONAL_DATA;
|
||||
|
||||
inst->tdefs = talloc_memdup(inst, osmo_sccp_timer_defaults,
|
||||
sizeof(osmo_sccp_timer_defaults));
|
||||
osmo_tdefs_reset(inst->tdefs);
|
||||
|
||||
rc = sccp_scmg_init(inst);
|
||||
if (rc < 0) {
|
||||
talloc_free(inst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
osmo_ss7_user_register(ss7, MTP_SI_SCCP, &inst->ss7_user);
|
||||
|
||||
|
@ -257,6 +270,16 @@ void osmo_sccp_instance_destroy(struct osmo_sccp_instance *inst)
|
|||
talloc_free(inst);
|
||||
}
|
||||
|
||||
void osmo_sccp_set_priv(struct osmo_sccp_instance *sccp, void *priv)
|
||||
{
|
||||
sccp->priv = priv;
|
||||
}
|
||||
|
||||
void *osmo_sccp_get_priv(struct osmo_sccp_instance *sccp)
|
||||
{
|
||||
return sccp->priv;
|
||||
}
|
||||
|
||||
/*! \brief derive a basic local SCCP-Address from a given SCCP instance.
|
||||
* \param[out] dest_addr pointer to output address memory
|
||||
* \param[in] inst SCCP instance
|
||||
|
@ -342,8 +365,8 @@ int osmo_sccp_gt_cmp(const struct osmo_sccp_gt *a, const struct osmo_sccp_gt *b)
|
|||
* The SCCP addresses' Routing Indicator is not compared, see osmo_sccp_addr_ri_cmp().
|
||||
* \param[in] a left side.
|
||||
* \param[in] b right side.
|
||||
* \param[in] presence_criteria A bitmask of OSMO_SCCP_ADDR_T_* values, or 0xffffffff to compare all parts, except the
|
||||
* routing indicator.
|
||||
* \param[in] presence_criteria A bitmask of OSMO_SCCP_ADDR_T_* values, or OSMO_SCCP_ADDR_T_MASK to compare all parts,
|
||||
* except the routing indicator.
|
||||
* \return -1 if a < b, 1 if a > b, and 0 if all checked values match.
|
||||
*/
|
||||
int osmo_sccp_addr_cmp(const struct osmo_sccp_addr *a, const struct osmo_sccp_addr *b, uint32_t presence_criteria)
|
||||
|
@ -468,10 +491,10 @@ const char *osmo_sccp_user_name(struct osmo_sccp_user *scu)
|
|||
* \param[in] name human readable name
|
||||
* \param[in] default_pc pointcode to be used on missing VTY setting
|
||||
* \param[in] prot protocol to be used (e.g OSMO_SS7_ASP_PROT_M3UA)
|
||||
* \param[in] default_local_port local port to be usd on missing VTY setting
|
||||
* \param[in] default_local_ip local IP-address to be usd on missing VTY setting
|
||||
* \param[in] default_remote_port remote port to be usd on missing VTY setting
|
||||
* \param[in] default_remote_ip remote IP-address to be usd on missing VTY setting
|
||||
* \param[in] default_local_port local port to be used on missing VTY setting
|
||||
* \param[in] default_local_ip local IP-address to be used on missing VTY setting (NULL: use library own defaults)
|
||||
* \param[in] default_remote_port remote port to be used on missing VTY setting
|
||||
* \param[in] default_remote_ip remote IP-address to be used on missing VTY setting (NULL: use library own defaults)
|
||||
* \returns callee-allocated SCCP instance on success; NULL on error */
|
||||
|
||||
struct osmo_sccp_instance *
|
||||
|
@ -492,6 +515,9 @@ osmo_sccp_simple_client_on_ss7_id(void *ctx, uint32_t ss7_id, const char *name,
|
|||
struct osmo_ss7_asp *asp;
|
||||
bool asp_created = false;
|
||||
char *as_name, *asp_name = NULL;
|
||||
int trans_proto;
|
||||
|
||||
trans_proto = ss7_default_trans_proto_for_asp_proto(prot);
|
||||
|
||||
/*! The function will examine the given CS7 instance and its sub
|
||||
* components (as, asp, etc.). If necessary it will allocate
|
||||
|
@ -602,26 +628,69 @@ osmo_sccp_simple_client_on_ss7_id(void *ctx, uint32_t ss7_id, const char *name,
|
|||
asp_name = talloc_asprintf(ctx, "asp-clnt-%s", name);
|
||||
LOGP(DLSCCP, LOGL_NOTICE, "%s: No unassociated ASP for %s, creating new ASP %s\n",
|
||||
name, osmo_ss7_asp_protocol_name(prot), asp_name);
|
||||
asp =
|
||||
osmo_ss7_asp_find_or_create(ss7, asp_name,
|
||||
default_remote_port,
|
||||
default_local_port,
|
||||
prot);
|
||||
asp = osmo_ss7_asp_find_or_create2(ss7, asp_name,
|
||||
default_remote_port,
|
||||
default_local_port,
|
||||
trans_proto, prot);
|
||||
talloc_free(asp_name);
|
||||
if (!asp)
|
||||
goto out_rt;
|
||||
asp_created = true;
|
||||
osmo_ss7_asp_peer_set_hosts(&asp->cfg.local, asp, &default_local_ip, 1);
|
||||
osmo_ss7_asp_peer_set_hosts(&asp->cfg.remote, asp, &default_remote_ip, 1);
|
||||
/* Ensure that the ASP we use is set to operate as a client. */
|
||||
asp->cfg.is_server = false;
|
||||
/* Ensure that the ASP we use is set to role ASP. */
|
||||
asp->cfg.role = OSMO_SS7_ASP_ROLE_ASP;
|
||||
if (default_local_ip)
|
||||
osmo_ss7_asp_peer_set_hosts(&asp->cfg.local, asp, &default_local_ip, 1);
|
||||
if (default_remote_ip)
|
||||
osmo_ss7_asp_peer_set_hosts(&asp->cfg.remote, asp, &default_remote_ip, 1);
|
||||
/* Make sure proper defaults are applied if app didn't provide specific default values */
|
||||
ss7_asp_set_default_peer_hosts(asp);
|
||||
asp->simple_client_allocated = true;
|
||||
}
|
||||
|
||||
osmo_ss7_as_add_asp(as, asp->cfg.name);
|
||||
}
|
||||
|
||||
/* Ensure that the ASP we use is set to client mode. */
|
||||
asp->cfg.is_server = false;
|
||||
asp->cfg.role = OSMO_SS7_ASP_ROLE_ASP;
|
||||
/* Extra sanity checks if the ASP asp-clnt-* was pre-configured over VTY: */
|
||||
if (!asp->simple_client_allocated) {
|
||||
/* Forbid ASPs defined through VTY that are not entirely
|
||||
* configured. "role" and "transport-role" must be explicitly provided:
|
||||
*/
|
||||
if (!asp->cfg.role_set_by_vty) {
|
||||
LOGP(DLSCCP, LOGL_ERROR,
|
||||
"%s: ASP %s defined in VTY but 'role' was not set there, please set it.\n",
|
||||
name, asp->cfg.name);
|
||||
goto out_asp;
|
||||
}
|
||||
if (!asp->cfg.trans_role_set_by_vty) {
|
||||
LOGP(DLSCCP, LOGL_ERROR,
|
||||
"%s: ASP %s defined in VTY but 'transport-role' was not set there, please set it.\n",
|
||||
name, asp->cfg.name);
|
||||
goto out_asp;
|
||||
}
|
||||
|
||||
/* If ASP was configured through VTY it may be explicitly configured as
|
||||
* SCTP server. It may be a bit confusing since this function is to create
|
||||
* a "SCCP simple client", but this allows users of this API such as
|
||||
* osmo-hnbgw to support transport-role server if properly configured through VTY.
|
||||
*/
|
||||
if (asp->cfg.is_server) {
|
||||
struct osmo_xua_server *xs;
|
||||
LOGP(DLSCCP, LOGL_NOTICE,
|
||||
"%s: Requesting an SCCP simple client on ASP %s configured with 'transport-role server'\n",
|
||||
name, asp->cfg.name);
|
||||
xs = osmo_ss7_xua_server_find2(ss7,
|
||||
asp->cfg.trans_proto, prot,
|
||||
asp->cfg.local.port);
|
||||
if (!xs) {
|
||||
LOGP(DLSCCP, LOGL_ERROR, "%s: Requesting an SCCP simple client on ASP %s configured "
|
||||
"with 'transport-role server' but no matching xUA server was configured!\n",
|
||||
name, asp->cfg.name);
|
||||
goto out_asp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Restart ASP */
|
||||
if (prot != OSMO_SS7_ASP_PROT_IPA)
|
||||
|
@ -657,10 +726,10 @@ out_ss7:
|
|||
* \param[in] name human readable name
|
||||
* \param[in] default_pc pointcode to be used on missing VTY setting
|
||||
* \param[in] prot protocol to be used (e.g OSMO_SS7_ASP_PROT_M3UA)
|
||||
* \param[in] default_local_port local port to be usd on missing VTY setting
|
||||
* \param[in] default_local_ip local IP-address to be usd on missing VTY setting
|
||||
* \param[in] default_remote_port remote port to be usd on missing VTY setting
|
||||
* \param[in] default_remote_ip remote IP-address to be usd on missing VTY setting
|
||||
* \param[in] default_local_port local port to be used on missing VTY setting
|
||||
* \param[in] default_local_ip local IP-address to be used on missing VTY setting
|
||||
* \param[in] default_remote_port remote port to be used on missing VTY setting
|
||||
* \param[in] default_remote_ip remote IP-address to be used on missing VTY setting
|
||||
* \returns callee-allocated SCCP instance on success; NULL on error */
|
||||
struct osmo_sccp_instance *
|
||||
osmo_sccp_simple_client(void *ctx, const char *name, uint32_t default_pc,
|
||||
|
@ -690,8 +759,11 @@ osmo_sccp_simple_server_on_ss7_id(void *ctx, uint32_t ss7_id, uint32_t pc,
|
|||
{
|
||||
struct osmo_ss7_instance *ss7;
|
||||
struct osmo_xua_server *xs;
|
||||
int trans_proto;
|
||||
int rc;
|
||||
|
||||
trans_proto = ss7_default_trans_proto_for_asp_proto(prot);
|
||||
|
||||
if (local_port < 0)
|
||||
local_port = osmo_ss7_asp_protocol_port(prot);
|
||||
|
||||
|
@ -701,7 +773,7 @@ osmo_sccp_simple_server_on_ss7_id(void *ctx, uint32_t ss7_id, uint32_t pc,
|
|||
return NULL;
|
||||
ss7->cfg.primary_pc = pc;
|
||||
|
||||
xs = osmo_ss7_xua_server_create(ss7, prot, local_port, local_ip);
|
||||
xs = osmo_ss7_xua_server_create2(ss7, trans_proto, prot, local_port, local_ip);
|
||||
if (!xs)
|
||||
goto out_ss7;
|
||||
|
||||
|
@ -746,6 +818,9 @@ osmo_sccp_simple_server_add_clnt(struct osmo_sccp_instance *inst,
|
|||
struct osmo_ss7_asp *asp;
|
||||
struct osmo_xua_server *oxs;
|
||||
char *as_name, *asp_name;
|
||||
int trans_proto;
|
||||
|
||||
trans_proto = ss7_default_trans_proto_for_asp_proto(prot);
|
||||
|
||||
if (local_port < 0)
|
||||
local_port = osmo_ss7_asp_protocol_port(prot);
|
||||
|
@ -766,10 +841,12 @@ osmo_sccp_simple_server_add_clnt(struct osmo_sccp_instance *inst,
|
|||
if (!rt)
|
||||
goto out_as;
|
||||
|
||||
asp = osmo_ss7_asp_find_or_create(ss7, asp_name, remote_port, local_port, prot);
|
||||
asp = osmo_ss7_asp_find_or_create2(ss7, asp_name,
|
||||
remote_port, local_port,
|
||||
trans_proto, prot);
|
||||
if (!asp)
|
||||
goto out_rt;
|
||||
oxs = osmo_ss7_xua_server_find(ss7, prot, local_port);
|
||||
oxs = osmo_ss7_xua_server_find2(ss7, asp->cfg.trans_proto, prot, local_port);
|
||||
if (!oxs)
|
||||
goto out_asp;
|
||||
if (osmo_ss7_asp_peer_set_hosts(&asp->cfg.local, asp,
|
||||
|
@ -800,6 +877,24 @@ out_strings:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*! Adjust the upper bound for the optional data length (the payload) for CR, CC, CREF and RLSD messages.
|
||||
* For any Optional Data part larger than this value in octets, send CR, CC, CREF and RLSD messages without any payload,
|
||||
* and send the data payload in a separate Data Form 1 message. ITU-T Q.713 sections 4.2 thru 4.5 define a limit of 130
|
||||
* bytes for the 'Data' parameter. This limit can be adjusted here. May be useful for interop with nonstandard SCCP
|
||||
* peers.
|
||||
* \param[in] sccp SCCP instance to reconfigure.
|
||||
* \param[in] val Number of bytes to set as upper bound for the optional data length, or pass a negative value to set
|
||||
* the standard value of SCCP_MAX_OPTIONAL_DATA == 130, which conforms to ITU-T Q.713.
|
||||
*/
|
||||
void osmo_sccp_set_max_optional_data(struct osmo_sccp_instance *inst, int val)
|
||||
{
|
||||
if (!inst)
|
||||
return;
|
||||
if (val < 0)
|
||||
val = SCCP_MAX_OPTIONAL_DATA;
|
||||
inst->max_optional_data = val;
|
||||
}
|
||||
|
||||
/*! \brief get the SS7 instance that is related to the given SCCP instance
|
||||
* \param[in] sccp SCCP instance
|
||||
* \returns SS7 instance; NULL if sccp was NULL */
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
#include <osmocom/sigtran/protocol/mtp.h>
|
||||
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
|
||||
#include "xua_internal.h"
|
||||
#include "sccp_internal.h"
|
||||
|
||||
|
@ -143,14 +145,13 @@ DEFUN(show_sccp_connections, show_sccp_connections_cmd,
|
|||
|
||||
/* sccp-timer <name> <1-999999>
|
||||
* (cmdstr and doc are dynamically generated from osmo_sccp_timer_names.) */
|
||||
DEFUN(sccp_timer, sccp_timer_cmd,
|
||||
NULL, NULL)
|
||||
DEFUN_ATTR(sccp_timer, sccp_timer_cmd,
|
||||
NULL, NULL, CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
struct osmo_ss7_instance *ss7 = vty->index;
|
||||
enum osmo_sccp_timer timer = get_string_value(osmo_sccp_timer_names, argv[0]);
|
||||
struct osmo_sccp_timer_val set_val = { .s = atoi(argv[1]) };
|
||||
|
||||
if (timer < 0 || timer >= OSMO_SCCP_TIMERS_COUNT) {
|
||||
if (timer <= 0 || timer >= OSMO_SCCP_TIMERS_LEN) {
|
||||
vty_out(vty, "%% Invalid timer: %s%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
@ -161,16 +162,39 @@ DEFUN(sccp_timer, sccp_timer_cmd,
|
|||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
ss7->sccp->timers[timer] = set_val;
|
||||
OSMO_ASSERT(ss7->sccp->tdefs);
|
||||
osmo_tdef_set(ss7->sccp->tdefs, timer, atoi(argv[1]), OSMO_TDEF_S);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static const char *osmo_sccp_timer_val_name(const struct osmo_sccp_timer_val *val)
|
||||
DEFUN_ATTR(sccp_max_optional_data, sccp_max_optional_data_cmd,
|
||||
"sccp max-optional-data (<0-999999>|standard)",
|
||||
"Configure SCCP behavior\n"
|
||||
"Adjust the upper bound for the optional data length (the payload) for CR, CC, CREF and RLSD messages."
|
||||
" For any Optional Data part larger than this value in octets, send CR, CC, CREF and RLSD"
|
||||
" messages without any payload, and send the data payload in a separate Data Form 1 message."
|
||||
" ITU-T Q.713 sections 4.2 thru 4.5 define a limit of 130 bytes for the 'Data' parameter. This limit can be"
|
||||
" adjusted here. May be useful for interop with nonstandard SCCP peers.\n"
|
||||
"Set a non-standard maximum allowed number of bytes\n"
|
||||
"Use the ITU-T Q.713 4.2 to 4.5 standard value of 130\n",
|
||||
CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
static char buf[16];
|
||||
struct osmo_ss7_instance *ss7 = vty->index;
|
||||
int val;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%u", val->s);
|
||||
return buf;
|
||||
if (!strcmp(argv[0], "standard"))
|
||||
val = -1;
|
||||
else
|
||||
val = atoi(argv[0]);
|
||||
|
||||
osmo_ss7_ensure_sccp(ss7);
|
||||
if (!ss7->sccp) {
|
||||
vty_out(vty, "%% Error: cannot instantiate SCCP instance%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
osmo_sccp_set_max_optional_data(ss7->sccp, val);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void gen_sccp_timer_cmd_strs(struct cmd_element *cmd)
|
||||
|
@ -187,19 +211,19 @@ static void gen_sccp_timer_cmd_strs(struct cmd_element *cmd)
|
|||
"Configure SCCP timer values, see ITU-T Q.714\n");
|
||||
|
||||
for (i = 0; osmo_sccp_timer_names[i].str; i++) {
|
||||
const struct osmo_sccp_timer_val *def;
|
||||
const struct osmo_tdef *def;
|
||||
enum osmo_sccp_timer timer;
|
||||
|
||||
timer = osmo_sccp_timer_names[i].value;
|
||||
def = &osmo_sccp_timer_defaults[timer];
|
||||
OSMO_ASSERT(timer >= 0 && timer < OSMO_SCCP_TIMERS_COUNT);
|
||||
def = osmo_tdef_get_entry((struct osmo_tdef *)&osmo_sccp_timer_defaults, timer);
|
||||
OSMO_ASSERT(def);
|
||||
|
||||
osmo_talloc_asprintf(tall_vty_ctx, cmd_str, "%s%s",
|
||||
i ? "|" : "",
|
||||
osmo_sccp_timer_name(timer));
|
||||
osmo_talloc_asprintf(tall_vty_ctx, doc_str, "%s (default: %s)\n",
|
||||
osmo_sccp_timer_description(timer),
|
||||
osmo_sccp_timer_val_name(def));
|
||||
osmo_sccp_timer_names[i].str);
|
||||
osmo_talloc_asprintf(tall_vty_ctx, doc_str, "%s (default: %lu)\n",
|
||||
def->desc,
|
||||
def->default_val);
|
||||
}
|
||||
|
||||
osmo_talloc_asprintf(tall_vty_ctx, cmd_str, ") <1-999999>");
|
||||
|
@ -215,18 +239,22 @@ static void write_sccp_timers(struct vty *vty, const char *indent,
|
|||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(inst->timers); i++) {
|
||||
const struct osmo_sccp_timer_val *val = osmo_sccp_timer_get(inst, i, default_if_unset);
|
||||
if (!val)
|
||||
for (i = 0; osmo_sccp_timer_names[i].str; i++) {
|
||||
const struct osmo_tdef *tdef = osmo_tdef_get_entry(inst->tdefs, osmo_sccp_timer_names[i].value);
|
||||
if (!tdef)
|
||||
continue;
|
||||
vty_out(vty, "%ssccp-timer %s %s%s", indent, osmo_sccp_timer_name(i),
|
||||
osmo_sccp_timer_val_name(val), VTY_NEWLINE);
|
||||
if (!default_if_unset && tdef->val == tdef->default_val)
|
||||
continue;
|
||||
vty_out(vty, "%ssccp-timer %s %lu%s", indent, osmo_sccp_timer_names[i].str,
|
||||
tdef->val, VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
|
||||
void osmo_sccp_vty_write_cs7_node(struct vty *vty, const char *indent, struct osmo_sccp_instance *inst)
|
||||
{
|
||||
write_sccp_timers(vty, indent, inst, false);
|
||||
if (inst->max_optional_data != SCCP_MAX_OPTIONAL_DATA)
|
||||
vty_out(vty, "%ssccp max-optional-data %u%s", indent, inst->max_optional_data, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
DEFUN(show_sccp_timers, show_sccp_timers_cmd,
|
||||
|
@ -255,11 +283,12 @@ DEFUN(show_sccp_timers, show_sccp_timers_cmd,
|
|||
|
||||
void osmo_sccp_vty_init(void)
|
||||
{
|
||||
install_element_ve(&show_sccp_users_cmd);
|
||||
install_element_ve(&show_sccp_user_ssn_cmd);
|
||||
install_element_ve(&show_sccp_connections_cmd);
|
||||
install_lib_element_ve(&show_sccp_users_cmd);
|
||||
install_lib_element_ve(&show_sccp_user_ssn_cmd);
|
||||
install_lib_element_ve(&show_sccp_connections_cmd);
|
||||
|
||||
install_element_ve(&show_sccp_timers_cmd);
|
||||
install_lib_element_ve(&show_sccp_timers_cmd);
|
||||
gen_sccp_timer_cmd_strs(&sccp_timer_cmd);
|
||||
install_element(L_CS7_NODE, &sccp_timer_cmd);
|
||||
install_lib_element(L_CS7_NODE, &sccp_timer_cmd);
|
||||
install_lib_element(L_CS7_NODE, &sccp_max_optional_data_cmd);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
/* Internal header used by libosmo-sccp, not available publicly for lib users */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
|
||||
extern bool ss7_initialized;
|
||||
uint32_t ss7_find_free_l_rk_id(struct osmo_ss7_instance *inst);
|
||||
|
||||
bool ss7_ipv6_sctp_supported(const char *host, bool bind);
|
||||
|
||||
struct osmo_ss7_as *ss7_as_alloc(struct osmo_ss7_instance *inst, const char *name,
|
||||
enum osmo_ss7_asp_protocol proto);
|
||||
|
||||
struct osmo_ss7_asp *ss7_asp_alloc(struct osmo_ss7_instance *inst, const char *name,
|
||||
uint16_t remote_port, uint16_t local_port,
|
||||
int trans_proto, enum osmo_ss7_asp_protocol proto);
|
||||
bool ss7_asp_set_default_peer_hosts(struct osmo_ss7_asp *asp);
|
||||
bool ss7_asp_is_started(const struct osmo_ss7_asp *asp);
|
||||
int ss7_asp_get_fd(const struct osmo_ss7_asp *asp);
|
||||
struct osmo_ss7_asp *ss7_asp_find_by_socket_addr(int fd, int trans_proto);
|
||||
|
||||
bool ss7_asp_protocol_check_trans_proto(enum osmo_ss7_asp_protocol proto, int trans_proto);
|
||||
int ss7_default_trans_proto_for_asp_proto(enum osmo_ss7_asp_protocol proto);
|
||||
int ss7_asp_ipa_srv_conn_rx_cb(struct osmo_stream_srv *conn, int res, struct msgb *msg);
|
||||
int ss7_asp_xua_srv_conn_rx_cb(struct osmo_stream_srv *conn, int res, struct msgb *msg);
|
||||
int ss7_asp_m3ua_tcp_srv_conn_rx_cb(struct osmo_stream_srv *conn, int res, struct msgb *msg);
|
||||
int ss7_asp_xua_srv_conn_closed_cb(struct osmo_stream_srv *srv);
|
||||
int ss7_asp_apply_peer_primary_address(const struct osmo_ss7_asp *asp);
|
||||
int ss7_asp_apply_primary_address(const struct osmo_ss7_asp *asp);
|
||||
int ss7_asp_apply_new_local_address(const struct osmo_ss7_asp *asp, unsigned int loc_idx);
|
||||
int ss7_asp_apply_drop_local_address(const struct osmo_ss7_asp *asp, unsigned int loc_idx);
|
||||
|
||||
bool ss7_asp_peer_match_host(const struct osmo_ss7_asp_peer *peer, const char *host, bool host_is_v6);
|
||||
int ss7_asp_peer_find_host(const struct osmo_ss7_asp_peer *peer, const char *host);
|
||||
|
||||
bool ss7_xua_server_set_default_local_hosts(struct osmo_xua_server *oxs);
|
||||
|
||||
int xua_tcp_segmentation_cb(struct msgb *msg);
|
||||
|
||||
enum ss7_as_ctr {
|
||||
SS7_AS_CTR_RX_MSU_TOTAL,
|
||||
SS7_AS_CTR_TX_MSU_TOTAL,
|
||||
};
|
||||
|
||||
enum ss7_asp_ctr {
|
||||
SS7_ASP_CTR_PKT_RX_TOTAL,
|
||||
SS7_ASP_CTR_PKT_RX_UNKNOWN,
|
||||
SS7_ASP_CTR_PKT_TX_TOTAL,
|
||||
};
|
286
src/sua.c
286
src/sua.c
|
@ -1,6 +1,6 @@
|
|||
/* Minimal implementation of RFC 3868 - SCCP User Adaptation Layer */
|
||||
|
||||
/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
|
||||
/* (C) 2015-2021 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
|
@ -46,6 +46,7 @@
|
|||
#include "xua_asp_fsm.h"
|
||||
#include "xua_internal.h"
|
||||
#include "sccp_internal.h"
|
||||
#include "ss7_internal.h"
|
||||
|
||||
/* Appendix C.4 of Q.714 (all in milliseconds) */
|
||||
#define CONNECTION_TIMER ( 1 * 60 * 100)
|
||||
|
@ -179,13 +180,15 @@ static const uint16_t coit_mand_ies[] = {
|
|||
SUA_IEI_ROUTE_CTX, SUA_IEI_PROTO_CLASS, SUA_IEI_SRC_REF,
|
||||
SUA_IEI_DEST_REF, 0
|
||||
};
|
||||
|
||||
/* ITU-T Rec Q.713 */
|
||||
static const struct value_string sua_co_msgt_names[] = {
|
||||
{ SUA_CO_CODT, "CODT" },
|
||||
{ SUA_CO_CODA, "CODA" },
|
||||
{ SUA_CO_CORE, "CORE" },
|
||||
{ SUA_CO_COAK, "COAK" },
|
||||
{ SUA_CO_COREF, "COREF" },
|
||||
{ SUA_CO_RELRE, "RELRE" },
|
||||
{ SUA_CO_CORE, "CORE" }, /* §4.2 Connection request (CR) */
|
||||
{ SUA_CO_COAK, "COAK" }, /* §4.3 Connection confirm (CC) */
|
||||
{ SUA_CO_COREF, "COREF" }, /* §4.4 Connection refused (CREF) */
|
||||
{ SUA_CO_RELRE, "RELRE" }, /* §4.5 Released (RLSD) */
|
||||
{ SUA_CO_RELCO, "RELCO" },
|
||||
{ SUA_CO_RESRE, "RESRE" },
|
||||
{ SUA_CO_RESCO, "RESCO" },
|
||||
|
@ -257,7 +260,7 @@ static struct xua_msg *sua_gen_error_msg(uint32_t err_code, struct msgb *msg)
|
|||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Transmitting SUA messsages to SCTP
|
||||
* Transmitting SUA messages to SCTP
|
||||
***********************************************************************/
|
||||
|
||||
static struct msgb *sua_to_msg(struct xua_msg *xua)
|
||||
|
@ -315,7 +318,7 @@ int sua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua)
|
|||
return -1;
|
||||
|
||||
/* send the msg to the AS for transmission. The AS FSM might
|
||||
* (depending on its state) enqueue it before trnsmission */
|
||||
* (depending on its state) enqueue it before transmission */
|
||||
rc = osmo_fsm_inst_dispatch(as->fi, XUA_AS_E_TRANSFER_REQ, msg);
|
||||
if (rc < 0)
|
||||
msgb_free(msg);
|
||||
|
@ -323,7 +326,7 @@ int sua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua)
|
|||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Receiving SUA messsages from SCTP
|
||||
* Receiving SUA messages from SCTP
|
||||
***********************************************************************/
|
||||
|
||||
/*! \brief Decode SUA Global Title according to RFC3868 Section 3.10.2.3
|
||||
|
@ -426,7 +429,7 @@ int sua_addr_parse_part(struct osmo_sccp_addr *out,
|
|||
* probably want to have a separate general parsing function storing
|
||||
* the subparts in xua_msg_part. But before we do, we should find more
|
||||
* users of this subpart parsing and be aware of the performance
|
||||
* tradeoff.
|
||||
* trade-off.
|
||||
*/
|
||||
|
||||
while (pos + sizeof(*par) < param->len) {
|
||||
|
@ -467,6 +470,13 @@ int sua_addr_parse_part(struct osmo_sccp_addr *out,
|
|||
out->ip.v4.s_addr = *p32;
|
||||
out->presence |= OSMO_SCCP_ADDR_T_IPv4;
|
||||
break;
|
||||
case SUA_IEI_IPv6:
|
||||
if (par_datalen != 16)
|
||||
goto subpar_fail;
|
||||
/* no endian conversion, both network order */
|
||||
memcpy(&out->ip.v6, par->data, 16);
|
||||
out->presence |= OSMO_SCCP_ADDR_T_IPv6;
|
||||
break;
|
||||
default:
|
||||
LOGP(DLSUA, LOGL_ERROR, "SUA IEI 0x%04x: Unknown subpart tag %hd\n",
|
||||
param->tag, par_tag);
|
||||
|
@ -503,10 +513,19 @@ int sua_addr_parse(struct osmo_sccp_addr *out, struct xua_msg *xua, uint16_t iei
|
|||
/* connectionless messages received from socket */
|
||||
static int sua_rx_cl(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
||||
{
|
||||
struct xua_msg_part *rctx_ie = xua_msg_find_tag(xua, SUA_IEI_ROUTE_CTX);
|
||||
struct osmo_sccp_instance *inst = asp->inst->sccp;
|
||||
struct osmo_ss7_as *as;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(xua->hdr.msg_class == SUA_MSGC_CL);
|
||||
|
||||
rc = xua_find_as_for_asp(&as, asp, rctx_ie);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rate_ctr_inc2(as->ctrg, SS7_AS_CTR_RX_MSU_TOTAL);
|
||||
|
||||
switch (xua->hdr.msg_type) {
|
||||
case 0: /* Reserved, permitted by ETSI TS 101 592 5.2.3.2 */
|
||||
case SUA_CL_CLDT:
|
||||
|
@ -524,10 +543,19 @@ static int sua_rx_cl(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
|||
/* connection-oriented messages received from socket */
|
||||
static int sua_rx_co(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
||||
{
|
||||
struct xua_msg_part *rctx_ie = xua_msg_find_tag(xua, SUA_IEI_ROUTE_CTX);
|
||||
struct osmo_sccp_instance *inst = asp->inst->sccp;
|
||||
struct osmo_ss7_as *as;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(xua->hdr.msg_class == SUA_MSGC_CO);
|
||||
|
||||
rc = xua_find_as_for_asp(&as, asp, rctx_ie);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rate_ctr_inc2(as->ctrg, SS7_AS_CTR_RX_MSU_TOTAL);
|
||||
|
||||
switch (xua->hdr.msg_type) {
|
||||
case 0: /* Reserved, permitted by ETSI TS 101 592 5.2.3.2 */
|
||||
case SUA_CO_CORE:
|
||||
|
@ -593,7 +621,7 @@ static int sua_rx_mgmt_ntfy(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
|||
if (ntfy.info_string)
|
||||
talloc_free(ntfy.info_string);
|
||||
|
||||
/* TODO: should we report this soemwhere? */
|
||||
/* TODO: should we report this somewhere? */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -640,6 +668,8 @@ static int sua_rx_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sua_rx_snm(struct osmo_ss7_asp *asp, struct xua_msg *xua);
|
||||
|
||||
/*! \brief process SUA message received from socket
|
||||
* \param[in] asp Application Server Process receiving \ref msg
|
||||
* \param[in] msg received message buffer
|
||||
|
@ -652,7 +682,7 @@ int sua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
|
|||
OSMO_ASSERT(asp->cfg.proto == OSMO_SS7_ASP_PROT_SUA);
|
||||
|
||||
/* caller owns msg memory, we shall neither free it here nor
|
||||
* keep references beyon the execution of this function and its
|
||||
* keep references beyond the execution of this function and its
|
||||
* callees. */
|
||||
|
||||
if (!asp->inst->sccp) {
|
||||
|
@ -715,6 +745,8 @@ int sua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
|
|||
rc = sua_rx_mgmt(asp, xua);
|
||||
break;
|
||||
case SUA_MSGC_SNM:
|
||||
rc = sua_rx_snm(asp, xua);
|
||||
break;
|
||||
case SUA_MSGC_RKM:
|
||||
/* FIXME */
|
||||
LOGPASP(asp, DLSUA, LOGL_NOTICE, "Received unsupported SUA "
|
||||
|
@ -740,3 +772,235 @@ out:
|
|||
return rc;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* SSNM msg generation
|
||||
***********************************************************************/
|
||||
|
||||
/* 3.4.1 Destination Unavailable (DUNA) */
|
||||
static struct xua_msg *sua_encode_duna(const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc,
|
||||
const uint32_t *ssn, const uint32_t *smi, const char *info_string)
|
||||
{
|
||||
struct xua_msg *xua = xua_msg_alloc();
|
||||
|
||||
xua->hdr = XUA_HDR(SUA_MSGC_SNM, SUA_SNM_DUNA);
|
||||
xua->hdr.version = SUA_VERSION;
|
||||
if (rctx && num_rctx)
|
||||
xua_msg_add_data(xua, SUA_IEI_ROUTE_CTX, num_rctx * 4, (const uint8_t *)rctx);
|
||||
|
||||
xua_msg_add_data(xua, SUA_IEI_AFFECTED_PC, num_aff_pc * 4, (const uint8_t *) aff_pc);
|
||||
|
||||
if (ssn)
|
||||
xua_msg_add_u32(xua, SUA_IEI_SSN, *ssn);
|
||||
|
||||
if (smi)
|
||||
xua_msg_add_u32(xua, SUA_IEI_SSN, *smi);
|
||||
|
||||
if (info_string) {
|
||||
xua_msg_add_data(xua, SUA_IEI_INFO_STRING,
|
||||
strlen(info_string)+1,
|
||||
(const uint8_t *) info_string);
|
||||
}
|
||||
return xua;
|
||||
}
|
||||
|
||||
/* 3.4.2 Destination Available (DAVA) */
|
||||
static struct xua_msg *sua_encode_dava(const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc,
|
||||
const uint32_t *ssn, const uint32_t *smi, const char *info_string)
|
||||
{
|
||||
/* encoding is exactly identical to DUNA */
|
||||
struct xua_msg *xua = sua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, ssn, smi, info_string);
|
||||
if (xua)
|
||||
xua->hdr.msg_type = SUA_SNM_DAVA;
|
||||
return xua;
|
||||
}
|
||||
|
||||
#if 0 /* not used so far */
|
||||
/* 3.4.3 Destination Available (DAUD) */
|
||||
static struct xua_msg *sua_encode_daud(const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc,
|
||||
const uint32_t *ssn, const uint32_t *smi, const char *info_string)
|
||||
{
|
||||
/* encoding is exactly identical to DUNA */
|
||||
struct xua_msg *xua = sua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, ssn, smi, info_string);
|
||||
if (xua)
|
||||
xua->hdr.msg_type = SUA_SNM_DAUD;
|
||||
return xua;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 3.4.5 Destination User Part Unavailable (DUPU) */
|
||||
static struct xua_msg *sua_encode_dupu(const uint32_t *rctx, unsigned int num_rctx,
|
||||
uint32_t dpc, uint16_t user, uint16_t cause,
|
||||
const char *info_string)
|
||||
{
|
||||
struct xua_msg *xua = xua_msg_alloc();
|
||||
uint32_t user_cause = (user << 16) | cause;
|
||||
|
||||
xua->hdr = XUA_HDR(SUA_MSGC_SNM, SUA_SNM_DUNA);
|
||||
xua->hdr.version = SUA_VERSION;
|
||||
if (rctx && num_rctx)
|
||||
xua_msg_add_data(xua, SUA_IEI_ROUTE_CTX, num_rctx * sizeof(*rctx), (const uint8_t *)rctx);
|
||||
|
||||
xua_msg_add_u32(xua, SUA_IEI_AFFECTED_PC, dpc);
|
||||
xua_msg_add_u32(xua, SUA_IEI_USER_CAUSE, user_cause);
|
||||
|
||||
if (info_string) {
|
||||
xua_msg_add_data(xua, SUA_IEI_INFO_STRING, strlen(info_string)+1,
|
||||
(const uint8_t *) info_string);
|
||||
}
|
||||
return xua;
|
||||
}
|
||||
|
||||
/*! Transmit SSNM DUNA/DAVA message indicating [un]availability of certain point code[s]
|
||||
* \param[in] asp ASP through whihc to transmit message. Must be ACTIVE.
|
||||
* \param[in] rctx array of Routing Contexts in network byte order.
|
||||
* \param[in] num_rctx number of rctx
|
||||
* \param[in] aff_pc array of 'Affected Point Code' in network byte order.
|
||||
* \param[in] num_aff_pc number of aff_pc
|
||||
* \param[in] aff_ssn affected SSN (optional)
|
||||
* \param[in] smi subsystem multiplicity indicator (optional)
|
||||
* \param[in] info_string optional information strng (can be NULL).
|
||||
* \param[in] available are aff_pc now available (true) or unavailable (false) */
|
||||
void sua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc, const uint32_t *aff_ssn,
|
||||
const uint32_t *smi, const char *info_string, bool available)
|
||||
{
|
||||
struct xua_msg *xua;
|
||||
|
||||
if (available)
|
||||
xua = sua_encode_dava(rctx, num_rctx, aff_pc, num_aff_pc, aff_ssn, smi, info_string);
|
||||
else
|
||||
xua = sua_encode_duna(rctx, num_rctx, aff_pc, num_aff_pc, aff_ssn, smi, info_string);
|
||||
|
||||
sua_tx_xua_asp(asp, xua);
|
||||
}
|
||||
|
||||
|
||||
/*! Transmit SSNM SCON message indicating congestion
|
||||
* \param[in] asp ASP through which to transmit message. Must be ACTIVE.
|
||||
* \param[in] rctx array of Routing Contexts in network byte order.
|
||||
* \param[in] num_rctx number of rctx
|
||||
* \param[in] aff_pc array of 'Affected Point Code' in network byte order.
|
||||
* \param[in] num_aff_pc number of aff_pc
|
||||
* \param[in] ssn optional SSN (can be NULL)
|
||||
* \param[in] cong_level optional congestion level (can be NULL)
|
||||
* \param[in] info_string optional information string (can be NULL). */
|
||||
void sua_tx_snm_congestion(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc, const uint32_t *ssn,
|
||||
const uint8_t cong_level, const char *info_string)
|
||||
{
|
||||
struct xua_msg *xua = xua_msg_alloc();
|
||||
|
||||
xua->hdr = XUA_HDR(SUA_MSGC_SNM, SUA_SNM_SCON);
|
||||
xua->hdr.version = SUA_VERSION;
|
||||
if (rctx && num_rctx)
|
||||
xua_msg_add_data(xua, SUA_IEI_ROUTE_CTX, num_rctx * sizeof(*rctx), (const uint8_t *)rctx);
|
||||
xua_msg_add_data(xua, SUA_IEI_AFFECTED_PC, num_aff_pc * sizeof(*aff_pc), (const uint8_t *) aff_pc);
|
||||
if (ssn)
|
||||
xua_msg_add_u32(xua, SUA_IEI_SSN, *ssn);
|
||||
xua_msg_add_u32(xua, SUA_IEI_CONG_LEVEL, cong_level);
|
||||
if (info_string)
|
||||
xua_msg_add_data(xua, SUA_IEI_INFO_STRING, strlen(info_string)+1, (const uint8_t *) info_string);
|
||||
}
|
||||
|
||||
|
||||
/*! Transmit SSNM DUPU message indicating user unavailability.
|
||||
* \param[in] asp ASP through which to transmit message. Must be ACTIVE.
|
||||
* \param[in] rctx array of Routing Contexts in network byte order.
|
||||
* \param[in] num_rctx number of rctx
|
||||
* \param[in] dpc affected point code
|
||||
* \param[in] user the user (SI) that is unavailable
|
||||
* \param[in] cause the cause of the user unavailability
|
||||
* \param[in] info_string optional information string (can be NULL). */
|
||||
void sua_tx_dupu(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
uint32_t dpc, uint16_t user, uint16_t cause, const char *info_str)
|
||||
{
|
||||
struct xua_msg *xua = sua_encode_dupu(rctx, num_rctx, dpc, user, cause, info_str);
|
||||
sua_tx_xua_asp(asp, xua);
|
||||
}
|
||||
|
||||
/* received SNM message on ASP side */
|
||||
static int sua_rx_snm_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
||||
{
|
||||
struct osmo_ss7_as *as = NULL;
|
||||
struct xua_msg_part *rctx_ie = xua_msg_find_tag(xua, SUA_IEI_ROUTE_CTX);
|
||||
int rc;
|
||||
|
||||
rc = xua_find_as_for_asp(&as, asp, rctx_ie);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
switch (xua->hdr.msg_type) {
|
||||
case SUA_SNM_DUNA:
|
||||
xua_snm_rx_duna(asp, as, xua);
|
||||
break;
|
||||
case SUA_SNM_DAVA:
|
||||
xua_snm_rx_dava(asp, as, xua);
|
||||
break;
|
||||
case SUA_SNM_DUPU:
|
||||
xua_snm_rx_dupu(asp, as, xua);
|
||||
break;
|
||||
case SUA_SNM_SCON:
|
||||
case SUA_SNM_DRST:
|
||||
LOGPASP(asp, DLSUA, LOGL_NOTICE, "Received unsupported SUA SNM message type %u\n",
|
||||
xua->hdr.msg_type);
|
||||
/* silently ignore those to not confuse the sender */
|
||||
break;
|
||||
case SUA_SNM_DAUD:
|
||||
/* RFC states only permitted in ASP->SG direction, not reverse. But some
|
||||
* equipment still sends it to us as ASP ?!? */
|
||||
if (asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP) {
|
||||
LOGPASP(asp, DLSUA, LOGL_NOTICE, "quirk daud_in_asp active: Accepting DAUD "
|
||||
"despite being in ASP role\n");
|
||||
xua_snm_rx_daud(asp, xua);
|
||||
} else {
|
||||
LOGPASP(asp, DLSUA, LOGL_ERROR, "DAUD not permitted in ASP role\n");
|
||||
return SUA_ERR_UNSUPP_MSG_TYPE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return SUA_ERR_UNSUPP_MSG_TYPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* received SNM message on SG side */
|
||||
static int sua_rx_snm_sg(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
||||
{
|
||||
switch (xua->hdr.msg_type) {
|
||||
case SUA_SNM_DAUD: /* Audit: ASP inquires about availability of Point Codes */
|
||||
xua_snm_rx_daud(asp, xua);
|
||||
break;
|
||||
default:
|
||||
return SUA_ERR_UNSUPP_MSG_TYPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sua_rx_snm(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
||||
{
|
||||
/* SNM only permitted in ACTIVE state */
|
||||
if (asp->fi->state != XUA_ASP_S_ACTIVE) {
|
||||
if (asp->fi->state == XUA_ASP_S_INACTIVE && asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_SNM_INACTIVE) {
|
||||
LOGPASP(asp, DLSUA, LOGL_NOTICE, "quirk snm_inactive active: "
|
||||
"Accepting SSNM in state %s\n", osmo_fsm_inst_state_name(asp->fi));
|
||||
} else {
|
||||
LOGPASP(asp, DLM3UA, LOGL_ERROR, "Rx SUA SSNM not permitted "
|
||||
"while ASP in state %s\n", osmo_fsm_inst_state_name(asp->fi));
|
||||
return SUA_ERR_UNEXPECTED_MSG;
|
||||
}
|
||||
}
|
||||
|
||||
switch (asp->cfg.role) {
|
||||
case OSMO_SS7_ASP_ROLE_SG:
|
||||
return sua_rx_snm_sg(asp, xua);
|
||||
case OSMO_SS7_ASP_ROLE_ASP:
|
||||
return sua_rx_snm_asp(asp, xua);
|
||||
default:
|
||||
return SUA_ERR_UNSUPP_MSG_CLASS;
|
||||
}
|
||||
}
|
||||
|
|
107
src/xua_as_fsm.c
107
src/xua_as_fsm.c
|
@ -75,6 +75,26 @@ static int as_notify_all_asp(struct osmo_ss7_as *as, struct osmo_xlm_prim_notify
|
|||
return sent;
|
||||
}
|
||||
|
||||
/* determine which role (SG/ASP/IPSP) we operate in */
|
||||
static int get_local_role(struct osmo_ss7_as *as)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* this is a bit tricky. "osmo_ss7_as" has no configuration of a role,
|
||||
* only the ASPs have. As they all must be of the same role, let's simply
|
||||
* find the first one and return its role */
|
||||
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
|
||||
struct osmo_ss7_asp *asp = as->cfg.asps[i];
|
||||
|
||||
if (!asp)
|
||||
continue;
|
||||
|
||||
return asp->cfg.role;
|
||||
}
|
||||
/* we don't have any ASPs in this AS? Strange */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct osmo_ss7_asp *xua_as_select_asp_override(struct osmo_ss7_as *as)
|
||||
{
|
||||
struct osmo_ss7_asp *asp;
|
||||
|
@ -138,6 +158,8 @@ int xua_as_transmit_msg(struct osmo_ss7_as *as, struct msgb *msg)
|
|||
asp = xua_as_select_asp_override(as);
|
||||
break;
|
||||
case OSMO_SS7_AS_TMOD_LOADSHARE:
|
||||
/* TODO: actually use the SLS value to ensure same SLS goes through same ASP. Not
|
||||
* strictly required by M3UA RFC, but would fit the overall principle. */
|
||||
case OSMO_SS7_AS_TMOD_ROUNDROBIN:
|
||||
asp = xua_as_select_asp_roundrobin(as);
|
||||
break;
|
||||
|
@ -180,8 +202,78 @@ struct xua_as_fsm_priv {
|
|||
struct osmo_timer_list t_r;
|
||||
struct llist_head queued_msgs;
|
||||
} recovery;
|
||||
bool ipa_route_created;
|
||||
};
|
||||
|
||||
/* is the given AS one with a single ASP of IPA type? */
|
||||
static bool is_single_ipa_asp(struct osmo_ss7_as *as)
|
||||
{
|
||||
unsigned int asp_count = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
|
||||
struct osmo_ss7_asp *asp = as->cfg.asps[i];
|
||||
if (!asp)
|
||||
continue;
|
||||
asp_count++;
|
||||
if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA)
|
||||
return false;
|
||||
}
|
||||
if (asp_count == 1)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ipa_add_route(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct xua_as_fsm_priv *xafp = (struct xua_as_fsm_priv *) fi->priv;
|
||||
struct osmo_ss7_as *as = xafp->as;
|
||||
struct osmo_ss7_instance *inst = as->inst;
|
||||
|
||||
if (osmo_ss7_route_find_dpc_mask(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff))
|
||||
return;
|
||||
|
||||
/* As opposed to M3UA, there is no RKM and we have to implicitly
|
||||
* automatically add a route once an IPA connection has come up */
|
||||
if (osmo_ss7_route_create(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff, as->cfg.name))
|
||||
xafp->ipa_route_created = true;
|
||||
}
|
||||
|
||||
static void ipa_del_route(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct xua_as_fsm_priv *xafp = (struct xua_as_fsm_priv *) fi->priv;
|
||||
struct osmo_ss7_as *as = xafp->as;
|
||||
struct osmo_ss7_instance *inst = as->inst;
|
||||
struct osmo_ss7_route *rt;
|
||||
|
||||
/* don't delete a route if we added none */
|
||||
if (!xafp->ipa_route_created)
|
||||
return;
|
||||
|
||||
/* find the route which we have created if we ever reached ipa_asp_fsm_wait_id_ack2 */
|
||||
rt = osmo_ss7_route_find_dpc_mask(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff);
|
||||
/* no route found, bail out */
|
||||
if (!rt) {
|
||||
LOGPFSML(fi, LOGL_NOTICE, "Attempting to delete route for this IPA AS, but cannot "
|
||||
"find route for DPC %s. Did you manually delete it?\n",
|
||||
osmo_ss7_pointcode_print(inst, as->cfg.routing_key.pc));
|
||||
return;
|
||||
}
|
||||
|
||||
/* route points to different AS, bail out */
|
||||
if (rt->dest.as != as) {
|
||||
LOGPFSML(fi, LOGL_NOTICE, "Attempting to delete route for this IPA ASP, but found "
|
||||
"route for DPC %s points to different AS (%s)\n",
|
||||
osmo_ss7_pointcode_print(inst, as->cfg.routing_key.pc), rt->dest.as->cfg.name);
|
||||
return;
|
||||
}
|
||||
|
||||
osmo_ss7_route_destroy(rt);
|
||||
xafp->ipa_route_created = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* is any other ASP in this AS in state != DOWN? */
|
||||
static bool check_any_other_asp_not_down(struct osmo_ss7_as *as, struct osmo_ss7_asp *asp_cmp)
|
||||
{
|
||||
|
@ -289,12 +381,16 @@ static void xua_as_fsm_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
|
|||
npar.status_info = M3UA_NOTIFY_I_AS_INACT;
|
||||
break;
|
||||
case XUA_AS_S_ACTIVE:
|
||||
if (is_single_ipa_asp(as))
|
||||
ipa_add_route(fi);
|
||||
npar.status_info = M3UA_NOTIFY_I_AS_ACT;
|
||||
break;
|
||||
case XUA_AS_S_PENDING:
|
||||
npar.status_info = M3UA_NOTIFY_I_AS_PEND;
|
||||
break;
|
||||
case XUA_AS_S_DOWN:
|
||||
if (is_single_ipa_asp(as))
|
||||
ipa_del_route(fi);
|
||||
/* RFC4666 sec 4.3.2 AS States:
|
||||
If we end up here, it means no ASP is ACTIVE or INACTIVE,
|
||||
meaning no ASP can have already configured the traffic mode
|
||||
|
@ -318,6 +414,17 @@ static void xua_as_fsm_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
|
|||
/* TODO: ASP-Id of ASP triggering this state change */
|
||||
|
||||
as_notify_all_asp(xafp->as, &npar);
|
||||
|
||||
/* only if we are the SG, we must start broadcasting availability information
|
||||
* to everyone else */
|
||||
if (get_local_role(xafp->as) == OSMO_SS7_ASP_ROLE_SG) {
|
||||
/* advertise availability of the routing key to others */
|
||||
uint32_t aff_pc = htonl(as->cfg.routing_key.pc);
|
||||
if (old_state != XUA_AS_S_ACTIVE && fi->state == XUA_AS_S_ACTIVE)
|
||||
xua_snm_pc_available(as, &aff_pc, 1, NULL, true);
|
||||
else if (old_state == XUA_AS_S_ACTIVE && fi->state != XUA_AS_S_ACTIVE)
|
||||
xua_snm_pc_available(as, &aff_pc, 1, NULL, false);
|
||||
}
|
||||
};
|
||||
|
||||
static void xua_as_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "xua_asp_fsm.h"
|
||||
#include "xua_as_fsm.h"
|
||||
#include "xua_internal.h"
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
|
@ -64,6 +65,8 @@ static const struct value_string xua_asp_event_names[] = {
|
|||
{ XUA_ASP_E_ASPSM_BEAT, "ASPSM_BEAT" },
|
||||
{ XUA_ASP_E_ASPSM_BEAT_ACK, "ASPSM_BEAT_ACK" },
|
||||
|
||||
{ XUA_ASP_E_AS_ASSIGNED, "AS_ASSIGNED" },
|
||||
|
||||
{ IPA_ASP_E_ID_RESP, "IPA_CCM_ID_RESP" },
|
||||
{ IPA_ASP_E_ID_GET, "IPA_CCM_ID_GET" },
|
||||
{ IPA_ASP_E_ID_ACK, "IPA_CCM_ID_ACK" },
|
||||
|
@ -109,8 +112,8 @@ void xua_asp_send_xlm_prim(struct osmo_ss7_asp *asp, struct osmo_xlm_prim *prim)
|
|||
if (lm && lm->prim_cb)
|
||||
lm->prim_cb(&prim->oph, asp);
|
||||
else {
|
||||
LOGPASP(asp, DLSS7, LOGL_DEBUG, "No Layer Manager, dropping %s\n",
|
||||
osmo_xlm_prim_name(&prim->oph));
|
||||
LOGPFSML(asp->fi, LOGL_DEBUG, "No Layer Manager, dropping %s\n",
|
||||
osmo_xlm_prim_name(&prim->oph));
|
||||
}
|
||||
|
||||
msgb_free(prim->oph.msg);
|
||||
|
@ -180,8 +183,15 @@ static int xua_msg_add_asp_rctx(struct xua_msg *xua, struct osmo_ss7_asp *asp)
|
|||
}
|
||||
}
|
||||
/* add xUA IE with routing contests to the message (if any) */
|
||||
if (i)
|
||||
if (i) {
|
||||
/* bail out (and not add the IE) if there's only one routing context (and hence
|
||||
* only one AS) within this ASP, and that routing context is zero, meaning no routing
|
||||
* context IE shall be used */
|
||||
if (i == 1 && rctx[0] == 0)
|
||||
return 0;
|
||||
|
||||
xua_msg_add_data(xua, M3UA_IEI_ROUTE_CTX, i*sizeof(uint32_t), (uint8_t *)rctx);
|
||||
}
|
||||
|
||||
/* return count of routing contexts added */
|
||||
return i;
|
||||
|
@ -283,6 +293,8 @@ static int peer_send_error(struct osmo_fsm_inst *fi, uint32_t err_code)
|
|||
struct xua_msg *xua = xua_msg_alloc();
|
||||
struct msgb *msg;
|
||||
|
||||
LOGPFSML(fi, LOGL_ERROR, "Tx MGMT_ERR '%s'\n", get_value_string(m3ua_err_names, err_code));
|
||||
|
||||
xua->hdr = XUA_HDR(SUA_MSGC_MGMT, SUA_MGMT_ERR);
|
||||
xua->hdr.version = SUA_VERSION;
|
||||
xua_msg_add_u32(xua, SUA_IEI_ERR_CODE, err_code);
|
||||
|
@ -337,6 +349,21 @@ static const uint32_t evt_ack_map[_NUM_XUA_ASP_E] = {
|
|||
[XUA_ASP_E_ASPSM_BEAT] = XUA_ASP_E_ASPSM_BEAT_ACK,
|
||||
};
|
||||
|
||||
/* Helper function to dispatch an ASP->AS event to all AS of which this
|
||||
* ASP is a memmber. Ignores routing contexts for now. */
|
||||
static void dispatch_to_all_as(struct osmo_fsm_inst *fi, uint32_t event)
|
||||
{
|
||||
struct xua_asp_fsm_priv *xafp = fi->priv;
|
||||
struct osmo_ss7_asp *asp = xafp->asp;
|
||||
struct osmo_ss7_instance *inst = asp->inst;
|
||||
struct osmo_ss7_as *as;
|
||||
|
||||
llist_for_each_entry(as, &inst->as_list, list) {
|
||||
if (!osmo_ss7_as_has_asp(as, asp))
|
||||
continue;
|
||||
osmo_fsm_inst_dispatch(as->fi, event, asp);
|
||||
}
|
||||
}
|
||||
|
||||
/* check if expected message was received + stop t_ack */
|
||||
static void check_stop_t_ack(struct osmo_fsm_inst *fi, uint32_t event)
|
||||
|
@ -380,6 +407,16 @@ static void check_stop_t_ack(struct osmo_fsm_inst *fi, uint32_t event)
|
|||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
/***************
|
||||
** FSM states **
|
||||
***************/
|
||||
|
||||
static void xua_asp_fsm_down_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
dispatch_to_all_as(fi, XUA_ASPAS_ASP_DOWN_IND);
|
||||
}
|
||||
|
||||
static void xua_asp_fsm_down(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct xua_asp_fsm_priv *xafp = fi->priv;
|
||||
|
@ -435,25 +472,9 @@ static void xua_asp_fsm_down(struct osmo_fsm_inst *fi, uint32_t event, void *dat
|
|||
}
|
||||
}
|
||||
|
||||
/* Helper function to dispatch an ASP->AS event to all AS of which this
|
||||
* ASP is a memmber. Ignores routing contexts for now. */
|
||||
static void dispatch_to_all_as(struct osmo_fsm_inst *fi, uint32_t event)
|
||||
static void xua_asp_fsm_inactive_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct xua_asp_fsm_priv *xafp = fi->priv;
|
||||
struct osmo_ss7_asp *asp = xafp->asp;
|
||||
struct osmo_ss7_instance *inst = asp->inst;
|
||||
struct osmo_ss7_as *as;
|
||||
|
||||
llist_for_each_entry(as, &inst->as_list, list) {
|
||||
if (!osmo_ss7_as_has_asp(as, asp))
|
||||
continue;
|
||||
osmo_fsm_inst_dispatch(as->fi, event, asp);
|
||||
}
|
||||
}
|
||||
|
||||
static void xua_asp_fsm_down_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
dispatch_to_all_as(fi, XUA_ASPAS_ASP_DOWN_IND);
|
||||
dispatch_to_all_as(fi, XUA_ASPAS_ASP_INACTIVE_IND);
|
||||
}
|
||||
|
||||
static void xua_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
|
@ -463,7 +484,6 @@ static void xua_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void
|
|||
struct osmo_ss7_as *as;
|
||||
struct xua_msg *xua_in;
|
||||
uint32_t traf_mode = 0;
|
||||
enum osmo_ss7_as_traffic_mode tmode;
|
||||
struct xua_msg_part *part;
|
||||
int i;
|
||||
|
||||
|
@ -505,7 +525,6 @@ static void xua_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void
|
|||
peer_send_error(fi, M3UA_ERR_UNSUPP_TRAF_MOD_TYP);
|
||||
return;
|
||||
}
|
||||
tmode = osmo_ss7_tmode_from_xua(traf_mode);
|
||||
}
|
||||
if ((part = xua_msg_find_tag(xua_in, M3UA_IEI_ROUTE_CTX))) {
|
||||
for (i = 0; i < part->len / sizeof(uint32_t); i++) {
|
||||
|
@ -519,6 +538,7 @@ static void xua_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void
|
|||
}
|
||||
|
||||
if (traf_mode) { /* if the peer has specified a traffic mode at all */
|
||||
enum osmo_ss7_as_traffic_mode tmode = osmo_ss7_tmode_from_xua(traf_mode);
|
||||
llist_for_each_entry(as, &asp->inst->as_list, list) {
|
||||
if (!osmo_ss7_as_has_asp(as, asp))
|
||||
continue;
|
||||
|
@ -570,9 +590,9 @@ static void xua_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void
|
|||
}
|
||||
}
|
||||
|
||||
static void xua_asp_fsm_inactive_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
static void xua_asp_fsm_active_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
dispatch_to_all_as(fi, XUA_ASPAS_ASP_INACTIVE_IND);
|
||||
dispatch_to_all_as(fi, XUA_ASPAS_ASP_ACTIVE_IND);
|
||||
}
|
||||
|
||||
static void xua_asp_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
|
@ -651,11 +671,6 @@ static void xua_asp_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *d
|
|||
}
|
||||
}
|
||||
|
||||
static void xua_asp_fsm_active_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
dispatch_to_all_as(fi, XUA_ASPAS_ASP_ACTIVE_IND);
|
||||
}
|
||||
|
||||
static void xua_asp_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct xua_asp_fsm_priv *xafp = fi->priv;
|
||||
|
@ -676,6 +691,9 @@ static void xua_asp_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *dat
|
|||
case XUA_ASP_E_ASPSM_BEAT_ACK:
|
||||
/* FIXME: stop timer, if any */
|
||||
break;
|
||||
case XUA_ASP_E_AS_ASSIGNED:
|
||||
/* Ignore, only used in IPA asps so far. */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -751,7 +769,8 @@ struct osmo_fsm xua_asp_fsm = {
|
|||
.allstate_event_mask = S(XUA_ASP_E_SCTP_COMM_DOWN_IND) |
|
||||
S(XUA_ASP_E_SCTP_RESTART_IND) |
|
||||
S(XUA_ASP_E_ASPSM_BEAT) |
|
||||
S(XUA_ASP_E_ASPSM_BEAT_ACK),
|
||||
S(XUA_ASP_E_ASPSM_BEAT_ACK) |
|
||||
S(XUA_ASP_E_AS_ASSIGNED),
|
||||
.allstate_action = xua_asp_allstate,
|
||||
.cleanup = xua_asp_fsm_cleanup,
|
||||
};
|
||||
|
@ -837,18 +856,22 @@ enum ipa_asp_fsm_t {
|
|||
static int get_fd_from_iafp(struct ipa_asp_fsm_priv *iafp)
|
||||
{
|
||||
struct osmo_ss7_asp *asp = iafp->asp;
|
||||
struct osmo_fd *ofd;
|
||||
int fd;
|
||||
|
||||
if (asp->server)
|
||||
ofd = osmo_stream_srv_get_ofd(asp->server);
|
||||
fd = osmo_stream_srv_get_fd(asp->server);
|
||||
else if (asp->client)
|
||||
ofd = osmo_stream_cli_get_ofd(asp->client);
|
||||
fd = osmo_stream_cli_get_fd(asp->client);
|
||||
else
|
||||
return -1;
|
||||
|
||||
return ofd->fd;
|
||||
return fd;
|
||||
}
|
||||
|
||||
/***************
|
||||
** FSM states **
|
||||
***************/
|
||||
|
||||
/* Server + Client: Initial State, wait for M-ASP-UP.req */
|
||||
static void ipa_asp_fsm_down(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
|
@ -866,7 +889,7 @@ static void ipa_asp_fsm_down(struct osmo_fsm_inst *fi, uint32_t event, void *dat
|
|||
}
|
||||
} else {
|
||||
/* Client: We simply wait for an ID GET */
|
||||
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_WAIT_ID_ACK, 10, T_WAIT_ID_ACK);
|
||||
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_WAIT_ID_GET, 10, T_WAIT_ID_GET);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -938,23 +961,11 @@ out_err:
|
|||
/* Server: We're waiting for an ID ACK */
|
||||
static void ipa_asp_fsm_wait_id_ack2(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct ipa_asp_fsm_priv *iafp = fi->priv;
|
||||
struct osmo_ss7_asp *asp = iafp->asp;
|
||||
struct osmo_ss7_instance *inst = asp->inst;
|
||||
/* We use routing-context '0' here, as that's the only one we support in IPA */
|
||||
struct osmo_ss7_as *as = osmo_ss7_as_find_by_rctx(inst, 0);
|
||||
|
||||
OSMO_ASSERT(as);
|
||||
|
||||
switch (event) {
|
||||
case IPA_ASP_E_ID_ACK:
|
||||
/* ACK received, we can go to active state now. The
|
||||
* ACTIVE onenter function will inform the AS */
|
||||
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_ACTIVE, 0, 0);
|
||||
/* As opposed to M3UA, there is no RKM and we have to implicitly automatically add
|
||||
* a route once an IPA connection has come up */
|
||||
osmo_ss7_route_create(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff,
|
||||
as->cfg.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -967,6 +978,7 @@ static void ipa_asp_fsm_wait_id_get(struct osmo_fsm_inst *fi, uint32_t event, vo
|
|||
struct msgb *msg_get, *msg_resp;
|
||||
const uint8_t *req_data;
|
||||
int data_len;
|
||||
int fd;
|
||||
|
||||
switch (event) {
|
||||
case IPA_ASP_E_ID_GET:
|
||||
|
@ -989,6 +1001,16 @@ static void ipa_asp_fsm_wait_id_get(struct osmo_fsm_inst *fi, uint32_t event, vo
|
|||
osmo_ss7_asp_send(asp, msg_resp);
|
||||
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_WAIT_ID_ACK, 10, T_WAIT_ID_ACK);
|
||||
break;
|
||||
case IPA_ASP_E_ID_ACK:
|
||||
/* Some SCCPLite MSCs are known to send an ACK directly instead
|
||||
* of GET. Support them and skip the GET+RESP handshake by
|
||||
* sending ACK2 to server directly */
|
||||
fd = get_fd_from_iafp(iafp);
|
||||
if (fd >= 0) {
|
||||
ipaccess_send_id_ack(fd);
|
||||
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_ACTIVE, 0, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1010,29 +1032,10 @@ static void ipa_asp_fsm_wait_id_ack(struct osmo_fsm_inst *fi, uint32_t event, vo
|
|||
}
|
||||
}
|
||||
|
||||
static void ipa_asp_fsm_del_route(struct ipa_asp_fsm_priv *iafp)
|
||||
static void ipa_asp_fsm_active_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct osmo_ss7_asp *asp = iafp->asp;
|
||||
struct osmo_ss7_instance *inst = asp->inst;
|
||||
/* We use routing-context '0' here, as that's the only one we support in IPA */
|
||||
struct osmo_ss7_as *as = osmo_ss7_as_find_by_rctx(inst, 0);
|
||||
struct osmo_ss7_route *rt;
|
||||
|
||||
OSMO_ASSERT(as);
|
||||
|
||||
/* find the route which we have created if we ever reached ipa_asp_fsm_wait_id_ack2 */
|
||||
rt = osmo_ss7_route_find_dpc_mask(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff);
|
||||
/* no route found, bail out */
|
||||
if (!rt)
|
||||
return;
|
||||
/* route points to different AS, bail out */
|
||||
if (rt->dest.as != as)
|
||||
return;
|
||||
|
||||
osmo_ss7_route_destroy(rt);
|
||||
/* FIXME: Why don't we also delete this timer if we return early above?
|
||||
* FIXME: Where is this timer even scheduled? */
|
||||
osmo_timer_del(&iafp->pong_timer);
|
||||
dispatch_to_all_as(fi, XUA_ASPAS_ASP_INACTIVE_IND);
|
||||
dispatch_to_all_as(fi, XUA_ASPAS_ASP_ACTIVE_IND);
|
||||
}
|
||||
|
||||
/* Server + Client: We're actively transmitting user data */
|
||||
|
@ -1041,17 +1044,20 @@ static void ipa_asp_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *d
|
|||
switch (event) {
|
||||
case XUA_ASP_E_M_ASP_DOWN_REQ:
|
||||
case XUA_ASP_E_M_ASP_INACTIVE_REQ:
|
||||
ipa_asp_fsm_del_route(fi->priv);
|
||||
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_DOWN, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipa_asp_fsm_inactive_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
dispatch_to_all_as(fi, XUA_ASPAS_ASP_INACTIVE_IND);
|
||||
}
|
||||
|
||||
static void ipa_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case XUA_ASP_E_M_ASP_DOWN_REQ:
|
||||
ipa_asp_fsm_del_route(fi->priv);
|
||||
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_DOWN, 0, 0);
|
||||
break;
|
||||
}
|
||||
|
@ -1060,6 +1066,7 @@ static void ipa_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void
|
|||
static void ipa_asp_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct ipa_asp_fsm_priv *iafp = fi->priv;
|
||||
struct osmo_ss7_as *as;
|
||||
int fd;
|
||||
|
||||
switch (event) {
|
||||
|
@ -1079,22 +1086,20 @@ static void ipa_asp_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *dat
|
|||
/* stop timer, if any */
|
||||
osmo_timer_del(&iafp->pong_timer);
|
||||
break;
|
||||
case XUA_ASP_E_AS_ASSIGNED:
|
||||
as = data;
|
||||
osmo_talloc_replace_string(iafp->ipa_unit, &iafp->ipa_unit->unit_name, as->cfg.name);
|
||||
/* Now that the AS is known, start the client side: */
|
||||
if (iafp->role == OSMO_SS7_ASP_ROLE_ASP && fi->state == IPA_ASP_S_DOWN) {
|
||||
LOGPFSML(fi, LOGL_NOTICE, "Bringing up ASP now once it has been assigned to an AS\n");
|
||||
osmo_fsm_inst_dispatch(fi, XUA_ASP_E_M_ASP_UP_REQ, NULL);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipa_asp_fsm_active_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
dispatch_to_all_as(fi, XUA_ASPAS_ASP_INACTIVE_IND);
|
||||
dispatch_to_all_as(fi, XUA_ASPAS_ASP_ACTIVE_IND);
|
||||
}
|
||||
|
||||
static void ipa_asp_fsm_inactive_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
dispatch_to_all_as(fi, XUA_ASPAS_ASP_INACTIVE_IND);
|
||||
}
|
||||
|
||||
static void ipa_pong_timer_cb(void *_fi)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = _fi;
|
||||
|
@ -1119,7 +1124,7 @@ static const struct osmo_fsm_state ipa_asp_states[] = {
|
|||
[IPA_ASP_S_DOWN] = {
|
||||
.in_event_mask = S(XUA_ASP_E_M_ASP_UP_REQ) |
|
||||
S(XUA_ASP_E_SCTP_EST_IND),
|
||||
.out_state_mask = S(IPA_ASP_S_WAIT_ID_ACK) |
|
||||
.out_state_mask = S(IPA_ASP_S_WAIT_ID_GET) |
|
||||
S(IPA_ASP_S_WAIT_ID_RESP),
|
||||
.name = "ASP_DOWN",
|
||||
.action = ipa_asp_fsm_down,
|
||||
|
@ -1144,8 +1149,10 @@ static const struct osmo_fsm_state ipa_asp_states[] = {
|
|||
},
|
||||
/* Client Side */
|
||||
[IPA_ASP_S_WAIT_ID_GET] = {
|
||||
.in_event_mask = S(IPA_ASP_E_ID_GET),
|
||||
.out_state_mask = S(IPA_ASP_S_WAIT_ID_ACK),
|
||||
.in_event_mask = S(IPA_ASP_E_ID_GET) |
|
||||
S(IPA_ASP_E_ID_ACK), /* support broken MSCs skipping GET+RESP */
|
||||
.out_state_mask = S(IPA_ASP_S_WAIT_ID_ACK) |
|
||||
S(IPA_ASP_S_ACTIVE), /* support broken MSCs skipping GET+RESP */
|
||||
.name = "WAIT_ID_GET",
|
||||
.action = ipa_asp_fsm_wait_id_get,
|
||||
},
|
||||
|
@ -1176,11 +1183,6 @@ static const struct osmo_fsm_state ipa_asp_states[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static void ipa_asp_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
ipa_asp_fsm_del_route(fi->priv);
|
||||
}
|
||||
|
||||
struct osmo_fsm ipa_asp_fsm = {
|
||||
.name = "IPA_ASP",
|
||||
.states = ipa_asp_states,
|
||||
|
@ -1191,13 +1193,13 @@ struct osmo_fsm ipa_asp_fsm = {
|
|||
.allstate_event_mask = S(XUA_ASP_E_SCTP_COMM_DOWN_IND) |
|
||||
S(XUA_ASP_E_SCTP_RESTART_IND) |
|
||||
S(XUA_ASP_E_ASPSM_BEAT) |
|
||||
S(XUA_ASP_E_ASPSM_BEAT_ACK),
|
||||
S(XUA_ASP_E_ASPSM_BEAT_ACK) |
|
||||
S(XUA_ASP_E_AS_ASSIGNED),
|
||||
.allstate_action = ipa_asp_allstate,
|
||||
.cleanup = ipa_asp_fsm_cleanup,
|
||||
};
|
||||
|
||||
|
||||
/*! \brief Start a new ASP finite stae machine for given ASP
|
||||
/*! \brief Start a new ASP finite state machine for given ASP
|
||||
* \param[in] asp Application Server Process for which to start FSM
|
||||
* \param[in] role Role (ASP, SG, IPSP) of this FSM
|
||||
* \param[in] log_level Logging Level for ASP FSM logging
|
||||
|
@ -1207,6 +1209,9 @@ static struct osmo_fsm_inst *ipa_asp_fsm_start(struct osmo_ss7_asp *asp,
|
|||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct ipa_asp_fsm_priv *iafp;
|
||||
struct osmo_ss7_as *as = ipa_find_as_for_asp(asp);
|
||||
const char *unit_name;
|
||||
bool can_start = true;
|
||||
|
||||
/* allocate as child of AS? */
|
||||
fi = osmo_fsm_inst_alloc(&ipa_asp_fsm, asp, NULL, log_level, asp->cfg.name);
|
||||
|
@ -1216,16 +1221,33 @@ static struct osmo_fsm_inst *ipa_asp_fsm_start(struct osmo_ss7_asp *asp,
|
|||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (as) {
|
||||
unit_name = as->cfg.name;
|
||||
} else if (asp->dyn_allocated) {
|
||||
LOGPFSML(fi, LOGL_INFO, "Dynamic ASP is not assigned to any AS, "
|
||||
"using ASP name instead of AS name as ipa_unit_name\n");
|
||||
unit_name = asp->cfg.name;
|
||||
} else {
|
||||
/* ASP in client mode will be brought up when this ASP is added
|
||||
* to an AS, see XUA_ASP_E_AS_ASSIGNED. */
|
||||
if (role == OSMO_SS7_ASP_ROLE_ASP) {
|
||||
LOGPFSML(fi, LOGL_NOTICE, "ASP is not assigned to any AS. ASP bring up delayed\n");
|
||||
can_start = false;
|
||||
}
|
||||
unit_name = asp->cfg.name;
|
||||
}
|
||||
|
||||
iafp->role = role;
|
||||
iafp->asp = asp;
|
||||
iafp->ipa_unit = talloc_zero(iafp, struct ipaccess_unit);
|
||||
iafp->ipa_unit->unit_name = talloc_strdup(iafp->ipa_unit, asp->cfg.name);
|
||||
iafp->ipa_unit->unit_name = talloc_strdup(iafp->ipa_unit, unit_name);
|
||||
iafp->pong_timer.cb = ipa_pong_timer_cb;
|
||||
iafp->pong_timer.data = fi;
|
||||
|
||||
fi->priv = iafp;
|
||||
|
||||
if (role == OSMO_SS7_ASP_ROLE_ASP)
|
||||
if (can_start && role == OSMO_SS7_ASP_ROLE_ASP)
|
||||
osmo_fsm_inst_dispatch(fi, XUA_ASP_E_M_ASP_UP_REQ, NULL);
|
||||
|
||||
return fi;
|
||||
|
|
|
@ -28,6 +28,9 @@ enum xua_asp_event {
|
|||
XUA_ASP_E_ASPSM_BEAT,
|
||||
XUA_ASP_E_ASPSM_BEAT_ACK,
|
||||
|
||||
/* The ASP was added to an AS. data: (struct osmo_ss7_as *) */
|
||||
XUA_ASP_E_AS_ASSIGNED,
|
||||
|
||||
/* IPA specific */
|
||||
IPA_ASP_E_ID_RESP,
|
||||
IPA_ASP_E_ID_ACK,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Default XUA Layer Manager */
|
||||
/* (C) 2017 by Harald Welte <laforge@gnumonks.org>
|
||||
/* (C) 2017-2021 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
|
@ -76,17 +76,53 @@ static const struct value_string lm_event_names[] = {
|
|||
{ 0, NULL }
|
||||
};
|
||||
|
||||
enum lm_timer {
|
||||
T_WAIT_ASP_UP,
|
||||
T_WAIT_NOTIFY,
|
||||
T_WAIT_NOTIFY_RKM,
|
||||
T_WAIT_RK_REG_RESP,
|
||||
/***********************************************************************
|
||||
* Timer Handling
|
||||
***********************************************************************/
|
||||
|
||||
const struct osmo_tdef ss7_asp_lm_timer_defaults[SS7_ASP_LM_TIMERS_LEN] = {
|
||||
{ .T = SS7_ASP_LM_T_WAIT_ASP_UP, .default_val = 20, .unit = OSMO_TDEF_S,
|
||||
.desc = "Restart ASP after timeout waiting for ASP UP (SG role) / ASP UP ACK (ASP role) (s)" },
|
||||
{ .T = SS7_ASP_LM_T_WAIT_NOTIFY, .default_val = 2, .unit = OSMO_TDEF_S,
|
||||
.desc = "Restart ASP after timeout waiting for NOTIFY (s)" },
|
||||
{ .T = SS7_ASP_LM_T_WAIT_NOTIY_RKM, .default_val = 20, .unit = OSMO_TDEF_S,
|
||||
.desc = "Restart ASP after timeout waiting for NOTIFY after RKM registration (s)" },
|
||||
{ .T = SS7_ASP_LM_T_WAIT_RK_REG_RESP, .default_val = 10, .unit = OSMO_TDEF_S,
|
||||
.desc = "Restart ASP after timeout waiting for RK_REG_RESP (s)" },
|
||||
{}
|
||||
};
|
||||
|
||||
/* Appendix C.4 of ITU-T Q.714 */
|
||||
const struct value_string ss7_asp_lm_timer_names[] = {
|
||||
{ SS7_ASP_LM_T_WAIT_ASP_UP, "wait_asp_up" },
|
||||
{ SS7_ASP_LM_T_WAIT_NOTIFY, "wait_notify" },
|
||||
{ SS7_ASP_LM_T_WAIT_NOTIY_RKM, "wait_notify_rkm" },
|
||||
{ SS7_ASP_LM_T_WAIT_RK_REG_RESP, "wait_rk_reg_resp" },
|
||||
{}
|
||||
};
|
||||
|
||||
osmo_static_assert(ARRAY_SIZE(ss7_asp_lm_timer_defaults) == (SS7_ASP_LM_TIMERS_LEN) &&
|
||||
ARRAY_SIZE(ss7_asp_lm_timer_names) == (SS7_ASP_LM_TIMERS_LEN),
|
||||
assert_ss7_asp_lm_timer_count);
|
||||
|
||||
static const struct osmo_tdef_state_timeout lm_fsm_timeouts[32] = {
|
||||
[S_IDLE] = { },
|
||||
[S_WAIT_ASP_UP] = { .T = SS7_ASP_LM_T_WAIT_ASP_UP },
|
||||
[S_WAIT_NOTIFY] = { .T = SS7_ASP_LM_T_WAIT_NOTIFY }, /* SS7_ASP_LM_T_WAIT_NOTIY_RKM if coming from S_RKM_REG */
|
||||
[S_RKM_REG] = { .T = SS7_ASP_LM_T_WAIT_RK_REG_RESP },
|
||||
[S_ACTIVE] = { },
|
||||
};
|
||||
|
||||
struct lm_fsm_priv {
|
||||
struct osmo_ss7_asp *asp;
|
||||
};
|
||||
|
||||
#define lm_fsm_state_chg(fi, NEXT_STATE) \
|
||||
osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
|
||||
lm_fsm_timeouts, \
|
||||
((struct lm_fsm_priv *)(fi->priv))->asp->cfg.T_defs_lm, \
|
||||
-1)
|
||||
|
||||
static struct osmo_ss7_as *find_first_as_in_asp(struct osmo_ss7_asp *asp)
|
||||
{
|
||||
struct osmo_ss7_as *as;
|
||||
|
@ -138,8 +174,8 @@ static void lm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|||
|
||||
switch (event) {
|
||||
case LM_E_SCTP_EST_IND:
|
||||
/* Try to transition to ASP-UP, wait for 20s */
|
||||
osmo_fsm_inst_state_chg(fi, S_WAIT_ASP_UP, 20, T_WAIT_ASP_UP);
|
||||
/* Try to transition to ASP-UP, wait to receive message for a few seconds */
|
||||
lm_fsm_state_chg(fi, S_WAIT_ASP_UP);
|
||||
osmo_fsm_inst_dispatch(lmp->asp->fi, XUA_ASP_E_M_ASP_UP_REQ, NULL);
|
||||
break;
|
||||
}
|
||||
|
@ -149,9 +185,9 @@ static void lm_wait_asp_up(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|||
{
|
||||
switch (event) {
|
||||
case LM_E_ASP_UP_CONF:
|
||||
/* ASP is sup, wait for some time if any NOTIFY
|
||||
/* ASP is up, wait for some time if any NOTIFY
|
||||
* indications about AS in this ASP are received */
|
||||
osmo_fsm_inst_state_chg(fi, S_WAIT_NOTIFY, 2, T_WAIT_NOTIFY);
|
||||
lm_fsm_state_chg(fi, S_WAIT_NOTIFY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -164,16 +200,24 @@ static int lm_timer_cb(struct osmo_fsm_inst *fi)
|
|||
struct osmo_ss7_as *as;
|
||||
|
||||
switch (fi->T) {
|
||||
case T_WAIT_ASP_UP:
|
||||
case SS7_ASP_LM_T_WAIT_ASP_UP:
|
||||
/* we have been waiting for the ASP to come up, but it
|
||||
* failed to do so */
|
||||
LOGPFSML(fi, LOGL_NOTICE, "Peer didn't send any ASP_UP in time! Restarting ASP\n");
|
||||
restart_asp(fi);
|
||||
break;
|
||||
case T_WAIT_NOTIFY:
|
||||
case SS7_ASP_LM_T_WAIT_NOTIFY:
|
||||
if (lmp->asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_NO_NOTIFY) {
|
||||
/* some implementations don't send the NOTIFY which they SHOULD
|
||||
* according to RFC4666 (see OS#5145) */
|
||||
LOGPFSM(fi, "quirk no_notify active; locally emulate AS-INACTIVE.ind\n");
|
||||
osmo_fsm_inst_dispatch(fi, LM_E_AS_INACTIVE_IND, NULL);
|
||||
break;
|
||||
}
|
||||
/* No AS has reported via NOTIFY that is was
|
||||
* (statically) configured at the SG for this ASP, so
|
||||
* let's dynamically register */
|
||||
osmo_fsm_inst_state_chg(fi, S_RKM_REG, 10, T_WAIT_RK_REG_RESP);
|
||||
lm_fsm_state_chg(fi, S_RKM_REG);
|
||||
prim = xua_xlm_prim_alloc(OSMO_XLM_PRIM_M_RK_REG, PRIM_OP_REQUEST);
|
||||
OSMO_ASSERT(prim);
|
||||
as = find_first_as_in_asp(lmp->asp);
|
||||
|
@ -187,12 +231,12 @@ static int lm_timer_cb(struct osmo_fsm_inst *fi)
|
|||
prim->u.rk_reg.traf_mode = as->cfg.mode;
|
||||
osmo_xlm_sap_down(lmp->asp, &prim->oph);
|
||||
break;
|
||||
case T_WAIT_NOTIFY_RKM:
|
||||
case SS7_ASP_LM_T_WAIT_NOTIY_RKM:
|
||||
/* No AS has reported via NOTIFY even after dynamic RKM
|
||||
* configuration */
|
||||
restart_asp(fi);
|
||||
break;
|
||||
case T_WAIT_RK_REG_RESP:
|
||||
case SS7_ASP_LM_T_WAIT_RK_REG_RESP:
|
||||
/* timeout of registration of routing key */
|
||||
restart_asp(fi);
|
||||
break;
|
||||
|
@ -212,7 +256,7 @@ static void lm_wait_notify(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|||
if (oxp->u.notify.status_type == M3UA_NOTIFY_T_STATCHG &&
|
||||
(oxp->u.notify.status_info == M3UA_NOTIFY_I_AS_INACT ||
|
||||
oxp->u.notify.status_info == M3UA_NOTIFY_I_AS_PEND)) {
|
||||
osmo_fsm_inst_state_chg(fi, S_ACTIVE, 0, 0);
|
||||
lm_fsm_state_chg(fi, S_ACTIVE);
|
||||
osmo_fsm_inst_dispatch(lmp->asp->fi, XUA_ASP_E_M_ASP_ACTIVE_REQ, NULL);
|
||||
}
|
||||
break;
|
||||
|
@ -221,7 +265,7 @@ static void lm_wait_notify(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|||
* the SG, and that this AS is currently inactive */
|
||||
/* request the ASP to go into active state (which
|
||||
* hopefully will bring the AS to active, too) */
|
||||
osmo_fsm_inst_state_chg(fi, S_ACTIVE, 0, 0);
|
||||
lm_fsm_state_chg(fi, S_ACTIVE);
|
||||
osmo_fsm_inst_dispatch(lmp->asp->fi, XUA_ASP_E_M_ASP_ACTIVE_REQ, NULL);
|
||||
break;
|
||||
}
|
||||
|
@ -229,6 +273,7 @@ static void lm_wait_notify(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|||
|
||||
static void lm_rkm_reg(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct lm_fsm_priv *lmp = fi->priv;
|
||||
struct osmo_xlm_prim *oxp;
|
||||
int rc;
|
||||
|
||||
|
@ -239,14 +284,15 @@ static void lm_rkm_reg(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|||
LOGPFSML(fi, LOGL_NOTICE, "Received RKM_REG_RSP with negative result\n");
|
||||
restart_asp(fi);
|
||||
} else {
|
||||
unsigned long timeout_sec;
|
||||
rc = handle_reg_conf(fi, oxp->u.rk_reg.key.l_rk_id, oxp->u.rk_reg.key.context);
|
||||
if (rc < 0)
|
||||
restart_asp(fi);
|
||||
/* RKM registration was successful, we can
|
||||
* transition to WAIT_NOTIFY state and assume
|
||||
* that an NOTIFY/AS-INACTIVE arrives within 20
|
||||
* seconds */
|
||||
osmo_fsm_inst_state_chg(fi, S_WAIT_NOTIFY, 20, T_WAIT_NOTIFY_RKM);
|
||||
/* RKM registration was successful, we can transition to WAIT_NOTIFY
|
||||
* state and assume that an NOTIFY/AS-INACTIVE arrives within
|
||||
* T_WAIT_NOTIFY_RKM seconds */
|
||||
timeout_sec = osmo_tdef_get(lmp->asp->cfg.T_defs_lm, SS7_ASP_LM_T_WAIT_NOTIY_RKM, OSMO_TDEF_S, -1);
|
||||
osmo_fsm_inst_state_chg(fi, S_WAIT_NOTIFY, timeout_sec, SS7_ASP_LM_T_WAIT_NOTIY_RKM);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
#include <osmocom/sigtran/xua_msg.h>
|
||||
|
||||
#define M3UA_MSG_SIZE 2048
|
||||
#define M3UA_MSG_HEADROOM 512
|
||||
|
||||
struct osmo_sccp_addr;
|
||||
struct m3ua_data_hdr;
|
||||
|
||||
|
@ -18,10 +22,33 @@ struct msgb *osmo_sua_to_sccp(struct xua_msg *xua);
|
|||
int sua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg);
|
||||
|
||||
int sua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua);
|
||||
void sua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc, const uint32_t *aff_ssn,
|
||||
const uint32_t *smi, const char *info_string, bool available);
|
||||
void sua_tx_snm_congestion(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc, const uint32_t *ssn,
|
||||
const uint8_t cong_level, const char *info_string);
|
||||
void sua_tx_dupu(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
uint32_t dpc, uint16_t user, uint16_t cause, const char *info_str);
|
||||
|
||||
struct osmo_mtp_prim *m3ua_to_xfer_ind(struct xua_msg *xua);
|
||||
int m3ua_hmdc_rx_from_l2(struct osmo_ss7_instance *inst, struct xua_msg *xua);
|
||||
int m3ua_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua);
|
||||
void m3ua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc,
|
||||
const char *info_string, bool available);
|
||||
void m3ua_tx_snm_congestion(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc,
|
||||
const uint32_t *concerned_dpc, const uint8_t *cong_level,
|
||||
const char *info_string);
|
||||
void m3ua_tx_dupu(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
uint32_t dpc, uint16_t user, uint16_t cause, const char *info_str);
|
||||
|
||||
void xua_snm_rx_daud(struct osmo_ss7_asp *asp, struct xua_msg *xua);
|
||||
void xua_snm_rx_duna(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua);
|
||||
void xua_snm_rx_dava(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua);
|
||||
void xua_snm_rx_dupu(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua);
|
||||
void xua_snm_rx_scon(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua);
|
||||
int m3ua_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg);
|
||||
|
||||
struct msgb *m3ua_msgb_alloc(const char *name);
|
||||
|
@ -59,6 +86,25 @@ void xua_asp_send_xlm_prim_simple(struct osmo_ss7_asp *asp,
|
|||
enum osmo_xlm_prim_type prim_type,
|
||||
enum osmo_prim_operation op);
|
||||
|
||||
void xua_snm_pc_available(struct osmo_ss7_as *as, const uint32_t *aff_pc,
|
||||
unsigned int num_aff_pc, const char *info_str, bool available);
|
||||
|
||||
enum ss7_asp_lm_timer {
|
||||
/* 0 kept unused on purpose since it's handled specially by osmo_fsm */
|
||||
SS7_ASP_LM_T_WAIT_ASP_UP = 1,
|
||||
SS7_ASP_LM_T_WAIT_NOTIFY,
|
||||
SS7_ASP_LM_T_WAIT_NOTIY_RKM,
|
||||
SS7_ASP_LM_T_WAIT_RK_REG_RESP,
|
||||
/* This must remain the last item: */
|
||||
SS7_ASP_LM_TIMERS_LEN
|
||||
};
|
||||
|
||||
extern const struct osmo_tdef ss7_asp_lm_timer_defaults[SS7_ASP_LM_TIMERS_LEN];
|
||||
|
||||
extern const struct value_string ss7_asp_lm_timer_names[];
|
||||
static inline const char *ss7_asp_lm_timer_name(enum ss7_asp_lm_timer val)
|
||||
{ return get_value_string(ss7_asp_lm_timer_names, val); }
|
||||
|
||||
extern struct osmo_fsm xua_default_lm_fsm;
|
||||
extern const struct value_string m3ua_rkm_reg_status_vals[];
|
||||
extern const struct value_string m3ua_rkm_dereg_status_vals[];
|
||||
|
@ -69,9 +115,12 @@ extern const struct value_string m3ua_rkm_dereg_status_vals[];
|
|||
|
||||
int xua_as_transmit_msg(struct osmo_ss7_as *as, struct msgb *msg);
|
||||
|
||||
int xua_find_as_for_asp(struct osmo_ss7_as **as, const struct osmo_ss7_asp *asp,
|
||||
const struct xua_msg_part *rctx_ie);
|
||||
|
||||
int ipa_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua);
|
||||
int ipa_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg);
|
||||
int ipa_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg, uint8_t sls);
|
||||
struct osmo_ss7_as *ipa_find_as_for_asp(struct osmo_ss7_asp *asp);
|
||||
|
||||
int osmo_isup_party_parse(char *out_digits, const uint8_t *in,
|
||||
unsigned int in_num_bytes, bool odd);
|
||||
|
|
|
@ -59,7 +59,7 @@ 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)
|
||||
uint16_t len, const uint8_t *dat)
|
||||
{
|
||||
struct xua_msg_part *part;
|
||||
|
||||
|
@ -283,13 +283,13 @@ int xua_msg_add_u32(struct xua_msg *xua, uint16_t iei, uint32_t val)
|
|||
return xua_msg_add_data(xua, iei, sizeof(val_n), (uint8_t *) &val_n);
|
||||
}
|
||||
|
||||
uint32_t xua_msg_part_get_u32(struct xua_msg_part *part)
|
||||
uint32_t xua_msg_part_get_u32(const struct xua_msg_part *part)
|
||||
{
|
||||
OSMO_ASSERT(part->len >= 4);
|
||||
return ntohl(*(uint32_t *)part->dat);
|
||||
}
|
||||
|
||||
uint32_t xua_msg_get_u32(struct xua_msg *xua, uint16_t iei)
|
||||
uint32_t xua_msg_get_u32(const struct xua_msg *xua, uint16_t iei)
|
||||
{
|
||||
struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
|
||||
if (!part)
|
||||
|
@ -297,6 +297,47 @@ uint32_t xua_msg_get_u32(struct xua_msg *xua, uint16_t iei)
|
|||
return xua_msg_part_get_u32(part);
|
||||
}
|
||||
|
||||
/* get a U32 IE in host-byte-order, returning whether it exists (!= NULL) or not (== NULL) */
|
||||
const uint32_t *xua_msg_get_u32p(const struct xua_msg *xua, uint16_t iei, uint32_t *out)
|
||||
{
|
||||
struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
|
||||
if (!part)
|
||||
return NULL;
|
||||
*out = xua_msg_part_get_u32(part);
|
||||
return out;
|
||||
}
|
||||
|
||||
const char *xua_msg_part_get_str(const struct xua_msg_part *part)
|
||||
{
|
||||
static char __thread buf[256];
|
||||
|
||||
if (part->len == 0)
|
||||
return "";
|
||||
/* RFC3868 3.9.4: Length of the INFO String parameter is from 0 to 255 octets */
|
||||
if (part->len > 255)
|
||||
return "<invalid-string-len>";
|
||||
|
||||
memcpy(buf, part->dat, part->len);
|
||||
buf[part->len] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char *xua_msg_get_str(const struct xua_msg *xua, uint16_t iei)
|
||||
{
|
||||
struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
|
||||
if (!part)
|
||||
return NULL;
|
||||
return xua_msg_part_get_str(part);
|
||||
}
|
||||
|
||||
int xua_msg_get_len(const struct xua_msg *xua, uint16_t iei)
|
||||
{
|
||||
struct xua_msg_part *part = xua_msg_find_tag(xua, iei);
|
||||
if (!part)
|
||||
return -1;
|
||||
return part->len;
|
||||
}
|
||||
|
||||
void xua_part_add_gt(struct msgb *msg, const struct osmo_sccp_gt *gt)
|
||||
{
|
||||
uint16_t *len_ptr;
|
||||
|
@ -376,9 +417,9 @@ int xua_msg_add_sccp_addr(struct xua_msg *xua, uint16_t iei, const struct osmo_s
|
|||
msgb_t16l16vp_put_u32(tmp, SUA_IEI_SSN, addr->ssn);
|
||||
}
|
||||
if (addr->presence & OSMO_SCCP_ADDR_T_IPv4) {
|
||||
msgb_t16l16vp_put_u32(tmp, SUA_IEI_IPv4, ntohl(addr->ip.v4.s_addr));
|
||||
msgb_t16l16vp_put(tmp, SUA_IEI_IPv4, sizeof(addr->ip.v4), (const uint8_t *)&addr->ip.v4);
|
||||
} else if (addr->presence & OSMO_SCCP_ADDR_T_IPv6) {
|
||||
/* FIXME: IPv6 address */
|
||||
msgb_t16l16vp_put(tmp, SUA_IEI_IPv6, sizeof(addr->ip.v6), (const uint8_t *)&addr->ip.v6);
|
||||
}
|
||||
rc = xua_msg_add_data(xua, iei, msgb_length(tmp), tmp->data);
|
||||
msgb_free(tmp);
|
||||
|
@ -478,37 +519,23 @@ int xua_dialect_check_all_mand_ies(const struct xua_dialect *dialect, struct xua
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void append_to_buf(char *buf, bool *comma, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (!comma || *comma == true) {
|
||||
strcat(buf, ",");
|
||||
} else if (comma)
|
||||
*comma = true;
|
||||
vsprintf(buf+strlen(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
char *xua_msg_dump(struct xua_msg *xua, const struct xua_dialect *dialect)
|
||||
{
|
||||
static char buf[1024];
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) };
|
||||
struct xua_msg_part *part;
|
||||
const struct xua_msg_class *xmc = NULL;
|
||||
bool comma = false;
|
||||
if (dialect)
|
||||
xmc = dialect->class[xua->hdr.msg_class];
|
||||
|
||||
buf[0] = '\0';
|
||||
|
||||
append_to_buf(buf, &comma, "HDR=(%s,V=%u,LEN=%u)",
|
||||
xua_hdr_dump(xua, dialect),
|
||||
xua->hdr.version, xua->hdr.msg_length);
|
||||
OSMO_STRBUF_PRINTF(sb, "HDR=(%s,V=%u,LEN=%u)", xua_hdr_dump(xua, dialect),
|
||||
xua->hdr.version, ntohl(xua->hdr.msg_length));
|
||||
|
||||
llist_for_each_entry(part, &xua->headers, entry)
|
||||
append_to_buf(buf, NULL, " PART(T=%s,L=%u,D=%s)",
|
||||
xua_class_iei_name(xmc, part->tag), part->len,
|
||||
osmo_hexdump_nospc(part->dat, part->len));
|
||||
return buf;
|
||||
OSMO_STRBUF_PRINTF(sb, ", PART(T=%s,L=%u,D=%s)",
|
||||
xua_class_iei_name(xmc, part->tag), part->len,
|
||||
osmo_hexdump_nospc(part->dat, part->len));
|
||||
return sb.buf;
|
||||
}
|
||||
|
|
|
@ -350,6 +350,12 @@ static int handle_rkey_dereg(struct osmo_ss7_asp *asp, uint32_t rctx,
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Reject if not dynamically allocated (OS#4239) */
|
||||
if (!as->rkm_dyn_allocated) {
|
||||
msgb_append_dereg_res(resp, M3UA_RKM_DEREG_ERR_NOT_REGD, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Reject if ASP is not even part of AS */
|
||||
if (!osmo_ss7_as_has_asp(as, asp)) {
|
||||
msgb_append_dereg_res(resp, M3UA_RKM_DEREG_ERR_INVAL_RCTX, 0);
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
/* Shared code between M3UA and SUA implementation */
|
||||
|
||||
/* (C) 2015-2021 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/sigtran/xua_msg.h>
|
||||
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
#include <osmocom/sigtran/protocol/m3ua.h>
|
||||
#include <osmocom/sigtran/protocol/sua.h>
|
||||
|
||||
#include "xua_internal.h"
|
||||
|
||||
/* if given ASP only has one AS, return that AS */
|
||||
static struct osmo_ss7_as *find_single_as_for_asp(const struct osmo_ss7_asp *asp)
|
||||
{
|
||||
struct osmo_ss7_as *as, *as_found = NULL;
|
||||
|
||||
llist_for_each_entry(as, &asp->inst->as_list, list) {
|
||||
if (!osmo_ss7_as_has_asp(as, asp))
|
||||
continue;
|
||||
/* check if we already had found another AS within this ASP -> not unique */
|
||||
if (as_found)
|
||||
return NULL;
|
||||
as_found = as;
|
||||
}
|
||||
|
||||
return as_found;
|
||||
}
|
||||
|
||||
/* this is why we can use the M3UA constants below in a function shared between M3UA + SUA */
|
||||
osmo_static_assert(M3UA_ERR_INVAL_ROUT_CTX == SUA_ERR_INVAL_ROUT_CTX, _err_rctx);
|
||||
osmo_static_assert(M3UA_ERR_NO_CONFGD_AS_FOR_ASP == SUA_ERR_NO_CONFGD_AS_FOR_ASP, _err_as_for_asp);
|
||||
|
||||
/*! Find the AS for given ASP + optional routing context IE.
|
||||
* if rctx_ie == NULL, we assume that this ASP is only part of a single AS;
|
||||
* if rctx_ie is given, then we look-up the ASP based on the routing context,
|
||||
* and verify that this ASP is part of it.
|
||||
* \param[out] as caller-provided address-of-pointer to store the found AS
|
||||
* \param[in] asp ASP for which we want to look-up the AS
|
||||
* \param[in] rctx_ie routing context IE (may be NULL) to use for look-up
|
||||
* \returns 0 in case of success; {M3UA,SUA}_ERR_* code in case of error. */
|
||||
int xua_find_as_for_asp(struct osmo_ss7_as **as, const struct osmo_ss7_asp *asp,
|
||||
const struct xua_msg_part *rctx_ie)
|
||||
{
|
||||
int log_ss = osmo_ss7_asp_get_log_subsys(asp);
|
||||
*as = NULL;
|
||||
|
||||
if (rctx_ie) {
|
||||
uint32_t rctx = xua_msg_part_get_u32(rctx_ie);
|
||||
/* Use routing context IE to look up the AS for which the
|
||||
* message was received. */
|
||||
*as = osmo_ss7_as_find_by_rctx(asp->inst, rctx);
|
||||
if (!*as) {
|
||||
LOGPASP(asp, log_ss, LOGL_ERROR, "%s(): invalid routing context: %u\n",
|
||||
__func__, rctx);
|
||||
return M3UA_ERR_INVAL_ROUT_CTX;
|
||||
}
|
||||
|
||||
/* Verify that this ASP is part of the AS. */
|
||||
if (!osmo_ss7_as_has_asp(*as, asp)) {
|
||||
LOGPASP(asp, log_ss, LOGL_ERROR,
|
||||
"%s(): This Application Server Process is not part of the AS %s "
|
||||
"resolved by routing context %u\n", __func__, (*as)->cfg.name, rctx);
|
||||
return M3UA_ERR_NO_CONFGD_AS_FOR_ASP;
|
||||
}
|
||||
} else {
|
||||
/* no explicit routing context; this only works if there is only one AS in the ASP */
|
||||
*as = find_single_as_for_asp(asp);
|
||||
if (!*as) {
|
||||
LOGPASP(asp, log_ss, LOGL_ERROR,
|
||||
"%s(): ASP sent M3UA without Routing Context IE but unable to uniquely "
|
||||
"identify the AS for this message\n", __func__);
|
||||
return M3UA_ERR_INVAL_ROUT_CTX;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,474 @@
|
|||
/* M3UA/SUA [S]SNM Handling */
|
||||
|
||||
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
#include <osmocom/sigtran/protocol/m3ua.h>
|
||||
#include <osmocom/sigtran/protocol/sua.h>
|
||||
#include <osmocom/sigtran/protocol/mtp.h>
|
||||
|
||||
#include "xua_internal.h"
|
||||
#include "sccp_internal.h"
|
||||
|
||||
/* we can share this code between M3UA and SUA as the below conditions are true */
|
||||
osmo_static_assert(M3UA_SNM_DUNA == SUA_SNM_DUNA, _sa_duna);
|
||||
osmo_static_assert(M3UA_SNM_DAVA == SUA_SNM_DAVA, _sa_dava);
|
||||
osmo_static_assert(M3UA_SNM_DAUD == SUA_SNM_DAUD, _sa_dava);
|
||||
osmo_static_assert(M3UA_IEI_AFFECTED_PC == SUA_IEI_AFFECTED_PC, _sa_aff_pc);
|
||||
osmo_static_assert(M3UA_IEI_ROUTE_CTX == SUA_IEI_ROUTE_CTX, _sa_rctx);
|
||||
osmo_static_assert(M3UA_IEI_INFO_STRING == SUA_IEI_INFO_STRING, _sa_inf_str);
|
||||
|
||||
static const char *format_affected_pcs_c(void *ctx, const struct osmo_ss7_instance *s7i,
|
||||
const struct xua_msg_part *ie_aff_pc)
|
||||
{
|
||||
const uint32_t *aff_pc = (const uint32_t *) ie_aff_pc->dat;
|
||||
unsigned int num_aff_pc = ie_aff_pc->len / sizeof(uint32_t);
|
||||
char *out = talloc_strdup(ctx, "");
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_aff_pc; i++) {
|
||||
uint32_t _aff_pc = ntohl(aff_pc[i]);
|
||||
uint32_t pc = _aff_pc & 0xffffff;
|
||||
uint8_t mask = _aff_pc >> 24;
|
||||
|
||||
/* append point code + mask */
|
||||
out = talloc_asprintf_append(out, "%s%s/%u, ", i == 0 ? "" : ", ",
|
||||
osmo_ss7_pointcode_print(s7i, pc), mask);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* obtain all routing contexts (in network byte order) that exist within the given ASP */
|
||||
static unsigned int get_all_rctx_for_asp(uint32_t *rctx, unsigned int rctx_size,
|
||||
struct osmo_ss7_asp *asp, struct osmo_ss7_as *excl_as)
|
||||
{
|
||||
unsigned int count = 0;
|
||||
struct osmo_ss7_as *as;
|
||||
|
||||
llist_for_each_entry(as, &asp->inst->as_list, list) {
|
||||
if (as == excl_as)
|
||||
continue;
|
||||
if (!osmo_ss7_as_has_asp(as, asp))
|
||||
continue;
|
||||
if (as->cfg.routing_key.context == 0)
|
||||
continue;
|
||||
if (count >= rctx_size)
|
||||
break;
|
||||
rctx[count] = htonl(as->cfg.routing_key.context);
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void xua_tx_snm_available(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc,
|
||||
const char *info_str, bool available)
|
||||
{
|
||||
switch (asp->cfg.proto) {
|
||||
case OSMO_SS7_ASP_PROT_M3UA:
|
||||
m3ua_tx_snm_available(asp, rctx, num_rctx, aff_pc, num_aff_pc, info_str, available);
|
||||
break;
|
||||
case OSMO_SS7_ASP_PROT_SUA:
|
||||
sua_tx_snm_available(asp, rctx, num_rctx, aff_pc, num_aff_pc, NULL, NULL, info_str, available);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void xua_tx_upu(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
uint32_t dpc, uint16_t user, uint16_t cause, const char *info_str)
|
||||
{
|
||||
switch (asp->cfg.proto) {
|
||||
case OSMO_SS7_ASP_PROT_M3UA:
|
||||
m3ua_tx_dupu(asp, rctx, num_rctx, dpc, user, cause, info_str);
|
||||
break;
|
||||
case OSMO_SS7_ASP_PROT_SUA:
|
||||
sua_tx_dupu(asp, rctx, num_rctx, dpc, user, cause, info_str);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void xua_tx_scon(struct osmo_ss7_asp *asp, const uint32_t *rctx, unsigned int num_rctx,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc,
|
||||
const uint32_t *concerned_dpc, const uint8_t *cong_level,
|
||||
const char *info_string)
|
||||
{
|
||||
switch (asp->cfg.proto) {
|
||||
case OSMO_SS7_ASP_PROT_M3UA:
|
||||
m3ua_tx_snm_congestion(asp, rctx, num_rctx, aff_pc, num_aff_pc,
|
||||
concerned_dpc, cong_level, info_string);
|
||||
break;
|
||||
case OSMO_SS7_ASP_PROT_SUA:
|
||||
sua_tx_snm_congestion(asp, rctx, num_rctx, aff_pc, num_aff_pc, NULL,
|
||||
cong_level ? *cong_level : 0, info_string);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* generate MTP-PAUSE / MTP-RESUME towards local SCCP users */
|
||||
static void xua_snm_pc_available_to_sccp(struct osmo_sccp_instance *sccp,
|
||||
const uint32_t *aff_pc, unsigned int num_aff_pc,
|
||||
bool available)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_aff_pc; i++) {
|
||||
uint32_t _aff_pc = ntohl(aff_pc[i]);
|
||||
uint32_t pc = _aff_pc & 0xffffff;
|
||||
uint8_t mask = _aff_pc >> 24;
|
||||
|
||||
if (!mask) {
|
||||
if (available)
|
||||
sccp_scmg_rx_mtp_resume(sccp, pc);
|
||||
else
|
||||
sccp_scmg_rx_mtp_pause(sccp, pc);
|
||||
} else {
|
||||
/* we have to send one MTP primitive for each individual point
|
||||
* code within that mask */
|
||||
uint32_t maskbits = (1 << mask) - 1;
|
||||
uint32_t fullpc;
|
||||
for (fullpc = (pc & ~maskbits); fullpc <= (pc | maskbits); fullpc++) {
|
||||
if (available)
|
||||
sccp_scmg_rx_mtp_resume(sccp, pc);
|
||||
else
|
||||
sccp_scmg_rx_mtp_pause(sccp, pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* advertise availability of point codes (with masks) */
|
||||
void xua_snm_pc_available(struct osmo_ss7_as *as, const uint32_t *aff_pc,
|
||||
unsigned int num_aff_pc, const char *info_str, bool available)
|
||||
{
|
||||
struct osmo_ss7_instance *s7i = as->inst;
|
||||
struct osmo_ss7_asp *asp;
|
||||
uint32_t rctx[32];
|
||||
unsigned int num_rctx;
|
||||
|
||||
/* inform local users via a MTP-{PAUSE, RESUME} primitive */
|
||||
if (s7i->sccp)
|
||||
xua_snm_pc_available_to_sccp(s7i->sccp, aff_pc, num_aff_pc, available);
|
||||
|
||||
/* inform remote ASPs via DUNA/DAVA */
|
||||
llist_for_each_entry(asp, &s7i->asp_list, list) {
|
||||
/* SSNM is only permitted for ASPs in ACTIVE state */
|
||||
if (!osmo_ss7_asp_active(asp))
|
||||
continue;
|
||||
|
||||
/* only send DAVA/DUNA if we locally are the SG and the remote is ASP */
|
||||
if (asp->cfg.role != OSMO_SS7_ASP_ROLE_SG)
|
||||
continue;
|
||||
|
||||
num_rctx = get_all_rctx_for_asp(rctx, ARRAY_SIZE(rctx), asp, as);
|
||||
/* this can happen if the given ASP is only in the AS that reports the change,
|
||||
* which shall be excluded */
|
||||
if (num_rctx == 0)
|
||||
continue;
|
||||
xua_tx_snm_available(asp, rctx, num_rctx, aff_pc, num_aff_pc, info_str, available);
|
||||
}
|
||||
}
|
||||
|
||||
/* generate SS-PROHIBITED / SS-ALLOWED towards local SCCP users */
|
||||
static void sua_snm_ssn_available_to_sccp(struct osmo_sccp_instance *sccp, uint32_t aff_pc,
|
||||
uint32_t aff_ssn, uint32_t smi, bool available)
|
||||
{
|
||||
if (available)
|
||||
sccp_scmg_rx_ssn_allowed(sccp, aff_pc, aff_ssn, smi);
|
||||
else
|
||||
sccp_scmg_rx_ssn_prohibited(sccp, aff_pc, aff_ssn, smi);
|
||||
}
|
||||
|
||||
/* advertise availability of a single subsystem */
|
||||
static void sua_snm_ssn_available(struct osmo_ss7_as *as, uint32_t aff_pc, uint32_t aff_ssn,
|
||||
const uint32_t *smi, const char *info_str, bool available)
|
||||
{
|
||||
struct osmo_ss7_instance *s7i = as->inst;
|
||||
struct osmo_ss7_asp *asp;
|
||||
uint32_t rctx[32];
|
||||
unsigned int num_rctx;
|
||||
uint32_t _smi = smi ? *smi : 0; /* 0 == reserved/unknown in SUA */
|
||||
|
||||
if (s7i->sccp)
|
||||
sua_snm_ssn_available_to_sccp(s7i->sccp, aff_pc, aff_ssn, _smi, available);
|
||||
|
||||
/* inform remote SUA ASPs via DUNA/DAVA */
|
||||
llist_for_each_entry(asp, &s7i->asp_list, list) {
|
||||
/* SSNM is only permitted for ASPs in ACTIVE state */
|
||||
if (!osmo_ss7_asp_active(asp))
|
||||
continue;
|
||||
|
||||
/* only send DAVA/DUNA if we locally are the SG and the remote is ASP */
|
||||
if (asp->cfg.role != OSMO_SS7_ASP_ROLE_SG)
|
||||
continue;
|
||||
|
||||
/* DUNA/DAVA for SSN only exists in SUA */
|
||||
if (asp->cfg.proto != OSMO_SS7_ASP_PROT_SUA)
|
||||
continue;
|
||||
|
||||
num_rctx = get_all_rctx_for_asp(rctx, ARRAY_SIZE(rctx), asp, as);
|
||||
/* this can happen if the given ASP is only in the AS that reports the change,
|
||||
* which shall be excluded */
|
||||
if (num_rctx == 0)
|
||||
continue;
|
||||
sua_tx_snm_available(asp, rctx, num_rctx, &aff_pc, 1, &aff_ssn, smi, info_str, available);
|
||||
}
|
||||
}
|
||||
|
||||
static void xua_snm_upu(struct osmo_ss7_as *as, uint32_t dpc, uint16_t user, uint16_t cause,
|
||||
const char *info_str)
|
||||
{
|
||||
struct osmo_ss7_instance *s7i = as->inst;
|
||||
struct osmo_ss7_asp *asp;
|
||||
uint32_t rctx[32];
|
||||
unsigned int num_rctx;
|
||||
|
||||
/* Translate to MTP-STATUS.ind towards SCCP (will create N-PCSTATE.ind to SCU) */
|
||||
if (s7i->sccp && user == MTP_SI_SCCP)
|
||||
sccp_scmg_rx_mtp_status(s7i->sccp, dpc, cause);
|
||||
|
||||
/* inform remote ASPs via DUPU */
|
||||
llist_for_each_entry(asp, &s7i->asp_list, list) {
|
||||
/* SSNM is only permitted for ASPs in ACTIVE state */
|
||||
if (!osmo_ss7_asp_active(asp))
|
||||
continue;
|
||||
|
||||
/* only send DAVA/DUNA if we locally are the SG and the remote is ASP */
|
||||
if (asp->cfg.role != OSMO_SS7_ASP_ROLE_SG)
|
||||
continue;
|
||||
|
||||
num_rctx = get_all_rctx_for_asp(rctx, ARRAY_SIZE(rctx), asp, as);
|
||||
/* this can happen if the given ASP is only in the AS that reports the change,
|
||||
* which shall be excluded */
|
||||
if (num_rctx == 0)
|
||||
continue;
|
||||
|
||||
xua_tx_upu(asp, rctx, num_rctx, dpc, user, cause, info_str);
|
||||
}
|
||||
}
|
||||
|
||||
static void xua_snm_scon(struct osmo_ss7_as *as, const uint32_t *aff_pc, unsigned int num_aff_pc,
|
||||
const uint32_t *concerned_dpc, const uint8_t *cong_level, const char *info_string)
|
||||
{
|
||||
struct osmo_ss7_instance *s7i = as->inst;
|
||||
struct osmo_ss7_asp *asp;
|
||||
uint32_t rctx[32];
|
||||
unsigned int num_rctx;
|
||||
|
||||
/* TODO: How to translate to MTP and towards SCCP (create N-PCSTATE.ind to SCU) */
|
||||
|
||||
/* inform remote ASPs via SCON */
|
||||
llist_for_each_entry(asp, &s7i->asp_list, list) {
|
||||
/* SSNM is only permitted for ASPs in ACTIVE state */
|
||||
if (!osmo_ss7_asp_active(asp))
|
||||
continue;
|
||||
|
||||
/* only send SCON if we locally are the SG and the remote is ASP */
|
||||
if (asp->cfg.role != OSMO_SS7_ASP_ROLE_SG)
|
||||
continue;
|
||||
|
||||
num_rctx = get_all_rctx_for_asp(rctx, ARRAY_SIZE(rctx), asp, as);
|
||||
/* this can happen if the given ASP is only in the AS that reports the change,
|
||||
* which shall be excluded */
|
||||
if (num_rctx == 0)
|
||||
continue;
|
||||
|
||||
xua_tx_scon(asp, rctx, num_rctx, aff_pc, num_aff_pc, concerned_dpc, cong_level, info_string);
|
||||
}
|
||||
}
|
||||
|
||||
/* receive DAUD from ASP; pc is 'affected PC' IE with mask in network byte order! */
|
||||
void xua_snm_rx_daud(struct osmo_ss7_asp *asp, struct xua_msg *xua)
|
||||
{
|
||||
struct xua_msg_part *ie_aff_pc = xua_msg_find_tag(xua, M3UA_IEI_AFFECTED_PC);
|
||||
const char *info_str = xua_msg_get_str(xua, M3UA_IEI_INFO_STRING);
|
||||
struct osmo_ss7_instance *s7i = asp->inst;
|
||||
unsigned int num_aff_pc;
|
||||
unsigned int num_rctx;
|
||||
const uint32_t *aff_pc;
|
||||
uint32_t rctx[32];
|
||||
int log_ss = osmo_ss7_asp_get_log_subsys(asp);
|
||||
int i;
|
||||
|
||||
OSMO_ASSERT(ie_aff_pc);
|
||||
aff_pc = (const uint32_t *) ie_aff_pc->dat;
|
||||
num_aff_pc = ie_aff_pc->len / sizeof(uint32_t);
|
||||
|
||||
num_rctx = get_all_rctx_for_asp(rctx, ARRAY_SIZE(rctx), asp, NULL);
|
||||
|
||||
LOGPASP(asp, log_ss, LOGL_INFO, "Rx DAUD(%s) for %s\n", info_str ? info_str : "",
|
||||
format_affected_pcs_c(xua, asp->inst, ie_aff_pc));
|
||||
|
||||
/* iterate over list of point codes, generate DAVA/DUPU */
|
||||
for (i = 0; i < num_aff_pc; i++) {
|
||||
uint32_t _aff_pc = ntohl(aff_pc[i]);
|
||||
uint32_t pc = _aff_pc & 0xffffff;
|
||||
uint8_t mask = _aff_pc >> 24;
|
||||
bool is_available = false;
|
||||
|
||||
if (mask == 0) {
|
||||
/* one single point code */
|
||||
|
||||
/* FIXME: don't just check for a route; but also check if the route is "active" */
|
||||
if (osmo_ss7_route_lookup(s7i, pc))
|
||||
is_available = true;
|
||||
|
||||
xua_tx_snm_available(asp, rctx, num_rctx, &aff_pc[i], 1, "Response to DAUD",
|
||||
is_available);
|
||||
} else {
|
||||
/* TODO: wildcard match */
|
||||
LOGPASP(asp, log_ss, LOGL_NOTICE, "DAUD with wildcard match not supported yet\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* an incoming xUA DUNA was received from a remote SG */
|
||||
void xua_snm_rx_duna(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua)
|
||||
{
|
||||
struct xua_msg_part *ie_aff_pc = xua_msg_find_tag(xua, M3UA_IEI_AFFECTED_PC);
|
||||
struct xua_msg_part *ie_ssn = xua_msg_find_tag(xua, SUA_IEI_SSN);
|
||||
const char *info_str = xua_msg_get_str(xua, M3UA_IEI_INFO_STRING);
|
||||
/* TODO: should our processing depend on the RCTX included? I somehow don't think so */
|
||||
//struct xua_msg_part *ie_rctx = xua_msg_find_tag(xua, M3UA_IEI_ROUTE_CTX);
|
||||
int log_ss = osmo_ss7_asp_get_log_subsys(asp);
|
||||
|
||||
OSMO_ASSERT(ie_aff_pc);
|
||||
|
||||
if (asp->cfg.role != OSMO_SS7_ASP_ROLE_ASP)
|
||||
return;
|
||||
|
||||
LOGPASP(asp, log_ss, LOGL_NOTICE, "Rx DUNA(%s) for %s\n", info_str ? info_str : "",
|
||||
format_affected_pcs_c(xua, asp->inst, ie_aff_pc));
|
||||
|
||||
if (asp->cfg.proto == OSMO_SS7_ASP_PROT_SUA && ie_ssn) {
|
||||
/* when the SSN is included, DUNA corresponds to the SCCP N-STATE primitive */
|
||||
uint32_t ssn = xua_msg_part_get_u32(ie_ssn);
|
||||
const uint32_t *aff_pc = (const uint32_t *)ie_aff_pc->dat;
|
||||
uint32_t pc, smi;
|
||||
/* The Affected Point Code can only contain one point code when SSN is present */
|
||||
if (ie_aff_pc->len/sizeof(uint32_t) != 1)
|
||||
return;
|
||||
pc = ntohl(aff_pc[0]) & 0xffffff;
|
||||
sua_snm_ssn_available(as, pc, ssn, xua_msg_get_u32p(xua, SUA_IEI_SMI, &smi), info_str, false);
|
||||
} else {
|
||||
/* when the SSN is not included, DUNA corresponds to the SCCP N-PCSTATE primitive */
|
||||
xua_snm_pc_available(as, (const uint32_t *)ie_aff_pc->dat,
|
||||
ie_aff_pc->len / sizeof(uint32_t), info_str, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* an incoming xUA DAVA was received from a remote SG */
|
||||
void xua_snm_rx_dava(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua)
|
||||
{
|
||||
struct xua_msg_part *ie_aff_pc = xua_msg_find_tag(xua, M3UA_IEI_AFFECTED_PC);
|
||||
struct xua_msg_part *ie_ssn = xua_msg_find_tag(xua, SUA_IEI_SSN);
|
||||
const char *info_str = xua_msg_get_str(xua, M3UA_IEI_INFO_STRING);
|
||||
/* TODO: should our processing depend on the RCTX included? I somehow don't think so */
|
||||
//struct xua_msg_part *ie_rctx = xua_msg_find_tag(xua, M3UA_IEI_ROUTE_CTX);
|
||||
int log_ss = osmo_ss7_asp_get_log_subsys(asp);
|
||||
|
||||
OSMO_ASSERT(ie_aff_pc);
|
||||
|
||||
if (asp->cfg.role != OSMO_SS7_ASP_ROLE_ASP)
|
||||
return;
|
||||
|
||||
LOGPASP(asp, log_ss, LOGL_NOTICE, "Rx DAVA(%s) for %s\n", info_str ? info_str : "",
|
||||
format_affected_pcs_c(xua, asp->inst, ie_aff_pc));
|
||||
|
||||
if (asp->cfg.proto == OSMO_SS7_ASP_PROT_SUA && ie_ssn) {
|
||||
/* when the SSN is included, DAVA corresponds to the SCCP N-STATE primitive */
|
||||
uint32_t ssn = xua_msg_part_get_u32(ie_ssn);
|
||||
const uint32_t *aff_pc = (const uint32_t *)ie_aff_pc->dat;
|
||||
uint32_t pc, smi;
|
||||
/* The Affected Point Code can only contain one point code when SSN is present */
|
||||
if (ie_aff_pc->len/sizeof(uint32_t) != 1)
|
||||
return;
|
||||
pc = ntohl(aff_pc[0]) & 0xffffff;
|
||||
sua_snm_ssn_available(as, pc, ssn, xua_msg_get_u32p(xua, SUA_IEI_SMI, &smi), info_str, true);
|
||||
} else {
|
||||
/* when the SSN is not included, DAVA corresponds to the SCCP N-PCSTATE primitive */
|
||||
xua_snm_pc_available(as, (const uint32_t *)ie_aff_pc->dat,
|
||||
ie_aff_pc->len / sizeof(uint32_t), info_str, true);
|
||||
}
|
||||
}
|
||||
|
||||
/* an incoming SUA/M3UA DUPU was received from a remote SG */
|
||||
void xua_snm_rx_dupu(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua)
|
||||
{
|
||||
uint32_t aff_pc = xua_msg_get_u32(xua, SUA_IEI_AFFECTED_PC);
|
||||
const char *info_str = xua_msg_get_str(xua, SUA_IEI_INFO_STRING);
|
||||
/* TODO: should our processing depend on the RCTX included? I somehow don't think so */
|
||||
//struct xua_msg_part *ie_rctx = xua_msg_find_tag(xua, SUA_IEI_ROUTE_CTX);
|
||||
int log_ss = osmo_ss7_asp_get_log_subsys(asp);
|
||||
uint32_t cause_user;
|
||||
uint16_t cause, user;
|
||||
|
||||
if (asp->cfg.role != OSMO_SS7_ASP_ROLE_ASP)
|
||||
return;
|
||||
|
||||
switch (asp->cfg.proto) {
|
||||
case OSMO_SS7_ASP_PROT_M3UA:
|
||||
cause_user = xua_msg_get_u32(xua, M3UA_IEI_USER_CAUSE);
|
||||
break;
|
||||
case OSMO_SS7_ASP_PROT_SUA:
|
||||
cause_user = xua_msg_get_u32(xua, SUA_IEI_USER_CAUSE);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
cause = cause_user >> 16;
|
||||
user = cause_user & 0xffff;
|
||||
LOGPASP(asp, log_ss, LOGL_NOTICE, "Rx DUPU(%s) for %s User %s, cause %u\n",
|
||||
info_str ? info_str : "", osmo_ss7_pointcode_print(asp->inst, aff_pc),
|
||||
get_value_string(mtp_si_vals, user), cause);
|
||||
|
||||
xua_snm_upu(as, aff_pc, user, cause, info_str);
|
||||
}
|
||||
|
||||
/* an incoming SUA/M3UA SCON was received from a remote SG */
|
||||
void xua_snm_rx_scon(struct osmo_ss7_asp *asp, struct osmo_ss7_as *as, struct xua_msg *xua)
|
||||
{
|
||||
struct xua_msg_part *ie_aff_pc = xua_msg_find_tag(xua, M3UA_IEI_AFFECTED_PC);
|
||||
const char *info_str = xua_msg_get_str(xua, M3UA_IEI_INFO_STRING);
|
||||
uint32_t _concerned_dpc, _cong_level;
|
||||
const uint32_t *concerned_dpc = xua_msg_get_u32p(xua, M3UA_IEI_CONC_DEST, &_concerned_dpc);
|
||||
const uint32_t *cong_level = xua_msg_get_u32p(xua, M3UA_IEI_CONG_IND, &_cong_level);
|
||||
int log_ss = osmo_ss7_asp_get_log_subsys(asp);
|
||||
|
||||
OSMO_ASSERT(ie_aff_pc);
|
||||
|
||||
LOGPASP(asp, log_ss, LOGL_NOTICE, "RX SCON(%s) for %s level=%u\n", info_str ? info_str : "",
|
||||
format_affected_pcs_c(xua, asp->inst, ie_aff_pc), cong_level ? *cong_level : 0);
|
||||
|
||||
xua_snm_scon(as, (const uint32_t *) ie_aff_pc->dat, ie_aff_pc->len / sizeof(uint32_t),
|
||||
concerned_dpc, (const uint8_t *) cong_level, info_str);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(COVERAGE_FLAGS)
|
||||
AM_CFLAGS=-Wall -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS)
|
||||
AM_LDFLAGS=$(COVERAGE_LDFLAGS)
|
||||
|
||||
bin_PROGRAMS = osmo-stp
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/stats.h>
|
||||
|
@ -40,6 +41,7 @@
|
|||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/vty/cpu_sched_vty.h>
|
||||
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
@ -59,10 +61,10 @@ static const struct log_info log_info = {
|
|||
};
|
||||
|
||||
static const char stp_copyright[] =
|
||||
"Copyright (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>\r\n"
|
||||
"Contributions by Holger Freyther, Neels Hofmeyr\r\n"
|
||||
"Copyright (C) 2015-2020 by Harald Welte <laforge@gnumonks.org>\r\n"
|
||||
"Contributions by Holger Freyther, Neels Hofmeyr, Pau Espin, Vadim Yanitskiy\r\n"
|
||||
"License GPLv2+: GNU GPL Version 2 or later <http://gnu.org/licenses/gpl-2.0.html>\r\n"
|
||||
"This is free software: you are free ot change and redistribute it.\r\n"
|
||||
"This is free software: you are free to change and redistribute it.\r\n"
|
||||
"There is NO WARRANTY, to the extent permitted by law.\r\n\r\n"
|
||||
"Free Software lives by contribution. If you use this, please contribute!\r\n";
|
||||
|
||||
|
@ -88,17 +90,49 @@ static void print_help(void)
|
|||
printf(" -D --daemonize Fork the process into a background daemon\n");
|
||||
printf(" -c --config-file filename The config file to use. Default: ./osmo-stp.cfg\n");
|
||||
printf(" -V --version Print the version of OsmoSTP\n");
|
||||
|
||||
printf("\nVTY reference generation:\n");
|
||||
printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n");
|
||||
printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
|
||||
}
|
||||
|
||||
static void handle_long_options(const char *prog_name, const int long_option)
|
||||
{
|
||||
static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
|
||||
|
||||
switch (long_option) {
|
||||
case 1:
|
||||
vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
|
||||
if (vty_ref_mode < 0) {
|
||||
fprintf(stderr, "%s: Unknown VTY reference generation "
|
||||
"mode '%s'\n", prog_name, optarg);
|
||||
exit(2);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
|
||||
get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
|
||||
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
|
||||
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
|
||||
exit(0);
|
||||
default:
|
||||
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
static int long_option = 0;
|
||||
static const struct option long_options[] = {
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ "daemonize", 0, 0, 'D' },
|
||||
{ "config-file", 1, 0, 'c' },
|
||||
{ "version", 0, 0, 'V' },
|
||||
{ "vty-ref-mode", 1, &long_option, 1 },
|
||||
{ "vty-ref-xml", 0, &long_option, 2 },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
@ -107,6 +141,9 @@ static void handle_options(int argc, char **argv)
|
|||
break;
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
handle_long_options(argv[0], long_option);
|
||||
break;
|
||||
case 'h':
|
||||
print_help();
|
||||
exit(0);
|
||||
|
@ -134,11 +171,11 @@ static void handle_options(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
static void signal_handler(int signal)
|
||||
static void signal_handler(int signum)
|
||||
{
|
||||
fprintf(stdout, "signal %u received\n", signal);
|
||||
fprintf(stdout, "signal %u received\n", signum);
|
||||
|
||||
switch (signal) {
|
||||
switch (signum) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
/* FIXME: handle the signal somewhere else and gracefully shut down
|
||||
|
@ -149,8 +186,17 @@ static void signal_handler(int signal)
|
|||
break;
|
||||
case SIGABRT:
|
||||
osmo_generate_backtrace();
|
||||
/* in case of abort, we want to obtain a talloc report
|
||||
* and then return to the caller, who will abort the process */
|
||||
/* in case of abort, we want to obtain a talloc report and
|
||||
* then run default SIGABRT handler, who will generate coredump
|
||||
* and abort the process. abort() should do this for us after we
|
||||
* return, but program wouldn't exit if an external SIGABRT is
|
||||
* received.
|
||||
*/
|
||||
talloc_report(tall_vty_ctx, stderr);
|
||||
talloc_report_full(tall_stp_ctx, stderr);
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
raise(SIGABRT);
|
||||
break;
|
||||
case SIGUSR1:
|
||||
talloc_report(tall_vty_ctx, stderr);
|
||||
talloc_report_full(tall_stp_ctx, stderr);
|
||||
|
@ -167,6 +213,8 @@ int main(int argc, char **argv)
|
|||
{
|
||||
int rc;
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
tall_stp_ctx = talloc_named_const(NULL, 1, "osmo-stp");
|
||||
msgb_talloc_ctx_init(tall_stp_ctx, 0);
|
||||
osmo_init_logging2(tall_stp_ctx, &log_info);
|
||||
|
@ -175,20 +223,21 @@ int main(int argc, char **argv)
|
|||
vty_info.tall_ctx = tall_stp_ctx;
|
||||
vty_init(&vty_info);
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
fputs(stp_copyright, stdout);
|
||||
fputs("\n", stdout);
|
||||
|
||||
OSMO_ASSERT(osmo_ss7_init() == 0);
|
||||
osmo_fsm_log_addr(false);
|
||||
logging_vty_add_cmds();
|
||||
osmo_stats_vty_add_cmds();
|
||||
osmo_ss7_vty_init_sg(tall_stp_ctx);
|
||||
osmo_sccp_vty_init();
|
||||
osmo_cpu_sched_vty_init(tall_stp_ctx);
|
||||
osmo_fsm_vty_add_cmds();
|
||||
osmo_talloc_vty_add_cmds();
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
fputs(stp_copyright, stdout);
|
||||
fputs("\n", stdout);
|
||||
|
||||
rc = vty_read_config_file(cmdline_config.config_file, NULL);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to parse the config file '%s'\n",
|
||||
|
@ -196,7 +245,7 @@ int main(int argc, char **argv)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
rc = telnet_init_dynif(tall_stp_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_STP);
|
||||
rc = telnet_init_default(tall_stp_ctx, NULL, OSMO_VTY_PORT_STP);
|
||||
if (rc < 0) {
|
||||
perror("Error binding VTY port\n");
|
||||
exit(1);
|
||||
|
@ -208,6 +257,7 @@ int main(int argc, char **argv)
|
|||
signal(SIGUSR1, &signal_handler);
|
||||
signal(SIGUSR2, &signal_handler);
|
||||
osmo_init_ignore_signals();
|
||||
rate_ctr_init(tall_stp_ctx);
|
||||
|
||||
if (cmdline_config.daemonize) {
|
||||
rc = osmo_daemonize();
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -Wall
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
|
||||
AM_LDFLAGS = -no-install
|
||||
|
||||
EXTRA_DIST = m2ua_test.ok
|
||||
|
||||
noinst_PROGRAMS = m2ua_test
|
||||
check_PROGRAMS = m2ua_test
|
||||
m2ua_test_SOURCES = m2ua_test.c
|
||||
m2ua_test_LDADD = $(top_builddir)/src/libxua.a $(LIBOSMOCORE_LIBS)
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) -Wall
|
||||
noinst_PROGRAMS = mtp_parse_test
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS)
|
||||
AM_LDFLAGS = -no-install
|
||||
|
||||
check_PROGRAMS = mtp_parse_test
|
||||
|
||||
EXTRA_DIST = mtp_parse_test.ok
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS)
|
||||
AM_LDFLAGS = -no-install
|
||||
|
||||
EXTRA_DIST = sccp_test.ok
|
||||
|
||||
noinst_PROGRAMS = sccp_test
|
||||
check_PROGRAMS = sccp_test
|
||||
|
||||
sccp_test_SOURCES = sccp_test.c
|
||||
sccp_test_LDADD = \
|
||||
|
|
|
@ -16,10 +16,6 @@
|
|||
* 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>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -Wall
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||
|
||||
AM_LDFLAGS = -static
|
||||
AM_LDFLAGS = -static -no-install
|
||||
LDADD = $(top_builddir)/src/libosmo-sigtran.la \
|
||||
$(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMONETIF_LIBS) $(LIBSCTP_LIBS)
|
||||
|
||||
EXTRA_DIST = ss7_test.ok ss7_test.err
|
||||
|
||||
noinst_PROGRAMS = ss7_test
|
||||
check_PROGRAMS = ss7_test
|
||||
|
||||
ss7_test_SOURCES = ss7_test.c
|
||||
|
|
|
@ -248,7 +248,8 @@ static void test_as(void)
|
|||
OSMO_ASSERT(osmo_ss7_as_find_by_rctx(s7i, 2342) == as);
|
||||
OSMO_ASSERT(osmo_ss7_as_add_asp(as, "asp1") == -ENODEV);
|
||||
|
||||
asp = osmo_ss7_asp_find_or_create(s7i, "asp1", 0, M3UA_PORT, OSMO_SS7_ASP_PROT_M3UA);
|
||||
asp = osmo_ss7_asp_find_or_create2(s7i, "asp1", 0, M3UA_PORT,
|
||||
IPPROTO_SCTP, OSMO_SS7_ASP_PROT_M3UA);
|
||||
OSMO_ASSERT(asp);
|
||||
|
||||
OSMO_ASSERT(osmo_ss7_as_has_asp(as, asp) == false);
|
||||
|
@ -290,7 +291,7 @@ static void init_logging(void)
|
|||
msgb_talloc_ctx_init(tall_ctx, 0);
|
||||
osmo_init_logging2(tall_ctx, &log_info);
|
||||
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(log_cats); i++)
|
||||
log_set_category_filter(osmo_stderr_target, log_cats[i], 1, LOGL_DEBUG);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -Wall
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||
|
||||
AM_LDFLAGS = -static
|
||||
AM_LDFLAGS = -static -no-install
|
||||
LDADD = $(top_builddir)/src/libosmo-sigtran.la \
|
||||
$(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMONETIF_LIBS) $(LIBSCTP_LIBS)
|
||||
|
||||
|
@ -10,19 +10,21 @@ EXTRA_DIST = \
|
|||
vty_test_runner.py \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = ss7_asp_vty_test
|
||||
check_PROGRAMS = ss7_asp_vty_test
|
||||
|
||||
ss7_asp_vty_test_SOURCES = ss7_asp_vty_test.c
|
||||
|
||||
if ENABLE_EXT_TESTS
|
||||
ext-tests:
|
||||
ext-tests: $(top_builddir)/stp/osmo-stp
|
||||
$(MAKE) vty-test
|
||||
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
else
|
||||
ext-tests:
|
||||
echo "Not running python-based external tests (determined at configure-time)"
|
||||
endif
|
||||
|
||||
vty-python-test: $(BUILT_SOURCES)
|
||||
vty-python-test: $(top_builddir)/stp/osmo-stp
|
||||
$(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v
|
||||
|
||||
# To update the VTY script from current application behavior,
|
||||
|
|
|
@ -2,9 +2,16 @@ ss7_asp_vty_test> list
|
|||
... !show cs7
|
||||
show cs7 instance <0-15> users
|
||||
show cs7 (sua|m3ua|ipa) [<0-65534>]
|
||||
show cs7 (sua|m3ua|ipa) (sctp|tcp) [<0-65534>]
|
||||
show cs7 config
|
||||
show cs7 instance <0-15> asp
|
||||
show cs7 instance <0-15> asp name ASP_NAME
|
||||
show cs7 instance <0-15> asp-remaddr
|
||||
show cs7 instance <0-15> asp-remaddr name ASP_NAME
|
||||
show cs7 instance <0-15> asp-assoc-status
|
||||
show cs7 instance <0-15> asp-assoc-status name ASP_NAME
|
||||
show cs7 instance <0-15> as (active|all|m3ua|sua)
|
||||
show cs7 instance <0-15> route
|
||||
show cs7 instance <0-15> sccp addressbook
|
||||
show cs7 instance <0-15> sccp users
|
||||
show cs7 instance <0-15> sccp ssn <0-65535>
|
||||
|
@ -17,9 +24,17 @@ ss7_asp_vty_test# list
|
|||
... !show cs7
|
||||
show cs7 instance <0-15> users
|
||||
show cs7 (sua|m3ua|ipa) [<0-65534>]
|
||||
show cs7 (sua|m3ua|ipa) (sctp|tcp) [<0-65534>]
|
||||
show cs7 config
|
||||
cs7 instance <0-15> asp NAME disconnect
|
||||
show cs7 instance <0-15> asp
|
||||
show cs7 instance <0-15> asp name ASP_NAME
|
||||
show cs7 instance <0-15> asp-remaddr
|
||||
show cs7 instance <0-15> asp-remaddr name ASP_NAME
|
||||
show cs7 instance <0-15> asp-assoc-status
|
||||
show cs7 instance <0-15> asp-assoc-status name ASP_NAME
|
||||
show cs7 instance <0-15> as (active|all|m3ua|sua)
|
||||
show cs7 instance <0-15> route
|
||||
show cs7 instance <0-15> sccp addressbook
|
||||
show cs7 instance <0-15> sccp users
|
||||
show cs7 instance <0-15> sccp ssn <0-65535>
|
||||
|
@ -40,16 +55,30 @@ ss7_asp_vty_test# show cs7 ?
|
|||
config Currently running cs7 configuration
|
||||
|
||||
ss7_asp_vty_test# show cs7 m3ua ?
|
||||
[<0-65534>] Port Number
|
||||
[<0-65534>] Local Port Number
|
||||
sctp SCTP (Stream Control Transmission Protocol)
|
||||
tcp TCP (Transmission Control Protocol)
|
||||
|
||||
ss7_asp_vty_test# show cs7 m3ua 2905 ?
|
||||
<cr>
|
||||
|
||||
ss7_asp_vty_test# show cs7 m3ua sctp ?
|
||||
[<0-65534>] Local Port Number
|
||||
|
||||
ss7_asp_vty_test# show cs7 m3ua sctp 2905 ?
|
||||
<cr>
|
||||
|
||||
ss7_asp_vty_test# show cs7 instance ?
|
||||
<0-15> An instance of the SS7 stack
|
||||
|
||||
ss7_asp_vty_test# show cs7 instance 0 ?
|
||||
users User Table
|
||||
asp Application Server Process (ASP)
|
||||
as Application Server (AS)
|
||||
sccp Signalling Connection Control Part
|
||||
users User Table
|
||||
asp Application Server Process (ASP)
|
||||
asp-remaddr Application Server Process (ASP) remote addresses information
|
||||
asp-assoc-status Application Server Process (ASP) SCTP association status
|
||||
as Application Server (AS)
|
||||
route Routing Table
|
||||
sccp Signalling Connection Control Part
|
||||
|
||||
ss7_asp_vty_test# show cs7 instance 0 as ?
|
||||
active Display all active ASs
|
||||
|
@ -80,12 +109,14 @@ ss7_asp_vty_test(config-cs7)# list
|
|||
point-code delimiter (default|dash)
|
||||
xua rkm routing-key-allocation (static-only|dynamic-permitted)
|
||||
asp NAME <0-65535> <0-65535> (sua|m3ua|ipa)
|
||||
asp NAME <0-65535> <0-65535> (sua|m3ua|ipa) (sctp|tcp)
|
||||
no asp NAME
|
||||
as NAME (sua|m3ua|ipa)
|
||||
no as NAME
|
||||
sccp-address NAME
|
||||
no sccp-address NAME
|
||||
sccp-timer (conn_est|ias|iar|rel|repeat_rel|int|guard|reset|reassembly) <1-999999>
|
||||
sccp max-optional-data (<0-999999>|standard)
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# ?
|
||||
...
|
||||
|
@ -98,6 +129,7 @@ ss7_asp_vty_test(config-cs7)# ?
|
|||
as Configure an Application Server
|
||||
sccp-address Create/Modify an SCCP addressbook entry
|
||||
sccp-timer Configure SCCP timer values, see ITU-T Q.714
|
||||
sccp Configure SCCP behavior
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# description ?
|
||||
TEXT Text until the end of the line
|
||||
|
@ -130,19 +162,23 @@ ss7_asp_vty_test(config-cs7)# xua ?
|
|||
ss7_asp_vty_test(config-cs7)# xua rkm ?
|
||||
routing-key-allocation Routing Key Management Allocation Policy
|
||||
ss7_asp_vty_test(config-cs7)# xua rkm routing-key-allocation ?
|
||||
static-only Only static (pre-confgured) Routing Keys permitted
|
||||
static-only Only static (pre-configured) Routing Keys permitted
|
||||
dynamic-permitted Dynamically allocate Routing Keys for what ASPs request
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# asp ?
|
||||
NAME Name of ASP
|
||||
ss7_asp_vty_test(config-cs7)# asp foo ?
|
||||
<0-65535> Remote SCTP port number
|
||||
<0-65535> Remote port number
|
||||
ss7_asp_vty_test(config-cs7)# asp foo 0 ?
|
||||
<0-65535> Local SCTP port number
|
||||
<0-65535> Local port number
|
||||
ss7_asp_vty_test(config-cs7)# asp foo 0 0 ?
|
||||
sua SCCP User Adaptation
|
||||
m3ua MTP3 User Adaptation
|
||||
ipa IPA Multiplex (SCCP Lite)
|
||||
ss7_asp_vty_test(config-cs7)# asp foo 0 0 m3ua ?
|
||||
sctp SCTP (Stream Control Transmission Protocol)
|
||||
tcp TCP (Transmission Control Protocol)
|
||||
<cr>
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# as ?
|
||||
NAME Name of the Application Server
|
||||
|
@ -215,32 +251,83 @@ ss7_asp_vty_test(config-cs7)# asp my-asp 12345 54321 m3ua
|
|||
ss7_asp_vty_test(config-cs7-asp)# list
|
||||
...
|
||||
description .TEXT
|
||||
remote-ip A.B.C.D
|
||||
local-ip A.B.C.D
|
||||
remote-ip (A.B.C.D|X:X::X:X) [primary]
|
||||
no remote-ip (A.B.C.D|X:X::X:X)
|
||||
local-ip (A.B.C.D|X:X::X:X) [primary]
|
||||
no local-ip (A.B.C.D|X:X::X:X)
|
||||
qos-class <0-255>
|
||||
role (sg|asp|ipsp)
|
||||
sctp-role (client|server)
|
||||
transport-role (client|server)
|
||||
sctp-param init (num-ostreams|max-instreams|max-attempts|timeout) <0-65535>
|
||||
no sctp-param init (num-ostreams|max-instreams|max-attempts|timeout)
|
||||
block
|
||||
shutdown
|
||||
...
|
||||
|
||||
ss7_asp_vty_test(config-cs7-asp)# ?
|
||||
...
|
||||
description Save human-readable description of the object
|
||||
remote-ip Specify Remote IP Address of ASP
|
||||
local-ip Specify Local IP Address from which to contact ASP
|
||||
qos-class Specify QoS Class of ASP
|
||||
role Specify the xUA role for this ASP
|
||||
sctp-role Specify the SCTP role for this ASP
|
||||
block Allows a SCTP Association with ASP, but doesn't let it become active
|
||||
shutdown Terminates SCTP association; New associations will be rejected
|
||||
description Save human-readable description of the object
|
||||
remote-ip Specify Remote IP Address of ASP
|
||||
no Negate a command or set its defaults
|
||||
local-ip Specify Local IP Address from which to contact ASP
|
||||
qos-class Specify QoS Class of ASP
|
||||
role Specify the xUA role for this ASP
|
||||
transport-role Specify the transport layer role for this ASP
|
||||
sctp-param Configure SCTP parameters
|
||||
block Allows a SCTP Association with ASP, but doesn't let it become active
|
||||
shutdown Terminates SCTP association; New associations will be rejected
|
||||
...
|
||||
|
||||
ss7_asp_vty_test(config-cs7-asp)# no ?
|
||||
...
|
||||
sctp-param Configure SCTP parameters
|
||||
quirk Disable quirk to work around interop issues
|
||||
...
|
||||
|
||||
ss7_asp_vty_test(config-cs7-asp)# remote-ip 127.0.0.200
|
||||
ss7_asp_vty_test(config-cs7-asp)# local-ip 127.0.0.100
|
||||
ss7_asp_vty_test(config-cs7-asp)# do show cs7 instance 0 asp
|
||||
Effect Primary
|
||||
ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP
|
||||
------------ ------------ ------------- ---- ----------------------- ----------
|
||||
my-asp ? uninitialized m3ua 127.0.0.200:12345
|
||||
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
|
||||
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
|
||||
my-asp ? uninitialized m3ua sg server 127.0.0.100:54321 127.0.0.200:12345
|
||||
ss7_asp_vty_test(config-cs7-asp)# remote-ip 127.0.0.201
|
||||
ss7_asp_vty_test(config-cs7-asp)# local-ip 127.0.0.101
|
||||
ss7_asp_vty_test(config-cs7-asp)# do show cs7 instance 0 asp
|
||||
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
|
||||
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
|
||||
my-asp ? uninitialized m3ua sg server (127.0.0.100|127.0.0.101):54321 (127.0.0.200|127.0.0.201):12345
|
||||
ss7_asp_vty_test(config-cs7-asp)# ! Mark as primary:
|
||||
ss7_asp_vty_test(config-cs7-asp)# remote-ip 127.0.0.201 primary
|
||||
ss7_asp_vty_test(config-cs7-asp)# ! 'local-ip 127.0.0.101 primary' cannot be tested here since output may be different based on sysctl available
|
||||
ss7_asp_vty_test(config-cs7-asp)# local-ip 127.0.0.101
|
||||
...
|
||||
ss7_asp_vty_test(config-cs7-asp)# do show cs7 instance 0 asp
|
||||
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
|
||||
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
|
||||
my-asp ? uninitialized m3ua sg server (127.0.0.100|127.0.0.101):54321 (127.0.0.200|127.0.0.201*):12345
|
||||
ss7_asp_vty_test(config-cs7-asp)# show running-config
|
||||
...
|
||||
local-ip 127.0.0.100
|
||||
local-ip 127.0.0.101
|
||||
remote-ip 127.0.0.200
|
||||
remote-ip 127.0.0.201 primary
|
||||
...
|
||||
end
|
||||
ss7_asp_vty_test(config-cs7-asp)# ! Mark as non-primary:
|
||||
ss7_asp_vty_test(config-cs7-asp)# remote-ip 127.0.0.201
|
||||
ss7_asp_vty_test(config-cs7-asp)# local-ip 127.0.0.101
|
||||
ss7_asp_vty_test(config-cs7-asp)# do show cs7 instance 0 asp
|
||||
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
|
||||
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
|
||||
my-asp ? uninitialized m3ua sg server (127.0.0.100|127.0.0.101):54321 (127.0.0.200|127.0.0.201):12345
|
||||
ss7_asp_vty_test(config-cs7-asp)# show running-config
|
||||
...
|
||||
local-ip 127.0.0.100
|
||||
local-ip 127.0.0.101
|
||||
remote-ip 127.0.0.200
|
||||
remote-ip 127.0.0.201
|
||||
...
|
||||
end
|
||||
ss7_asp_vty_test(config-cs7-asp)# exit
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# as my-ass m3ua
|
||||
|
@ -301,26 +388,23 @@ ss7_asp_vty_test(config-cs7-as)# asp my-asp
|
|||
ss7_asp_vty_test(config-cs7-as)# routing-key 0 3.2.1
|
||||
|
||||
ss7_asp_vty_test(config-cs7-as)# do show cs7 instance 0 asp
|
||||
Effect Primary
|
||||
ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP
|
||||
------------ ------------ ------------- ---- ----------------------- ----------
|
||||
my-asp ? ASP_DOWN m3ua 127.0.0.200:12345
|
||||
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
|
||||
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
|
||||
my-asp my-ass ASP_DOWN m3ua sg server (127.0.0.100|127.0.0.101):54321 (127.0.0.200|127.0.0.201):12345
|
||||
|
||||
ss7_asp_vty_test(config-cs7-as)# exit
|
||||
ss7_asp_vty_test(config-cs7)# do show cs7 instance 0 asp
|
||||
Effect Primary
|
||||
ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP
|
||||
------------ ------------ ------------- ---- ----------------------- ----------
|
||||
my-asp ? ASP_DOWN m3ua 127.0.0.200:12345
|
||||
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
|
||||
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
|
||||
my-asp my-ass ASP_DOWN m3ua sg server (127.0.0.100|127.0.0.101):54321 (127.0.0.200|127.0.0.201):12345
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# exit
|
||||
|
||||
|
||||
ss7_asp_vty_test(config)# do show cs7 instance 0 asp
|
||||
Effect Primary
|
||||
ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP
|
||||
------------ ------------ ------------- ---- ----------------------- ----------
|
||||
my-asp ? ASP_DOWN m3ua 127.0.0.200:12345
|
||||
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
|
||||
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
|
||||
my-asp my-ass ASP_DOWN m3ua sg server (127.0.0.100|127.0.0.101):54321 (127.0.0.200|127.0.0.201):12345
|
||||
|
||||
ss7_asp_vty_test(config)# do show cs7 instance 0 as all
|
||||
Routing Routing Key Cic Cic Traffic
|
||||
|
@ -332,20 +416,24 @@ my-ass AS_DOWN 0 3.2.1
|
|||
|
||||
ss7_asp_vty_test(config)# show running-config
|
||||
...
|
||||
cs7 instance 1
|
||||
sccp-address bar
|
||||
routing-indicator PC
|
||||
point-code 1.2.3
|
||||
cs7 instance 0
|
||||
asp my-asp 12345 54321 m3ua
|
||||
local-ip 127.0.0.100
|
||||
local-ip 127.0.0.101
|
||||
remote-ip 127.0.0.200
|
||||
remote-ip 127.0.0.201
|
||||
role sg
|
||||
sctp-role server
|
||||
as my-ass m3ua
|
||||
asp my-asp
|
||||
routing-key 0 3.2.1
|
||||
sccp-address foo
|
||||
routing-indicator PC
|
||||
point-code 1.2.3
|
||||
cs7 instance 1
|
||||
sccp-address bar
|
||||
routing-indicator PC
|
||||
point-code 1.2.3
|
||||
end
|
||||
|
||||
ss7_asp_vty_test(config)# do show cs7 instance 0 users
|
||||
|
@ -359,9 +447,8 @@ No ASP named 'unknown-asp' found
|
|||
ss7_asp_vty_test(config-cs7)# no asp my-asp
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# do show cs7 instance 0 asp
|
||||
Effect Primary
|
||||
ASP Name AS Name State Type Remote IP Addr:Rmt Port SCTP
|
||||
------------ ------------ ------------- ---- ----------------------- ----------
|
||||
ASP Name AS Name State Type Role SCTP Role Local Addresses Remote Addresses
|
||||
------------ ------------ ------------- ---- ---- --------- ----------------------- -----------------------
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# do show cs7 instance 0 as all
|
||||
Routing Routing Key Cic Cic Traffic
|
||||
|
@ -422,3 +509,43 @@ ss7_asp_vty_test(config-cs7)# sccp-timer ?
|
|||
|
||||
ss7_asp_vty_test(config-cs7)# sccp-timer conn_est ?
|
||||
<1-999999> Timer value, in seconds
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# sccp ?
|
||||
max-optional-data Adjust the upper bound for the optional data length (the payload) for CR, CC, CREF and RLSD messages. For any Optional Data part larger than this value in octets, send CR, CC, CREF and RLSD messages without any payload, and send the data payload in a separate Data Form 1 message. ITU-T Q.713 sections 4.2 thru 4.5 define a limit of 130 bytes for the 'Data' parameter. This limit can be adjusted here. May be useful for interop with nonstandard SCCP peers.
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# sccp max-optional-data ?
|
||||
<0-999999> Set a non-standard maximum allowed number of bytes
|
||||
standard Use the ITU-T Q.713 4.2 to 4.5 standard value of 130
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# show running-config
|
||||
... !sccp max-optional-data
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# sccp max-optional-data 0
|
||||
ss7_asp_vty_test(config-cs7)# show running-config
|
||||
...
|
||||
sccp max-optional-data 0
|
||||
...
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# sccp max-optional-data 123
|
||||
ss7_asp_vty_test(config-cs7)# show running-config
|
||||
...
|
||||
sccp max-optional-data 123
|
||||
...
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# sccp max-optional-data 999999
|
||||
ss7_asp_vty_test(config-cs7)# show running-config
|
||||
...
|
||||
cs7 instance 0
|
||||
...
|
||||
sccp max-optional-data 999999
|
||||
...
|
||||
cs7 instance 1
|
||||
... !sccp max-optional-data
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# sccp max-optional-data standard
|
||||
ss7_asp_vty_test(config-cs7)# show running-config
|
||||
... !sccp max-optional-data
|
||||
|
||||
ss7_asp_vty_test(config-cs7)# sccp max-optional-data 130
|
||||
ss7_asp_vty_test(config-cs7)# show running-config
|
||||
... !sccp max-optional-data
|
||||
|
|
|
@ -15,10 +15,6 @@
|
|||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
@ -45,7 +41,7 @@ void *root_ctx = NULL;
|
|||
const struct log_info log_info = {
|
||||
};
|
||||
|
||||
static void print_help()
|
||||
static void print_help(void)
|
||||
{
|
||||
printf( "options:\n"
|
||||
" -h --help this text\n"
|
||||
|
@ -186,7 +182,7 @@ int main(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42043);
|
||||
rc = telnet_init_default(root_ctx, NULL, 42043);
|
||||
if (rc < 0)
|
||||
return 2;
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ from osmopy.osmo_ipa import IPA
|
|||
# to be able to find $top_srcdir/doc/...
|
||||
confpath = os.path.join(sys.path[0], '..')
|
||||
|
||||
TIMEOUT = 10
|
||||
|
||||
class TestVTYBase(unittest.TestCase):
|
||||
|
||||
def checkForEndAndExit(self):
|
||||
|
@ -111,7 +113,9 @@ class TestVTYSTP(TestVTYBase):
|
|||
# first check if STP is listening in required addresses:
|
||||
found = False
|
||||
for i in range(5):
|
||||
if self.check_sctp_sock_local(['127.0.0.1', '127.0.0.2'], 2905):
|
||||
if self.check_sctp_sock_local(['127.0.0.1', '127.0.0.2',
|
||||
'0000:0000:0000:0000:0000:0000:0000:0001'],
|
||||
2905):
|
||||
found = True
|
||||
break
|
||||
else:
|
||||
|
@ -122,14 +126,27 @@ class TestVTYSTP(TestVTYBase):
|
|||
proto = socket.IPPROTO_SCTP
|
||||
except AttributeError: # it seems to be not defined under python2?
|
||||
proto = 132
|
||||
# IPv4:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, proto)
|
||||
s.bind(('127.0.0.3', 0))
|
||||
s.settimeout(TIMEOUT)
|
||||
try:
|
||||
s.connect(('127.0.0.2',2905))
|
||||
except socket.error as msg:
|
||||
s.close()
|
||||
self.assertTrue(False)
|
||||
print("Connected to STP through SCTP")
|
||||
self.fail("Failed to connect IPv4 socket: %s" % msg)
|
||||
print("Connected to STP through SCTP (IPv4)")
|
||||
s.close()
|
||||
# IPv6:
|
||||
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, proto)
|
||||
s.bind(('::1', 0))
|
||||
s.settimeout(TIMEOUT)
|
||||
try:
|
||||
s.connect(('::1',2905))
|
||||
except socket.error as msg:
|
||||
s.close()
|
||||
self.fail("Failed to connect IPv6 socket: %s" % msg)
|
||||
print("Connected to STP through SCTP (IPv6)")
|
||||
s.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -Wall
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||
|
||||
AM_LDFLAGS = -static
|
||||
AM_LDFLAGS = -static -no-install
|
||||
LDADD = $(top_builddir)/src/libosmo-sigtran.la \
|
||||
$(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMONETIF_LIBS) $(LIBSCTP_LIBS)
|
||||
|
||||
EXTRA_DIST = xua_test.ok xua_test.err
|
||||
|
||||
noinst_HEADERS = sccp_test_data.h
|
||||
noinst_PROGRAMS = xua_test
|
||||
check_PROGRAMS = xua_test
|
||||
|
||||
xua_test_SOURCES = xua_test.c sccp_test_data.c
|
||||
|
|
|
@ -100,3 +100,56 @@ const uint8_t tcap_global_title[183] = {
|
|||
0x01, 0x41, 0x84, 0x01, 0x04, 0x30, 0x03, 0x82,
|
||||
0x01, 0x18, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
/* SCCP LUDT message containing a RANAP RESET */
|
||||
const uint8_t ludt_ranap_reset[40] = {
|
||||
0x13, 0x00, 0x0f, 0x07, 0x00, 0x0a, 0x00, 0x0d,
|
||||
0x00, 0x00, 0x00, 0x04, 0x43, 0xc4, 0x04, 0x8e,
|
||||
0x04, 0x43, 0xe9, 0x03, 0x8e, 0x11, 0x00, 0x00,
|
||||
0x09, 0x00, 0x0d, 0x00, 0x00, 0x02, 0x00, 0x04,
|
||||
0x40, 0x01, 0x42, 0x00, 0x03, 0x00, 0x01, 0x00
|
||||
};
|
||||
|
||||
const uint8_t ludt_data300bytes[323] = {
|
||||
0x13, 0x00, 0x0f, 0x07, 0x00, 0x0a, 0x00, 0x0d,
|
||||
0x00, 0x00, 0x00, 0x04, 0x43, 0xc4, 0x04, 0x8e,
|
||||
0x04, 0x43, 0xe9, 0x03, 0x8e, 0x2c, 0x01, 0xab, /* 0x2c, 0x01 (network order) = len = 300 */
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
|
||||
0xab, 0xab, 0xab,
|
||||
};
|
||||
|
|
|
@ -12,3 +12,5 @@ extern const uint8_t bssmap_clear[13];
|
|||
extern const uint8_t bssmap_released[14];
|
||||
extern const uint8_t bssmap_release_complete[7];
|
||||
extern const uint8_t tcap_global_title[183];
|
||||
extern const uint8_t ludt_ranap_reset[40];
|
||||
extern const uint8_t ludt_data300bytes[323];
|
||||
|
|
|
@ -369,9 +369,13 @@ struct sccp2sua_testcase {
|
|||
#define PANDSIZ(x) { x, ARRAY_SIZE(x) }
|
||||
#define PARTU32(x, data) { .tag = x, .len = 4, .dat = (uint8_t *) data }
|
||||
#define PARTARR(x, data) { .tag = x, .len = ARRAY_SIZE(data), .dat = (uint8_t *) data }
|
||||
/* GCC-4 errors with 'initializer element is not constant' if using XUA_HDR
|
||||
* inside a const struct (OS#5004) */
|
||||
#define _XUA_HDR(class, type) { .spare = 0, .msg_class = (class), .msg_type = (type) }
|
||||
|
||||
const uint32_t sua_proto_class0 = 0;
|
||||
const uint32_t sua_proto_class2 = 2;
|
||||
const uint32_t sua_hop_ctr_0x0f = 0x0f;
|
||||
const uint32_t sua_loc_ref_bsc = 0x10203;
|
||||
const uint32_t sua_loc_ref_msc = 0x00003;
|
||||
const uint32_t sua_cause0 = 0x00003;
|
||||
|
@ -384,7 +388,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
|
|||
.name = "BSSMAP-RESET",
|
||||
.sccp = PANDSIZ(bssmap_reset),
|
||||
.sua = {
|
||||
.hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
|
||||
.hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
|
||||
.parts = {
|
||||
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class0),
|
||||
PARTARR(SUA_IEI_DEST_ADDR, sua_addr_ssn_bssmap),
|
||||
|
@ -395,7 +399,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
|
|||
.name = "BSSMAP-RESET-ACK",
|
||||
.sccp = PANDSIZ(bssmap_reset_ack),
|
||||
.sua = {
|
||||
.hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
|
||||
.hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
|
||||
.parts = {
|
||||
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class0),
|
||||
PARTARR(SUA_IEI_DEST_ADDR, sua_addr_ssn_bssmap_pc1),
|
||||
|
@ -406,7 +410,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
|
|||
.name = "BSSMAP-PAGING",
|
||||
.sccp = PANDSIZ(bssmap_paging),
|
||||
.sua = {
|
||||
.hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
|
||||
.hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
|
||||
.parts = {
|
||||
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class0),
|
||||
PARTARR(SUA_IEI_DEST_ADDR, sua_addr_ssn_bssmap_pc1),
|
||||
|
@ -417,7 +421,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
|
|||
.name = "BSSMAP-UDT",
|
||||
.sccp = PANDSIZ(bssmap_udt),
|
||||
.sua = {
|
||||
.hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
|
||||
.hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
|
||||
.parts = {
|
||||
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class0),
|
||||
PARTARR(SUA_IEI_DEST_ADDR, sua_addr_ssn_bssmap),
|
||||
|
@ -428,7 +432,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
|
|||
.name = "BSSMAP-CR",
|
||||
.sccp = PANDSIZ(bssmap_cr),
|
||||
.sua = {
|
||||
.hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_CORE),
|
||||
.hdr = _XUA_HDR(SUA_MSGC_CO, SUA_CO_CORE),
|
||||
.parts = {
|
||||
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class2),
|
||||
PARTU32(SUA_IEI_SRC_REF, &sua_loc_ref_bsc),
|
||||
|
@ -436,10 +440,14 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
|
|||
},
|
||||
},
|
||||
}, {
|
||||
/* This case expectedly prints "Input != re-encoded output!" because input has
|
||||
* (struct sccp_connection_confirm).optional_start=1 pointing to SCCP_PNC_END_OF_OPTIONAL,
|
||||
* while our (re-)encoder uses optional_start=0 which means entire msg is 1 byte
|
||||
* lower (hence better). Both are valid. */
|
||||
.name = "BSSMAP-CC",
|
||||
.sccp = PANDSIZ(bssmap_cc),
|
||||
.sua = {
|
||||
.hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_COAK),
|
||||
.hdr = _XUA_HDR(SUA_MSGC_CO, SUA_CO_COAK),
|
||||
.parts = {
|
||||
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class2),
|
||||
PARTU32(SUA_IEI_SRC_REF, &sua_loc_ref_msc),
|
||||
|
@ -450,7 +458,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
|
|||
.name = "BSSMAP-DTAP",
|
||||
.sccp = PANDSIZ(bssmap_dtap),
|
||||
.sua = {
|
||||
.hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_CODT),
|
||||
.hdr = _XUA_HDR(SUA_MSGC_CO, SUA_CO_CODT),
|
||||
.parts = {
|
||||
PARTU32(SUA_IEI_SRC_REF, &sua_loc_ref_msc),
|
||||
},
|
||||
|
@ -459,7 +467,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
|
|||
.name = "BSSMAP-CLEAR",
|
||||
.sccp = PANDSIZ(bssmap_clear),
|
||||
.sua = {
|
||||
.hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_CODT),
|
||||
.hdr = _XUA_HDR(SUA_MSGC_CO, SUA_CO_CODT),
|
||||
.parts = {
|
||||
PARTU32(SUA_IEI_SRC_REF, &sua_loc_ref_msc),
|
||||
},
|
||||
|
@ -468,7 +476,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
|
|||
.name = "BSSMAP-RELEASED",
|
||||
.sccp = PANDSIZ(bssmap_released),
|
||||
.sua = {
|
||||
.hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_RELRE),
|
||||
.hdr = _XUA_HDR(SUA_MSGC_CO, SUA_CO_RELRE),
|
||||
.parts = {
|
||||
PARTU32(SUA_IEI_DEST_REF, &sua_loc_ref_msc),
|
||||
PARTU32(SUA_IEI_SRC_REF, &sua_loc_ref_bsc),
|
||||
|
@ -479,7 +487,7 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
|
|||
.name = "BSSMAP-RELEASE_COMPLETE",
|
||||
.sccp = PANDSIZ(bssmap_release_complete),
|
||||
.sua = {
|
||||
.hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_RELCO),
|
||||
.hdr = _XUA_HDR(SUA_MSGC_CO, SUA_CO_RELCO),
|
||||
.parts = {
|
||||
PARTU32(SUA_IEI_DEST_REF, &sua_loc_ref_bsc),
|
||||
PARTU32(SUA_IEI_SRC_REF, &sua_loc_ref_msc),
|
||||
|
@ -489,17 +497,45 @@ static const struct sccp2sua_testcase sccp2sua_testcases[] = {
|
|||
.name = "TCAP",
|
||||
.sccp = PANDSIZ(tcap_global_title),
|
||||
.sua = {
|
||||
.hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
|
||||
.hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
|
||||
.parts = {
|
||||
},
|
||||
},
|
||||
}, {
|
||||
/* This case expectedly prints "Input != re-encoded output!" because input is a
|
||||
* LUDT with a small payload size fitting 1-byte length, while our (re-)encoder
|
||||
* encodes it as UDT in this case because it's enough to encode and transmit the
|
||||
* message. Both are valid. */
|
||||
.name = "LUDT-RANAP_RELEASE",
|
||||
.sccp = PANDSIZ(ludt_ranap_reset),
|
||||
.sua = {
|
||||
.hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
|
||||
.parts = {
|
||||
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class0),
|
||||
PARTU32(SUA_IEI_S7_HOP_CTR, &sua_hop_ctr_0x0f),
|
||||
PARTARR(SUA_IEI_DEST_ADDR, sua_addr_ssn_bssmap),
|
||||
PARTARR(SUA_IEI_SRC_ADDR, sua_addr_ssn_bssmap),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.name = "LUDT-data300bytes",
|
||||
.sccp = PANDSIZ(ludt_data300bytes),
|
||||
.sua = {
|
||||
.hdr = _XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT),
|
||||
.parts = {
|
||||
PARTU32(SUA_IEI_PROTO_CLASS, &sua_proto_class0),
|
||||
PARTU32(SUA_IEI_S7_HOP_CTR, &sua_hop_ctr_0x0f),
|
||||
PARTARR(SUA_IEI_DEST_ADDR, sua_addr_ssn_bssmap),
|
||||
PARTARR(SUA_IEI_SRC_ADDR, sua_addr_ssn_bssmap),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static void test_sccp2sua_case(const struct sccp2sua_testcase *tcase)
|
||||
{
|
||||
struct xua_msg *xua;
|
||||
struct msgb *msg = msgb_alloc(300, "SCCP2SUA Test Input");
|
||||
struct msgb *msg = msgb_alloc(500, "SCCP2SUA Test Input");
|
||||
struct msgb *msg2;
|
||||
|
||||
printf("\n=> %s\n", tcase->name);
|
||||
|
@ -569,7 +605,7 @@ static void test_rkm(void)
|
|||
talloc_free(xua);
|
||||
}
|
||||
|
||||
void test_sccp_addr_cmp()
|
||||
static void test_sccp_addr_cmp(void)
|
||||
{
|
||||
int ai;
|
||||
int bi;
|
||||
|
@ -616,7 +652,9 @@ int main(int argc, char **argv)
|
|||
stderr_target = log_target_create_stderr();
|
||||
log_add_target(stderr_target);
|
||||
log_set_use_color(stderr_target, 0);
|
||||
log_set_print_filename(stderr_target, 0);
|
||||
log_set_print_filename2(stderr_target, LOG_FILENAME_NONE);
|
||||
log_set_print_category(stderr_target, 0);
|
||||
log_set_print_category_hex(stderr_target, 0);
|
||||
|
||||
test_isup_parse();
|
||||
test_sccp_addr_parser();
|
||||
|
|
|
@ -55,7 +55,8 @@ SCCP Input: [L2]> 02 01 02 03 00 00 03 02 01 00
|
|||
Transcoding message SCCP -> XUA
|
||||
Decoded SUA: HDR=(CO:COAK,V=0,LEN=0), PART(T=Protocol Class,L=4,D=00000002), PART(T=Destination Reference,L=4,D=00010203), PART(T=Source Reference,L=4,D=00000003)
|
||||
Re-Encoding decoded SUA to SCCP
|
||||
SCCP Output: [L2]> 02 01 02 03 00 00 03 02 01 00
|
||||
SCCP Output: [L2]> 02 01 02 03 00 00 03 02 00
|
||||
Input != re-encoded output!
|
||||
|
||||
=> BSSMAP-DTAP
|
||||
SCCP Input: [L2]> 06 00 00 03 00 01 0f 01 00 0c 03 05 5c 08 11 81 33 66 02 13 45 f4
|
||||
|
@ -91,6 +92,21 @@ Transcoding message SCCP -> XUA
|
|||
Decoded SUA: HDR=(CL:CLDT,V=0,LEN=0), PART(T=Protocol Class,L=4,D=00000081), PART(T=Destination Address,L=32,D=0001000580010014000000040a00010453840900170000008003000800000007), PART(T=Source Address,L=32,D=0001000580010014000000040c00010444872000206500008003000800000006), PART(T=Data,L=154,D=6581974804260001984904510103df6c8188a181850201440201073080a780a08004012b30803012830110840107850791445776671697860120300682011884010400000000a306040142840105a306040151840105a306040131840105a309040112840105820102a309040111840105810101a306040114840100a30b0401418401043003830110a30b040141840104300382011800000000)
|
||||
Re-Encoding decoded SUA to SCCP
|
||||
SCCP Output: [L2]> 09 81 03 0d 18 0a 12 07 00 12 04 53 84 09 00 17 0b 12 06 00 12 04 44 87 20 00 20 65 9a 65 81 97 48 04 26 00 01 98 49 04 51 01 03 df 6c 81 88 a1 81 85 02 01 44 02 01 07 30 80 a7 80 a0 80 04 01 2b 30 80 30 12 83 01 10 84 01 07 85 07 91 44 57 76 67 16 97 86 01 20 30 06 82 01 18 84 01 04 00 00 00 00 a3 06 04 01 42 84 01 05 a3 06 04 01 51 84 01 05 a3 06 04 01 31 84 01 05 a3 09 04 01 12 84 01 05 82 01 02 a3 09 04 01 11 84 01 05 81 01 01 a3 06 04 01 14 84 01 00 a3 0b 04 01 41 84 01 04 30 03 83 01 10 a3 0b 04 01 41 84 01 04 30 03 82 01 18 00 00 00 00
|
||||
|
||||
=> LUDT-RANAP_RELEASE
|
||||
SCCP Input: [L2]> 13 00 0f 07 00 0a 00 0d 00 00 00 04 43 c4 04 8e 04 43 e9 03 8e 11 00 00 09 00 0d 00 00 02 00 04 40 01 42 00 03 00 01 00
|
||||
Transcoding message SCCP -> XUA
|
||||
Decoded SUA: HDR=(CL:CLDT,V=0,LEN=0), PART(T=Protocol Class,L=4,D=00000000), PART(T=SS7 Hop Counter,L=4,D=0000000f), PART(T=Destination Address,L=20,D=0002000380020008000004c4800300080000008e), PART(T=Source Address,L=20,D=0002000380020008000003e9800300080000008e), PART(T=Data,L=17,D=0009000d00000200044001420003000100)
|
||||
Re-Encoding decoded SUA to SCCP
|
||||
SCCP Output: [L2]> 11 00 0f 04 08 0c 00 04 43 c4 04 8e 04 43 e9 03 8e 11 00 09 00 0d 00 00 02 00 04 40 01 42 00 03 00 01 00
|
||||
Input != re-encoded output!
|
||||
|
||||
=> LUDT-data300bytes
|
||||
SCCP Input: [L2]> 13 00 0f 07 00 0a 00 0d 00 00 00 04 43 c4 04 8e 04 43 e9 03 8e 2c 01 ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab
|
||||
Transcoding message SCCP -> XUA
|
||||
Decoded SUA: HDR=(CL:CLDT,V=0,LEN=0), PART(T=Protocol Class,L=4,D=00000000), PART(T=SS7 Hop Counter,L=4,D=0000000f), PART(T=Destination Address,L=20,D=0002000380020008000004c4800300080000008e), PART(T=Source Address,L=20,D=0002000380020008000003e9800300080000008e), PART(T=Data,L=300,D=abababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab)
|
||||
Re-Encoding decoded SUA to SCCP
|
||||
SCCP Output: [L2]> 13 00 0f 07 00 0a 00 0d 00 00 00 04 43 c4 04 8e 04 43 e9 03 8e 2c 01 ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab
|
||||
Parsing M3UA Message
|
||||
Parsing Nested M3UA Routing Key IE
|
||||
Testing SCCP Address Encode/Decode
|
||||
|
|
Loading…
Reference in New Issue