Compare commits

..

2 Commits

Author SHA1 Message Date
Harald Welte
8e22379d7d WIP: simcom2rtp bridge, interfacing SIMcom audio over USB to RTP
Change-Id: Id3f8633fae8881c2168d61732371e65eaff140ad
2020-10-19 13:31:40 +02:00
Harald Welte
1bc597c752 use osmo_fd_setup() whenever applicable
Change-Id: I1586e855d37670af2602fc26b5d1fc72a32d1929
2020-10-19 13:31:40 +02:00
97 changed files with 5326 additions and 10096 deletions

View File

@@ -1,118 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
#
# clang-format configuration file. Intended for clang-format >= 4.
#
# For more information, see:
#
# Documentation/process/clang-format.rst
# https://clang.llvm.org/docs/ClangFormat.html
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
#
---
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
#AlignEscapedNewlines: Left # Unknown to clang-format-4.0
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
#AfterExternBlock: false # Unknown to clang-format-5.0
BeforeCatch: false
BeforeElse: false
IndentBraces: false
#SplitEmptyFunction: true # Unknown to clang-format-4.0
#SplitEmptyRecord: true # Unknown to clang-format-4.0
#SplitEmptyNamespace: true # Unknown to clang-format-4.0
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
#CompactNamespaces: false # Unknown to clang-format-4.0
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
#FixNamespaceComments: false # Unknown to clang-format-4.0
#IncludeBlocks: Preserve # Unknown to clang-format-5.0
IncludeCategories:
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
#IndentPPDirectives: None # Unknown to clang-format-5.0
IndentWidth: 8
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
# Taken from git's rules
#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
#SortUsingDeclarations: false # Unknown to clang-format-4.0
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0
#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0
SpaceBeforeParens: ControlStatements
#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp03
TabWidth: 8
UseTab: Always
...

8
.gitignore vendored
View File

@@ -8,8 +8,6 @@ Makefile
Makefile.in
bscconfig.h
bscconfig.h.in
include/osmocom/mgcp_client/mgcp_common.h
include/osmocom/mgcp_client/version.h
src/osmo-mgw/osmo-mgw
*.*~
*.sw?
@@ -18,7 +16,6 @@ src/osmo-mgw/osmo-mgw
*.gcda
*.gcno
*.pc
*~
#configure
aclocal.m4
@@ -66,8 +63,3 @@ doc/manuals/common
doc/manuals/build
contrib/osmo-mgw.spec
#vs code
.cache
.vscode

View File

@@ -24,7 +24,7 @@ pkgconfig_DATA = \
BUILT_SOURCES = $(top_srcdir)/.version
EXTRA_DIST = \
.version \
README.md \
contrib/osmo-mgw.spec.in \
debian \
git-version-gen \
osmoappdesc.py \

24
README Normal file
View File

@@ -0,0 +1,24 @@
About OsmoMGW
=============
OsmoMGW originated from the OpenBSC project, which started as a minimalistic
all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached
maturity and diversity (including M3UA SIGTRAN and 3G support in the form of
IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one
approach to fully independent separate programs as in typical GSM networks.
OsmoMGW was one of the parts split off from the old openbsc.git. It originated
as a solution to merely navigate RTP streams through a NAT, but has since
matured to a Media Gateway implementation that is capable of streaming RTP for
2G (AoIP) and 3G (IuCS) GSM networks as well as (still not implemented at time
of writing) transcoding between TRAU, various RTP payloads and IuUP.
The OsmoMGW program exposes an MGCP interface towards clients like OsmoMSC and
OsmoBSC, and receives and sends RTP streams as configured via MGCP.
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC (found
in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
Find OsmoMGW issue tracker and wiki online at
https://osmocom.org/projects/osmo-mgw
https://osmocom.org/projects/osmo-mgw/wiki

102
README.md
View File

@@ -1,102 +0,0 @@
osmo-mgw - Osmocom MGW (Media GateWay) Implementation
=====================================================
This repository contains a C-language implementation of an MGW (Media
GateWay) for use [not only] within the 2G (GSM) and/or 3G (UMTS)
Cellular Network built using Osmocom CNI (Cellular Network
Infrastructure) software.
The OsmoMGW program provides an MGCP interface towards an MGCP call agent
(client) like OsmoMSC and OsmoBSC, and receives and sends RTP streams as
configured via the MGCP control plane.
This Media Gateway implementation is capable of
* streaming RTP for 2G (3GPP AoIP and Abis-over-IP)
* streaming RTP for 3G (IuCS including the IuFP protocol)
* TDM (E1/T1) based Abis interface with TRAU frames on 16k sub-slots
* basic support for LCLS (Local Call, Local Switch) related features
* various built-in translation capabilities
* between Abis TRAU frames and RTP formats
* between 2G AMR/RTP and 3G AMR/IuFP/RTP
* between bandwidth-efficient and octet-aligned AMR
* between different standards for encapsulating GSM HR codec frames in RTP
osmo-mgw is typically co-located with
* osmo-bsc (GSM BSC)
* osmo-msc (GSM/UMTS MSC)
* osmo-hnbgw (UMTS HNBGW); osmo-mgw implements RTP relay between Iuh
and IuCS interfaces
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC
(found in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
Homepage
--------
You can find the OsmoMGW issue tracker and wiki online at
<https://osmocom.org/projects/osmo-mgw> and <https://osmocom.org/projects/osmo-mgw/wiki>.
GIT Repository
--------------
You can clone from the official osmo-mgw.git repository using
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw>
Documentation
-------------
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/osmomgw-usermanual.pdf)
as well as the [VTY Reference Manual](https://ftp.osmocom.org/docs/latest/osmomgw-vty-reference.pdf)
Mailing List
------------
Discussions related to osmo-mgw 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.
Contributing
------------
Our coding standards are described at
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
We use 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 for osmo-mgw can be seen at
<https://gerrit.osmocom.org/#/q/project:osmo-mgw+status:open>
History
-------
OsmoMGW originated from the OpenBSC project, which started as a minimalistic
all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached
maturity and diversity (including M3UA SIGTRAN and 3G support in the form of
IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one
approach to fully independent separate programs as in typical GSM networks.
OsmoMGW was one of the parts split off from the old openbsc.git. It originated
as a solution to merely navigate RTP streams through a NAT, but has since
matured.

View File

@@ -1,6 +1,6 @@
To run the configuration parsing and output (VTY) test suite, first install
https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests
git://git.osmocom.org/python/osmo-python-tests
and pass the following configure options here:

View File

@@ -1,12 +1,28 @@
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
# according to https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release
# In short: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
# LIBVERSION=c:r:a
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:a.
# 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 bump_dep; workaround Bump libosmocore version dependency after I68328adb952ca8833ba047cb3b49ccc6f8a1f1b5
has been merged to libosmocore.git; then remove my_msgb_copy_c wrapper function.
libosmocodec bump_dep We depend on the additions of I2c510ac62a0786c137115c45eee7a48b9736265f
# When cleaning up this file upon a release:
#
# - Note that the release version number is entirely unrelated to the API
# versions. A release version 5.2.3 may happily have an API version of 42:7:5.
#
# - Bump API version in src/lib*/Makefile.am files according to chapter
# "Library interface versions" of the libtool documentation.
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
#
# - Iff the 'current' API version has changed, rename debian/lib*.install
#
# API version bumping for the impatient:
# LIBVERSION=c:r:a (current:revision_of_current:backwards_compat_age)
# 5:2:4 means that
# - this implements version 5 of the API;
# - this is the 2nd (compatible) revision of API version 5;
# - this is backwards compatible to all APIs since 4 versions ago,
# i.e. callers that need API versions from 1 to 5 can use this.
#
# Bumping API versions recipe:
# If the library source code has changed at all since the last update, r++;
# If any interfaces have been added, removed, or changed since the last update, c++, r=0;
# If any interfaces have been added since the last public release, a++;
# If any interfaces have been removed or changed since the last public release, a=0.
#
#library what description / commit summary line
osmo-mgw update osmo-gsm-manuals dependency to > 0.3.0 for vty_cpu_sched.adoc include
libosmo-mgcp-client mgcp_response, mgcp_conn_peer struct size change, breaks ABI

View File

@@ -9,8 +9,6 @@ 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])])
@@ -36,6 +34,11 @@ if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
fi
PKG_PROG_PKG_CONFIG([0.20])
dnl check for AX_CHECK_COMPILE_FLAG
m4_ifdef([AX_CHECK_COMPILE_FLAG], [], [
AC_MSG_ERROR([Please install autoconf-archive; re-run 'autoreconf -fi' for it to take effect.])
])
dnl checks for libraries
AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DL)
@@ -44,18 +47,13 @@ AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DLSYM)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.11.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 2.0.0)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 2.0.0)
CFLAGS="$CFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
CPPFLAGS="$CPPFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
LDFLAGS="$LDFLAGS -pthread"
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.4.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.4.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.4.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.4.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.6.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.0.0)
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
@@ -81,13 +79,21 @@ AC_ARG_ENABLE(werror,
if test x"$werror" = x"yes"
then
WERROR_FLAGS="-Werror"
WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS"
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
fi
dnl Checks for typedefs, structures and compiler characteristics
AX_CHECK_COMPILE_FLAG([-Werror=implicit], [CFLAGS="$CFLAGS -Werror=implicit"])
AX_CHECK_COMPILE_FLAG([-Werror=maybe-uninitialized], [CFLAGS="$CFLAGS -Werror=maybe-uninitialized"])
AX_CHECK_COMPILE_FLAG([-Werror=memset-transposed-args], [CFLAGS="$CFLAGS -Werror=memset-transposed-args"])
AX_CHECK_COMPILE_FLAG([-Wnull-dereference], [CFLAGS="$CFLAGS -Wnull-dereference"])
AX_CHECK_COMPILE_FLAG([-Werror=sizeof-array-argument], [CFLAGS="$CFLAGS -Werror=sizeof-array-argument"])
AX_CHECK_COMPILE_FLAG([-Werror=sizeof-pointer-memaccess], [CFLAGS="$CFLAGS -Werror=sizeof-pointer-memaccess"])
# Coverage build taken from WebKit's configure.in
AC_MSG_CHECKING([whether to enable code coverage support])
AC_ARG_ENABLE(coverage,
@@ -115,7 +121,7 @@ if test "x$enable_ext_tests" = "xyes" ; then
AM_PATH_PYTHON
AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
fi
fi
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
@@ -205,4 +211,5 @@ AC_OUTPUT(
doc/manuals/Makefile
contrib/Makefile
contrib/systemd/Makefile
contrib/osmo-mgw.spec
Makefile)

View File

@@ -15,6 +15,10 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
"""

View File

@@ -22,7 +22,7 @@ export deps inst
osmo-clean-workspace.sh
mkdir "$deps" || true
osmo-build-dep.sh libosmocore "" --disable-doxygen
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
@@ -30,12 +30,13 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib"
export PATH="$inst/bin:$PATH"
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
osmo-build-dep.sh libosmo-abis
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
@@ -49,18 +50,18 @@ set -x
cd "$base"
autoreconf --install --force
./configure --enable-sanitize --enable-vty-tests --enable-external-tests --enable-werror $CONFIG
./configure --enable-vty-tests --enable-external-tests --enable-werror $CONFIG
$MAKE $PARALLEL_MAKE
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
|| cat-testlogs.sh
LD_LIBRARY_PATH="$inst/lib" \
DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests $CONFIG" \
$MAKE $PARALLEL_MAKE distcheck \
$MAKE distcheck \
|| cat-testlogs.sh
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
make -C "$base/doc/manuals" publish
fi
$MAKE $PARALLEL_MAKE maintainer-clean
$MAKE maintainer-clean
osmo-clean-workspace.sh

137
contrib/osmo-mgw.spec.in Normal file
View File

@@ -0,0 +1,137 @@
#
# spec file for package osmo-mgw
#
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
#
# 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.
# Please submit bugfixes or comments via http://bugs.opensuse.org/
#
Name: osmo-mgw
Version: @VERSION@
Release: 0
Summary: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
License: AGPL-3.0-or-later AND GPL-2.0-or-later
Group: Hardware/Mobile
URL: https://osmocom.org/projects/osmo-mgw
Source: %{name}-%{version}.tar.xz
BuildRequires: automake >= 1.9
BuildRequires: libtool >= 2
BuildRequires: pkgconfig >= 0.20
%if 0%{?suse_version}
BuildRequires: systemd-rpm-macros
%endif
BuildRequires: pkgconfig(libosmo-netif) >= 0.6.0
BuildRequires: pkgconfig(libosmocore) >= 1.0.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.1.0
BuildRequires: pkgconfig(libosmogsm) >= 1.0.0
BuildRequires: pkgconfig(libosmovty) >= 1.0.0
BuildRequires: pkgconfig(libosmocoding) >= 1.0.0
BuildRequires: pkgconfig(libosmoabis) >= 1.0.0
BuildRequires: pkgconfig(libosmotrau) >= 1.0.0
%{?systemd_requires}
%description
OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks.
%package -n libosmo-mgcp-client6
Summary: Osmocom's Media Gateway Control Protocol client library
Group: System/Libraries
%description -n libosmo-mgcp-client6
Osmocom's Media Gateway Control Protocol client library.
%package -n libosmo-mgcp-client-devel
Summary: Development files for Osmocom's Media Gateway Control Protocol client library
Group: Development/Libraries/C and C++
Requires: libosmo-mgcp-client6 = %{version}
%description -n libosmo-mgcp-client-devel
Osmocom's Media Gateway Control Protocol client librarary.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmo-mgcp-client.
%package -n libosmo-mgcp-devel
Summary: Development files for Osmocom's Media Gateway server library
Group: Development/Libraries/C and C++
%description -n libosmo-mgcp-devel
Osmocom's Media Gateway Control Protocol server library.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmo-mgcp.
%prep
%setup -q
%build
echo "%{version}" >.tarball-version
autoreconf -fi
%configure \
--disable-static \
--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-mgcp-client6 -p /sbin/ldconfig
%postun -n libosmo-mgcp-client6 -p /sbin/ldconfig
%if 0%{?suse_version}
%preun
%service_del_preun osmo-mgw.service
%postun
%service_del_postun osmo-mgw.service
%pre
%service_add_pre osmo-mgw.service
%post
%service_add_post osmo-mgw.service
%endif
%files
%license COPYING
%doc AUTHORS README
%dir %{_docdir}/%{name}/examples
%dir %{_docdir}/%{name}/examples/osmo-mgw
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw.cfg
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
%{_bindir}/osmo-mgw
%{_unitdir}/osmo-mgw.service
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg
%files -n libosmo-mgcp-client6
%{_libdir}/libosmo-mgcp-client.so.6*
%files -n libosmo-mgcp-client-devel
%{_libdir}/libosmo-mgcp-client.so
%{_libdir}/pkgconfig/libosmo-mgcp-client.pc
%dir %{_includedir}/osmocom
%dir %{_includedir}/osmocom/mgcp_client
%{_includedir}/osmocom/mgcp_client/*.h
%files -n libosmo-mgcp-devel
%dir %{_includedir}/osmocom
%dir %{_includedir}/osmocom/mgcp
%{_includedir}/osmocom/mgcp/*.h
%changelog

View File

@@ -0,0 +1,11 @@
CFLAGS:= -O2 -g -Wall $(shell pkg-config --cflags libosmocore libosmotrau)
LIBS:= $(shell pkg-config --libs libosmocore libosmotrau)
all: osmo-simcom2rtp
osmo-simcom2rtp: g711.o g711_table.o simcom2rtp.o
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
%.o: %.c
$(CC) $(CFLAGS) -o $@ -c $^

313
contrib/simcom2rtp/g711.c Normal file
View File

@@ -0,0 +1,313 @@
/*
* This source code is a product of Sun Microsystems, Inc. and is provided
* for unrestricted use. Users may copy or modify this source code without
* charge.
*
* SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
* THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
*
* Sun source code is provided with no support and without any obligation on
* the part of Sun Microsystems, Inc. to assist in its use, correction,
* modification or enhancement.
*
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
* OR ANY PART THEREOF.
*
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
* or profits or other special, indirect and consequential damages, even if
* Sun has been advised of the possibility of such damages.
*
* Sun Microsystems, Inc.
* 2550 Garcia Avenue
* Mountain View, California 94043
*/
/*
* December 30, 1994:
* Functions linear2alaw, linear2ulaw have been updated to correctly
* convert unquantized 16 bit values.
* Tables for direct u- to A-law and A- to u-law conversions have been
* corrected.
* Borge Lindberg, Center for PersonKommunikation, Aalborg University.
* bli@cpk.auc.dk
*
*/
/*
* Downloaded from comp.speech site in Cambridge.
*
*/
#include "g711.h"
/*
* g711.c
*
* u-law, A-law and linear PCM conversions.
* Source: http://www.speech.kth.se/cost250/refsys/latest/src/g711.c
*/
#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
#define QUANT_MASK (0xf) /* Quantization field mask. */
#define NSEGS (8) /* Number of A-law segments. */
#define SEG_SHIFT (4) /* Left shift for segment number. */
#define SEG_MASK (0x70) /* Segment field mask. */
static short seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
0x1FF, 0x3FF, 0x7FF, 0xFFF};
static short seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
0x3FF, 0x7FF, 0xFFF, 0x1FFF};
/* copy from CCITT G.711 specifications */
unsigned char _u2a[128] = { /* u- to A-law conversions */
1, 1, 2, 2, 3, 3, 4, 4,
5, 5, 6, 6, 7, 7, 8, 8,
9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24,
25, 27, 29, 31, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44,
46, 48, 49, 50, 51, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, 62,
64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
/* corrected:
81, 82, 83, 84, 85, 86, 87, 88,
should be: */
80, 82, 83, 84, 85, 86, 87, 88,
89, 90, 91, 92, 93, 94, 95, 96,
97, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110, 111, 112,
113, 114, 115, 116, 117, 118, 119, 120,
121, 122, 123, 124, 125, 126, 127, 128};
unsigned char _a2u[128] = { /* A- to u-law conversions */
1, 3, 5, 7, 9, 11, 13, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 32, 33, 33, 34, 34, 35, 35,
36, 37, 38, 39, 40, 41, 42, 43,
44, 45, 46, 47, 48, 48, 49, 49,
50, 51, 52, 53, 54, 55, 56, 57,
58, 59, 60, 61, 62, 63, 64, 64,
65, 66, 67, 68, 69, 70, 71, 72,
/* corrected:
73, 74, 75, 76, 77, 78, 79, 79,
should be: */
73, 74, 75, 76, 77, 78, 79, 80,
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127};
static short search(
short val,
short *table,
short size)
{
short i;
for (i = 0; i < size; i++) {
if (val <= *table++)
return (i);
}
return (size);
}
/*
* linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
*
* linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
*
* Linear Input Code Compressed Code
* ------------------------ ---------------
* 0000000wxyza 000wxyz
* 0000001wxyza 001wxyz
* 000001wxyzab 010wxyz
* 00001wxyzabc 011wxyz
* 0001wxyzabcd 100wxyz
* 001wxyzabcde 101wxyz
* 01wxyzabcdef 110wxyz
* 1wxyzabcdefg 111wxyz
*
* For further information see John C. Bellamy's Digital Telephony, 1982,
* John Wiley & Sons, pps 98-111 and 472-476.
*/
unsigned char
linear2alaw(short pcm_val) /* 2's complement (16-bit range) */
{
short mask;
short seg;
unsigned char aval;
pcm_val = pcm_val >> 3;
if (pcm_val >= 0) {
mask = 0xD5; /* sign (7th) bit = 1 */
} else {
mask = 0x55; /* sign bit = 0 */
pcm_val = -pcm_val - 1;
}
/* Convert the scaled magnitude to segment number. */
seg = search(pcm_val, seg_aend, 8);
/* Combine the sign, segment, and quantization bits. */
if (seg >= 8) /* out of range, return maximum value. */
return (unsigned char) (0x7F ^ mask);
else {
aval = (unsigned char) seg << SEG_SHIFT;
if (seg < 2)
aval |= (pcm_val >> 1) & QUANT_MASK;
else
aval |= (pcm_val >> seg) & QUANT_MASK;
return (aval ^ mask);
}
}
/*
* alaw2linear() - Convert an A-law value to 16-bit linear PCM
*
*/
short
alaw2linear(
unsigned char a_val)
{
short t;
short seg;
a_val ^= 0x55;
t = (a_val & QUANT_MASK) << 4;
seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
switch (seg) {
case 0:
t += 8;
break;
case 1:
t += 0x108;
break;
default:
t += 0x108;
t <<= seg - 1;
}
return ((a_val & SIGN_BIT) ? t : -t);
}
#define BIAS (0x84) /* Bias for linear code. */
#define CLIP 8159
/*
* linear2ulaw() - Convert a linear PCM value to u-law
*
* In order to simplify the encoding process, the original linear magnitude
* is biased by adding 33 which shifts the encoding range from (0 - 8158) to
* (33 - 8191). The result can be seen in the following encoding table:
*
* Biased Linear Input Code Compressed Code
* ------------------------ ---------------
* 00000001wxyza 000wxyz
* 0000001wxyzab 001wxyz
* 000001wxyzabc 010wxyz
* 00001wxyzabcd 011wxyz
* 0001wxyzabcde 100wxyz
* 001wxyzabcdef 101wxyz
* 01wxyzabcdefg 110wxyz
* 1wxyzabcdefgh 111wxyz
*
* Each biased linear code has a leading 1 which identifies the segment
* number. The value of the segment number is equal to 7 minus the number
* of leading 0's. The quantization interval is directly available as the
* four bits wxyz. * The trailing bits (a - h) are ignored.
*
* Ordinarily the complement of the resulting code word is used for
* transmission, and so the code word is complemented before it is returned.
*
* For further information see John C. Bellamy's Digital Telephony, 1982,
* John Wiley & Sons, pps 98-111 and 472-476.
*/
unsigned char
linear2ulaw(
short pcm_val) /* 2's complement (16-bit range) */
{
short mask;
short seg;
unsigned char uval;
/* Get the sign and the magnitude of the value. */
pcm_val = pcm_val >> 2;
if (pcm_val < 0) {
pcm_val = -pcm_val;
mask = 0x7F;
} else {
mask = 0xFF;
}
if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */
pcm_val += (BIAS >> 2);
/* Convert the scaled magnitude to segment number. */
seg = search(pcm_val, seg_uend, 8);
/*
* Combine the sign, segment, quantization bits;
* and complement the code word.
*/
if (seg >= 8) /* out of range, return maximum value. */
return (unsigned char) (0x7F ^ mask);
else {
uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
return (uval ^ mask);
}
}
/*
* ulaw2linear() - Convert a u-law value to 16-bit linear PCM
*
* First, a biased linear code is derived from the code word. An unbiased
* output can then be obtained by subtracting 33 from the biased code.
*
* Note that this function expects to be passed the complement of the
* original code word. This is in keeping with ISDN conventions.
*/
short
ulaw2linear(
unsigned char u_val)
{
short t;
/* Complement to obtain normal u-law value. */
u_val = ~u_val;
/*
* Extract and bias the quantization bits. Then
* shift up by the segment number and subtract out the bias.
*/
t = ((u_val & QUANT_MASK) << 3) + BIAS;
t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
}
/* A-law to u-law conversion */
unsigned char
alaw2ulaw(
unsigned char aval)
{
aval &= 0xff;
return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
(0x7F ^ _a2u[aval ^ 0x55]));
}
/* u-law to A-law conversion */
unsigned char
ulaw2alaw(
unsigned char uval)
{
uval &= 0xff;
return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
(0x55 ^ (_u2a[0x7F ^ uval] - 1)));
}
/* ---------- end of g711.c ----------------------------------------------------- */

27
contrib/simcom2rtp/g711.h Normal file
View File

@@ -0,0 +1,27 @@
/*
* g711.h
*
* u-law, A-law and linear PCM conversions.
* Source: http://www.speech.kth.se/cost250/refsys/latest/src/g711.h
*/
#ifndef _G711_H_
#define _G711_H_
#ifdef __cplusplus
extern "C" {
#endif
unsigned char linear2alaw(short pcm_val);
short alaw2linear(unsigned char a_val);
unsigned char linear2ulaw(short pcm_val);
short ulaw2linear(unsigned char u_val);
unsigned char alaw2ulaw(unsigned char aval);
unsigned char ulaw2alaw(unsigned char uval);
#ifdef __cplusplus
}
#endif
#endif /* _G711_H_ */

View File

@@ -0,0 +1,102 @@
#ifndef G711_TABLE_H
#define G711_TABLE_H
#include "g711.h"
/* 16384 entries per table (16 bit) */
unsigned char linear_to_alaw[65536];
unsigned char linear_to_ulaw[65536];
/* 16384 entries per table (8 bit) */
unsigned short alaw_to_linear[256];
unsigned short ulaw_to_linear[256];
static void build_linear_to_xlaw_table(unsigned char *linear_to_xlaw,
unsigned char (*linear2xlaw)(short))
{
int i;
for (i=0; i<65536;i++){
linear_to_xlaw[i] = linear2xlaw((short) i);
}
}
static void build_xlaw_to_linear_table(unsigned short *xlaw_to_linear,
short (*xlaw2linear)(unsigned char))
{
int i;
for (i=0; i<256;i++){
xlaw_to_linear[i] = (unsigned short) xlaw2linear(i);
}
}
static void pcm16_to_xlaw(unsigned char *linear_to_xlaw, int src_length, const char *src_samples, char *dst_samples)
{
int i;
const unsigned short *s_samples;
s_samples = (const unsigned short *)src_samples;
for (i=0; i < src_length / 2; i++)
{
dst_samples[i] = linear_to_xlaw[s_samples[i]];
}
}
static void xlaw_to_pcm16(unsigned short *xlaw_to_linear, int src_length, const char *src_samples, char *dst_samples)
{
int i;
unsigned char *s_samples;
unsigned short *d_samples;
s_samples = (unsigned char *) src_samples;
d_samples = (unsigned short *)dst_samples;
for (i=0; i < src_length; i++)
{
d_samples[i] = xlaw_to_linear[s_samples[i]];
}
}
void pcm16_to_alaw(int src_length, const char *src_samples, char *dst_samples)
{
pcm16_to_xlaw(linear_to_alaw, src_length, src_samples, dst_samples);
}
void pcm16_to_ulaw(int src_length, const char *src_samples, char *dst_samples)
{
pcm16_to_xlaw(linear_to_ulaw, src_length, src_samples, dst_samples);
}
void alaw_to_pcm16(int src_length, const char *src_samples, char *dst_samples)
{
xlaw_to_pcm16(alaw_to_linear, src_length, src_samples, dst_samples);
}
void ulaw_to_pcm16(int src_length, const char *src_samples, char *dst_samples)
{
xlaw_to_pcm16(ulaw_to_linear, src_length, src_samples, dst_samples);
}
void pcm16_alaw_tableinit()
{
build_linear_to_xlaw_table(linear_to_alaw, linear2alaw);
}
void pcm16_ulaw_tableinit()
{
build_linear_to_xlaw_table(linear_to_ulaw, linear2ulaw);
}
void alaw_pcm16_tableinit()
{
build_xlaw_to_linear_table(alaw_to_linear, alaw2linear);
}
void ulaw_pcm16_tableinit()
{
build_xlaw_to_linear_table(ulaw_to_linear, ulaw2linear);
}
#endif // G711_TABLE_H

View File

@@ -0,0 +1,14 @@
#ifndef G711_TABLE_H
#define G711_TABLE_H
void pcm16_to_alaw(int length, const char *src_samples, char *dst_samples);
void pcm16_to_ulaw(int length, const char *src_samples, char *dst_samples);
void alaw_to_pcm16(int length, const char *src_samples, char *dst_samples);
void ulaw_to_pcm16(int length, const char *src_samples, char *dst_samples);
void pcm16_alaw_tableinit();
void pcm16_ulaw_tableinit();
void alaw_pcm16_tableinit();
void ulaw_pcm16_tableinit();
#endif // G711_TABLE_H

View File

@@ -0,0 +1,218 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <osmocom/core/select.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/application.h>
#include <osmocom/core/serial.h>
#include <osmocom/trau/osmo_ortp.h>
#include "g711.h"
#define RTP_PT_PCMU 0
#define RTP_PT_PCMA 8
struct modem_state {
struct osmo_fd data_fd;
struct osmo_rtp_socket *rtp;
/* queue of linear PCM audio in RTP -> modem direction */
struct llist_head rtp2modem;
/* message buffer used if samples insufficient for next RTP frame were received */
struct msgb *modem2rtp;
};
static void *g_tall_ctx;
/* call-back on received RTP data */
static void ortp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *payload,
unsigned int payload_len, uint16_t seq_nr, uint32_t timestamp, bool marker)
{
/* we received a RTP frame */
struct modem_state *ms = rs->priv;
struct msgb *msg = msgb_alloc(payload_len*2, "RTP Rx");
unsigned int i;
int16_t *out;
OSMO_ASSERT(msg);
out = (int16_t *) msgb_put(msg, payload_len*2);
if (payload_len != 160) {
fprintf(stderr, "RTP payload length %d != 160, dropping\n", payload_len);
msgb_free(msg);
return;
}
/* convert from Alaw to linear PCM (160 -> 320 bytes) */
for (i = 0; i < payload_len; i++)
out[i] = alaw2linear(payload[i]);
/* append to the write queue */
msgb_enqueue(&ms->rtp2modem, msg);
ms->data_fd.when |= OSMO_FD_WRITE;
}
static void modem2rtp(struct modem_state *ms, const uint8_t *data, unsigned int len)
{
const int16_t *data16 = (const int16_t *)data;
unsigned int samples = len / 2;
unsigned int offset = 0;
unsigned int i;
/* samples are always 16bit, we cannot read half a sample */
OSMO_ASSERT((len & 1) == 0);
/* first complete any pending incomplete RTP frame */
if (ms->modem2rtp) {
struct msgb *msg = ms->modem2rtp;
unsigned int missing_samples = 160 - msgb_length(msg);
for (i = 0; i < missing_samples; i++) {
if (i >= samples)
break;
msgb_put_u8(msg, linear2alaw(data16[i]));
}
offset = i;
if (msgb_length(msg) == 160) {
osmo_rtp_send_frame_ext(ms->rtp, msgb_data(msg), msgb_length(msg), 160, false);
msgb_free(msg);
}
}
/* then send as many RTP frames as we have samples */
for (offset = offset; offset + 160 <= samples; offset += 160) {
uint8_t buf[160];
for (i = 0; i < sizeof(buf); i++)
buf[i] = linear2alaw(data16[offset + i]);
osmo_rtp_send_frame_ext(ms->rtp, buf, sizeof(buf), 160, false);
}
/* store remainder in msgb */
if (offset < samples) {
struct msgb *msg = msgb_alloc_c(ms, 160, "modem2rtp");
OSMO_ASSERT(msg);
OSMO_ASSERT(len - offset < 160);
for (i = 0; i < len - offset; i++)
msgb_put_u8(msg, linear2alaw(data16[offset + i]));
ms->modem2rtp = msg;
}
}
/* call back on file descriptor events of the modem DATA ttyUSB device */
static int modem_data_fd_cb(struct osmo_fd *ofd, unsigned int what)
{
struct modem_state *ms = ofd->data;
int rc;
if (what & OSMO_FD_READ) {
/* SIM5360 USB AUDIO Application Note v1.01 states 1600 bytes every 100ms */
uint8_t rx_buf[1600];
rc = read(ofd->fd, rx_buf, sizeof(rx_buf));
OSMO_ASSERT(rc > 0);
modem2rtp(ms, rx_buf, rc);
}
if (what & OSMO_FD_WRITE) {
struct msgb *msg = msgb_dequeue(&ms->rtp2modem);
if (!msg)
ofd->when &= ~OSMO_FD_WRITE;
else {
/* SIM5300 USB AUDIO Application Note v1.01 states 640 bytes every 40ms;
* we simply write every RTP frame individually (320 bytes every 20ms) */
rc = write(ofd->fd, msgb_data(msg), msgb_length(msg));
if (rc != msgb_length(msg))
fprintf(stderr, "Short write: %d < %u\n", rc, msgb_length(msg));
msgb_free(msg);
}
}
return 0;
}
static int modem_data_open(struct modem_state *ms, const char *basepath)
{
char fname[PATH_MAX+1];
int fd;
/* the assumption is that the caller provides something like
* "/dev/serial/by-path/pci-0000:00:14.0-usb-0:2:1" */
snprintf(fname, sizeof(fname), "%s.0-port0", basepath);
fd = osmo_serial_init(fname, 921600);
if (fd < 0) {
fprintf(stderr, "failed to open device '%s': %s\n", fname, strerror(errno));
return -1;
}
osmo_fd_setup(&ms->data_fd, fd, OSMO_FD_READ, modem_data_fd_cb, ms, 0);
osmo_fd_register(&ms->data_fd);
return 0;
}
static struct modem_state *modem_create(void *ctx)
{
struct modem_state *ms = talloc_zero(ctx, struct modem_state);
int rc;
INIT_LLIST_HEAD(&ms->rtp2modem);
ms->rtp = osmo_rtp_socket_create(ms, 0);
OSMO_ASSERT(ms->rtp);
osmo_rtp_socket_set_pt(ms->rtp, RTP_PT_PCMA);
ms->rtp->priv = ms;
ms->rtp->rx_cb = ortp_rx_cb;
rc = osmo_rtp_socket_bind(ms->rtp, "0.0.0.0", 1111);
OSMO_ASSERT(rc == 0);
rc = osmo_rtp_socket_connect(ms->rtp, "127.0.0.1", 2222);
//rc = osmo_rtp_socket_autoconnect(ms->rtp);
OSMO_ASSERT(rc == 0);
osmo_rtp_set_source_desc(ms->rtp, "cname", "simcom2rtp", NULL, NULL, NULL,
"osmo-simcom2rtp", NULL);
return ms;
}
int main(int argc, char **argv)
{
talloc_enable_null_tracking();
g_tall_ctx = talloc_named_const(NULL, 1, "simcom2rtp");
msgb_talloc_ctx_init(g_tall_ctx, 0);
osmo_init_logging2(g_tall_ctx, NULL);
osmo_fsm_log_timeouts(true);
osmo_fsm_log_addr(true);
//osmo_stats_init(g_tall_ctx);
osmo_rtp_init(g_tall_ctx);
struct modem_state *ms = modem_create(g_tall_ctx);
int rc;
OSMO_ASSERT(ms);
rc = modem_data_open(ms, "/dev/serial/by-path/pci-0000:00:14.0-usb-0:2:1");
OSMO_ASSERT(rc == 0);
while (1) {
osmo_select_main(0);
}
}

View File

@@ -1,24 +1,11 @@
[Unit]
Description=Osmocom Media Gateway (MGW)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
LimitNOFILE=65536
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
Restart=always
User=osmocom
Group=osmocom
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
RestartSec=2
AmbientCapabilities=CAP_SYS_NICE
# CPU scheduling policy:
CPUSchedulingPolicy=rr
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
CPUSchedulingPriority=1
# See sched(7) for further details on real-time policies and priorities
[Install]
WantedBy=multi-user.target

679
debian/changelog vendored
View File

@@ -1,682 +1,3 @@
osmo-mgw (1.14.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* jenkins.sh: libosmo-netif no longer depends on libosmo-abis
* jenkins.sh: Use --disable-doxygen configure param
* mgcp-client: Fix wrong value passed to strerror()
* mgcp_client_internal.h: Add missing header dependency
* mgcp-cli: Mark iofd ptr as NULL when freed
* mgcp-cli: Improve error handling around mgcp_msg_gen() return
* mgcp-cli: Fix filling in wrong local IP address of SDP Origin o=
* mgw: Drop own MGCP extension 'noanswer'
* cosmetic: mgw: Fix indentation whitespace
* mgcp-client: Fix regression checking null ptr
* mgw: Rename and move code freeing endp connection
* mgw: Rename and move several get_conn funcs acting on endp object
* mgw: mgcp_network.c: Simplify use of conn_rtp ptr
* mgw: Clean up access to conn_rtp from conn
* mgw: Avoid 2nd lookup of conn in endp during CRCX
* cosmetic: mgw: iuup: Update comment
* mgw: constify mgcp_endp_avail() param
* mgcp_endp: Add helpers accessing endp connections
* mgw: mgcp_protocol: assert freeing last conn allows creating new conn
* mgw: Split conn mode parsing and applying into conn
* mgw: CRCXMDCX/DLCX: rename conn and conn_rtp variables
* mgw: Use bool instead of int in local var
* Rename mgcp_free_rtp_port() to mgcp_rtp_end_free_port()
* mgw: Split mgcp_rtp_end to its own file
* mgw: Introduce mgcp_rtp_end_init()
* mgw: Introduce struct mgcp_codecset struct
* mgw: Cleanup rtp_endp fields in its own function
* mgw: Move force_ptime logic outside of main CRCX func handler
* mgw: Move several params setting to mgcp_rtp_end_init()
* mgw: Simplify and redo code around ssrc patch feature
* mgw: Clean up code allocating conn_rtp rtp/rtcp sockets
* mgw: Rename and cleanup code allocating rtp/rtcp ports in trunk
* mgw: CRCX: Split mgcp header pars parsing into a previous step
* mgw: MDCX: Split mgcp header pars parsing into a previous step
* mgw: DLCX: Split mgcp header pars parsing into a previous step
* mgw: Decouple SDP parsing step from conn obj update
* mgw: Remove wrong TODO comment
* mgw: MDCX: Simplify early return code paths
[ Mychaela N. Falconia ]
* E1 cosmetic: reduce white space in hard-coded TRAU-DL frames
* E1: replace idle_tf_efr[] with a better version
* E1: replace idle_tf_fr[] with a better version
-- Oliver Smith <osmith@sysmocom.de> Wed, 12 Feb 2025 12:30:33 +0100
osmo-mgw (1.13.1) unstable; urgency=medium
[ Philipp Maier ]
* mgcp_network: use an uint16_t to store the port number
* mgcp_network: add missing ntohs
[ Pau Espin Pedrol ]
* tests/mgcp/mgcp_test: Add some extra asserts in code
-- Oliver Smith <osmith@sysmocom.de> Thu, 12 Sep 2024 13:53:19 +0200
osmo-mgw (1.13.0) unstable; urgency=medium
[ Neels Hofmeyr ]
* add mgcp_conn_rtp_type_names[]
* mgcp_parse_audio_port_pt(): fix buffer overflow
* client: replace two assertions with graceful error handling
* systemd,manual: set LimitNOFILE=65536
* IuUP: allow Initialization from any address if not yet set
* check_rtp_origin: drop special case for legacy IuUP hack
* mgcp_client_test: fix function name
* fix possible NULL deref on early media
* client: move some items to internal header
* client: safely handle dealloc on event dispatch
* build: move mgcp/*.h to noinst_HEADERS, drop RPM libosmo-mgcp-devel
* client: deprecate legacy API
* client: collapse codecs[] and ptmap[]; allow codec variants
* client: allow MGCP_MAX_CODECS entries
* client SDP: more verbose error logging
* mgcp_client_test: add test_parse_response()
* drop (now) unused code
* tests/mgcp: add update_exp target
* drop get_net_downlink_format_cb
* drop cfg 'sdp audio fmtp-extra'
* mgcp_codec_decide: remove redundant lookup
* tweak DEBUG log
* mgcp_test: fix false negatives in test output
* mgw: do not fail MGCP on codec mismatch
* mgcp-client: always send 'm=audio' line
* mgcp_test.c: verify osmo-mgw accepts m=audio 0
* mgcp_test.c: fix various missing '\r' and '\n'
* mgcp_test: test a=ptime:20, not 40
* mgcp_test: add CRCX for IUFP in sendrecv
* do not FAIL on CRCX in sendrecv mode
[ Keith Whyte ]
* vty and log: also show local port for RTP conns
[ Andreas Eversberg ]
* Use uniform log format for default config files
[ Pau Espin Pedrol ]
* mgcp_network: Improve err logging when rtp pkt from unexpected origin comes in
* cosmetic: Fix line indentation
* IuUP: Allow Initialization with set rem IP address and unset rem port
* mgcp-client: Transmit remote IP addr in CRCX if known and port=0
* Fix IuUP RTP hdr seqnr field not incremented
* iuup: Increment RTP hdr seqnr even if Tx over UDP fails
[ Vadim Yanitskiy ]
* mgcp: simplify getting msgb tail in mgcp_msg_terminate_nul()
* mgcp: reserve once byte for '\0' in mgcp_do_read()
* mgcp: correctly put NUL character in mgcp_msg_terminate_nul()
* build: include README into the release tarball
[ neels ]
* Revert "drop (now) unused code"
[ Harald Welte ]
* Convert README to README.md and expand like in other projects
* migrate mgcp_client from osmo_wqueue to osmo_io
* mgw: Add our usual SIGABRT, SIGUSR1 signal handlers
* don't log useless "transcoding disabled" message
* remove strange loop for non-existant transcoding support
* simplify unused transcoding/processing call-back
* remove osmo_fd from mgcp_create_bind()
* Change msgb ownership in processing of received msgb
* cosmetic: make linter happy
* Convert RTP/RTCP/OSMUX I/O from osmo_fd to osmo_io
[ Oliver Smith ]
* contrib: remove rpm spec file
* contrib/systemd: run as osmocom user
* doc: example configs: fix deprecation warnings
[ Mychaela N. Falconia ]
* E1: support HRv1 codec on both 16k and 8k subslots
* fix E1 TS output when used with osmo-e1d
-- Oliver Smith <osmith@sysmocom.de> Wed, 24 Jul 2024 15:44:47 +0200
osmo-mgw (1.12.1) unstable; urgency=medium
[ Pau Espin Pedrol ]
* mgcp-client: Fix missing include in mgcp_client_pool.h
* mgcp-client: Introduce API osmo_mgcpc_ep_local_name()
* mgw: Configure IuUP if codec set during MDCX
-- Oliver Smith <osmith@sysmocom.de> Thu, 28 Sep 2023 15:58:17 +0200
osmo-mgw (1.12.0) unstable; urgency=medium
[ Philipp Maier ]
* mgcp_sdp: add spec reference
* mgcp_sdp: cosmetic: remove newline
* mgcp_endp: cosmetic move mgcp_endp_release to the end
* mgcp_endp: cosmetic: remove unnecessary new line
* mgcp_e1: fix log output
* mgcp_e1: be more frugal withe E1 line resources
* mgcp_client: fix sourcecode formatting
* mgcp_e1: fix typo
* mgcp_e1: rewrite comment
* mgcp_e1: rename e1_send to e1_send_ts_frame
* mgcp_e1: cosmetic: rewrite comment
* Revert "mgcp_codec: do not differentiate between oa and bwe when comparing codec"
* mgcp_codec: fix oa/bwe comparison in mgcp_codec_pt_translate()
* mgcp_codec: refactor payload type converstion
* mgcp_codec: cosmetic: remove line break in api-doc
* mgcp_network: fix apidoc
* mgcp_vty: add warnings for deprecated config options
* mgcp_codec: move mgcp_codec_decide down
* mgcp_codec: fix codec decision
* mgcp_network: do not deliver RTP packets with unpatched PT
* mgcp_codec: be sensitive about IuFP when checking codecs
* mgcp_client.h: also add spec ref to the other 3gpp defined payload types
[ Harald Welte ]
* cosmetic: Fix grammar suggesting reading _the_ user manual
[ arehbein ]
* Transition to use of 'telnet_init_default'
[ Oliver Smith ]
* mgcp_client: mgcp_msg_gen: add more error logs
* mgcp_client_pool: add mgcp_client_pool_empty()
* Cosmetic: mgcp_client: fix typo
* debian: set compat level to 10
* systemd: depend on networking-online.target
* Cosmetic: fix a typo
* mgcp_client: check rc of map_str_to_codec
[ Pau Espin Pedrol ]
* mgcp_network: Unregister osmo_fd before closing fd
* mgcp-client: Call osmo_fd_unregister() before closing and changing bfd->fd
* mgcp-client: Drop unused struct mgcp_client field
* mgcp_client: Introduce mgcp_client_conf_alloc(), deprecate mgcp_client_conf_init()
* mgcp-client: Move some static functions further above
* mgw: Allow auditing speciall 'null' endpoint
* mgcp-client: Add keepalive feature
* mgcp_client: pool: Only pick clients with an MGCP link considered to be UP
* mgcp-client: Always mark client as UP if keepalive request-interval disabled
* mgcp-client: Mark client as UP when keepalive request-interval/timeout is disabled through VTY
[ Vadim Yanitskiy ]
* */Makefile.am: libraries shall not be in AM_LDFLAGS
* tests: use -no-install libtool flag to avoid ./lt-* scripts
* tests: $(BUILT_SOURCES) is not defined, depend on osmo-mgw
* copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
[ Neels Hofmeyr ]
* mgcp_find_section_end(): skip spaces at start of SDP
* mgcp_client: simpler error handling
* mgcp: fix "L: a:" header parsing: heed ";" separator
* mgcp_client: tweak extract_codec_name() implementation
[ Andreas Eversberg ]
* ASCI: Add new mode for voice group/broadcast call
* ASCI: Support conference briding with 1..n connections
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 14:48:51 +0200
osmo-mgw (1.11.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* mgcp-client: Remove impossible code path
* mgcp_osmux: Drop duplicated conn_osmux_release_cid() in code path
* Use Osmux default port define from libosmo-netif
* osmux: Use new osmux APIs to let libosmo-netif alloc struct osmux_out_handle
* mgw: Fix osmux conn local IP selection
* cosmetic: osmux: Fix wrong indentation
* mgw: Use X-Osmux string define
* Add Osmux log category
* cosmetic: main: Properly format log_info_cat
* osmux: Use better name for function which may allocate a new struct
* osmux: Use osmo_sockaddr wherever possible
* osmux: Log refcounting of osmux_handle_list
* osmux: Fix memleak on error code path
* osmux: don't store conn ptr inside shared osmux_handle
* osmux: set log level of expected code path to INFO
* cosmetic: vty: Fix indentation whitespace
* osmux: Log sendto() error
* cosmetic: mgcp_conn.h: fix indentation whitespace
* osmux: Attach osmux to virtual trunk
* osmux: Clean up helper macro osmux_chunk_length()
* osmux: Get rid of static NULL talloc context
* Fix typo in ratectr description
* mgcp_conn: rename field s/rate_ctr_group/ctrg/g
* Use 'static const' instead of 'const static' everywhere
* osmux: Add connection and global rate counters
* cosmetic: osmux: Fix formatting of if-else brackets
* osmux: Rename field osmux usage policy and define it with proper type
* Use bool type instead of int in config field
* osmux: Support local CID != remote CID
* osmux: Fix incorrect rate_ctr_group used in mgcp_osmux.c
* osmux: Improve per-conn tx rate counters
* vty: show per-connection Osmux VTY stats
* osmux: Rename field s/init/initialized
* osmux: Allocate rate counters during initialization of osmux conn
* cosmetic: osmux: Drop extra empty line
* osmux: Drop unneeded comment block
* osmux: Lower log level when osmux batch received for unknown CID
* osmux: Keep decoding osmux pkt if a batch contains an unknown CID
* Allocate struct osmux_in_handle through new libosmo-netif APIs
* osmux: Drop logging of osmux internal counters
* osmux: cleanup misleading code calling rtp_bridge_cb
* osmux: Unify rtp_conn osmux type into a single type
* osmux: Match remote address in osmux_conn_lookup()
* osmux: Fill in from_addr in struct osmo_rtp_msg_ctx
* osmux: Log remote address upon rx of osmux pkt
* send_dummy: Use proper condition to test if conn is osmux
* Fix regression in detection of legacy dummy packets
* Use new libosmocore API osmo_sockaddr_is_any()
* Get rid of separate rtp_port field
* osmux: Use available API to check if remote end is known
* Add Osmux IPv6 support
* Clean up local var pointers in mgcp_get_local_addr()
* osmux: Change couple log lines to OSMUX category
* osmux: Add square brackets around IPv6 address to distinguish port in log line
* osmux: Drop unused role parameter
* osmux: cosmetic: Fix indentation
* Check once if remote addr is available when sending dummy packet
* osmux: Simplify and constify param passing
* osmux: Set conn->type during osmux_init_conn()
* osmux: Make conn_osmux_{allocate,release}_local_cid() APIs static
* osmux: Define osmux_dummy cfg as boolean
* osmux: Move setting OSMUX_STATE_DISABLED to initializer function
* osmux: Erase references to bsc-nat
* mgcp_conn_dump(): Separate dump for osmux and iuup connections
* osmux: Introduce osmux peer-behind-nat (on|off) and rework conn activation
* osmux: Clean up mgcp_config osmux fields
* cosmetic: Fix typo in comment
* mgcp-client: pool: Improve documentation of some internal fields
* mgcp-client: Avoid double iteration picking client from pool
* mgcp-client: Fix typo in internal function name
* mgcp-client: Rename internal field in mgcp_client_pool
* mgcp-client: Move & rename helper function outside of vty code
* mgcp-client: Create alloc() and free() internal APIs for mgcp_client_pool_member
* mgcp-client: Move internal API acting on mgcp_client_pool to the correct file section
* mgcp-client: Refactor reinit of mgcp clients
* mgcp-client: Rearrange order of structs and APIs in header
* mgcp-client: Rearrange internal backpointers
* mgcp-client: Introduce APIs to manually select mgcp_client from pool
* mgcp-client: vty: Write deprecation warning using non-mgw nodes
* mgcp-client: Use random free local port by default
* mgcp-client: Convert users supporting new MGW Pool VTY node during write-config
* mgcp-client: Introduce API mgcp_client_pool_config_write
* mgcp-client: Add new VTY commands under mgw node without mgw prefix
* mgcp-client: Fix 'mgw endpoint-range' command dropped from old VTY node
* mgcp-client: Refactor system keeping old users not calling mgcp_client_pool_config_write() working
* mgcp-client: Fix no 'mgw ' prefix written in old VTY node
* Fix misleading error log
* mgcp_send: Use mgcp_conn_rtp_is_iuup() helper
* cosmetic: Clarify and fix typos in comment
* mgw: Log unexpected RTP AMR OA-vs-BE payload
* Rename and move func checking if amr mode is explicitly configured
* mgw: rx_rtp(): reorder checks and handlings
* mgw: Rename s/mgcp_send_rtp/mgcp_conn_rtp_dispatch_rtp/
* osmux: Rename function and pass msgb directly to it
* osmux: Make sure RTP AMR feed to osmux is in octet-aligned mode
* Improve logging on AMR OA<->BWE conversion failure
* osmux: Rotate over available Osmux CID when allocating a new one
* osmux: Use new osmux_xfrm_input API to set name on each link
* iuup: Use osmo_amr_ft_valid() API
[ Vadim Yanitskiy ]
* libosmo-mgcp-client: add -no-undefined to *_la_LDFLAGS
[ Philipp Maier ]
* mgcp_e1: fix apidoc
* mgcp_network: improve coment
[ Max ]
* Set working directory in systemd service file
* Add realtime scheduling and set priority in service file
* ctrl: take both address and port from vty config
[ Harald Welte ]
* Make osmo_mgcpc_ep_fsm_pre_term() static
* Support building with -Werror=strict-prototypes / -Werror=old-style-definition
* Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition
* update outdated vty copyright statement
[ Oliver Smith ]
* mgcp_client_pool.h: add missing stdbool include
* Fix various typos
* mgcp_client: add new clearmode codec
[ Neels Hofmeyr ]
* AMR->IuUP: do not crash on AMR data before IuUP Init
* AMR->IuUP: properly translate Q -> FQC
* IuUP->AMR: do not patch payload type a second time
* IuUP->AMR: log whether converting to AMR OA or BE
* AMR->IuUP: log conversion, like for the flipside
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 16:58:41 +0100
osmo-mgw (1.10.0) unstable; urgency=medium
[ Eric ]
* adjust mgcp response context
* rework message handling
* add a lock-free bounded spsc interthread queue
* clang-format: remove foreach macros
* fix mgcp_conn_free_all ubsan complaints
[ Philipp Maier ]
* configuration: point out difference between trunk-nr and e1 line nr
* mgcp_client: add new codec IUFP as VND.3GPP.IUFP
* mgcp_codec: do not differentiate between oa and bwe when comparing codec
* mgcp_network: do not try to convert RTCP packets
* mgcp_network: fix typo RTPC -> RTCP
[ Oliver Smith ]
* treewide: remove FSF address
[ Pau Espin Pedrol ]
* mgcp_network.c: Set proper CRC Header for ACK Initialization
* cosmetic: Rename variable payload=>payload_type
* mgcp_network.c: Fix byte alignment of CRC Header for ACK Initialization
* Define mgcp_rtp_end.output_enabled as bool
* cosmetic: mgcp_codec.c: Fix typo in comment
* Drop unneeded ax_check_compile_flag.m4
* Make function amr_is_octet_aligned publicly available
* Initial IuUP support using proper FSMs
* IuUP: Support RFCI ID != RFCI Index
* iuup: Fix caps in logging message
* iuup: Check for IuUP Initialization retrans
[ Alexander Couzens ]
* doc/overview: fix wrong project page link
[ Vadim Yanitskiy ]
* tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
* libosmo-mgcp: e1: fix memleaks in e1_recv_cb()
[ Harald Welte ]
* update git URLs (git -> https; gitea)
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 28 Jun 2022 18:50:25 +0200
osmo-mgw (1.9.0) unstable; urgency=medium
[ Harald Welte ]
* TOS bits != DSCP
* manual: don't define fig-bsc twice
* switch from osmo_sock_set_dscp() to OSMO_SOCK_F_DSCP()
* mgw: Add support for setting socket priority from VTY
* manual: Include QoS chapter and add osmo-mgw specific example
[ Keith ]
* Add vty command 'show mgcp active'
* Log some useful messages at ERROR/INFO instead of DEBUG
[ Neels Hofmeyr ]
* send DLCX only once
* tweak termination DLCX log msg
* add osmo_mgcpc_ep_ci_get_remote_rtp_info()
* check_rtp_destin(): clarify log msg
* mgcp_client_fsm delete: set mgcp_client as ctx, not NULL
* mgcp_client_endpoint_fsm: on term, still let conns wait for DLCX OK
* mgcp_client_fsm: add missing log_subsys
* mgcp_client: add logging on received MGCP messages
[ Philipp Maier ]
* mgcp_network: fix implicit address loopback
* mgcp_common, mgcp_udp_send: make parameter buf const
* mgcp_network: refactor MGCP_DUMMY_LOAD
* mgcp_client: drop nunnecessary else statement
* mgcp_client: fix error handling in mgcp message generation
* mgcp_protocol: fix loop that sends dummy RTP packets
* mgw_main: fix loop that resets all endpoints
* mgcp_trunk: drop ws line
* mgcp_ratectr: drop ws line
* mgcp_ratectr: fix sourcecode formatting
* mgcp_trunk: add value string for trunk type.
* mgcp_ratectr: refactor rate counter and set group name
* mgcp_protocol: forward declare mgcp_endpoint
* mgcp_endp: make wildcarded detection separate
* mgcp_protocol: refactor request handler array
* mgcp_trunk: use unsigned int instead of int as trunk_nr
* mgcp_protocol: refactor MGCP request handling
* mgcp_ratectr: add stats items to monitor trunk usage
* mgcp_msg: add trunk parameter to mgcp_check_param for logging
* mgcp_protocol: refactor function create_response_with_sdp
* mgcp_protocol: add support for wildcarded DLCX
* remove struct member wildcarded_req from struct mgcp_endpoint
* mgcp_lient: remove unsubstantial FIXME note
* mgcp_protocol: assert endp when it becomes mandatory
* mgcp_trunk: check MGW domain name earlier
* mgcp_ratectr: add stats items to monitor trunk usage
* mgcp_client: fix typo Initalize -> Initialize
* mgcp_protocol: get rid of policy_cb and change_cb
* mgcp_ratectr: do not set talloc destructor on library allocated item
* mgcp_client: allow to reset endpoints on startup
* mgcp_client_vty: remove unnecessary checks
* mgcp_client: do not print (null) when address is ANY
* mgcp_client: refactor function init_socket
* libosmo-mgcp-client: extend the mgcp_client for MGW pooling
* mgcp_client_vty: add missing docstrings
* mgcp_client_vty: add OSMO_ASSERT on pool parameter
* mgcp_client_vty: fix docstrings for mgw-pool
* mgcp_client_vty: cosmetic: doc string should terminated with \n
* mgcp_client_vty: fixing docstring
* mgcp_ratectr: remove unusued rate counters
* mgcp_client: fix typo in doxygen comment
* mgcp_client: add MGW name as logging context
[ Pau Espin Pedrol ]
* Use new stat item/ctr getter APIs
* constify arg in addr_is_any()
* Define patch_ssrc as bool type
* constify some function arg pointers
* mgcp_network.c: Reorder some functions in file
* mgcp_send_dummy: Check RTP destination is available before attempt tx
* mgw: rx CRCX: Avoid sending dummy rtp if remote address not provided
* Fail rx MDCX sendrecv with invalid remote addr
* Take into account Marker bit when patching RTP stream
* Use DLMGCP instead of DLGLOBAL in log lines
* Fix attribute parsing on gcc 11.1.0
[ neels ]
* Revert "mgcp_ratectr: add stats items to monitor trunk usage"
[ Daniel Willmann ]
* contrib/jenkins: Use ASAN for osmo-mgw
[ Oliver Smith ]
* mgcp_client_vty: add missing NO_STR
* Revert "Turn some compiler warnings into errors"
[ Eric ]
* add vscode stuff to gitignore
* configure.ac: fix maybe-uninitialized for clang
* fix missing includes and forward declarations
* rename do_retransmission
* mgcp_sdp: fix potential leak
* stats: make sanitizers happy
* libosmo-mgcp: cleanup audio codex alloc
* libosmo-mgcp: atomic rate counter group indexes
* libosmo-mgcp: do not use the default msgb talloc context
* globally lock the portrange when trying to grab a port to prep for multithreading
* embed strings into structs
* adjust talloc context
* endp: do not cache cfg pointer
* add modified .clang-format
* rename strip_epname and find_specific_endpoint and make them available
* fix mgcp_conn_free_all
* fix up some docstrings that lost their dot
[ Vadim Yanitskiy ]
* libosmo-mgcp: use OSMO_STRLCPY_ARRAY in mgcp_codec_add()
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 16:59:21 +0100
osmo-mgw (1.8.1) unstable; urgency=medium
* attempt to fix RPM spec file after recent soversion bump
-- Harald Welte <laforge@osmocom.org> Wed, 24 Feb 2021 10:56:17 +0100
osmo-mgw (1.8.0) unstable; urgency=medium
[ Harald Welte ]
* Fix number of endpoints of default trunk
* Add CTRL interface to osmo-mgw
* Update per-trunk global packet/byte counters in real-time
* remove accidential TODO-RELEASE entry
* mgcp_protocol: Avoid code duplication between virtual + other trunks
* osmo-mgw.spec.in: Use %config(noreplace) to retain current config file
* mgcp_client_init(): consider "talloc returns NULL" case
* mgcp_client_pending_add(): Consider "talloc returns NULL" case
* libosmo-mgcp-client: fix memleak in case if no response is received
* debian/control + SPEC: Add missing build dependency to libosmo-abis
* osmo-mgw.spec.in: Fix dependency to libosmoabis
* osmo-mgw.spec.in: Add missing dependency to libosmotrau
* Add example osmo-mgw configuration file for Abis/E1
* use osmo_fd_setup() whenever applicable
[ Neels Hofmeyr ]
* fix vty dump_trunk: start from zero, do not omit first CONN
* allow larger MGCP client wqueue: 10 -> 1024
* add osmo-mgw --vty-ref-xml: dump VTY ref XML to stdout
* manuals: generate vty reference xml at build time
* refactor: use msgb to receive, pass and send RTP packets
* change timer T2427001 to X2427
[ Philipp Maier ]
* doc: do not bind osmo-mgw to random ip-address
* cosmetic: remove excess newlines
* mgcp: remove unused callback pointer
* mgcp: find better locations for LOGPCONN and LOGPENDP
* vty: fix unreachable code (error msg on trunk alloc fail)
* mgcp_vty: fix indentation
* osmo-mgw: rename struct mgcp_trunk_config and symbol tcfg
* mgcp_vty: fix indentation in VTY config write
* osmo-mgw: refactor endpoint and trunk handling
* mgcp_trunk: remove audio_name and audio_payload
* ratectr: move rate counter definitions into mgcp_ratectr.h
* cosmetic: fix doxygen
* cosmetic: fix doxygen
* cosmetic: fix doxygen for mgcp_cleanup_rtp_bridge_cb()
* trunk: get rid of virt_trunk pointer
* cosmetic: remove excess space
* endp: move endpoint name generation into mgcp_endp.c
* endp: add name generator function for E1 endpoints
* mgcp_client: add docstring for mgcp_client_rtpbridge_wildcard()
* mgcp_trunk: fix docstring for mgcp_trunk_alloc()
* mgcp_osmux: remove unused define constants
* mgcp_trunk: move enum mgcp_trunk_type to mgcp_trunk.h
* mgcp_trunk: use enum type for trunk type variable
* trunk: parse E1 trunk number
* endp: add typeset for e1-endpoints
* mgcp_conn: move struct mgcp_conn mgcp_conn.h
* mgcp_internal: remove forward declaration struct mgcp_endpoint_type
* endp: add E1 endpoint interlocking
* endp: require domain name also for E1 endpoints
* mgcp_trunk: pick trunk by number and type
* mgcp_client: add function to generate e1-endpoint names
* mgcp_endp: use define constant to define max number of E1 subslots
* mgcp_endp.c: cosmetic: fix sourcecode formatting
* mgcp_trunk: remove double check
* mgcp_test: do not access endpoint array elements directly
* mgcp_vty: fix endpoint number configuration
* mgcp_test: remove trunk2 from unit-test
* mgcp_trunk: use talloc_zero_array instead of _talloc_zero_array
* mgcp_protocol: remove unused variable
* mgcp_e1: finish E1 support, add E1 support from libosmoabis
* get rid of mgcp_internal.h
* mgcp_ratectr: fix comments in header file
* mgcp_e1: use return value of e1inp_line_update()
* mgcp_protocol: log when endpoint is unavailable
* cosmetic: add missing new-line
* mgcp_trunk: drop "trunk 0" limitation
* mgcp_e1: make E1 ts initalization more debugable
* mgcp_e1: remove unused struct member trunk->e1.line
* mgcp_endp: use NUM_E1_TS from e1_input.h
* mgcp_trunk: increase default number of virtual endpoints
* cosmetic: mgcp_client_fsm: change error message.
* mgcp_vty: remove remains of loopback functionality
* mgcp_vty: deprecate bind early command
* mgcp_e1: do not expose function mgcp_e1_init()
* mgcp_vty: add user attributes to configuration commands
* overview: update section limitations.
* overview: fix graph "OsmoMGW used with OsmoBSC"
* overview: fix graph "OsmoMGW used with OsmoMSC"
* overview: add graph to show E1 integration
* configuration: remove hint towards trunk 0 limit
* configuration: drop note about lackin E1 support
* configuration: add note that changes to trunks need a restart
* mgcp_vty: fix config write for trunk 0
* mgcp_vty: add missing VTY commands for E1 trunks
* mgcp_trunk: get rid of magic numbers for E1 slots
* configuration: add section about E1 trunks
* usermanual: add chapter about mgcp endpoints
* mgcp_client: get rid of magic numbers for E1 slots
[ Eric ]
* tests: dlopen does not imply availability of dlsym..
* configure.ac: fix libtool issue with clang and sanitizer
[ Alexander Chemeris ]
* vty: Prepend VTY output of counters for better visual separation.
* counters: Implement more useful counters.
* mgcp_network: Fix a typo in the comment bahviour -> behaviour
* rtp_bridge: Demote a chatty ERROR log message to DEBUG level.
[ Pau Espin Pedrol ]
* Use OSMO_FD_* instead of deprecated BSC_FD_*
* Support setting rt-prio and cpu-affinity mask through VTY
* cosmetic: Rename main talloc ctx
* mgcp-client: Support IPv6 in osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr() implementation
* mgcp-client: Fix trailing whitespace in mgcp_client_fsm.h
* mgcp_client: Use INET6_ADDRSTRLEN to store addresses in str format
* mgcp_client: Allow submitting and parsing IPv6 addr in SDP
* mgcp_client: Allow setting IPv6 addresses
* mgcp_client: Make MGCP_CLIENT_LOCAL_ADDR_DEFAULT IPv6 compatible
* mgcp_client: Support validating IPv6 addresses in CRCX and MDCX commands
* mgcp_client: Deprecate unused IPv4-only API
* mgcp_client: copy back Connection Information from MDCX ACK
* mgw: Fix mgcp_rtp_end field description comment
* mgw: Initial IPv6 support
* mgw: Introduce VTY cmd 'rtp bind-ip-v6' command
* mgw: Find and store RTP conn local_addr once during CRCX handling
* mgw: Announce and rebind new local address if change required during MDCX
* mgw: osmux: Avoid sending packets on recvonly connection
* mgw: Release endpoint after last conn times out
* mgw: osmux: Fix conn watchdog timeout not updated
* mgw: Don't be case-sensitive when parsing X-Osmo-IGN param
* cosmetic: Fix typo in comment
* mgw: Avoid logging notice message each time we receive nt param in LCO
* mgw: Fix return value documentation for API mgcp_verify_call_id
* cosmetic: Fix typo in comment
* contrib/jenkins: Enable parallel make in make distcheck
* .gitignore: Ignore new autofoo tmp files
* tests: Replace deprecated API log_set_print_filename
[ Oliver Smith ]
* contrib: import RPM spec
* contrib: integrate RPM spec
* Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
* contrib/jenkins: don't build osmo-gsm-manuals
* configure.ac: set -std=gnu11
[ Vadim Yanitskiy ]
* libosmo-mgcp-client: fix use-after-free in mgcp_client_tx()
* libosmo-mgcp-client: fix use-after-free in mgcp_msg_gen()
* libosmo-mgcp: fix unsigned compared against 0 in mgcp_trunk_by_name()
* libosmo-mgcp: fix unused extra argument to printf() in add_fmtp()
* libosmo-mgcp: always check result of msgb_printf() in add_fmtp()
* libosmo-mgcp-client: mgcp_client_tx(): return rc on error
* debian/control: change maintainer to the Osmocom team / mailing list
* vty: use install_lib_element() and install_lib_element_ve()
* main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
* fixup mgcp_trunk: increase default number of virtual endpoints
[ Alexander Couzens ]
* configure.ac: require libosmoabis + libosmotrau >= 1.0.0
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 18:28:45 +0100
osmo-mgw (1.7.0) unstable; urgency=medium
[ Neels Hofmeyr ]

2
debian/compat vendored
View File

@@ -1 +1 @@
10
9

18
debian/control vendored
View File

@@ -2,17 +2,17 @@ Source: osmo-mgw
Section: net
Priority: extra
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
Build-Depends: debhelper (>= 10),
Build-Depends: debhelper (>=9),
dh-autoreconf,
pkg-config,
autotools-dev,
libosmocore-dev (>= 1.11.0),
libosmo-netif-dev (>= 1.6.0),
libosmo-abis-dev (>= 2.0.0),
osmo-gsm-manuals-dev (>= 1.6.0)
libosmocore-dev (>= 1.4.0),
libosmo-netif-dev,
libosmo-abis-dev,
osmo-gsm-manuals-dev
Standards-Version: 3.9.8
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
Vcs-Git: git://git.osmocom.org/osmo-mgw.git
Vcs-Browser: https://git.osmocom.org/osmo-mgw/
Homepage: https://osmocom.org/projects/osmo-mgw
Package: osmo-mgw
@@ -21,7 +21,7 @@ Multi-Arch: foreign
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
Package: libosmo-mgcp-client14
Package: libosmo-mgcp-client6
Section: libs
Architecture: any
Multi-Arch: same
@@ -33,7 +33,7 @@ Package: libosmo-mgcp-client-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-mgcp-client14 (= ${binary:Version}), ${misc:Depends}
Depends: libosmo-mgcp-client6 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
Package: osmo-mgw-doc

6
debian/copyright vendored
View File

@@ -1,12 +1,12 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: osmo-mgw
Source: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
Source: git://git.osmocom.org/osmo-mgw
Files: *
Copyright: 2009-2014 On-Waves
2009-2015 Holger Hans Peter Freyther <zecke@selfish.org>
2013 Jacob Erlbeck <jerlbeck@sysmocom.de>
2016-2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
2016-2017 sysmocom s.m.f.c. GmbH <info@sysmocom.de>
License: AGPL-3.0+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@@ -22,7 +22,7 @@ License: AGPL-3.0+
along with this program. If not, see <http://www.gnu.org/licenses/>.
Files: src/libosmo-mgcp-client/* include/osmocom/mgcp_client/*
Copyright: 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
Copyright: 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
2009 by Harald Welte <laforge@gnumonks.org>
2009-2011 by Holger Hans Peter Freyther

38
debian/postinst vendored
View File

@@ -1,38 +0,0 @@
#!/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.13.0"; then
if [ -e /etc/osmocom/osmo-mgw.cfg ]; then
chown -v osmocom:osmocom /etc/osmocom/osmo-mgw.cfg
chmod -v 0660 /etc/osmocom/osmo-mgw.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#

View File

@@ -1,25 +1,20 @@
!
! MGCP configuration example
!
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
e1_input
e1_line 0 driver dahdi
e1_line 0 port 0
mgcp
bind ip 127.0.0.1
rtp port-range 4002 16001
rtp port-range 4002 16000
rtp bind-ip 127.0.0.1
rtp ip-probing
rtp ip-dscp 46
rtp ip-tos 184
bind port 2427
number endpoints 512
sdp audio payload number 98
sdp audio payload name GSM
number endpoints 31
loop 0
force-realloc 1
rtcp-omit
rtp-patch ssrc

View File

@@ -1,22 +1,17 @@
!
! MGCP configuration example
!
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
mgcp
bind ip 127.0.0.1
rtp port-range 4002 16001
rtp port-range 4002 16000
rtp bind-ip 127.0.0.1
rtp ip-probing
rtp ip-dscp 46
rtp ip-tos 184
bind port 2427
number endpoints 512
sdp audio payload number 98
sdp audio payload name GSM
number endpoints 31
loop 0
force-realloc 1
rtcp-omit
rtp-patch ssrc

View File

@@ -23,13 +23,14 @@ OsmoMGW(config-mgcp)# local ip 127.0.0.1
=== Configuring the trunk
The first trunk is considered a virtual trunk in OsmoMGW. All
The first trunk (trunk 0) is considered a virtual trunk in OsmoMGW. All
endpoints of type "rtpbridge" are routed here. The virtual trunk is configured
in the config-mgcp context.
All other trunks are configured in the config-mgcp-trunk context, but the
commands used are identical. Right now trunks are considered only for "ds/e1"
type endpoints.
commands used are identical. Right now trunks are considered only for ds/e1
type endpoints which are not yet implemented. Don't use trunks other than the
"virtual" trunk 0.
.Example: MGCP trunk configuration
----
@@ -53,77 +54,4 @@ OsmoMGW(config-mgcp)# rtp-patch timestamp <3>
<2> Hide SSRC changes
<3> Ensure RTP timestamp is aligned with frame duration
NOTE: Changes to trunks that affect resource allocation, such as newly created
trunks or a change of the number of available endpoints, require a full restart
of osmo-mgw!
=== E1 trunk considerations
While the RTP bridge trunks are natively based on IP no special considerations
are required during setup. E1 trunks are mapped on a physical E1 line, which has
to be configured as shown below.
.Example: E1 line setup
----
OsmoMGW(config-e1_input)# e1_line 0 driver dahdi <1>
OsmoMGW(config-e1_input)# e1_line 0 port 2 <2>
----
<1> Name of the libosmo-abis driver implementation ("dahdi")
<2> Port number of the physical E1 port to use (2)
In osmo-mgw the e1_input node is used to configure the physical E1 line. The
line number will be used internally to identify the configured E1 line. The
port number is the physical E1 connector (sometimes called 'span') at the E1
hardware. Per trunk an individual E1 line will be needed. Beware that the E1
driver may also need configuration settings that are not discussed here.
.Example: E1 trunk setup
----
OsmoMGW(config-mgcp)# trunk 0 <1>
OsmoMGW(config-mgcp-trunk)# line 0 <2>
----
<1> Creation of a trunk (0)
<2> Reference to the E1 line to use (0)
The E1 trunk is created along with a number, typically starting at 0, but if
required any number from 0-64 is allowed. The E1 trunk configuration concerning
the IP related aspects is nearly identical to the configuration of the virtual
trunk. However, it is important that the user assigns one of the E1 line numbers
that were configured under the e1_input node.
NOTE: The endpoint name that is used on MGCP level will include the trunk number,
not the E1 line number. For simplicity (and compatibility with OsmoBSC) it is
recommended to use equal numbers for trunk and E1 line. However, if required any
E1 line can be mapped flexible on any trunk as long as the mapping is bijective.
.Example: A typical configuration with one E1 trunk
----
e1_input
e1_line 0 driver dahdi
e1_line 0 port 2
mgcp
bind ip 127.0.0.1
rtp net-range 6000 6011
rtp net-bind-ip 192.168.100.130
rtp ip-probing
rtp ip-dscp 46
no rtp keep-alive
bind port 2428
number endpoints 30
loop 0
force-realloc 1
osmux off
rtp-patch rfc5993hr
trunk 0
rtp keep-alive once
no rtp keep-alive
line 0
----
NOTE: One E1 trunk always covers a whole E1 line. All subslots (I.640) will be mapped
to individual MGCP endpoints. As long as the endpoints remain unused the
underlying E1 timeslot is not used.
NOTE: The E1 trunk implementation also works with T1 lines, however since T1 has
24 instead of 31 usable timeslots only the endpoints that fall into that 1-24 timeslot
range will be useable.

View File

@@ -1,100 +0,0 @@
== MGCP Endpoints
MGCP organizes the switching resources in so called endpoints. Each endpoint is
referenced by its unique identifier. While RFC 3435 specifies a naming scheme, the
actual identifier naming is subject to the implementation and configuration.
=== RTP proxy / RTP bridge endpoints
OsmoMGW implements a freely configurable number of `rtpbridge` endpoints. Those
endpoints are able to host two connections at a time to model the functionality
of a tandem switch.
RTP bridge endpoint identifiers are referenced by the string `rtpbridge/`, a
hexadecimal number without leading zeros and a domain name (configurable).
----
rtpbridge/<number>@<domain>
----
.Example: List of virtual endpoints
----
rtpbridge/1@mgw
rtpbridge/2@mgw
rtpbridge/3@mgw
rtpbridge/4@mgw
rtpbridge/5@mgw
rtpbridge/6@mgw
rtpbridge/7@mgw
rtpbridge/8@mgw
rtpbridge/9@mgw
rtpbridge/a@mgw
rtpbridge/b@mgw
rtpbridge/c@mgw
rtpbridge/d@mgw
rtpbridge/e@mgw
rtpbridge/f@mgw
rtpbridge/10@mgw
----
=== E1/T1 endpoints
OsmoMGW supports E1 subslot multiplexing as specified by I.460. All possible
subslot combinations are mapped on individual endpoints. The endpoint names
are prefixed with `ds/e1-` followed by the trunk number and the E1 timeslot.
The subslot is defined by a bit rate and a bit offset.
----
ds/e1-<trunk>/s-<timeslot>/su<bitrate>-<bitoffset>@<domain>
----
.Example: List of endpoints on E1 trunk 0 at E1 timeslot 2
----
ds/e1-0/s-2/su64-0@mgw
ds/e1-0/s-2/su32-0@mgw
ds/e1-0/s-2/su32-4@mgw
ds/e1-0/s-2/su16-0@mgw
ds/e1-0/s-2/su16-2@mgw
ds/e1-0/s-2/su16-4@mgw
ds/e1-0/s-2/su16-6@mgw
ds/e1-0/s-2/su8-0@mgw
ds/e1-0/s-2/su8-1@mgw
ds/e1-0/s-2/su8-2@mgw
ds/e1-0/s-2/su8-3@mgw
ds/e1-0/s-2/su8-4@mgw
ds/e1-0/s-2/su8-5@mgw
ds/e1-0/s-2/su8-6@mgw
ds/e1-0/s-2/su8-7@mgw
----
When creating connections on endpoints that reside in one E1 timeslot the call
agent must make sure that no overlapping endpoints are used. It is for example
not possible to use `ds/e1-0/s-2/su16-2@mgw` and `ds/e1-0/s-2/su8-3@mgw` at the
same time because they overlap.
.Subslot overlapping
[options="header"]
|===
| Bit offset 4+| Subslots
| 0 | 8k .2+| 16k .4+| 32k .8+| 64k
| 1 | 8k
| 2 | 8k .2+| 16k
| 3 | 8k
| 4 | 8k .2+| 16k .4+| 32k
| 5 | 8k
| 6 | 8k .2+| 16k
| 7 | 8k
|===
NOTE: The current implementation (December 2020) only implements TRAU frame
encoding/decoding for 16K and 8K subslots. Endpoints with other bitrates are
not yet useable.
NOTE: the VTY command "show mgcp" can be used to get a list of all available
endpoints (including identifiers)
=== The `null` endpoint
OsmoMGW offers a special `null@<domain>` endpoint which can be audited at all times.
This is useful for MGCP clients who wish to submit requests to OsmoMGW
periodically to find out whether it is still reachable and in a working state.

View File

@@ -70,85 +70,3 @@ a=ptime:40
=== `X-Osmux`
See <<mgcp-extension-osmux>>
=== Non-standard SDP codec parameters
CRCX command message normally includes an SDP block that specifies the RTP
address of the connecting peer and the list of codecs accepted by that entity.
Codec specifications may include optional parameters given via `a=fmtp` lines;
one well-known parameter is `octet-align`, defined for AMR and AMR-WB codecs.
In addition to standard (RFC-defined) codec parameters (of which the
just-mentioned `octet-align` parameter is the only one implemented so far),
OsmoMGW also understands some non-standard (Osmocom-private) codec parameters
as listed below.
These Osmocom-private codec parameters are meaningful only in the context
of OsmoBSC driving an E1 Abis MGW, which may be either OsmoMGW or a specialized
third-party replacement.
The MGCP extension described in this section is meaningful only for E1
endpoints - it has absolutely no effect on `rtpbridge` endpoints!
==== `tw-ts-001` parameter for FR and EFR codecs
GSM Full Rate codec is known in SDP as `GSM`; GSM Enhanced Full Rate codec
is known in SDP as `GSM-EFR`.
When either codec is to be transported in RTP, there is a choice between
two different payload formats: RFC 3551 or TW-TS-001.
RFC 3551 is the standard in non-GSM Internet applications, and it is the
format specified in later 3GPP releases for IP-based transport of
compressed speech - however, it exhibits functional regressions compared
to the original TRAU-UL frame format of GSM 08.60.
TW-TS-001 is an enhanced alternative payload format for the same codecs
that restores the original functionality and semantics of TRAU-UL frames.
In the context of CRCX or MDCX command messages from OsmoBSC to an E1 Abis MGW,
`tw-ts-001` codec parameter (attached to either `GSM` or `GSM-EFR` codec)
instructs the MGW whether or not it should emit the extended payload format
of TW-TS-001 for GSM uplink traffic converted to RTP.
In the absence of this parameter, IETF-standard and 3GPP-standard RFC 3551
format is emitted; if TW-TS-001 output is desired, please specify
`tw-ts-001=1` on the appropriate `a=fmtp` line.
.Example: `CRCX` message requesting EFR codec with TW-TS-001 extensions
----
CRCX 2 ds/e1-0/s-2/su16-2@mgw MGCP 1.0
M: recvonly
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 1234 RTP/AVP 110
a=rtpmap:110 GSM-EFR/8000/1
a=fmtp:110 tw-ts-001=1
----
==== `tw-ts-002` parameter for HR codec
GSM Half Rate codec is known in SDP as `GSM-HR-08`.
The same dichotomy that exists between RFC 3551 and TW-TS-001 RTP payload
formats for FR and EFR codecs also exists for HR codec, except that
the two opposing alternatives are now RFC 5993 and TW-TS-002.
In the context of CRCX or MDCX command messages from OsmoBSC to an E1 Abis MGW,
`tw-ts-002` codec parameter (attached to `GSM-HR-08` codec)
instructs the MGW whether or not it should emit the extended payload format
of TW-TS-002 for GSM uplink traffic converted to RTP.
In the absence of this parameter, IETF-standard and 3GPP-standard RFC 5993
format is emitted; if TW-TS-002 output is desired, please specify
`tw-ts-002=1` on the appropriate `a=fmtp` line.
.Example: `CRCX` message requesting HR codec with TW-TS-002 extensions
----
CRCX 2 ds/e1-0/s-2/su8-2@mgw MGCP 1.0
M: recvonly
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 1234 RTP/AVP 111
a=rtpmap:111 GSM-HR-08/8000/1
a=fmtp:111 tw-ts-002=1
----

View File

@@ -21,10 +21,10 @@ Protocol.
digraph G {
rankdir = LR;
OsmoBTS -> OsmoBSC [label="Abis/IP"];
OsmoBSC -> "core-network" [label="3GPP AoIP"];
OsmoBSC -> OsmoMSC [label="3GPP AoIP"];
OsmoBSC -> OsmoMGW [label="MGCP"];
OsmoBTS -> OsmoMGW [label="RTP",dir=both];
OsmoMGW -> "core-network" [label="RTP",dir=both];
OsmoMGW -> OsmoMSC [label="RTP",dir=both];
{rank=same OsmoBSC OsmoMGW}
OsmoMGW [color=red];
}
@@ -36,9 +36,10 @@ digraph G {
----
digraph G {
rankdir = LR;
"2G BSS" -> OsmoMSC [label="3GPP AoIP"];
BTS -> BSC [label="Abis"];
BSC -> OsmoMSC [label="3GPP AoIP"];
OsmoMSC -> OsmoMGW [label="MGCP"];
"2G BSS" -> OsmoMGW [label="RTP",dir=both];
BSC -> OsmoMGW [label="RTP",dir=both];
OsmoMSC -> OsmoSIP [label="MNCC"];
OsmoSIP -> PBX [label="SIP Trunk"];
OsmoMGW -> PBX [label="RTP",dir=both];
@@ -52,22 +53,6 @@ digraph G {
}
----
[[fig-bsc-e1]]
.Integration of legacy E1 BTS in AoIP network
[graphviz]
----
digraph G {
rankdir = LR;
BTS -> OsmoBSC [label="Abis/E1"];
OsmoBSC -> "core-network" [label="3GPP AoIP"];
OsmoBSC -> OsmoMGW [label="MGCP"];
BTS -> OsmoMGW [label="TRAU/E1",dir=both];
OsmoMGW -> "core-network" [label="RTP",dir=both];
{rank=same OsmoBSC OsmoMGW}
OsmoMGW [color=red];
}
----
=== Software Components
OsmoMGW contains a variety of different software components, which well
@@ -92,14 +77,14 @@ Transcoding is currently not supported in OsmoMGW.
=== Limitations
At the moment (November 2020), OsmoMGW implements RTP proxy / RTP bridge
type endpoints and E1/T1 16k/8k sub-slots with TRAU frames for classic BTS
support. To the RTP proxy / RTP bridge endpoints two RTP connections can
be established, which then work as a tandem. E1/T1 endpoints support one
RTP connection at a time that is associated with a sub-slot on an E1 line.
We are planning to add further endpoint types for:
Osmux is not yet supported in OsmoMGW.
At the moment (July 2018), OsmoMGW only implements RTP proxy / RTP bridge
type endpoints, to each of which two RTP connections can be established.
We are planning to add endpoint types for:
- classic E1/T1 timeslots (64kBps alaw/ulaw)
- classic E1/T1 16k sub-slots with TRAU frames for classic BTS support
- announcement/playout end-points
- conference endpoints
@@ -107,8 +92,8 @@ We are planning to add further endpoint types for:
You can find the OsmoMGW issue tracker and wiki online at
- https://osmocom.org/projects/osmo-mgw
- https://osmocom.org/projects/osmo-mgw/wiki
- https://osmocom.org/projects/osmomgw
- https://osmocom.org/projects/osmomgw/wiki
RFC 3435 for MGCP is located at

View File

@@ -1,42 +0,0 @@
==== Full example of QoS for osmo-mgw
In the below example we will show the full set of configuration required
for both DSCP and PCP differentiation of RTP traffic by osmo-mgw.
What we want to achieve in this example is the following configuration:
.DSCP and PCP assignments for osmo-mgw Abis downlink traffic in this example
[options="header",width="30%",cols="2,1,1"]
|===
|Traffic |DSCP|PCP
|RTP | 46| 6
|===
. configure the osmo-mgw program to set the DSCP value
. configure an egrees QoS map to map from priority to PCP
.Example Step 1: add related VTY configuration to `osmo-mgw.cfg`
----
...
mgcp
rtp ip-dscp 46
rtp socket-priority 6
...
----
.Example Step 2: egress QoS map to map from socket priority to PCP values
----
$ sudo ip link set dev eth0.9<1> type vlan egress-qos-map 0:0 5:5 6:6 7:7 <2>
----
<1> make sure to specify your specific VLAN interface name here instead of `eth0.9`.
<2> create a egress QoS map that maps the priority value 1:1 to the PCP. We also include the
mapping 5:5 and 7:7 from the osmo-bsc example (see <<userman-osmobsc>>) here.
NOTE:: The settings of the `ip` command are volatile and only active until
the next reboot (or the network device or VLAN is removed). Please refer to
the documentation of your specific Linux distribution in order to find out how
to make such settings persistent by means of an `ifup` hook whenever the interface
comes up. For CentOS/RHEL 8 this can e.g. be achieved by means of an `/sbin/ifup-local
script` (when using `network-scripts` and not NetworkManager). For Debian or Ubuntu,
this typically involves adding `up` lines to `/etc/network/interfaces` or a `/etc/network/if-up.d`
script.

View File

@@ -23,20 +23,3 @@ arguments:
Disable colors for logging to stderr. This has mostly been
deprecated by VTY based logging configuration, see <<logging>>
for more information.
=== Configure limits
When servicing hundreds of media endpoints, it may be necessary to adjust the
operating system's limit on open file descriptors for the osmo-mgw process. A
typical default limit imposed by operating systems is 1024; this would be
exceeded by, for example, about 256 active voice calls with 4 RTP/RTPC ports
each, sockets for other interfaces not considered yet.
It should be ok to set an OS limit on open file descriptors as high as 65536
for osmo-mgw, which practically rules out failure from running out of file
descriptors anywhere (<16,000 active calls).
When using systemd, the file descriptor limit may be adjusted in the service
file by the `LimitNOFILE` setting ("Number of Open FILE descriptors"). OsmoMGW
ships a systemd service file with a high LimitNOFILE setting.

View File

@@ -18,14 +18,10 @@ include::./common/chapters/logging.adoc[]
include::{srcdir}/chapters/configuration.adoc[]
include::{srcdir}/chapters/mgcp_endpoints.adoc[]
include::{srcdir}/chapters/mgcp_extensions.adoc[]
include::./common/chapters/osmux/osmux.adoc[]
include::./common/chapters/qos-dscp-pcp.adoc[]
//include::{srcdir}/chapters/counters.adoc[]
include::./common/chapters/vty_cpu_sched.adoc[]

View File

@@ -3,14 +3,9 @@ SUBDIRS = \
$(NULL)
nobase_include_HEADERS = \
osmocom/mgcp_client/defs.h \
osmocom/mgcp_client/mgcp_client.h \
osmocom/mgcp_client/mgcp_client_endpoint_fsm.h \
osmocom/mgcp_client/mgcp_client_fsm.h \
osmocom/mgcp_client/mgcp_client_pool.h \
$(NULL)
noinst_HEADERS = \
osmocom/mgcp/mgcp.h \
osmocom/mgcp/mgcp_common.h \
osmocom/mgcp/osmux.h \

View File

@@ -1,18 +1,16 @@
noinst_HEADERS = \
vty.h \
mgcp_msg.h \
mgcp_codec.h \
mgcp_conn.h \
mgcp_stat.h \
mgcp_endp.h \
mgcp_sdp.h \
mgcp_codec.h \
mgcp_ctrl.h \
mgcp_trunk.h \
debug.h \
mgcp_ratectr.h \
mgcp_e1.h \
mgcp_network.h \
mgcp_protocol.h \
mgcp_iuup.h \
mgcp_rtp_end.h \
$(NULL)

View File

@@ -30,7 +30,6 @@
enum {
DRTP,
DE1,
DOSMUX,
Debug_LastEntry,
};

View File

@@ -24,24 +24,19 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/logging.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/osmux.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include "mgcp_ratectr.h"
extern void *tall_mgw_ctx;
#define RTP_PORT_DEFAULT_RANGE_START 16002
#define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64
@@ -52,6 +47,11 @@ struct mgcp_endpoint;
struct mgcp_config;
struct mgcp_trunk;
struct mgcp_rtp_end;
#define MGCP_ENDP_CRCX 1
#define MGCP_ENDP_DLCX 2
#define MGCP_ENDP_MDCX 3
/*
* what to do with the msg?
* - continue as usual?
@@ -62,16 +62,20 @@ struct mgcp_rtp_end;
#define MGCP_POLICY_REJECT 5
#define MGCP_POLICY_DEFER 6
typedef int (*mgcp_change)(struct mgcp_endpoint *endp, int state);
typedef int (*mgcp_policy)(struct mgcp_endpoint *endp, int state, const char *transaction_id);
typedef int (*mgcp_reset)(struct mgcp_trunk *cfg);
typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
/**
* Return:
* < 0 in case no audio was processed
* >= 0 in case audio was processed.
* >= 0 in case audio was processed. The remaining payload
* length will be returned.
*/
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end, struct msgb *msg);
struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
struct mgcp_conn_rtp;
@@ -90,10 +94,9 @@ typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
* This holds information on how to allocate ports
*/
struct mgcp_port_range {
pthread_mutex_t lock;
/* addr or NULL to fall-back to default */
char bind_addr_v4[INET6_ADDRSTRLEN];
char bind_addr_v6[INET6_ADDRSTRLEN];
char *bind_addr_v4;
char *bind_addr_v6;
/* dynamically allocated */
int range_start;
@@ -126,47 +129,54 @@ enum mgcp_role {
struct mgcp_config {
int source_port;
char local_ip[INET6_ADDRSTRLEN];
char source_addr[INET6_ADDRSTRLEN];
char call_agent_addr[INET6_ADDRSTRLEN];
char *local_ip;
char *source_addr;
char *call_agent_addr;
/* RTP processing */
mgcp_processing rtp_processing_cb;
mgcp_processing_setup setup_rtp_processing_cb;
mgcp_get_format get_net_downlink_format_cb;
struct osmo_wqueue gw_fd;
struct mgcp_port_range net_ports;
int endp_dscp;
int endp_priority;
int force_ptime;
mgcp_change change_cb;
mgcp_policy policy_cb;
mgcp_reset reset_cb;
mgcp_rqnt rqnt_cb;
void *data;
uint32_t last_call_id;
/* list holding the trunks */
struct llist_head trunks;
enum mgcp_role role;
struct {
/* Osmux usage policy: */
enum osmux_usage usage;
/* addr to bind the server to */
char *local_addr_v4;
char *local_addr_v6;
/* osmux port */
uint16_t local_port;
/* The osmux socket is allocated on demand (1st time used).
* This tells us if the osmux socket is already initialized. */
bool initialized;
/* osmux batch factor: from 1 to 4 maximum */
int batch_factor;
/* osmux batch size (in bytes) */
int batch_size;
/* Pad circuit with dummy AMR frames if no payload to transmit is available */
bool dummy_padding;
/* Whether peer is behind NAT (Retrieve remote addr from 1st received Osmux packet) */
bool peer_behind_nat;
} osmux;
/* osmux translator: 0 means disabled, 1 means enabled */
int osmux;
/* addr to bind the server to */
char *osmux_addr;
/* The BSC-NAT may ask for enabling osmux on demand. This tells us if
* the osmux socket is already initialized.
*/
int osmux_init;
/* osmux batch factor: from 1 to 4 maximum */
int osmux_batch;
/* osmux batch size (in bytes) */
int osmux_batch_size;
/* osmux port */
uint16_t osmux_port;
/* Pad circuit with dummy messages until we see the first voice
* message.
*/
uint16_t osmux_dummy;
/* domain name of the media gateway */
char domain[255+1];
@@ -198,5 +208,5 @@ int mgcp_send_reset_ep(struct mgcp_endpoint *endp);
int mgcp_send_reset_all(struct mgcp_config *cfg);
int mgcp_create_bind(const char *source_addr, int port, uint8_t dscp, uint8_t prio);
int mgcp_udp_send(struct osmo_io_fd *iofd, const struct osmo_sockaddr *addr, const char *buf, int len);
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port);
int mgcp_udp_send(int fd, struct osmo_sockaddr *addr, int port, char *buf, int len);

View File

@@ -1,9 +1,5 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <osmocom/mgcp/mgcp_common.h>
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
@@ -12,37 +8,12 @@
#define PTYPE_UNDEFINED (-1)
struct mgcp_rtp_codec {
uint32_t rate;
int channels;
uint32_t frame_duration_num;
uint32_t frame_duration_den;
struct mgcp_conn_rtp;
int payload_type;
char audio_name[64];
char subtype_name[64];
bool param_present;
struct mgcp_codec_param param;
};
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec);
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec);
struct mgcp_rtp_codecset {
/* currently selected audio codec */
struct mgcp_rtp_codec *codec;
/* array with assigned audio codecs to choose from (SDP) */
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
/* number of assigned audio codecs (SDP) */
unsigned int codecs_assigned;
};
void mgcp_codecset_reset(struct mgcp_rtp_codecset *cset);
void mgcp_codecset_summary(struct mgcp_rtp_codecset *cset, const char *prefix_str);
int mgcp_codecset_add_codec(struct mgcp_rtp_codecset *cset, int payload_type,
const char *audio_name, const struct mgcp_codec_param *param);
int mgcp_codecset_decide(struct mgcp_rtp_codecset *cset_src, struct mgcp_rtp_codecset *cset_dst);
const struct mgcp_rtp_codec *mgcp_codecset_pt_find_by_subtype_name(const struct mgcp_rtp_codecset *cset,
void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param);
int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
const char *subtype_name, unsigned int match_nr);
struct mgcp_rtp_codec *mgcp_codecset_find_codec_from_pt(struct mgcp_rtp_codecset *cset, int payload_type);

View File

@@ -47,7 +47,6 @@ enum mgcp_connection_mode {
MGCP_CONN_SEND_ONLY = 2,
MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
MGCP_CONN_LOOPBACK = 4 | MGCP_CONN_RECV_SEND,
MGCP_CONN_CONFECHO = 8 | MGCP_CONN_RECV_SEND,
};
#define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:"
@@ -63,20 +62,16 @@ enum mgcp_x_osmo_ign {
struct mgcp_codec_param {
bool amr_octet_aligned_present;
bool amr_octet_aligned;
bool fr_efr_twts001_present;
bool fr_efr_twts001;
bool hr_twts002_present;
bool hr_twts002;
};
/* Ensure that the msg->l2h is NUL terminated. */
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
{
unsigned char *tail = msg->tail; /* char after l2 data */
unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */
if (tail[-1] == '\0')
/* nothing to do */;
else if (msgb_tailroom(msg) > 0)
msgb_put_u8(msg, (uint8_t)'\0');
tail[0] = '\0';
else if (tail[-1] == '\r' || tail[-1] == '\n')
tail[-1] = '\0';
else {

View File

@@ -24,14 +24,10 @@
#pragma once
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_network.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/iuup.h>
#include <osmocom/mgcp/mgcp_rtp_end.h>
#include <inttypes.h>
#define LOGPCONN(conn, cat, level, fmt, args...) \
@@ -49,14 +45,9 @@ LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
enum mgcp_conn_rtp_type {
MGCP_RTP_DEFAULT = 0,
MGCP_RTP_OSMUX,
MGCP_RTP_IUUP,
MGCP_OSMUX_BSC,
MGCP_OSMUX_BSC_NAT,
};
extern const struct value_string mgcp_conn_rtp_type_names[];
static inline const char *mgcp_conn_rtp_type_name(enum mgcp_conn_rtp_type val)
{
return get_value_string(mgcp_conn_rtp_type_names, val);
}
/*! Connection type, specifies which member of the union "u" in mgcp_conn
* contains a useful connection description (currently only RTP) */
@@ -87,31 +78,22 @@ struct mgcp_conn_rtp {
struct {
/* Osmux state: disabled, activating, active */
enum osmux_state state;
/* Is local_cid holding valid data? is it allocated from pool? */
bool local_cid_allocated;
/* Allocated local Osmux circuit ID for this conn */
uint8_t local_cid;
/* Is remote_cid holding valid data? was it already received from client? */
bool remote_cid_present;
/* Received remote Osmux circuit ID for this conn */
uint8_t remote_cid;
/* handle to batch messages, shared (refcounted) among several conns */
/* Is cid holding valid data? is it allocated from pool? */
bool cid_allocated;
/* Allocated Osmux circuit ID for this conn */
uint8_t cid;
/* handle to batch messages */
struct osmux_in_handle *in;
/* handle to unbatch messages, one allocated and owned per conn */
struct osmux_out_handle *out;
/* statistics: */
struct rate_ctr_group *ctrg;
/* handle to unbatch messages */
struct osmux_out_handle out;
/* statistics */
struct {
uint32_t chunks;
uint32_t octets;
} stats;
} osmux;
struct {
struct osmo_iuup_instance *iui;
bool active_init; /* true: Send IuUP Init */
int rfci_id_no_data; /* RFCI Id for RFCI NO_DATA (-1 if not available) */
bool configured;
struct osmo_iuup_rnl_prim *init_ind;
} iuup;
struct rate_ctr_group *ctrg;
struct rate_ctr_group *rate_ctr_group;
};
/*! MGCP connection (untyped) */
@@ -153,12 +135,12 @@ struct mgcp_conn {
enum {
IN_STREAM_ERR_TSTMP_CTR,
OUT_STREAM_ERR_TSTMP_CTR,
RTP_PACKETS_RX_CTR,
RTP_OCTETS_RX_CTR,
RTP_PACKETS_TX_CTR,
RTP_OCTETS_TX_CTR,
RTP_DROPPED_PACKETS_CTR,
RTP_NUM_CONNECTIONS,
RTP_PACKETS_RX_CTR,
RTP_OCTETS_RX_CTR,
RTP_PACKETS_TX_CTR,
RTP_OCTETS_TX_CTR,
RTP_DROPPED_PACKETS_CTR,
RTP_NUM_CONNECTIONS,
};
/* RTP per-connection statistics. Instances of the corresponding rate counter group
@@ -171,7 +153,7 @@ static const struct rate_ctr_desc mgcp_conn_rate_ctr_desc[] = {
[RTP_OCTETS_RX_CTR] = {"rtp:octets_rx", "Inbound rtp octets."},
[RTP_PACKETS_TX_CTR] = {"rtp:packets_tx", "Outbound rtp packets."},
[RTP_OCTETS_TX_CTR] = {"rtp:octets_tx", "Outbound rtp octets."},
[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "Dropped rtp packets."}
[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."}
};
/* Aggregated RTP connection stats. These are updated when an RTP connection is freed.
@@ -189,67 +171,20 @@ static const struct rate_ctr_desc all_rtp_conn_rate_ctr_desc[] = {
[RTP_NUM_CONNECTIONS] = {"all_rtp:num_closed_conns", "Total number of rtp connections closed."}
};
/* Osmux connection related counters */
enum {
OSMUX_CHUNKS_RX_CTR,
OSMUX_OCTETS_RX_CTR,
OSMUX_RTP_PACKETS_TX_CTR,
OSMUX_RTP_PACKETS_TX_DROPPED_CTR,
OSMUX_AMR_OCTETS_TX_CTR,
/* Only available in global stats: */
OSMUX_NUM_CONNECTIONS,
OSMUX_PACKETS_RX_CTR,
OSMUX_PACKETS_TX_CTR,
OSMUX_DROPPED_PACKETS_CTR,
};
/* RTP per-connection statistics. Instances of the corresponding rate counter group
* exist for the lifetime of an RTP connection.
* Must be kept in sync with all_rtp_conn_rate_ctr_desc below */
static const struct rate_ctr_desc mgcp_conn_osmux_rate_ctr_desc[] = {
[OSMUX_CHUNKS_RX_CTR] = {"osmux:chunks_rx", "Inbound Osmux chunks."},
[OSMUX_OCTETS_RX_CTR] = {"osmux:octets_rx", "Inbound Osmux octets."},
[OSMUX_RTP_PACKETS_TX_CTR] = {"osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."},
[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."},
[OSMUX_AMR_OCTETS_TX_CTR] = {"osmux:amr_octets_tx", "Tx outbound AMD payload octets."},
};
/* Aggregated Osmux connection stats. These are updated when an Osmux connection is freed.
* Must be kept in sync with mgcp_conn_osmux_rate_ctr_desc above */
static const struct rate_ctr_desc all_osmux_conn_rate_ctr_desc[] = {
[OSMUX_CHUNKS_RX_CTR] = {"all_osmux:chunks_rx", "Inbound Osmux chunks."},
[OSMUX_OCTETS_RX_CTR] = {"all_osmux:octets_rx", "Inbound Osmux octets."},
[OSMUX_RTP_PACKETS_TX_CTR] = {"all_osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."},
[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"all_osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."},
[OSMUX_AMR_OCTETS_TX_CTR] = {"all_osmux:amr_octets_tx", "Tx outbound AMD payload octets."},
/* These last counters below do not exist in per-connection stats, only here: */
[OSMUX_NUM_CONNECTIONS] = {"all_osmux:num_closed_conns", "Total number of osmux connections closed."},
[OSMUX_PACKETS_RX_CTR] = {"all_osmux:packets_rx", "Total inbound UDP/Osmux packets."},
[OSMUX_PACKETS_TX_CTR] = {"all_osmux:packets_tx", "Total outbound UDP/Osmux packets."},
[OSMUX_DROPPED_PACKETS_CTR] = {"all_osmux:dropped_packets", "Dropped outbound UDP/Osmux packets."}
};
/* Was conn configured to handle Osmux? */
static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) {
return conn->type == MGCP_RTP_OSMUX;
}
/* Was conn configured to handle Osmux? */
static inline bool mgcp_conn_rtp_is_iuup(const struct mgcp_conn_rtp *conn)
{
return conn->type == MGCP_RTP_IUUP;
}
static inline struct mgcp_conn_rtp *mgcp_conn_get_conn_rtp(struct mgcp_conn *conn)
{
OSMO_ASSERT(conn->type == MGCP_CONN_TYPE_RTP);
return &conn->u.rtp;
return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT;
}
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
enum mgcp_conn_type type, char *name);
void mgcp_conn_free(struct mgcp_conn *conn);
int mgcp_conn_set_mode(struct mgcp_conn *conn, enum mgcp_connection_mode mode);
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
const char *id);
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id);
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
char *mgcp_conn_dump(struct mgcp_conn *conn);
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp);
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);

View File

@@ -1,11 +1,7 @@
/* IuUP connection functionalitites */
/*
* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* Author: Pau Espin Pedrol
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
@@ -20,15 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/core/msgb.h>
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
const char *bind_addr, uint16_t port);
struct mgcp_conn_rtp;
int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp);
void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp);
int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg);
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg);
int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp);
int mgcp_conn_iuup_event_rx_crcx_mdcx(struct mgcp_conn_rtp *conn_rtp);

View File

@@ -22,6 +22,6 @@ static const uint8_t e1_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8
static const uint8_t e1_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs);
int mgcp_e1_endp_update(struct mgcp_endpoint *endp);
void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts);
void mgcp_e1_endp_update(struct mgcp_endpoint *endp);
void mgcp_e1_endp_release(struct mgcp_endpoint *endp);
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg);

View File

@@ -25,7 +25,6 @@
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/i460_mux.h>
#include <osmocom/mgcp/mgcp_protocol.h>
struct sockaddr;
struct mgcp_conn;
@@ -40,13 +39,8 @@ LOGP(cat, level, "endpoint:%s " fmt, \
endp ? endp->name : "none", \
## args)
enum rtp_proto {
MGCP_PROTO_RTP,
MGCP_PROTO_RTCP,
};
struct osmo_rtp_msg_ctx {
enum rtp_proto proto;
int proto;
struct mgcp_conn_rtp *conn_src;
struct osmo_sockaddr *from_addr;
};
@@ -102,6 +96,9 @@ struct mgcp_endpoint {
/*! List of struct mgcp_conn, of the connections active on this endpoint */
struct llist_head conns;
/*! Backpointer to the MGW configuration */
struct mgcp_config *cfg;
/*! Backpointer to the trunk this endpoint belongs to */
struct mgcp_trunk *trunk;
@@ -114,6 +111,10 @@ struct mgcp_endpoint {
/*! Last MGCP response (in case re-transmission is required) */
char *last_response;
/*! Memorize if this endpoint was choosen by the MGW (wildcarded, true)
* or if the user has choosen the particular endpoint explicitly. */
bool wildcarded_req;
/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */
uint32_t x_osmo_ign;
@@ -124,36 +125,19 @@ struct mgcp_endpoint {
struct osmo_fsm_inst *trau_sync_fi;
struct osmo_trau2rtp_state *trau_rtp_st;
uint8_t last_amr_ft;
uint8_t rtp_extensions;
struct mgcp_rtp_codec *last_codec;
} e1;
};
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int index);
void mgcp_endp_release(struct mgcp_endpoint *endp);
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid);
int mgcp_endp_update(struct mgcp_endpoint *endp, struct mgcp_conn *conn, enum mgcp_verb verb);
bool mgcp_endp_is_wildcarded(const char *epname);
bool mgcp_endp_is_null(const char *epname);
void mgcp_endp_update(struct mgcp_endpoint *endp);
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
const struct mgcp_trunk *trunk);
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
struct mgcp_config *cfg);
bool mgcp_endp_avail(const struct mgcp_endpoint *endp);
unsigned int mgcp_endp_num_conns(const struct mgcp_endpoint *endp);
bool mgcp_endp_is_full(const struct mgcp_endpoint *endp);
bool mgcp_endp_avail(struct mgcp_endpoint *endp);
void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
void mgcp_endp_free_conn_oldest(struct mgcp_endpoint *endp);
void mgcp_endp_free_conn_all(struct mgcp_endpoint *endp);
void mgcp_endp_strip_name(char *epname_stripped, const char *epname,
const struct mgcp_trunk *trunk);
struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
const struct mgcp_trunk *trunk);
void mgcp_endp_update_lco(struct mgcp_endpoint *endp, const struct mgcp_lco *lco);
void mgcp_endp_release(struct mgcp_endpoint *endp);
struct mgcp_conn *mgcp_endp_get_conn(struct mgcp_endpoint *endp, const char *id);
struct mgcp_conn *mgcp_endp_get_conn_oldest(struct mgcp_endpoint *endp);
struct mgcp_conn_rtp *mgcp_endp_get_conn_rtp(struct mgcp_endpoint *endp,
const char *id);

View File

@@ -25,25 +25,21 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <osmocom/mgcp/mgcp_common.h>
struct mgcp_conn;
struct mgcp_parse_data;
struct mgcp_endpoint;
struct mgcp_trunk;
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble);
enum mgcp_connection_mode mgcp_parse_conn_mode(const char *msg);
int mgcp_parse_conn_mode(const char *msg, struct mgcp_endpoint *endp,
struct mgcp_conn *conn);
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data);
int mgcp_parse_hdr_pars(struct mgcp_parse_data *pdata);
int mgcp_parse_osmux_cid(const char *line);
bool mgcp_check_param(const char *line);
int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line);
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid);

View File

@@ -4,21 +4,10 @@
#include <stdbool.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/mgcp/mgcp.h>
/* The following constant defines an RTP dummy payload that is used for
* "UDP Hole Punching" (NAT) */
#define MGCP_DUMMY_LOAD 0x23
static const char rtp_dummy_payload[] = { MGCP_DUMMY_LOAD };
/* Check if the data in a given message buffer matches the rtp dummy payload
* defined above */
#define mgcp_is_rtp_dummy_payload(msg) \
(msgb_length(msg) == sizeof(rtp_dummy_payload) && \
memcmp(msgb_data(msg), rtp_dummy_payload, sizeof(rtp_dummy_payload)) == 0)
#define RTP_BUF_SIZE 4096
struct mgcp_rtp_stream_state {
@@ -36,7 +25,7 @@ struct mgcp_rtp_state {
struct {
/* are we patching the SSRC value? */
bool patch_ssrc;
int patch_ssrc;
/* original SSRC (to which we shall patch any different SSRC) */
uint32_t orig_ssrc;
/* offset to apply on the sequence number */
@@ -72,6 +61,66 @@ struct mgcp_rtp_state {
* data is just re-used) */
uint16_t alt_rtp_tx_sequence;
uint32_t alt_rtp_tx_ssrc;
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
};
struct mgcp_rtp_codec {
uint32_t rate;
int channels;
uint32_t frame_duration_num;
uint32_t frame_duration_den;
int payload_type;
char *audio_name;
char *subtype_name;
bool param_present;
struct mgcp_codec_param param;
};
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
struct mgcp_rtp_end {
/* remote IP address of the RTP socket */
struct osmo_sockaddr addr;
/* in network byte order */
int rtp_port, rtcp_port;
/* currently selected audio codec */
struct mgcp_rtp_codec *codec;
/* array with assigned audio codecs to choose from (SDP) */
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
/* number of assigned audio codecs (SDP) */
unsigned int codecs_assigned;
/* per endpoint data */
int frames_per_packet;
uint32_t packet_duration_ms;
int maximum_packet_time; /* -1: not set */
char *fmtp_extra;
/* are we transmitting packets (1) or dropping (0) outbound packets */
int output_enabled;
/* FIXME: This parameter can be set + printed, but is nowhere used! */
int force_output_ptime;
/* RTP patching */
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
int force_aligned_timing;
bool rfc5993_hr_convert;
/* Each end has a separate socket for RTP and RTCP */
struct osmo_fd rtp;
struct osmo_fd rtcp;
/* local UDP port number of the RTP socket; RTCP is +1 */
int local_port;
/* where the endpoint RTP connection binds to, set during CRCX and
* possibly updated during MDCX */
char local_addr[INET6_ADDRSTRLEN];
};
struct mgcp_rtp_tap {
@@ -91,23 +140,30 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
int mgcp_dispatch_e1_bridge_cb(struct msgb *msg);
void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
int mgcp_conn_rtp_bind_rtp_ports(struct mgcp_conn_rtp *conn, int rtp_port);
void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
struct mgcp_conn_rtp *conn);
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
struct mgcp_rtp_state *state,
struct mgcp_rtp_end *rtp_end,
struct osmo_sockaddr *addr, struct msgb *msg);
int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
int mgcp_set_ip_tos(int fd, int tos);
/* payload processing default functions */
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn_dst,
struct mgcp_conn_rtp *conn_src);
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
const struct mgcp_rtp_codec **codec,
const char **fmtp_extra,
struct mgcp_conn_rtp *conn);
/* internal RTP Annex A counting */
void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
const uint16_t seq, const int32_t transit,
const uint32_t ssrc, const bool marker_bit);
void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
int id, int inc);
void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
int id);
void forward_data_tap(struct osmo_io_fd *iofd, struct mgcp_rtp_tap *tap, struct msgb *msg);
uint32_t mgcp_get_current_ts(unsigned codec_rate);
int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg, bool target_is_oa);
const uint32_t ssrc);

View File

@@ -1,140 +1,27 @@
#pragma once
#include <stdint.h>
#include <sys/socket.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/socket.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_codec.h>
enum mgcp_verb {
MGCP_VERB_CRCX,
MGCP_VERB_MDCX,
MGCP_VERB_DLCX,
MGCP_VERB_AUEP,
MGCP_VERB_RQNT,
MGCP_VERB_RSIP,
/* Internal structure while parsing a request */
struct mgcp_parse_data {
struct mgcp_config *cfg;
struct mgcp_endpoint *endp;
char *trans;
char *save;
};
extern const struct value_string mgcp_verb_names[];
static inline const char *mgcp_verb_name(enum mgcp_verb val)
{ return get_value_string(mgcp_verb_names, val); }
#define MGCP_PARSE_SDP_PTIME_UNSET (-1)
#define MGCP_PARSE_SDP_MAXPTIME_UNSET (-1)
#define MGCP_PARSE_SDP_RTP_PORT_UNSET (0)
struct mgcp_parse_sdp {
int ptime;
int maxptime;
int rtp_port;
struct osmo_sockaddr rem_addr; /* Only IP address, port is in rtp_port above */
struct mgcp_rtp_codecset cset;
};
static inline void mgcp_parse_sdp_init(struct mgcp_parse_sdp *sdp)
{
sdp->ptime = MGCP_PARSE_SDP_PTIME_UNSET;
sdp->maxptime = MGCP_PARSE_SDP_MAXPTIME_UNSET;
sdp->rtp_port = MGCP_PARSE_SDP_RTP_PORT_UNSET;
sdp->rem_addr = (struct osmo_sockaddr){ .u.sa.sa_family = AF_UNSPEC };
mgcp_codecset_reset(&sdp->cset);
}
/* Local connection options */
struct mgcp_lco {
bool present;
char *codec; /* talloc-allocated to some parent */
char *string;
char *codec;
int pkt_period_min; /* time in ms */
int pkt_period_max; /* time in ms */
};
static inline void mgcp_lco_init(struct mgcp_lco *lco)
{
*lco = (struct mgcp_lco){};
}
char *get_lco_identifier(const char *options);
int check_local_cx_options(void *ctx, const char *options);
#define MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET (-2)
#define MGCP_PARSE_HDR_PARS_OSMUX_CID_WILDCARD (-1)
struct mgcp_parse_hdr_pars {
const char *lco_string;
struct mgcp_lco lco;
const char *callid;
const char *connid;
enum mgcp_connection_mode mode;
int remote_osmux_cid;
bool have_sdp;
/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */
uint32_t x_osmo_ign;
};
static inline void mgcp_parse_hdr_pars_init(struct mgcp_parse_hdr_pars *hpars)
{
hpars->lco_string = NULL;
mgcp_lco_init(&hpars->lco);
hpars->callid = NULL;
hpars->connid = NULL;
hpars->mode = MGCP_CONN_NONE;
hpars->remote_osmux_cid = MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET;
hpars->have_sdp = false;
hpars->x_osmo_ign = 0;
}
/* Internal structure while parsing a request */
struct mgcp_request_data;
struct mgcp_parse_data {
struct mgcp_request_data *rq; /* backpointer to request context */
char *save;
/* MGCP Header: */
char *epname;
char *trans;
struct mgcp_parse_hdr_pars hpars;
/* MGCP Body: */
struct mgcp_parse_sdp sdp;
};
/* Request data passed to the request handler */
struct mgcp_request_data {
enum mgcp_verb verb;
/* Verb string (e.g. "MDCX") */
char name[4+1];
/* Global MGW config */
struct mgcp_config *cfg;
/* parsing results from the MGCP header (trans id, endpoint name ...) */
struct mgcp_parse_data *pdata;
/* pointer to endpoint resource (may be NULL for wildcarded requests) */
struct mgcp_endpoint *endp;
/* pointer to trunk resource */
struct mgcp_trunk *trunk;
/* set to true when the request has been classified as wildcarded */
bool wildcarded;
/* Set to true when the request is targeted at the "null" endpoint */
bool null_endp;
/* contains cause code in case of problems during endp/trunk resolution */
int mgcp_cause;
};
char *mgcp_debug_get_last_endpoint_name(void);
struct mgcp_rtp_end;
struct mgcp_endpoint;
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
struct mgcp_rtp_end *rtp);
uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
const struct mgcp_rtp_end *rtp);
extern const struct value_string mgcp_connection_mode_strs[];
static inline const char *mgcp_cmode_name(enum mgcp_connection_mode mode)
{
return get_value_string(mgcp_connection_mode_strs, mode);
}
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *rtp);

View File

@@ -1,5 +1,6 @@
#pragma once
/* Global MCGP general rate counters */
enum {
MGCP_GENERAL_RX_MSGS_TOTAL,
@@ -22,6 +23,7 @@ enum {
MGCP_CRCX_FAIL_ALLOC_CONN,
MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC,
MGCP_CRCX_FAIL_START_RTP,
MGCP_CRCX_FAIL_REJECTED_BY_POLICY,
MGCP_CRCX_FAIL_NO_OSMUX,
MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS,
MGCP_CRCX_FAIL_CODEC_NEGOTIATION,
@@ -43,17 +45,22 @@ enum {
MGCP_MDCX_FAIL_INVALID_MODE,
MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS,
MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
MGCP_MDCX_FAIL_BIND_PORT,
MGCP_MDCX_FAIL_START_RTP,
MGCP_MDCX_FAIL_REJECTED_BY_POLICY,
MGCP_MDCX_DEFERRED_BY_POLICY,
MGCP_MDCX_FAIL_AVAIL,
};
/* Trunk-global MCGP DLCX related rate counters */
enum {
MGCP_DLCX_SUCCESS,
MGCP_DLCX_FAIL_WILDCARD,
MGCP_DLCX_FAIL_NO_CONN,
MGCP_DLCX_FAIL_INVALID_CALLID,
MGCP_DLCX_FAIL_INVALID_CONNID,
MGCP_DLCX_FAIL_UNHANDLED_PARAM,
MGCP_DLCX_FAIL_REJECTED_BY_POLICY,
MGCP_DLCX_DEFERRED_BY_POLICY,
MGCP_DLCX_FAIL_AVAIL,
};
@@ -80,30 +87,9 @@ struct mgcp_ratectr_trunk {
struct rate_ctr_group *mgcp_dlcx_ctr_group;
/* Rate counter group which aggregates stats of individual RTP connections. */
struct rate_ctr_group *all_rtp_conn_stats;
/* Rate counter group which aggregates stats of individual Osmux connections. */
struct rate_ctr_group *all_osmux_conn_stats;
/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */
struct rate_ctr_group *e1_stats;
};
struct mgcp_config;
struct mgcp_trunk;
int mgcp_ratectr_global_alloc(struct mgcp_config *cfg);
void mgcp_ratectr_global_free(struct mgcp_config *cfg);
int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk);
void mgcp_ratectr_trunk_free(struct mgcp_trunk *trunk);
/* Trunk-global common stat items */
enum {
TRUNK_STAT_ENDPOINTS_TOTAL,
TRUNK_STAT_ENDPOINTS_USED,
};
struct mgcp_stat_trunk {
/* Stat item group which contains general status values of the trunk. */
struct osmo_stat_item_group *common;
};
int mgcp_stat_trunk_alloc(struct mgcp_trunk *trunk);
void mgcp_stat_trunk_free(struct mgcp_trunk *trunk);
int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr);
int mgcp_ratectr_trunk_alloc(void *ctx, struct mgcp_ratectr_trunk *ratectr);

View File

@@ -1,53 +0,0 @@
#pragma once
#include <inttypes.h>
#include <stdbool.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_codec.h>
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
struct mgcp_rtp_end {
struct mgcp_conn_rtp *conn_rtp; /* backpointer */
/* remote IP address of the RTP socket */
struct osmo_sockaddr addr;
/* in network byte order */
uint16_t rtcp_port;
struct mgcp_rtp_codecset cset;
/* per endpoint data */
int frames_per_packet;
uint32_t packet_duration_ms;
int maximum_packet_time; /* -1: not set */
/* are we transmitting packets (true) or dropping (false) outbound packets */
bool output_enabled;
/* FIXME: This parameter can be set + printed, but is nowhere used! */
int force_output_ptime;
/* RTP patching */
bool force_constant_ssrc;
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
int force_aligned_timing;
bool rfc5993_hr_convert;
/* Each end has a separate socket for RTP and RTCP */
struct osmo_io_fd *rtp;
struct osmo_io_fd *rtcp;
/* local UDP port number of the RTP socket; RTCP is +1 */
int local_port;
/* where the endpoint RTP connection binds to, set during CRCX and
* possibly updated during MDCX */
char local_addr[INET6_ADDRSTRLEN];
};
void mgcp_rtp_end_init(struct mgcp_rtp_end *end, struct mgcp_conn_rtp *conn_rtp);
void mgcp_rtp_end_cleanup(struct mgcp_rtp_end *end);
void mgcp_rtp_end_set_packet_duration_ms(struct mgcp_rtp_end *end, uint32_t packet_duration_ms);
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end);
void mgcp_rtp_end_free_port(struct mgcp_rtp_end *end);

View File

@@ -22,9 +22,9 @@
#pragma once
struct mgcp_parse_data;
int mgcp_parse_sdp_data(struct mgcp_parse_data *p);
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn,
struct mgcp_parse_data *p);
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
const struct mgcp_conn_rtp *conn, struct msgb *sdp,

View File

@@ -1,52 +0,0 @@
/*
* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdatomic.h>
#include <stdbool.h>
#include <stdlib.h>
struct spsc {
atomic_uint readptr;
atomic_uint writeptr;
int efd_r, efd_w; /* eventfds used to block/notify readers/writers */
int count;
int size_per_buf;
void *buf; /* buffer size count*size_per_buf */
uintptr_t data[0]; /* count sized array of pointers to size_per_buf chunks in buf array*/
};
struct qchan {
struct spsc *a;
struct spsc *b;
};
bool spsc_push(struct spsc *q, void *elem);
bool spsc_pop(struct spsc *q, void *elem);
ssize_t spsc_prep_pop(struct spsc *q);
int spsc_get_a_rdfd(struct qchan *q);
struct qchan spsc_chan_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf);
struct qchan spsc_chan_init_ex(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr_a,
bool blockw_a, bool blockr_b, bool blockw_b);
void spsc_chan_close(struct qchan *q);

View File

@@ -1,41 +1,37 @@
#pragma once
#include <osmocom/gsm/i460_mux.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_network.h>
#include <osmocom/mgcp/mgcp_ratectr.h>
#define LOGPTRUNK(trunk, cat, level, fmt, args...) \
LOGP(cat, level, "trunk:%u " fmt, \
trunk ? trunk->trunk_nr : 0, \
## args)
enum mgcp_trunk_type {
MGCP_TRUNK_VIRTUAL,
MGCP_TRUNK_E1,
};
extern const struct value_string mgcp_trunk_type_strs[];
static inline const char *mgcp_trunk_type_strs_str(enum mgcp_trunk_type val)
{ return get_value_string(mgcp_trunk_type_strs, val); }
struct mgcp_trunk {
struct llist_head entry;
struct mgcp_config *cfg;
unsigned int trunk_nr;
int trunk_nr;
enum mgcp_trunk_type trunk_type;
char *audio_fmtp_extra;
int audio_send_ptime;
int audio_send_name;
int no_audio_transcoding;
int omit_rtcp;
int keepalive_interval;
/* RTP patching */
bool force_constant_ssrc;
int force_constant_ssrc; /* 0: don't, 1: once */
int force_aligned_timing;
bool rfc5993_hr_convert;
@@ -52,9 +48,8 @@ struct mgcp_trunk {
unsigned int number_endpoints;
struct mgcp_endpoint **endpoints;
/* rate counters and stat items to measure the trunks overall performance and health */
/* global rate counters to measure the trunks overall performance and health */
struct mgcp_ratectr_trunk ratectr;
struct mgcp_stat_trunk stats;
union {
/* Virtual trunk specific */
@@ -64,22 +59,18 @@ struct mgcp_trunk {
/* E1 specific */
struct {
unsigned int vty_line_nr;
uint8_t ts_usecount[NUM_E1_TS-1];
struct osmo_i460_timeslot i460_ts[NUM_E1_TS-1];
/* Note: on an E1 line TS 0 is devoted to framing and
* alignment and therefore only NUM_E1_TS-1 timeslots
* are available for traffic. */
bool ts_in_use[31];
struct osmo_i460_timeslot i460_ts[31];
} e1;
};
};
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, unsigned int nr);
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
int mgcp_trunk_equip(struct mgcp_trunk *trunk);
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, unsigned int nr);
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname);
int e1_trunk_nr_from_epname(unsigned int *trunk_nr, const char *epname);
int e1_trunk_nr_from_epname(const char *epname);
struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num);
int mgcp_trunk_allocate_conn_rtp_ports(struct mgcp_trunk *trunk, struct mgcp_conn_rtp *conn_rtp);
/* The virtual trunk is always created on trunk id 0 for historical reasons,
* use this define constant as ID when allocating a virtual trunk. Other

View File

@@ -1,22 +1,25 @@
#pragma once
#include <stdint.h>
#include <osmocom/core/socket.h>
#include <osmocom/netif/osmux.h>
struct mgcp_conn_rtp;
struct mgcp_trunk;
struct mgcp_endpoint;
struct mgcp_conn_rtp;
int osmux_init(struct mgcp_trunk *trunk);
int osmux_init_conn(struct mgcp_conn_rtp *conn);
int conn_osmux_enable(struct mgcp_conn_rtp *conn);
#define OSMUX_PORT 1984
enum {
OSMUX_ROLE_BSC = 0,
OSMUX_ROLE_BSC_NAT,
};
int osmux_init(int role, struct mgcp_config *cfg);
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
struct osmo_sockaddr *addr, uint16_t port);
void conn_osmux_disable(struct mgcp_conn_rtp *conn);
int conn_osmux_event_rx_crcx_mdcx(struct mgcp_conn_rtp *conn);
int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg);
int osmux_send_dummy(struct mgcp_conn_rtp *conn);
int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid);
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn);
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
void osmux_cid_pool_get(uint8_t osmux_cid);
int osmux_cid_pool_get_next(void);
@@ -26,14 +29,10 @@ int osmux_cid_pool_count_used(void);
enum osmux_state {
OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */
OSMUX_STATE_ACTIVATING, /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref conn_osmux_enable. */
OSMUX_STATE_ENABLED, /* Osmux was initialized by \ref conn_osmux_enable and can process frames */
OSMUX_STATE_ACTIVATING, /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref osmux_enable_endpoint. */
OSMUX_STATE_ENABLED, /* Osmux was initialized by \ref osmux_enable_endpoint and can process frames */
};
extern const struct value_string osmux_state_strs[];
static inline const char *osmux_state_str(enum osmux_state val)
{ return get_value_string(osmux_state_strs, val); }
enum osmux_usage {
OSMUX_USAGE_OFF = 0,
OSMUX_USAGE_ON = 1,

View File

@@ -1,31 +1,13 @@
BUILT_SOURCES = \
mgcp_common.h \
version.h \
$(NULL)
noinst_HEADERS = \
mgcp_client_internal.h \
mgcp_client_pool_internal.h \
$(NULL)
mgcp_common.h: $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h
echo -e "/*\n\n DO NOT EDIT THIS FILE!\n THIS IS OVERWRITTEN DURING BUILD\n This is an automatic copy of <osmocom/mgcp/mgcp_common.h>\n\n */" > mgcp_common.h
cat $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h >> mgcp_common.h
version.h: version.h.tpl
$(AM_V_GEN)$(MKDIR_P) $(dir $@)
$(AM_V_GEN)sed \
-e "s/{{VERSION}}/$$(echo '@VERSION@' | cut -d. -f1-3)/g" \
-e "s/{{VERSION_MAJOR}}/$$(echo '@VERSION@' | cut -d. -f1)/g" \
-e "s/{{VERSION_MINOR}}/$$(echo '@VERSION@' | cut -d. -f2)/g" \
-e "s/{{VERSION_PATCH}}/$$(echo '@VERSION@' | cut -d. -f3)/g" \
$< > $@
EXTRA_DIST = \
version.h.tpl \
$(NULL)
CLEANFILES = \
mgcp_common.h \
version.h \
$(NULL)
CLEANFILES = mgcp_common.h

View File

@@ -1,7 +0,0 @@
#include <osmocom/core/defs.h>
#if BUILDING_LIBOSMOMGCPCLIENT
# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT(text)
#else
# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT(text) OSMO_DEPRECATED(text)
#endif

View File

@@ -3,15 +3,14 @@
#include <stdint.h>
#include <arpa/inet.h>
#include <osmocom/mgcp_client/defs.h>
#include <osmocom/mgcp_client/mgcp_common.h>
/* See also: RFC 3435, chapter 3.5 Transmission over UDP */
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT NULL /* INADDR(6)_ANY */
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727
#define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
#define MGCP_CLIENT_KEEPALIVE_DEFAULT_ENDP "null"
#define MGCP_CLIENT_MGW_STR "Configure MGCP connection to Media Gateway\n"
struct msgb;
@@ -27,21 +26,6 @@ struct mgcp_client_conf {
/* By default, we are always addressing the MGW with e.g. 'rtpbridge/123@mgw'.
* If this is nonempty, the contained name will be used instead of 'mgw'. */
char endpoint_domain_name[MGCP_ENDPOINT_MAXLEN];
/* The user may configure certain endpoint names that are reset via DLCX
* on startup. Usually this will be one wildcarded endpoint e.g.
* 'rtpbridge/(wildcard)' or a number of specific E1 like e.g.
* 'ds/e1-0/s-3/su16-4' */
struct llist_head reset_epnames;
/* human readable name / description */
char *description;
struct {
uint32_t timeout_sec;
uint32_t req_interval_sec;
char req_endpoint_name[MGCP_ENDPOINT_MAXLEN];
} keepalive;
};
typedef unsigned int mgcp_trans_id_t;
@@ -52,12 +36,10 @@ enum mgcp_codecs {
CODEC_GSM_8000_1 = 3,
CODEC_PCMA_8000_1 = 8,
CODEC_G729_8000_1 = 18,
CODEC_GSMEFR_8000_1 = 110, /* 3GPP TS 48.103 table 5.4.2.2.1 */
CODEC_GSMHR_8000_1 = 111, /* 3GPP TS 48.103 table 5.4.2.2.1 */
CODEC_AMR_8000_1 = 112, /* 3GPP TS 48.103 table 5.4.2.2.1 */
CODEC_AMRWB_16000_1 = 113, /* 3GPP TS 48.103 table 5.4.2.2.1 */
CODEC_IUFP = 96,
CODEC_CLEARMODE = 120, /* 3GPP TS 48.103 table 5.4.2.2.1 */
CODEC_GSMEFR_8000_1 = 110,
CODEC_GSMHR_8000_1 = 111,
CODEC_AMR_8000_1 = 112,
CODEC_AMRWB_16000_1 = 113,
};
/* Note: when new codec types are added, the corresponding value strings
* in mgcp_client.c (codec_table) must be updated as well. Enumerations
@@ -79,7 +61,27 @@ struct ptmap {
unsigned int pt;
};
int ptmap_cmp(const struct ptmap *a, const struct ptmap *b);
struct mgcp_response_head {
int response_code;
mgcp_trans_id_t trans_id;
char comment[MGCP_COMMENT_MAXLEN];
char conn_id[MGCP_CONN_ID_MAXLEN];
char endpoint[MGCP_ENDPOINT_MAXLEN];
bool x_osmo_osmux_use;
uint8_t x_osmo_osmux_cid;
};
struct mgcp_response {
char *body;
struct mgcp_response_head head;
uint16_t audio_port;
char audio_ip[INET6_ADDRSTRLEN];
unsigned int ptime;
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
unsigned int codecs_len;
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
};
enum mgcp_verb {
MGCP_VERB_CRCX,
@@ -89,8 +91,38 @@ enum mgcp_verb {
MGCP_VERB_RSIP,
};
struct mgcp_client_conf *mgcp_client_conf_alloc(void *ctx);
void mgcp_client_conf_init(struct mgcp_client_conf *conf) OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use mgcp_client_conf_alloc() (or even better, switch to the mgcp_client_pool API!)");
#define MGCP_MSG_PRESENCE_ENDPOINT 0x0001
#define MGCP_MSG_PRESENCE_CALL_ID 0x0002
#define MGCP_MSG_PRESENCE_CONN_ID 0x0004
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
#define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000
#define MGCP_MSG_PRESENCE_X_OSMO_IGN 0x8000
struct mgcp_msg {
enum mgcp_verb verb;
/* See MGCP_MSG_PRESENCE_* constants */
uint32_t presence;
char endpoint[MGCP_ENDPOINT_MAXLEN];
unsigned int call_id;
char *conn_id;
uint16_t audio_port;
char *audio_ip;
enum mgcp_connection_mode conn_mode;
unsigned int ptime;
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
unsigned int codecs_len;
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
uint32_t x_osmo_ign;
bool x_osmo_osmux_use;
int x_osmo_osmux_cid; /* -1 is wildcard */
bool param_present;
struct mgcp_codec_param param;
};
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf);
int mgcp_client_config_write(struct vty *vty, const char *indent);
struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp);
@@ -98,8 +130,6 @@ struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp);
struct mgcp_client *mgcp_client_init(void *ctx,
struct mgcp_client_conf *conf);
int mgcp_client_connect(struct mgcp_client *mgcp);
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports) OSMO_DEPRECATED("Use mgcp_client_connect() instead");
void mgcp_client_disconnect(struct mgcp_client *mgcp);
const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp);
uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp);
@@ -110,8 +140,20 @@ const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp);
const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts,
uint8_t rate, uint8_t offset);
/* Invoked when an MGCP response is received or sending failed. When the
* response is passed as NULL, this indicates failure during transmission. */
typedef void (* mgcp_response_cb_t )(struct mgcp_response *response, void *priv);
int mgcp_response_parse_params(struct mgcp_response *r);
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
mgcp_response_cb_t response_cb, void *priv);
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
enum mgcp_connection_mode;
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
extern const struct value_string mgcp_client_connection_mode_strs[];
static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
{
@@ -123,5 +165,3 @@ unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
enum mgcp_codecs codec);
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
unsigned int pt);
const char *mgcp_client_name(const struct mgcp_client *mgcp);

View File

@@ -23,8 +23,6 @@ const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgc
bool osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(const struct osmo_mgcpc_ep_ci *ci, struct sockaddr_storage *dest);
bool osmo_mgcpc_ep_ci_get_crcx_info_to_osmux_cid(const struct osmo_mgcpc_ep_ci *ci, uint8_t* cid);
const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_remote_rtp_info(const struct osmo_mgcpc_ep_ci *ci);
void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
enum mgcp_verb verb, const struct mgcp_conn_peer *verb_info,
struct osmo_fsm_inst *notify,
@@ -45,10 +43,8 @@ static inline void osmo_mgcpc_ep_ci_dlcx(struct osmo_mgcpc_ep_ci *ci)
void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep);
const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep);
const char *osmo_mgcpc_ep_local_name(const struct osmo_mgcpc_ep *ep);
const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci);
const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci);
struct mgcp_client *osmo_mgcpc_ep_client(const struct osmo_mgcpc_ep *ep);
extern const struct value_string osmo_mgcp_verb_names[];
static inline const char *osmo_mgcp_verb_name(enum mgcp_verb val)

View File

@@ -29,11 +29,11 @@ struct mgcp_conn_peer {
/*! RTP packetization interval (optional) */
unsigned int ptime;
/*! Deprecated. Use only ptmap[].codec in new code. */
enum mgcp_codecs codecs[MGCP_MAX_CODECS]
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[i].codec instead");
unsigned int codecs_len
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[] and ptmap_len instead");
/*! RTP codec list (optional) */
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
/*! Number of codecs in RTP codec list (optional) */
unsigned int codecs_len;
/*! RTP payload type map (optional, only needed when payload types are
* used that differ from what IANA/3GPP defines) */
@@ -64,15 +64,10 @@ struct mgcp_conn_peer {
};
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_alloc() and osmo_mgcpc_ep_ci_add() instead");
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci_request() instead");
void mgcp_conn_delete(struct osmo_fsm_inst *fi)
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci_dlcx() instead");
uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
void mgcp_conn_delete(struct osmo_fsm_inst *fi);
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi)
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci.mgcp_ci_str instead");
struct mgcp_client *mgcp_conn_get_client(struct osmo_fsm_inst *fi);
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info);

View File

@@ -1,53 +1,22 @@
#pragma once
#include <osmocom/core/osmo_io.h>
#include <osmocom/core/timer.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/core/write_queue.h>
#define MSGB_CB_MGCP_TRANS_ID 0
/* Struct that holds one endpoint name */
struct reset_ep {
struct llist_head list;
char name[MGCP_ENDPOINT_MAXLEN];
};
struct mgcp_client {
struct mgcp_client_conf actual;
struct osmo_io_fd *iofd;
struct osmo_wqueue wq;
mgcp_trans_id_t next_trans_id;
struct llist_head responses_pending;
struct mgcp_client_pool_member *pool_member;
struct osmo_timer_list keepalive_tx_timer;
struct osmo_timer_list keepalive_rx_timer;
bool conn_up;
struct llist_head inuse_endpoints;
};
struct mgcp_response_head {
int response_code;
mgcp_trans_id_t trans_id;
char comment[MGCP_COMMENT_MAXLEN];
char conn_id[MGCP_CONN_ID_MAXLEN];
char endpoint[MGCP_ENDPOINT_MAXLEN];
bool x_osmo_osmux_use;
uint8_t x_osmo_osmux_cid;
struct mgcp_inuse_endpoint {
struct llist_head entry;
uint16_t id;
};
struct mgcp_response {
char *body;
struct mgcp_response_head head;
uint16_t audio_port;
char audio_ip[INET6_ADDRSTRLEN];
unsigned int ptime;
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
};
/* Invoked when an MGCP response is received or sending failed. When the
* response is passed as NULL, this indicates failure during transmission. */
typedef void (*mgcp_response_cb_t)(struct mgcp_response *response, void *priv);
struct mgcp_response_pending {
struct llist_head entry;
@@ -63,41 +32,3 @@ struct mgcp_response_pending * mgcp_client_pending_add(
mgcp_trans_id_t trans_id,
mgcp_response_cb_t response_cb,
void *priv);
#define MGCP_MSG_PRESENCE_ENDPOINT 0x0001
#define MGCP_MSG_PRESENCE_CALL_ID 0x0002
#define MGCP_MSG_PRESENCE_CONN_ID 0x0004
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
#define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000
#define MGCP_MSG_PRESENCE_X_OSMO_IGN 0x8000
struct mgcp_msg {
enum mgcp_verb verb;
/* See MGCP_MSG_PRESENCE_* constants */
uint32_t presence;
char endpoint[MGCP_ENDPOINT_MAXLEN];
unsigned int call_id;
char *conn_id;
uint16_t audio_port;
char *audio_ip;
enum mgcp_connection_mode conn_mode;
unsigned int ptime;
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
uint32_t x_osmo_ign;
bool x_osmo_osmux_use;
int x_osmo_osmux_cid; /* -1 is wildcard */
bool param_present;
struct mgcp_codec_param param;
};
int mgcp_response_parse_params(struct mgcp_response *r);
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
mgcp_response_cb_t response_cb, void *priv);
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);

View File

@@ -1,24 +0,0 @@
#pragma once
#include <stdbool.h>
#include <osmocom/vty/vty.h>
struct mgcp_client;
struct mgcp_client_pool;
struct mgcp_client_pool_member;
struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx);
void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool);
int mgcp_client_pool_config_write(struct vty *vty, const char *indent);
unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool);
void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp_client *mgcp_client);
bool mgcp_client_pool_empty(const struct mgcp_client_pool *pool);
struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool);
void mgcp_client_pool_put(struct mgcp_client *mgcp_client);
struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr);
struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member);
bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member);

View File

@@ -1,52 +0,0 @@
#pragma once
/* Struct to handle a pool of MGWs. (Use _pool functions) */
struct mgcp_client_pool {
/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using
* mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */
struct mgcp_client *mgcp_client_single;
/* A list that manages the pool members (see mgcp_client_pool_member->list above) */
struct llist_head member_list;
/* String to use for indentation when writing the configuration file to the VTY. This field is populated by
* mgcp_client_pool_vty_init() */
char *vty_indent;
/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */
struct cmd_node *vty_node;
};
/* Struct to handle a member of a pool of MGWs. */
struct mgcp_client_pool_member {
/* Entry in llist mgcp_client_pool->pool. */
struct llist_head list;
/* The pool managing this object: */
struct mgcp_client_pool *pool;
/* Reference number assinged by VTY. This number is used to manage the pool from the VTY and to identify it in
* the log. */
unsigned int nr;
/* MGCP client configuration, this is not the running configuration, when mgcp_client_init() is executed, a
* copy of this config is created. */
struct mgcp_client_conf conf;
/* MGCP client descriptor, will be automatically allocated when mgcp_client_pool_connect() is called. (the MGCP
* client is connected when this pointer is populated) */
struct mgcp_client *client;
/* A pool member may be set as 'blocked' from the VTY, this means that the pool member may still work and serve
* ongoing calls, but it won't be picked from the pool anymore. */
bool blocked;
/* Reference counter to count how often this pool member is currently picked. */
unsigned int refcount;
};
struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr);
void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member);
int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member);
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member);

View File

@@ -1,16 +0,0 @@
#pragma once
#define LIBOSMO_MGCP_CLIENT_VERSION {{VERSION}}
#define LIBOSMO_MGCP_CLIENT_VERSION_STR "{{VERSION}}"
#define LIBOSMO_MGCP_CLIENT_VERSION_MAJOR {{VERSION_MAJOR}}
#define LIBOSMO_MGCP_CLIENT_VERSION_MINOR {{VERSION_MINOR}}
#define LIBOSMO_MGCP_CLIENT_VERSION_PATCH {{VERSION_PATCH}}
#define LIBOSMO_MGCP_CLIENT_VERSION_GREATER_EQUAL(major, minor, patch) \
(LIBOSMO_MGCP_CLIENT_VERSION_MAJOR > (major) || \
(LIBOSMO_MGCP_CLIENT_VERSION_MAJOR == (major) && \
LIBOSMO_MGCP_CLIENT_VERSION_MINOR > (minor)) || \
(LIBOSMO_MGCP_CLIENT_VERSION_MAJOR == (major) && \
LIBOSMO_MGCP_CLIENT_VERSION_MINOR == (minor) && \
LIBOSMO_MGCP_CLIENT_VERSION_PATCH >= (patch)))

View File

@@ -0,0 +1,74 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
#
# DESCRIPTION
#
# Check whether the given FLAG works with the current language's compiler
# or gives an error. (Warnings, however, are ignored)
#
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
# success/failure.
#
# If EXTRA-FLAGS is defined, it is added to the current language's default
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
# force the compiler to issue an error when a bad flag is given.
#
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
#
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
#
# LICENSE
#
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 4
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
[AS_VAR_SET(CACHEVAR,[yes])],
[AS_VAR_SET(CACHEVAR,[no])])
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
AS_VAR_IF(CACHEVAR,yes,
[m4_default([$2], :)],
[m4_default([$3], :)])
AS_VAR_POPDEF([CACHEVAR])dnl
])dnl AX_CHECK_COMPILE_FLAGS

View File

@@ -13,6 +13,12 @@ AM_CFLAGS = \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
# Libraries
SUBDIRS = \
libosmo-mgcp-client \

View File

@@ -9,17 +9,18 @@ AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
# This is not at all related to the release version, but a range of supported
# API versions. Read TODO_RELEASE in the source tree's root!
MGCP_CLIENT_LIBVERSION=14:1:0
MGCP_CLIENT_LIBVERSION=7:0:1
lib_LTLIBRARIES = \
libosmo-mgcp-client.la \
@@ -30,16 +31,6 @@ libosmo_mgcp_client_la_SOURCES = \
mgcp_client_vty.c \
mgcp_client_fsm.c \
mgcp_client_endpoint_fsm.c \
mgcp_client_pool.c \
$(NULL)
libosmo_mgcp_client_la_LDFLAGS = \
$(AM_LDFLAGS) \
-version-info $(MGCP_CLIENT_LIBVERSION) \
-no-undefined \
$(NULL)
libosmo_mgcp_client_la_LIBADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(NULL)
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)

File diff suppressed because it is too large Load Diff

View File

@@ -33,7 +33,7 @@
#define LOG_CI(ci, level, fmt, args...) do { \
if (!ci || !ci->ep) \
LOGP(DLMGCP, level, "(unknown MGW endpoint) " fmt, ## args); \
LOGP(DLGLOBAL, level, "(unknown MGW endpoint) " fmt, ## args); \
else \
LOG_MGCPC_EP(ci->ep, level, "CI[%d] %s%s%s: " fmt, \
(int)(ci - ci->ep->ci), \
@@ -173,30 +173,6 @@ const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep)
return osmo_fsm_inst_name(ep->fi);
}
/*! Get "local endpoint name" part of the endpoint name: (local-endpoint-name@domain-name)
*
* \param ep The MGCP Endpoint
* \returns the local endpoint name if found, NULL on error.
*/
const char *osmo_mgcpc_ep_local_name(const struct osmo_mgcpc_ep *ep)
{
static char buf[1024];
const char *sep;
OSMO_ASSERT(ep);
sep = strchr(ep->endpoint, '@');
if (!sep) {
OSMO_STRLCPY_ARRAY(buf, ep->endpoint);
return buf;
}
if (sep - ep->endpoint >= sizeof(buf))
return NULL;
memcpy(buf, ep->endpoint, sep - ep->endpoint);
buf[sep - ep->endpoint] = '\0';
return buf;
}
const char *mgcp_conn_peer_name(const struct mgcp_conn_peer *info)
{
/* I'd be fine with a smaller buffer and accept truncation, but gcc possibly refuses to build if
@@ -240,18 +216,11 @@ const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci)
return ci->mgcp_ci_str;
}
struct mgcp_client *osmo_mgcpc_ep_client(const struct osmo_mgcpc_ep *ep)
{
if (!ep)
return NULL;
return ep->mgcp_client;
}
static struct value_string osmo_mgcpc_ep_fsm_event_names[33] = {};
static char osmo_mgcpc_ep_fsm_event_name_bufs[32][32] = {};
static void fill_event_names(void)
static void fill_event_names()
{
int i;
for (i = 0; i < (ARRAY_SIZE(osmo_mgcpc_ep_fsm_event_names) - 1); i++) {
@@ -267,7 +236,7 @@ static void fill_event_names(void)
}
}
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init(void)
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init()
{
OSMO_ASSERT(osmo_fsm_register(&osmo_mgcpc_ep_fsm) == 0);
fill_event_names();
@@ -533,46 +502,13 @@ static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data)
mgcp_conn_peer_name(ci->got_port_info? &ci->rtp_info : NULL),
ci->notify.fi ? "" : " (not sending a notification)");
/* Below ordering is a delicate decision:
*
* We want to
* - emit the resulting event to ci->notify.fi,
* - check whether we want to tx the next pending MGCP message.
* Both these steps may terminate (=deallocate) the ep.
* So whichever one goes first may cause a use-after-free in the other.
*
* When dispatching the FSM event, we don't get an rc indicating dealloc of the FSM -- it may deallocate and we
* cannot tell. The common mechanism for that is osmo_fsm_set_dealloc_ctx(OTC_SELECT) and query the still
* allocated FSM state after termination (here we would check 'if (ci->ep != NULL)'), but we cannot assume the
* caller has actually set up an osmo_fsm_set_dealloc_ctx(). At time of writing, e.g. osmo-hnbgw does not use
* it.
*
* In osmo_mgcpc_ep_fsm_check_state_chg_after_response(), we do get an rc: false means FSM has terminated.
* On termination, the ep emits a term event to the FSM's parent.
* That may cause the notify.fi to be terminated in turn, depending on how the caller set things up.
* So: we cannot store notify.fi before, then call osmo_mgcpc_ep_fsm_check_state_chg_after_response(), and then
* emit the event, because notify.fi may have deallocated. We cannot look up whether
* osmo_mgcpc_ep_cancel_notify() has been called, because ci may have deallocated along with ci->ep.
*
* We have to skip emitting below success event in case the ep is now terminated.
* - It may be the final DLCX OK: not a problem, osmo_mgcpc_ep_ci_dlcx() has no notify args on purpose, so we do
* make all callers not set a notify event for DLCX by design. notify.fi should always be NULL when the final
* DLCX OK terminates the local endpoint state.
* - It may also be sudden termination due to a bad problem, in which case we shouldn't emit success.
* The osmo_fsm_inst.parent_term_event should suffice as feedback to the caller.
*/
if (osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi) == false) {
/* false means, the ci->ep has been terminated. */
return;
}
if (ci->notify.fi)
osmo_fsm_inst_dispatch(ci->notify.fi, ci->notify.success, ci->notify.data);
osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi);
}
/*! Return the MGW's local RTP port information for this connection, i.e. the local port that MGW is receiving on, as
* returned by the last CRCX-OK / MDCX-OK message. */
/*! Return the MGW's RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */
const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci)
{
ci = osmo_mgcpc_ep_check_ci((struct osmo_mgcpc_ep_ci*)ci);
@@ -583,16 +519,6 @@ const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgc
return &ci->rtp_info;
}
/*! Return the MGW's remote RTP port information for this connection, i.e. the remote RTP port that the MGW is sending
* to, as sent to the MGW by the last CRCX / MDCX message. */
const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_remote_rtp_info(const struct osmo_mgcpc_ep_ci *ci)
{
ci = osmo_mgcpc_ep_check_ci((struct osmo_mgcpc_ep_ci*)ci);
if (!ci)
return NULL;
return &ci->verb_info;
}
/*! Return the MGW's RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */
bool osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(const struct osmo_mgcpc_ep_ci *ci, struct sockaddr_storage *dest)
{
@@ -679,7 +605,7 @@ void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
ci = osmo_mgcpc_ep_check_ci(ci);
if (!ci) {
LOGP(DLMGCP, LOGL_ERROR, "Invalid MGW endpoint request: no ci\n");
LOGP(DLGLOBAL, LOGL_ERROR, "Invalid MGW endpoint request: no ci\n");
goto dispatch_error;
}
if (!verb_info && verb != MGCP_VERB_DLCX) {
@@ -718,11 +644,11 @@ void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
osmo_strlcpy(cleared_ci.mgcp_ci_str, ci->mgcp_ci_str, sizeof(cleared_ci.mgcp_ci_str));
*ci = cleared_ci;
LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify.fi));
if (verb_info)
ci->verb_info = *verb_info;
LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify.fi));
if (ep->endpoint[0]) {
if (ci->verb_info.endpoint[0] && strcmp(ci->verb_info.endpoint, ep->endpoint))
LOG_CI(ci, LOGL_ERROR,
@@ -1053,34 +979,6 @@ static int osmo_mgcpc_ep_fsm_timer_cb(struct osmo_fsm_inst *fi)
return 0;
}
static void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
int i;
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
/* We want the mgcp_client_fsm to still stick around until it received the DLCX "OK" responses from the MGW. So
* it should not dealloc along with this ep_fsm instance. Instead, signal DLCX for each conn on the endpoint,
* and detach the mgcp_client_fsm from being a child-fsm.
*
* After mgcp_conn_delete(), an mgcp_client_fsm instance goes into ST_DLCX_RESP, which waits up to 4 seconds for
* a DLCX OK. If none is received in that time, the instance terminates. So cleanup of the instance is
* guaranteed. */
for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
if (!ci->occupied || !ci->mgcp_client_fi)
continue;
/* mgcp_conn_delete() unlinks itself from this parent FSM implicitly and waits for the DLCX OK. */
mgcp_conn_delete(ci->mgcp_client_fi);
/* Forget all about this ci */
*ci = (struct osmo_mgcpc_ep_ci){
.ep = ep,
};
}
}
static struct osmo_fsm osmo_mgcpc_ep_fsm = {
.name = "mgw-endp",
.states = osmo_mgcpc_ep_fsm_states,
@@ -1088,5 +986,5 @@ static struct osmo_fsm osmo_mgcpc_ep_fsm = {
.log_subsys = DLMGCP,
.event_names = osmo_mgcpc_ep_fsm_event_names,
.timer_cb = osmo_mgcpc_ep_fsm_timer_cb,
.pre_term = osmo_mgcpc_ep_fsm_pre_term,
/* The FSM termination will automatically trigger any mgcp_client_fsm instances to DLCX. */
};

View File

@@ -19,7 +19,6 @@
*/
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
#include <osmocom/mgcp_client/mgcp_client_fsm.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/fsm.h>
@@ -115,10 +114,12 @@ static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info
.call_id = info->call_id,
.conn_mode = MGCP_CONN_RECV_ONLY,
.ptime = info->ptime,
.codecs_len = info->codecs_len,
.ptmap_len = info->ptmap_len,
.param_present = info->param_present
};
osmo_strlcpy(mgcp_msg->endpoint, info->endpoint, MGCP_ENDPOINT_MAXLEN);
memcpy(mgcp_msg->codecs, info->codecs, sizeof(mgcp_msg->codecs));
memcpy(mgcp_msg->ptmap, info->ptmap, sizeof(mgcp_msg->ptmap));
memcpy(&mgcp_msg->param, &info->param, sizeof(mgcp_msg->param));
@@ -135,19 +136,10 @@ static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info
static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
{
bool ip_is_set = info->addr[0] != '\0' &&
strncmp(info->addr, "::", sizeof(info->addr)) != 0 &&
strncmp(info->addr, "0.0.0.0", sizeof(info->addr)) != 0;
if (ip_is_set) {
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP;
mgcp_msg->audio_ip = info->addr;
}
if (info->port) {
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_PORT;
mgcp_msg->audio_port = info->port;
}
if (ip_is_set && info->port)
mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT;
mgcp_msg->audio_ip = info->addr;
mgcp_msg->audio_port = info->port;
mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
}
static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer)
@@ -157,7 +149,6 @@ static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer
mgcp_msg->conn_mode = conn_mode;
}
/* returns message buffer containing MGXP MDCX on success, NULL on error. */
static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
{
struct mgcp_msg mgcp_msg;
@@ -172,10 +163,12 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
.audio_ip = mgcp_ctx->conn_peer_local.addr,
.audio_port = mgcp_ctx->conn_peer_local.port,
.ptime = mgcp_ctx->conn_peer_local.ptime,
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len,
.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len,
.param_present = mgcp_ctx->conn_peer_local.param_present
};
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
memcpy(mgcp_msg.ptmap, mgcp_ctx->conn_peer_local.ptmap, sizeof(mgcp_msg.ptmap));
memcpy(&mgcp_msg.param, &mgcp_ctx->conn_peer_local.param, sizeof(mgcp_ctx->conn_peer_local.param));
@@ -193,7 +186,6 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
}
/* returns message buffer containing MGXP DLCX on success, NULL on error. */
struct msgb *make_dlcx_msg(struct mgcp_ctx *mgcp_ctx)
{
struct mgcp_msg mgcp_msg;
@@ -229,14 +221,13 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
mgcp_ctx->conn_peer_local.endpoint);
make_crcx_msg(&mgcp_msg, &mgcp_ctx->conn_peer_local);
add_audio(&mgcp_msg, &mgcp_ctx->conn_peer_local);
if (mgcp_ctx->conn_peer_local.port)
add_audio(&mgcp_msg, &mgcp_ctx->conn_peer_local);
set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
msg = mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
if (!msg) {
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
OSMO_ASSERT(msg);
mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
mgcp_ctx->mgw_trans_pending = true;
rc = mgcp_client_tx(mgcp, msg, mgw_crcx_resp_cb, fi);
@@ -261,18 +252,6 @@ const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi)
return mgcp_ctx->conn_id;
}
/* Get the mgcp_client that is used with this mgcp_client_fsm instance */
struct mgcp_client *mgcp_conn_get_client(struct osmo_fsm_inst *fi)
{
struct mgcp_ctx *mgcp_ctx;
if (!fi)
return NULL;
mgcp_ctx = fi->priv;
return mgcp_ctx->mgcp;
}
static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv)
{
struct osmo_fsm_inst *fi = priv;
@@ -373,21 +352,13 @@ static void fsm_ready_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
switch (event) {
case EV_MDCX:
msg = make_mdcx_msg(mgcp_ctx);
if (!msg) {
/* make_mdcx_msg() should already have logged the error */
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
OSMO_ASSERT(msg);
rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_resp_cb, fi);
new_state = ST_MDCX_RESP;
break;
case EV_DLCX:
msg = make_dlcx_msg(mgcp_ctx);
if (!msg) {
/* make_dlcx_msg() should already have logged the error */
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
OSMO_ASSERT(msg);
rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_resp_cb, fi);
new_state = ST_DLCX_RESP;
break;
@@ -550,12 +521,10 @@ static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca
/* Should the FSM be terminated while there are still open connections
* on the MGW, we send an unconditional DLCX to terminate the
* connection. This is not the normal case. The user should always use
* mgcp_conn_delete() to instruct the FSM to perform a graceful exit.
* If in ST_DLCX_RESP, a DLCX was already sent and we did not get a
* response. No point in sending another one. */
if (fi->state != ST_DLCX_RESP && strlen(mgcp_ctx->conn_id)) {
LOGPFSML(fi, LOGL_INFO, "Conn cleanup, sending DLCX for %s %s\n", mgcp_ctx->conn_peer_remote.endpoint,
mgcp_ctx->conn_id);
* mgcp_conn_delete() to instruct the FSM to perform a graceful exit */
if (strlen(mgcp_ctx->conn_id)) {
LOGPFSML(fi, LOGL_NOTICE,
"MGW/DLCX: FSM termination with connections still present, sending unconditional DLCX...\n");
msg = make_dlcx_msg(mgcp_ctx);
if (!msg)
LOGPFSML(fi, LOGL_ERROR, "MGW/DLCX: Error composing DLCX message\n");
@@ -625,75 +594,8 @@ static struct osmo_fsm fsm_mgcp_client = {
.timer_cb = fsm_timeout_cb,
.cleanup = fsm_cleanup_cb,
.event_names = fsm_mgcp_client_evt_names,
.log_subsys = DLMGCP,
};
/* Provide backwards compat for deprecated conn_peer->codecs[]: when the caller passes in an mgcp_conn_peer instance
* that has codecs[] set, apply it to ptmap[] instead. */
static void mgcp_conn_peer_compat(struct mgcp_conn_peer *conn_peer)
{
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
if (!conn_peer->codecs_len)
return;
/* Before dropping codecs[], codecs[] would indicate the order in which the codecs should appear in SDP. ptmap[]
* would indicate payload type numbers when not using a default payload type number (may omit entries).
* Now, ptmap[] just indicates both at the same time; codecs[] should be empty, and ptmap[] lists all codecs.
* So if any codecs[] are present, recreate ptmap[] in the order of codecs[]. */
ptmap_len = 0;
for (int i = 0; i < conn_peer->codecs_len; i++) {
enum mgcp_codecs codec = conn_peer->codecs[i];
struct ptmap *found = NULL;
/* Look up whether a specific pt was indicated for this codec */
for (int p = 0; p < conn_peer->ptmap_len; p++) {
if (conn_peer->ptmap[p].codec != codec)
continue;
found = &conn_peer->ptmap[p];
break;
}
if (found) {
ptmap[ptmap_len] = *found;
} else {
ptmap[ptmap_len] = (struct ptmap){
.codec = codec,
/* some enum mgcp_codecs correspond to their standard PT nr, so for compat: */
.pt = codec,
};
}
ptmap_len++;
}
/* Are there any entries in the old ptmap that were omitted by codecs[]? */
for (int p = 0; p < conn_peer->ptmap_len; p++) {
bool exists = false;
for (int i = 0; i < ptmap_len; i++) {
if (ptmap_cmp(&ptmap[i], &conn_peer->ptmap[p]))
continue;
exists = true;
break;
}
if (exists)
continue;
if (ptmap_len >= ARRAY_SIZE(ptmap))
break;
/* Not present yet, add it to the end */
ptmap[ptmap_len] = conn_peer->ptmap[p];
ptmap_len++;
}
/* Use the new ptmap[], and clear out legacy codecs[]. */
memcpy(conn_peer->ptmap, ptmap, sizeof(conn_peer->ptmap));
conn_peer->ptmap_len = ptmap_len;
conn_peer->codecs_len = 0;
}
/*! allocate FSM, and create a new connection on the MGW.
* \param[in] mgcp MGCP client descriptor.
* \param[in] parent_fi Parent FSM instance.
@@ -708,7 +610,6 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm
struct osmo_fsm_inst *fi;
struct in6_addr ip_test;
mgcp_conn_peer_compat(conn_peer);
OSMO_ASSERT(parent_fi);
OSMO_ASSERT(mgcp);
@@ -748,8 +649,6 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
struct mgcp_ctx *mgcp_ctx = fi->priv;
struct in6_addr ip_test;
mgcp_conn_peer_compat(conn_peer);
OSMO_ASSERT(mgcp_ctx);
OSMO_ASSERT(conn_peer);
@@ -808,8 +707,8 @@ void mgcp_conn_delete(struct osmo_fsm_inst *fi)
if (fi->proc.terminating)
return;
/* Unlink FSM from parent, set the struct mgcp_client as new talloc ctx. */
osmo_fsm_inst_unlink_parent(fi, mgcp_ctx->mgcp);
/* Unlink FSM from parent */
osmo_fsm_inst_unlink_parent(fi, NULL);
/* An error situation where the parent FSM must be killed immediately
* may lead into a situation where the DLCX can not be executed right
@@ -846,7 +745,7 @@ const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info)
return buf;
}
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init(void)
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init()
{
OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0);
}

View File

@@ -1,298 +0,0 @@
/* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* 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 <asm-generic/errno.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
#include <osmocom/mgcp_client/mgcp_client_pool_internal.h>
#include <osmocom/mgcp_client/mgcp_client_pool.h>
#include <stddef.h>
#define LOGPPMGW(pool_member, level, fmt, args...) \
LOGP(DLMGCP, level, "MGW-pool(%s) " fmt, mgcp_client_pool_member_name(pool_member), ## args)
/*! Allocate MGCP client pool. This is called once on startup and before the pool is used with
* mgcp_client_pool_vty_init(). Since the pool is linked with the VTY it must exist througout the entire runtime.
* \param[in] talloc_ctx talloc context. */
struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx)
{
struct mgcp_client_pool *pool;
pool = talloc_zero(talloc_ctx, struct mgcp_client_pool);
if (!pool)
return NULL;
INIT_LLIST_HEAD(&pool->member_list);
return pool;
}
/*! Initialize and connect an mcgp client pool.
* \param[in,out] mgcp MGCP client pool descriptor.
* \returns number of successfully initialized pool members. */
unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool)
{
struct mgcp_client_pool_member *pool_member;
unsigned int pool_members_initialized = 0;
llist_for_each_entry(pool_member, &pool->member_list, list) {
/* Initialize client */
if (mgcp_client_pool_member_reinit_client(pool_member) == 0)
pool_members_initialized++;
}
return pool_members_initialized;
}
/*! register a single mgcp_client instance to the pool.
* \param[out] pool MGCP client pool descriptor.
* \param[in] mgcp MGCP client descriptor. */
void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp_client *mgcp_client)
{
/*! Some applications still support the non-pooled MGW VTY configuration variant provided by
* mgcp_client_vty_init(). If this is the case the mgcp_client instance created by mgcp_client_init()
* can be registered here so that it will appear as if it were part of the pool. When the user actively
* configures MGW pool members, the MGCP client registered here will be ignored. (The registration of
* multiple singe mgcp_client instances is not possible.) */
pool->mgcp_client_single = mgcp_client;
}
bool mgcp_client_pool_empty(const struct mgcp_client_pool *pool)
{
return llist_empty(&pool->member_list);
}
/*! Lookup the selected MGCP client config by its reference number */
struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr)
{
struct mgcp_client_pool_member *pool_member;
llist_for_each_entry(pool_member, &pool->member_list, list) {
if (pool_member->nr == nr)
return pool_member;
}
return NULL;
}
/* Not every pool member may have a functional MGCP client, we will run through the pool once until we meet a
* pool member that is suitable (is not blocked, has a client with a working link, has a low load). */
static struct mgcp_client_pool_member *mgcp_client_pool_pick(struct mgcp_client_pool *pool)
{
struct mgcp_client_pool_member *pool_member;
struct mgcp_client_pool_member *pool_member_picked = NULL;
unsigned int n_pool_members = 0;
llist_for_each_entry(pool_member, &pool->member_list, list) {
n_pool_members++;
bool conn_up = pool_member->client && pool_member->client->conn_up;
if (pool_member->blocked == false && conn_up) {
if (!pool_member_picked)
pool_member_picked = pool_member;
else if (pool_member_picked->refcount > pool_member->refcount)
pool_member_picked = pool_member;
} else {
LOGPPMGW(pool_member, LOGL_DEBUG, "%s -- MGW %u is unusable (blocked=%u, cli=%u, link=%u)\n",
__func__, pool_member->nr, pool_member->blocked, !!pool_member->client, conn_up);
}
}
if (pool_member_picked) {
LOGPPMGW(pool_member_picked, LOGL_DEBUG, "MGW pool has %u members -- using MGW %u (active calls: %u)\n",
n_pool_members, pool_member_picked->nr, pool_member_picked->refcount);
return pool_member_picked;
}
LOGP(DLMGCP, LOGL_ERROR,
"MGW pool has %u members, but no functional MGW pool member found -- check configuration!\n",
n_pool_members);
return NULL;
}
/*! get an MGCP client from the pool (increment reference counter).
* \param[in,out] pool MGCP client pool descriptor.
* \returns MGCP client descriptor, NULL if no member was found (empty pool). */
struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool)
{
struct mgcp_client_pool_member *pool_member;
/*! When an MGCP client is taken from the pool it is still available for other calls. In fact only a reference
* counter is incremented to keep track on how many references to a specific MGCP client are currently used
* by the application code. */
/* When the pool is empty, return a single MGCP client if it is registered. */
if (llist_empty(&pool->member_list) && pool->mgcp_client_single) {
LOGP(DLMGCP, LOGL_DEBUG, "MGW pool is empty -- using (single) MGW %s\n",
mgcp_client_name(pool->mgcp_client_single));
return pool->mgcp_client_single;
}
/* Abort when the pool is empty */
if (llist_empty(&pool->member_list)) {
LOGP(DLMGCP, LOGL_ERROR, "MGW pool is empty -- no MGW available!\n");
return NULL;
}
/* Pick a suitable pool member */
pool_member = mgcp_client_pool_pick(pool);
if (!pool_member)
return NULL;
return mgcp_client_pool_member_get(pool_member);
}
/*! put an MGCP client back into the pool (decrement reference counter).
* \param[in,out] pool MGCP client pool descriptor.
* \param[in] mgcp MGCP client descriptor.
*
* This function is able to detect automatically to which pool the mgcp_client belongs. If the mgcp_client does
* not belong to a pool at all, the function call will have no effect. */
void mgcp_client_pool_put(struct mgcp_client *mgcp_client)
{
struct mgcp_client_pool_member *pool_member;
if (!mgcp_client)
return;
if (!mgcp_client->pool_member)
return;
pool_member = mgcp_client->pool_member;
if (pool_member->refcount == 0) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n");
return;
}
pool_member->refcount--;
}
/***************************
* mgcp_client_pool_member:
***************************/
/*! Allocate an mgcp_client_pool_member.
* \param[in] pool MGCP client pool descriptor.
* \param[in] nr Reference number of the pool member.
*/
struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr)
{
struct mgcp_client_pool_member *pool_member;
pool_member = talloc_zero(pool, struct mgcp_client_pool_member);
OSMO_ASSERT(pool_member);
mgcp_client_conf_init(&pool_member->conf);
pool_member->pool = pool;
pool_member->nr = nr;
llist_add_tail(&pool_member->list, &pool->member_list);
return pool_member;
}
/*! Free an mgcp_client_pool_member allocated through mgcp_client_pool_member_alloc().
* \param[in] pool_member MGCP client pool descriptor.
*
* It also frees the associated MGCP client if present.
*/
void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member)
{
llist_del(&pool_member->list);
if (pool_member->client) {
mgcp_client_disconnect(pool_member->client);
talloc_free(pool_member->client);
}
talloc_free(pool_member);
}
/*! Recreate and reconnect the MGCP client associated to the pool descriptor.
* \param[in] pool_member MGCP client pool descriptor.
*/
int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member)
{
/* Get rid of a possibly existing old MGCP client instance first */
if (pool_member->client) {
mgcp_client_disconnect(pool_member->client);
talloc_free(pool_member->client);
}
/* Initialize client */
pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
if (!pool_member->client) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n");
return -EINVAL;
}
/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */
pool_member->client->pool_member = pool_member;
/* Connect client */
if (mgcp_client_connect(pool_member->client)) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n",
pool_member->conf.remote_addr, pool_member->conf.remote_port);
talloc_free(pool_member->client);
pool_member->client = NULL;
return -ECONNABORTED;
}
return 0;
}
/* Get a human readable name for a given pool member. */
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member)
{
const struct mgcp_client *mpcp_client;
struct mgcp_client mpcp_client_dummy;
static char name[512];
const char *description;
if (!pool_member)
return "(null)";
/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the
* initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */
if (!pool_member->client) {
memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual));
mpcp_client = &mpcp_client_dummy;
} else {
mpcp_client = pool_member->client;
}
description = mgcp_client_name(mpcp_client);
snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description);
return name;
}
/*! Get the MGCP client associated with the pool reference from the pool (increment reference counter).
* \param[in] pool_member MGCP client pool descriptor.
* \returns MGCP client descriptor, NULL if no member was not ready.
*/
struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member)
{
pool_member->refcount++;
return pool_member->client;
}
/*! Get whether the MGCP client associated with the pool reference is blocked by policy.
* \param[in] pool_member MGCP client pool descriptor.
* \returns true if blocked, false otherwise
*/
bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member)
{
return pool_member->blocked;
}

View File

@@ -1,5 +1,5 @@
/* MGCP client interface to quagga VTY */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
* Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
* (C) 2009 by Harald Welte <laforge@gnumonks.org>
* (C) 2009-2011 by Holger Hans Peter Freyther
@@ -24,78 +24,28 @@
#include <stdlib.h>
#include <talloc.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/misc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/timer.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
#include <osmocom/mgcp_client/mgcp_client_pool_internal.h>
#include <osmocom/mgcp_client/mgcp_client_pool.h>
#define MGW_STR MGCP_CLIENT_MGW_STR
/* Only common (non-pooled) VTY commands will use this talloc context. All
* pooled VTY commands will use the pool (global_mgcp_client_pool) as
* talloc context. */
static void *global_mgcp_client_ctx = NULL;
/* MGCP Client configuration used with mgcp_client_vty_init(). (This pointer
* points to user provided memory, so it cannot be used as talloc context.) */
static struct mgcp_client_conf *global_mgcp_client_conf = NULL;
/* Pointer to the MGCP pool that is managed by mgcp_client_pool_vty_init() */
static struct mgcp_client_pool *global_mgcp_client_pool = NULL;
static struct mgcp_client_conf *get_mgcp_client_config(struct vty *vty)
{
if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node)
return vty->index;
/* Global single MGCP config, deprecated: */
vty_out(vty, "%% MGCP commands outside of 'mgw' nodes are deprecated. "
"You should consider reading the User Manual and migrating to 'mgw' node.%s",
VTY_NEWLINE);
return global_mgcp_client_conf;
}
static struct mgcp_client *get_mgcp_client(struct vty *vty)
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
struct mgcp_client_pool_member *pool_member;
if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node) {
llist_for_each_entry(pool_member, &global_mgcp_client_pool->member_list, list) {
/* Find matching the conf pointer: */
if (&pool_member->conf != conf)
continue;
return pool_member->client;
}
}
/* Global single MGCP config, deprecated: */
vty_out(vty, "%% MGCP commands outside of 'mgw' nodes are deprecated. "
"You should consider reading the User Manual and migrating to 'mgw' node.%s",
VTY_NEWLINE);
/* There's no way to obtain the struct mgcp_client in old interface, but anyway it's deprecated. */
return NULL;
}
void *global_mgcp_client_ctx = NULL;
struct mgcp_client_conf *global_mgcp_client_conf = NULL;
DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
"local-ip " VTY_IPV46_CMD,
"local bind to connect to MGW from\n"
"mgw local-ip " VTY_IPV46_CMD,
MGW_STR "local bind to connect to MGW from\n"
"local bind IPv4 address\n"
"local bind IPv6 address\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
if (!global_mgcp_client_conf)
return CMD_ERR_NOTHING_TODO;
OSMO_ASSERT(global_mgcp_client_ctx);
osmo_talloc_replace_string(global_mgcp_client_ctx,
(char **)&conf->local_addr,
(char**)&global_mgcp_client_conf->local_addr,
argv[0]);
return CMD_SUCCESS;
}
@@ -103,77 +53,57 @@ ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
"mgcpgw local-ip A.B.C.D",
MGW_STR "local bind to connect to MGCP gateway with\n"
"local bind IP address\n")
ALIAS_DEPRECATED(cfg_mgw_local_ip,
cfg_mgw_mgw_local_ip_cmd,
"mgw local-ip " VTY_IPV46_CMD,
MGW_STR "local bind to connect to MGW from\n"
"local bind IPv4 address\n"
"local bind IPv6 address\n")
DEFUN(cfg_mgw_local_port, cfg_mgw_local_port_cmd,
"local-port <0-65535>",
"local port to connect to MGW from\n"
"mgw local-port <0-65535>",
MGW_STR "local port to connect to MGW from\n"
"local bind port\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
conf->local_port = atoi(argv[0]);
if (!global_mgcp_client_conf)
return CMD_ERR_NOTHING_TODO;
global_mgcp_client_conf->local_port = atoi(argv[0]);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd,
"mgcpgw local-port <0-65535>",
MGW_STR "local bind to connect to MGCP gateway with\n"
"local bind port\n")
ALIAS_DEPRECATED(cfg_mgw_local_port,
cfg_mgw_mgw_local_port_cmd,
"mgw local-port <0-65535>",
MGW_STR "local port to connect to MGW from\n"
"local bind port\n")
DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
"remote-ip " VTY_IPV46_CMD,
"remote IP address to reach the MGW at\n"
"mgw remote-ip " VTY_IPV46_CMD,
MGW_STR "remote IP address to reach the MGW at\n"
"remote IPv4 address\n"
"remote IPv6 address\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
if (!global_mgcp_client_conf)
return CMD_ERR_NOTHING_TODO;
OSMO_ASSERT(global_mgcp_client_ctx);
osmo_talloc_replace_string(global_mgcp_client_ctx,
(char **)&conf->remote_addr, argv[0]);
(char**)&global_mgcp_client_conf->remote_addr,
argv[0]);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
"mgcpgw remote-ip A.B.C.D",
MGW_STR "remote bind to connect to MGCP gateway with\n"
"remote bind IP address\n")
ALIAS_DEPRECATED(cfg_mgw_remote_ip,
cfg_mgw_mgw_remote_ip_cmd,
"mgw remote-ip " VTY_IPV46_CMD,
MGW_STR "remote IP address to reach the MGW at\n"
"remote IPv4 address\n"
"remote IPv6 address\n")
DEFUN(cfg_mgw_remote_port, cfg_mgw_remote_port_cmd,
"remote-port <0-65535>",
"remote port to reach the MGW at\n"
"mgw remote-port <0-65535>",
MGW_STR "remote port to reach the MGW at\n"
"remote port\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
conf->remote_port = atoi(argv[0]);
if (!global_mgcp_client_conf)
return CMD_ERR_NOTHING_TODO;
global_mgcp_client_conf->remote_port = atoi(argv[0]);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
"mgcpgw remote-port <0-65535>",
MGW_STR "remote bind to connect to MGCP gateway with\n"
"remote bind port\n")
ALIAS_DEPRECATED(cfg_mgw_remote_port,
cfg_mgw_mgw_remote_port_cmd,
"mgw remote-port <0-65535>",
MGW_STR "remote port to reach the MGW at\n"
"remote port\n")
DEFUN_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgw_mgw_endpoint_range_cmd,
DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
"mgw endpoint-range <1-65534> <1-65534>",
MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
"-\n" "-\n")
@@ -183,7 +113,7 @@ DEFUN_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgw_mgw_endpoint_range_cmd,
VTY_NEWLINE);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
"mgcpgw endpoint-range <1-65534> <1-65534>",
MGW_STR "usable range of endpoint identifiers\n"
"set first useable endpoint identifier\n"
@@ -211,279 +141,63 @@ ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
DEFUN(cfg_mgw_endpoint_domain_name,
cfg_mgw_endpoint_domain_name_cmd,
"endpoint-domain NAME",
"Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
"Domain name, should be alphanumeric.\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
if (osmo_strlcpy(conf->endpoint_domain_name, argv[0], sizeof(conf->endpoint_domain_name))
>= sizeof(conf->endpoint_domain_name)) {
vty_out(vty, "%% Error: 'mgw endpoint-domain' name too long, max length is %zu: '%s'%s",
sizeof(conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_endpoint_domain_name,
cfg_mgw_mgw_endpoint_domain_name_cmd,
"mgw endpoint-domain NAME",
MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
"Domain name, should be alphanumeric.\n")
DEFUN(cfg_mgw_reset_ep_name,
cfg_mgw_reset_ep_name_cmd,
"reset-endpoint NAME",
"Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
"e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
{
int rc;
struct reset_ep *reset_ep;
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
/* stop when the address is already in the list */
llist_for_each_entry(reset_ep, &conf->reset_epnames, list) {
if (strcmp(argv[0], reset_ep->name) == 0) {
vty_out(vty, "%% duplicate endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
}
/* the domain name is not part of the actual endpoint name */
if (strchr(argv[0], '@')) {
vty_out(vty, "%% the endpoint name must be given without domain name ('%s')%s",
argv[0], VTY_NEWLINE);
if (osmo_strlcpy(global_mgcp_client_conf->endpoint_domain_name, argv[0],
sizeof(global_mgcp_client_conf->endpoint_domain_name))
>= sizeof(global_mgcp_client_conf->endpoint_domain_name)) {
vty_out(vty, "%% Error: 'mgw endpoint-domain' name too long, max length is %zu: '%s'%s",
sizeof(global_mgcp_client_conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
reset_ep = talloc_zero(global_mgcp_client_ctx, struct reset_ep);
OSMO_ASSERT(reset_ep);
rc = osmo_strlcpy(reset_ep->name, argv[0], sizeof(reset_ep->name));
if (rc >= sizeof(reset_ep->name)) {
vty_out(vty, "%% Error: 'mgw reset-endpoint' name too long, max length is %zu: '%s'%s",
sizeof(reset_ep->name) - 1, argv[0], VTY_NEWLINE);
talloc_free(reset_ep);
return CMD_WARNING;
}
llist_add_tail(&reset_ep->list, &conf->reset_epnames);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_reset_ep_name,
cfg_mgw_mgw_reset_ep_name_cmd,
"mgw reset-endpoint NAME",
MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
"e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
DEFUN(cfg_mgw_no_reset_ep_name,
cfg_mgw_no_reset_ep_name_cmd,
"no reset-endpoint NAME",
NO_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
{
struct reset_ep *reset_ep;
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
llist_for_each_entry(reset_ep, &conf->reset_epnames, list) {
if (strcmp(argv[0], reset_ep->name) == 0) {
llist_del(&reset_ep->list);
talloc_free(reset_ep);
return CMD_SUCCESS;
}
}
vty_out(vty, "%% no such endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
ALIAS_DEPRECATED(cfg_mgw_no_reset_ep_name,
cfg_mgw_mgw_no_reset_ep_name_cmd,
"no mgw reset-endpoint NAME",
NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
DEFUN(cfg_mgw_mgw_keepalive_req_interval,
cfg_mgw_mgw_keepalive_req_interval_cmd,
"keepalive request-interval <0-4294967295>",
"Monitor if the MGCP link against MGW is still usable\n"
"Send an MGCP command to the MGW at given interval if no other commands are sent\n"
"The interval at which send MGCP commands (s), 0 to disable\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
struct mgcp_client *mgcp = get_mgcp_client(vty);
conf->keepalive.req_interval_sec = atoi(argv[0]);
if (!mgcp)
return CMD_SUCCESS;
/* If client already exists, apply the change immediately if possible: */
mgcp->actual.keepalive.req_interval_sec = atoi(argv[0]);
if (mgcp->iofd) { /* UDP MGCP socket connected */
if (mgcp->actual.keepalive.req_interval_sec > 0) {
/* Re-schedule: */
osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0);
} else {
if (osmo_timer_pending(&mgcp->keepalive_tx_timer))
osmo_timer_del(&mgcp->keepalive_tx_timer);
/* Assume link is UP by default, so that this MGW can be selected: */
mgcp->conn_up = true;
}
} /* else: wait until connect() to do first scheduling */
return CMD_SUCCESS;
}
DEFUN(cfg_mgw_mgw_keepalive_req_endpoint,
cfg_mgw_mgw_keepalive_req_endpoint_cmd,
"keepalive request-endpoint NAME",
"Monitor if the MGCP link against MGW is still usable\n"
"Use a given endpoint name when sending an MGCP command to the MGW for keepalive purposes\n"
"The name of the endpoint to use\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
struct mgcp_client *mgcp = get_mgcp_client(vty);
OSMO_STRLCPY_ARRAY(conf->keepalive.req_endpoint_name, argv[0]);
if (!mgcp)
return CMD_SUCCESS;
/* If client already exists, apply the change immediately if possible: */
OSMO_STRLCPY_ARRAY(mgcp->actual.keepalive.req_endpoint_name, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgw_mgw_keepalive_timeout,
cfg_mgw_mgw_keepalive_timeout_cmd,
"keepalive timeout <0-4294967295>",
"Monitor if the MGCP link against MGW is still usable\n"
"Consider the link to the MGW to be down after time without receiving any message from it\n"
"The timeout (s), 0 to disable\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
struct mgcp_client *mgcp = get_mgcp_client(vty);
conf->keepalive.timeout_sec = atoi(argv[0]);
if (!mgcp)
return CMD_SUCCESS;
/* If client already exists, apply the change immediately if possible: */
mgcp->actual.keepalive.timeout_sec = atoi(argv[0]);
if (mgcp->iofd) { /* UDP MGCP socket connected */
if (mgcp->actual.keepalive.timeout_sec > 0) {
/* Re-schedule: */
osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0);
} else {
if (osmo_timer_pending(&mgcp->keepalive_rx_timer))
osmo_timer_del(&mgcp->keepalive_rx_timer);
/* Assume link is UP by default, so that this MGW can be selected: */
mgcp->conn_up = true;
}
} /* else: wait until connect() to do first scheduling */
return CMD_SUCCESS;
}
static int config_write(struct vty *vty, const char *indent, struct mgcp_client_conf *conf)
int mgcp_client_config_write(struct vty *vty, const char *indent)
{
const char *addr;
int port;
struct reset_ep *reset_ep;
/* If caller doesn't the MGW pool API (mgcp_client_pool_vty_init was never called),
* then the "mgw" cmd prefix must be added since the old node always contained it.
*/
const char *mgw_prefix = global_mgcp_client_pool ? "" : "mgw ";
if (conf->description) /* description never had "mgw" prefix even on old node: */
vty_out(vty, "%sdescription %s%s", indent, conf->description, VTY_NEWLINE);
addr = conf->local_addr;
addr = global_mgcp_client_conf->local_addr;
if (addr)
vty_out(vty, "%s%slocal-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE);
port = conf->local_port;
vty_out(vty, "%smgw local-ip %s%s", indent, addr,
VTY_NEWLINE);
port = global_mgcp_client_conf->local_port;
if (port >= 0)
vty_out(vty, "%s%slocal-port %u%s", indent, mgw_prefix,
vty_out(vty, "%smgw local-port %u%s", indent,
(uint16_t)port, VTY_NEWLINE);
addr = conf->remote_addr;
addr = global_mgcp_client_conf->remote_addr;
if (addr)
vty_out(vty, "%s%sremote-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE);
port = conf->remote_port;
vty_out(vty, "%smgw remote-ip %s%s", indent, addr,
VTY_NEWLINE);
port = global_mgcp_client_conf->remote_port;
if (port >= 0)
vty_out(vty, "%s%sremote-port %u%s", indent, mgw_prefix,
vty_out(vty, "%smgw remote-port %u%s", indent,
(uint16_t)port, VTY_NEWLINE);
if (conf->endpoint_domain_name[0])
vty_out(vty, "%s%sendpoint-domain %s%s", indent, mgw_prefix,
conf->endpoint_domain_name, VTY_NEWLINE);
llist_for_each_entry(reset_ep, &conf->reset_epnames, list)
vty_out(vty, "%s%sreset-endpoint %s%s", indent, mgw_prefix, reset_ep->name, VTY_NEWLINE);
if (conf->keepalive.req_interval_sec != 0)
vty_out(vty, "%s%skeepalive request-interval %u%s", indent, mgw_prefix,
conf->keepalive.req_interval_sec, VTY_NEWLINE);
if (strncmp(conf->keepalive.req_endpoint_name, MGCP_CLIENT_KEEPALIVE_DEFAULT_ENDP,
sizeof(conf->keepalive.req_endpoint_name)) != 0)
vty_out(vty, "%s%skeepalive request-endpoint %s%s", indent, mgw_prefix,
conf->keepalive.req_endpoint_name, VTY_NEWLINE);
if (conf->keepalive.timeout_sec != 0)
vty_out(vty, "%s%skeepalive timeout %u%s", indent, mgw_prefix,
conf->keepalive.timeout_sec, VTY_NEWLINE);
if (global_mgcp_client_conf->endpoint_domain_name[0])
vty_out(vty, "%smgw endpoint-domain %s%s", indent,
global_mgcp_client_conf->endpoint_domain_name, VTY_NEWLINE);
return CMD_SUCCESS;
}
/*! Write out MGCP client config to VTY.
* \param[in] vty VTY to which we should print.
* \param[in] string used for indentation (e.g. " ").
* \returns CMD_SUCCESS on success, CMD_WARNING on error */
int mgcp_client_config_write(struct vty *vty, const char *indent)
{
/* If caller supports MGW pool API (mgcp_client_pool_vty_init was
* called), then skip printing any config in this node and print it when
* the whole 'mgw' node is printed. */
if (global_mgcp_client_pool)
return CMD_SUCCESS;
return config_write(vty, indent, global_mgcp_client_conf);
}
static void vty_init_common(void *talloc_ctx, int node)
{
global_mgcp_client_ctx = talloc_ctx;
/* deprecated 'mgw' commands ('mgw' prepended as first arg) */
install_lib_element(node, &cfg_mgw_mgw_local_ip_cmd);
install_lib_element(node, &cfg_mgw_mgw_local_port_cmd);
install_lib_element(node, &cfg_mgw_mgw_remote_ip_cmd);
install_lib_element(node, &cfg_mgw_mgw_remote_port_cmd);
install_lib_element(node, &cfg_mgw_mgw_endpoint_range_cmd);
install_lib_element(node, &cfg_mgw_mgw_endpoint_domain_name_cmd);
install_lib_element(node, &cfg_mgw_mgw_reset_ep_name_cmd);
install_lib_element(node, &cfg_mgw_mgw_no_reset_ep_name_cmd);
install_lib_element(node, &cfg_mgw_mgw_keepalive_req_interval_cmd);
install_lib_element(node, &cfg_mgw_mgw_keepalive_req_endpoint_cmd);
install_lib_element(node, &cfg_mgw_mgw_keepalive_timeout_cmd);
osmo_fsm_vty_add_cmds();
}
/*! Set up MGCP client VTY
* (called once at startup by the application process).
* \param[in] talloc_ctx talloc context to be used by the VTY for allocating memory.
* \param[in] node identifier of the node on which the VTY commands should be installed.
* \param[in] conf user provided memory to to store the MGCP client configuration data. */
void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf)
{
global_mgcp_client_ctx = talloc_ctx;
global_mgcp_client_conf = conf;
install_lib_element(node, &cfg_mgw_local_ip_cmd);
install_lib_element(node, &cfg_mgw_local_port_cmd);
install_lib_element(node, &cfg_mgw_remote_ip_cmd);
install_lib_element(node, &cfg_mgw_remote_port_cmd);
install_lib_element(node, &cfg_mgw_endpoint_range_cmd);
install_lib_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
install_lib_element(node, &cfg_mgw_endpoint_domain_name_cmd);
/* deprecated 'mgcpgw' commands */
install_lib_element(node, &cfg_mgcpgw_local_ip_cmd);
install_lib_element(node, &cfg_mgcpgw_local_port_cmd);
@@ -492,254 +206,5 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
install_lib_element(node, &cfg_mgcpgw_endpoint_range_cmd);
install_lib_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
vty_init_common(talloc_ctx, node);
}
/* Mark whether user called mgcp_client_pool_config_write() and hence support new API */
static bool mgcp_client_pool_config_write_called = false;
static int _mgcp_client_pool_config_write(struct vty *vty, const char *indent)
{
struct mgcp_client_pool *pool = global_mgcp_client_pool;
struct mgcp_client_pool_member *pool_member;
unsigned int subindent_buf_len;
char *subindent;
if (!indent)
indent = pool->vty_indent ? : "";
subindent_buf_len = strlen(indent) + 1 + 1;
subindent = talloc_zero_size(vty, subindent_buf_len);
snprintf(subindent, subindent_buf_len, "%s ", indent);
llist_for_each_entry(pool_member, &pool->member_list, list) {
vty_out(vty, "%smgw %u%s", indent, pool_member->nr, VTY_NEWLINE);
config_write(vty, subindent, &pool_member->conf);
}
/* MGW pool API is supported by user (global_mgcp_client_pool is set
* because mgcp_client_pool_vty_init was called). If single MGW was
* configured through old VTY and no mgw in the new MGW pool VTY is
* replacing it, then output the single MGW converted to the new MGW
* pool VTY. */
if (llist_empty(&pool->member_list) && pool->mgcp_client_single) {
vty_out(vty, "%smgw 0%s", indent, VTY_NEWLINE);
config_write(vty, subindent, global_mgcp_client_conf);
}
talloc_free(subindent);
return CMD_SUCCESS;
}
/* Deprecated, used for backward compatibility with older users which didn't call
* mgcp_client_pool_config_write(): */
static int config_write_pool(struct vty *vty)
{
if (mgcp_client_pool_config_write_called)
return CMD_SUCCESS;
return _mgcp_client_pool_config_write(vty, NULL);
}
/*! Write out MGCP client config to VTY.
* \param[in] vty VTY to which we should print.
* \param[in] indent string used for indentation (e.g. " ").
If NULL, indentation passed during mgcp_client_pool_vty_init() will be used.
* \returns CMD_SUCCESS on success, CMD_WARNING on error */
int mgcp_client_pool_config_write(struct vty *vty, const char *indent)
{
/* Tell internal node write function that the user supports calling proper API: */
mgcp_client_pool_config_write_called = true;
return _mgcp_client_pool_config_write(vty, indent);
}
DEFUN_ATTR(cfg_mgw,
cfg_mgw_cmd, "mgw <0-255>", "Select a MGCP client config to setup\n" "reference number\n", CMD_ATTR_IMMEDIATE)
{
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
if (!pool_member) {
pool_member = mgcp_client_pool_member_alloc(global_mgcp_client_pool, nr);
OSMO_ASSERT(pool_member);
}
vty->index = &pool_member->conf;
vty->index_sub = &pool_member->conf.description;
vty->node = global_mgcp_client_pool->vty_node->node;
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_no_mgw,
cfg_no_mgw_cmd,
"no mgw <0-255>", NO_STR "Select a MGCP client config to remove\n" "reference number\n", CMD_ATTR_IMMEDIATE)
{
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
/* Make sure that there are no ongoing calls */
if (pool_member->refcount > 0) {
vty_out(vty, "%% MGCP client (MGW %s) is still serving ongoing calls -- can't remove it now!%s",
mgcp_client_pool_member_name(pool_member), VTY_NEWLINE);
return CMD_WARNING;
}
mgcp_client_pool_member_free(pool_member);
return CMD_SUCCESS;
}
DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd,
"mgw <0-255> reconnect",
MGW_STR "reference number\n" "reconfigure and reconnect MGCP client\n", CMD_ATTR_IMMEDIATE)
{
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member = NULL;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
/* Make sure that there are no ongoing calls */
if (pool_member->refcount > 0) {
vty_out(vty, "%% MGCP client (MGW %s) is still serving ongoing calls -- can't reconnect it now!%s",
mgcp_client_pool_member_name(pool_member), VTY_NEWLINE);
return CMD_WARNING;
}
if (mgcp_client_pool_member_reinit_client(pool_member) < 0) {
LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s connect failed at (%s:%u)\n",
mgcp_client_pool_member_name(pool_member), pool_member->conf.remote_addr,
pool_member->conf.remote_port);
vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s",
mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN_ATTR(mgw_block, mgw_block_cmd,
"mgw <0-255> block",
MGW_STR "reference number\n" "block MGCP client so that it won't be used for new calls\n", CMD_ATTR_IMMEDIATE)
{
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member = NULL;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
pool_member->blocked = true;
return CMD_SUCCESS;
}
DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd,
"mgw <0-255> unblock",
MGW_STR "reference number\n" "unblock MGCP client so that it will be available for new calls\n", CMD_ATTR_IMMEDIATE)
{
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member = NULL;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
pool_member->blocked = false;
return CMD_SUCCESS;
}
DEFUN(mgw_show, mgw_show_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n")
{
vty_out(vty, "%% MGW-Pool:%s", VTY_NEWLINE);
struct mgcp_client_pool_member *pool_member;
if (llist_empty(&global_mgcp_client_pool->member_list) && global_mgcp_client_pool->mgcp_client_single) {
vty_out(vty, "%% (pool is empty, single MGCP client will be used)%s", VTY_NEWLINE);
return CMD_SUCCESS;
} else if (llist_empty(&global_mgcp_client_pool->member_list)) {
vty_out(vty, "%% (pool is empty)%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
llist_for_each_entry(pool_member, &global_mgcp_client_pool->member_list, list) {
const struct mgcp_client *cli = pool_member->client;
vty_out(vty, "%% MGW %s%s", mgcp_client_pool_member_name(pool_member), VTY_NEWLINE);
vty_out(vty, "%% MGCP link: %s,%s%s",
cli && cli->iofd ? "connected" : "disconnected",
cli && cli->conn_up ?
((cli->actual.keepalive.timeout_sec > 0) ? "UP" : "MAYBE") :
"DOWN",
VTY_NEWLINE);
vty_out(vty, "%% service: %s%s", pool_member->blocked ? "blocked" : "unblocked", VTY_NEWLINE);
vty_out(vty, "%% ongoing calls: %u%s", pool_member->refcount, VTY_NEWLINE);
}
return CMD_SUCCESS;
}
/*! Set up MGCP client VTY (pooled)
* (called once at startup by the application process).
* \param[in] parent_node identifier of the parent node on which the mgw node appears.
* \param[in] mgw_node identifier that should be used with the newly installed MGW node.
* \param[in] indent indentation string to match the indentation in the VTY config.
If NULL, it must be passed explicitly each time mgcp_client_pool_config_write() is called.
* \param[in] pool user provided memory to store the configured MGCP client (MGW) pool. */
void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool)
{
/* A pool must be allocated before this function can be called */
OSMO_ASSERT(pool);
/* Never allow this function to be called twice on the same pool */
OSMO_ASSERT(!pool->vty_node);
if (indent) {
pool->vty_indent = talloc_strdup(pool, indent);
OSMO_ASSERT(pool->vty_indent);
}
pool->vty_node = talloc_zero(pool, struct cmd_node);
OSMO_ASSERT(pool->vty_node);
pool->vty_node->node = mgw_node;
pool->vty_node->vtysh = 1;
pool->vty_node->prompt = talloc_strdup(pool->vty_node, "%s(config-mgw)# ");
install_lib_element(parent_node, &cfg_mgw_cmd);
install_lib_element(parent_node, &cfg_no_mgw_cmd);
/* Note: config_write_pool is deprecated and user is expected to
* manually call mgcp_client_pool_config_write() when printing the VTY
* config */
install_node(pool->vty_node, config_write_pool);
vty_init_common(pool, mgw_node);
install_lib_element(mgw_node, &cfg_mgw_local_ip_cmd);
install_lib_element(mgw_node, &cfg_mgw_local_port_cmd);
install_lib_element(mgw_node, &cfg_mgw_remote_ip_cmd);
install_lib_element(mgw_node, &cfg_mgw_remote_port_cmd);
install_lib_element(mgw_node, &cfg_mgw_rtp_bts_base_port_cmd);
install_lib_element(mgw_node, &cfg_mgw_endpoint_domain_name_cmd);
install_lib_element(mgw_node, &cfg_mgw_reset_ep_name_cmd);
install_lib_element(mgw_node, &cfg_mgw_no_reset_ep_name_cmd);
install_element(mgw_node, &cfg_description_cmd);
install_lib_element(ENABLE_NODE, &mgw_reconnect_cmd);
install_lib_element(ENABLE_NODE, &mgw_block_cmd);
install_lib_element(ENABLE_NODE, &mgw_unblock_cmd);
install_lib_element_ve(&mgw_show_cmd);
global_mgcp_client_pool = pool;
osmo_fsm_vty_add_cmds();
}

View File

@@ -7,7 +7,6 @@ AM_CPPFLAGS = \
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOCODEC_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
@@ -17,6 +16,12 @@ AM_CFLAGS = \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
@@ -40,8 +45,7 @@ libosmo_mgcp_a_SOURCES = \
mgcp_stat.c \
mgcp_endp.c \
mgcp_trunk.c \
mgcp_ctrl.c \
mgcp_ratectr.c \
mgcp_rtp_end.c \
mgcp_e1.c \
mgcp_iuup.c \
$(NULL)

View File

@@ -14,6 +14,10 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
static inline int val_seg(int val)

View File

@@ -28,7 +28,7 @@
/* Helper function to dump codec information of a specified codec to a printable
* string, used by dump_codec_summary() */
static char *mgcp_codec_dump(struct mgcp_rtp_codec *codec)
static char *dump_codec(struct mgcp_rtp_codec *codec)
{
static char str[256];
char *pt_str;
@@ -49,25 +49,31 @@ static char *mgcp_codec_dump(struct mgcp_rtp_codec *codec)
}
/*! Dump a summary of all negotiated codecs to debug log
* \param[in] cset related codecset.
* \param[in] prefix_str Prefix string to print during logging.
* */
void mgcp_codecset_summary(struct mgcp_rtp_codecset *cset, const char *prefix_str)
* \param[in] conn related rtp-connection. */
void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
{
struct mgcp_rtp_end *rtp;
unsigned int i;
struct mgcp_rtp_codec *codec;
struct mgcp_endpoint *endp;
if (cset->codecs_assigned == 0) {
LOGP(DLMGCP, LOGL_ERROR, "%s no codecs available\n", prefix_str);
rtp = &conn->end;
endp = conn->conn->endp;
if (rtp->codecs_assigned == 0) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "conn:%s no codecs available\n",
mgcp_conn_dump(conn->conn));
return;
}
/* Store parsed codec information */
for (i = 0; i < cset->codecs_assigned; i++) {
struct mgcp_rtp_codec *codec = &cset->codecs[i];
for (i = 0; i < rtp->codecs_assigned; i++) {
codec = &rtp->codecs[i];
LOGP(DLMGCP, LOGL_DEBUG, "%s codecs[%u]:%s", prefix_str, i, mgcp_codec_dump(codec));
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s codecs[%u]:%s",
mgcp_conn_dump(conn->conn), i, dump_codec(codec));
if (codec == cset->codec)
if (codec == rtp->codec)
LOGPC(DLMGCP, LOGL_DEBUG, " [selected]");
LOGPC(DLMGCP, LOGL_DEBUG, "\n");
@@ -75,7 +81,7 @@ void mgcp_codecset_summary(struct mgcp_rtp_codecset *cset, const char *prefix_st
}
/* Initalize or reset codec information with default data. */
static void mgcp_codec_init(struct mgcp_rtp_codec *codec)
static void codec_init(struct mgcp_rtp_codec *codec)
{
*codec = (struct mgcp_rtp_codec){
.payload_type = -1,
@@ -83,25 +89,27 @@ static void mgcp_codec_init(struct mgcp_rtp_codec *codec)
.frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN,
.rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE,
.channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS,
.subtype_name = "",
.audio_name = "",
};
}
static void mgcp_codec_free(struct mgcp_rtp_codec *codec)
static void codec_free(struct mgcp_rtp_codec *codec)
{
if (codec->subtype_name)
talloc_free(codec->subtype_name);
if (codec->audio_name)
talloc_free(codec->audio_name);
*codec = (struct mgcp_rtp_codec){};
}
/*! Initalize or reset codec information with default data.
* \param[out] conn related rtp-connection. */
void mgcp_codecset_reset(struct mgcp_rtp_codecset *cset)
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
{
int i;
for (i = 0; i < cset->codecs_assigned; i++)
mgcp_codec_free(&cset->codecs[i]);
cset->codecs_assigned = 0;
cset->codec = NULL;
for (i = 0; i < conn->end.codecs_assigned; i++)
codec_free(&conn->end.codecs[i]);
conn->end.codecs_assigned = 0;
conn->end.codec = NULL;
}
/*! Add codec configuration depending on payload type and/or codec name. This
@@ -112,23 +120,25 @@ void mgcp_codecset_reset(struct mgcp_rtp_codecset *cset)
* \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
* \param[in] param optional codec parameters (set to NULL when unused).
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codecset_add_codec(struct mgcp_rtp_codecset *cset, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
{
int rate;
int channels;
char audio_codec[64];
struct mgcp_rtp_codec *codec;
unsigned int pt_offset = cset->codecs_assigned;
unsigned int pt_offset = conn->end.codecs_assigned;
void *ctx = conn->conn;
/* The amount of codecs we can store is limited, make sure we do not
* overrun this limit. */
if (cset->codecs_assigned >= MGCP_MAX_CODECS)
if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
return -EINVAL;
/* First unused entry */
codec = &cset->codecs[cset->codecs_assigned];
codec = &conn->end.codecs[conn->end.codecs_assigned];
/* Initalize the codec struct with some default data to begin with */
mgcp_codec_init(codec);
codec_init(codec);
if (payload_type != PTYPE_UNDEFINED) {
/* Make sure we do not get any reserved or undefined type numbers */
@@ -150,16 +160,16 @@ int mgcp_codecset_add_codec(struct mgcp_rtp_codecset *cset, int payload_type, co
if (!audio_name) {
switch (payload_type) {
case 0:
strcpy(codec->audio_name, "PCMU/8000/1");
audio_name = talloc_strdup(ctx, "PCMU/8000/1");
break;
case 3:
strcpy(codec->audio_name, "GSM/8000/1");
audio_name = talloc_strdup(ctx, "GSM/8000/1");
break;
case 8:
strcpy(codec->audio_name, "PCMA/8000/1");
audio_name = talloc_strdup(ctx, "PCMA/8000/1");
break;
case 18:
strcpy(codec->audio_name, "G729/8000/1");
audio_name = talloc_strdup(ctx, "G729/8000/1");
break;
default:
/* The given payload type is not known to us, or it
@@ -169,36 +179,36 @@ int mgcp_codecset_add_codec(struct mgcp_rtp_codecset *cset, int payload_type, co
payload_type);
goto error;
}
} else {
OSMO_STRLCPY_ARRAY(codec->audio_name, audio_name);
}
/* Now we extract the codec subtype name, rate and channels. The latter
* two are optional. If they are not present we use the safe defaults
* above. */
if (strlen(codec->audio_name) >= sizeof(codec->subtype_name)) {
LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(codec->audio_name, -1));
if (strlen(audio_name) >= sizeof(audio_codec)) {
LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(audio_name, -1));
goto error;
}
channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
if (sscanf(codec->audio_name, "%63[^/]/%d/%d", codec->subtype_name, &rate, &channels) < 1) {
LOGP(DLMGCP, LOGL_ERROR, "Invalid audio codec: %s\n", osmo_quote_str(codec->audio_name, -1));
if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1) {
LOGP(DLMGCP, LOGL_ERROR, "Invalid audio codec: %s\n", osmo_quote_str(audio_name, -1));
goto error;
}
/* Note: We only accept configurations with one audio channel! */
if (channels != 1) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot handle audio codec with more than one channel: %s\n",
osmo_quote_str(codec->audio_name, -1));
osmo_quote_str(audio_name, -1));
goto error;
}
codec->rate = rate;
codec->channels = channels;
codec->subtype_name = talloc_strdup(ctx, audio_codec);
codec->audio_name = talloc_strdup(ctx, audio_name);
codec->payload_type = payload_type;
if (!strcmp(codec->subtype_name, "G729")) {
if (!strcmp(audio_codec, "G729")) {
codec->frame_duration_num = 10;
codec->frame_duration_den = 1000;
} else {
@@ -262,11 +272,80 @@ int mgcp_codecset_add_codec(struct mgcp_rtp_codecset *cset, int payload_type, co
} else
codec->param_present = false;
cset->codecs_assigned++;
conn->end.codecs_assigned++;
return 0;
error:
/* Make sure we leave a clean codec entry on error. */
mgcp_codec_free(codec);
codec_free(codec);
return -EINVAL;
}
/* Check if the given codec is applicable on the specified endpoint
* Helper function for mgcp_codec_decide() */
static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
{
/* A codec name must be set, if not, this might mean that the codec
* (payload type) that was assigned is unknown to us so we must stop
* here. */
if (!codec->subtype_name)
return false;
/* FIXME: implement meaningful checks to make sure that the given codec
* is compatible with the given endpoint */
return true;
}
/*! Decide for one suitable codec
* \param[in] conn related rtp-connection.
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
{
struct mgcp_rtp_end *rtp;
unsigned int i;
struct mgcp_endpoint *endp;
bool codec_assigned = false;
endp = conn->conn->endp;
rtp = &conn->end;
/* This function works on the results the SDP/LCO parser has extracted
* from the MGCP message. The goal is to select a suitable codec for
* the given connection. When transcoding is available, the first codec
* from the codec list is taken without further checking. When
* transcoding is not available, then the choice must be made more
* carefully. Each codec in the list is checked until one is found that
* is rated compatible. The rating is done by the helper function
* is_codec_compatible(), which does the actual checking. */
for (i = 0; i < rtp->codecs_assigned; i++) {
/* When no transcoding is available, avoid codecs that would
* require transcoding. */
if (endp->trunk->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
continue;
}
rtp->codec = &rtp->codecs[i];
codec_assigned = true;
break;
}
/* FIXME: To the reviewes: This is problematic. I do not get why we
* need to reset the packet_duration_ms depending on the codec
* selection. I thought it were all 20ms? Is this to address some
* cornercase. (This piece of code was in the code path before,
* together with the note: "TODO/XXX: Store this per codec and derive
* it on use" */
if (codec_assigned) {
if (rtp->maximum_packet_time >= 0
&& rtp->maximum_packet_time * rtp->codec->frame_duration_den >
rtp->codec->frame_duration_num * 1500)
rtp->packet_duration_ms = 0;
return 0;
}
return -EINVAL;
}
@@ -280,7 +359,7 @@ error:
*
* https://tools.ietf.org/html/rfc4867
*/
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
static bool amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
{
if (!codec->param_present)
return false;
@@ -289,15 +368,10 @@ bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
return codec->param.amr_octet_aligned;
}
/* Compare two codecs, all parameters must match up */
static bool codecs_same(const struct mgcp_rtp_codec *codec_a, const struct mgcp_rtp_codec *codec_b)
/* Compare two codecs, all parameters must match up, except for the payload type
* number. */
static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
{
/* All codec properties must match up, except the payload type number. Even though standardisd payload numbers
* exist for certain situations, the call agent may still assign them freely. Hence we must not insist on equal
* payload type numbers. Also the audio_name is not checked since it is already parsed into subtype_name, rate,
* and channels, which are checked. */
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
return false;
if (codec_a->rate != codec_b->rate)
return false;
if (codec_a->channels != codec_b->channels)
@@ -306,167 +380,61 @@ static bool codecs_same(const struct mgcp_rtp_codec *codec_a, const struct mgcp_
return false;
if (codec_a->frame_duration_den != codec_b->frame_duration_den)
return false;
/* AMR payload may be formatted in two different payload formats, it is still the same codec but since the
* formatting of the payload is different, conversation is required, so we must treat it as a different
* codec here. */
if (strcmp(codec_a->subtype_name, "AMR") == 0) {
if (mgcp_codec_amr_is_octet_aligned(codec_a) != mgcp_codec_amr_is_octet_aligned(codec_b))
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
return false;
if (!strcmp(codec_a->subtype_name, "AMR")) {
if (amr_is_octet_aligned(codec_a) != amr_is_octet_aligned(codec_b))
return false;
}
return true;
}
/* Compare two codecs, all parameters must match up, except parameters related to payload formatting (not checked). */
static bool codecs_convertible(const struct mgcp_rtp_codec *codec_a, const struct mgcp_rtp_codec *codec_b)
{
/* OsmoMGW currently has no ability to transcode from one codec to another. However OsmoMGW is still able to
* translate between different payload formats as long as the encoded voice data itself does not change.
* Therefore we must insist on equal codecs but still allow different payload formatting. */
/* In 3G IuUP, AMR may be encapsulated in IuFP, this means even though the codec name and negotiated rate is
* different, the formatting can still be converted by OsmoMGW. Therefore we won't insist on equal
* subtype_name and rate if we detect IuFP and AMR is used on the same tandem. */
if (strcmp(codec_a->subtype_name, "AMR") == 0 && strcmp(codec_b->subtype_name, "VND.3GPP.IUFP") == 0)
goto iufp;
if (strcmp(codec_a->subtype_name, "VND.3GPP.IUFP") == 0 && strcmp(codec_b->subtype_name, "AMR") == 0)
goto iufp;
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
return false;
if (codec_a->rate != codec_b->rate)
return false;
iufp:
if (codec_a->channels != codec_b->channels)
return false;
if (codec_a->frame_duration_num != codec_b->frame_duration_num)
return false;
if (codec_a->frame_duration_den != codec_b->frame_duration_den)
return false;
return true;
}
struct mgcp_rtp_codec *mgcp_codecset_find_same(struct mgcp_rtp_codecset *cset, const struct mgcp_rtp_codec *codec)
/*! Translate a given payload type number that belongs to the packet of a
* source connection to the equivalent payload type number that matches the
* configuration of a destination connection.
* \param[in] conn_src related source rtp-connection.
* \param[in] conn_dst related destination rtp-connection.
* \param[in] payload_type number from the source packet or source connection.
* \returns translated payload type number on success, -EINVAL on failure. */
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type)
{
struct mgcp_rtp_end *rtp_src;
struct mgcp_rtp_end *rtp_dst;
struct mgcp_rtp_codec *codec_src = NULL;
struct mgcp_rtp_codec *codec_dst = NULL;
unsigned int i;
unsigned int codecs_assigned;
/* Use the codec information from the source and try to find the equivalent of it on the destination side. In
* the first run we will look for an exact match. */
codecs_assigned = cset->codecs_assigned;
rtp_src = &conn_src->end;
rtp_dst = &conn_dst->end;
/* Find the codec information that is used on the source side */
codecs_assigned = rtp_src->codecs_assigned;
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
for (i = 0; i < codecs_assigned; i++) {
if (codecs_same(codec, &cset->codecs[i])) {
return &cset->codecs[i];
if (payload_type == rtp_src->codecs[i].payload_type) {
codec_src = &rtp_src->codecs[i];
break;
}
}
if (!codec_src)
return -EINVAL;
return NULL;
}
/* For a given codec, find a convertible codec in the given connection. */
static struct mgcp_rtp_codec *codecset_find_convertible(struct mgcp_rtp_codecset *cset, const struct mgcp_rtp_codec *codec)
{
unsigned int i;
unsigned int codecs_assigned;
struct mgcp_rtp_codec *codec_convertible = NULL;
/* Use the codec information from the source and try to find the equivalent of it on the destination side. In
* the first run we will look for an exact match. */
codec_convertible = mgcp_codecset_find_same(cset, codec);
if (codec_convertible)
return codec_convertible;
/* In case we weren't able to find an exact match, we will try to find a match that is the same codec, but the
* payload format may be different. This alternative will require a frame format conversion (i.e. AMR bwe->oe) */
codecs_assigned = cset->codecs_assigned;
/* Use the codec infrmation from the source and try to find the
* equivalent of it on the destination side */
codecs_assigned = rtp_dst->codecs_assigned;
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
for (i = 0; i < codecs_assigned; i++) {
if (codecs_convertible(codec, &cset->codecs[i])) {
codec_convertible = &cset->codecs[i];
if (codecs_same(codec_src, &rtp_dst->codecs[i])) {
codec_dst = &rtp_dst->codecs[i];
break;
}
}
return codec_convertible;
}
/*! Decide for one suitable codec on both of the given connections. In case a destination connection is not available,
* a tentative decision is made.
* \param[in] cset_src related codec set.
* \param[inout] cset_dst related destination codec set (NULL if not present).
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codecset_decide(struct mgcp_rtp_codecset *cset_src, struct mgcp_rtp_codecset *cset_dst)
{
unsigned int i;
/* In case no destination connection is available (yet), or in case the destination connection exists but has
* no codecs assigned, we are forced to make a simple tentative decision:
* We just use the first codec of the source connection (conn_src) */
OSMO_ASSERT(cset_src->codecs_assigned <= MGCP_MAX_CODECS);
if (!cset_dst || cset_dst->codecs_assigned == 0) {
if (cset_src->codecs_assigned >= 1) {
cset_src->codec = &cset_src->codecs[0];
return 0;
} else
return -EINVAL;
}
/* Compare all codecs of the source connection (conn_src) to the codecs of the destination connection (conn_dst). In case
* of a match set this codec on both connections. This would be an ideal selection since no codec conversion would be
* required. */
for (i = 0; i < cset_src->codecs_assigned; i++) {
struct mgcp_rtp_codec *codec_cset_src = &cset_src->codecs[i];
struct mgcp_rtp_codec *codec_cset_dst = mgcp_codecset_find_same(cset_dst, codec_cset_src);
if (codec_cset_dst) {
/* We found the a codec that is exactly the same (same codec, same payload format etc.) on both
* sides. We now set this codec on both connections. */
cset_dst->codec = codec_cset_dst;
cset_src->codec = codec_cset_src;
return 0;
}
}
/* In case we could not find a codec that is exactly the same, let's at least try to find a codec that we are able
* to convert. */
for (i = 0; i < cset_src->codecs_assigned; i++) {
struct mgcp_rtp_codec *codec_cset_src = &cset_src->codecs[i];
struct mgcp_rtp_codec *codec_cset_dst = codecset_find_convertible(cset_dst, codec_cset_src);
if (codec_cset_dst) {
/* We found the a codec that we can convert to. Set each side to its codec. */
cset_dst->codec = codec_cset_dst;
cset_src->codec = codec_cset_src;
return 0;
}
}
if (cset_dst->codecs_assigned)
cset_dst->codec = &cset_dst->codecs[0];
else
if (!codec_dst)
return -EINVAL;
if (cset_src->codecs_assigned)
cset_src->codec = &cset_src->codecs[0];
else
return -EINVAL;
return 0;
}
/* Check if the codec has a specific AMR mode (octet-aligned or bandwith-efficient) set. */
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
{
if (codec->param_present == false)
return false;
if (!codec->param.amr_octet_aligned_present)
return false;
if (strcmp(codec->subtype_name, "AMR") != 0)
return false;
return true;
return codec_dst->payload_type;
}
/* Find the payload type number configured for a specific codec by SDP.
@@ -476,40 +444,20 @@ bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
* \param subtype_name SDP codec name without parameters (e.g. "AMR").
* \param match_nr Index for the match found, first being match_nr == 0. Iterate all matches by calling multiple times
* with incrementing match_nr.
* \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found. */
const struct mgcp_rtp_codec *mgcp_codecset_pt_find_by_subtype_name(const struct mgcp_rtp_codecset *cset,
* \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found.
*/
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
const char *subtype_name, unsigned int match_nr)
{
int i;
for (i = 0; i < cset->codecs_assigned; i++) {
if (!strcmp(cset->codecs[i].subtype_name, subtype_name)) {
for (i = 0; i < conn->end.codecs_assigned; i++) {
if (!strcmp(conn->end.codecs[i].subtype_name, subtype_name)) {
if (match_nr) {
match_nr--;
continue;
}
return &cset->codecs[i];
return &conn->end.codecs[i];
}
}
return NULL;
}
/*! Lookup a codec that is assigned to a connection by its payload type number.
* \param[in] cset related codec set.
* \param[in] payload_type number of the codec to look up.
* \returns pointer to codec struct on success, NULL on failure. */
struct mgcp_rtp_codec *mgcp_codecset_find_codec_from_pt(struct mgcp_rtp_codecset *cset, int payload_type)
{
struct mgcp_rtp_codec *codec = NULL;
size_t i;
OSMO_ASSERT(cset->codecs_assigned <= MGCP_MAX_CODECS);
for (i = 0; i < cset->codecs_assigned; i++) {
if (payload_type == cset->codecs[i].payload_type) {
codec = &cset->codecs[i];
break;
}
}
return codec;
}

View File

@@ -21,7 +21,6 @@
*
*/
#include <stdatomic.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_network.h>
#include <osmocom/mgcp/mgcp_protocol.h>
@@ -30,14 +29,12 @@
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_iuup.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/timer.h>
#include <ctype.h>
static const struct rate_ctr_group_desc rate_ctr_group_desc = {
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
.group_name_prefix = "conn_rtp",
.group_description = "rtp connection statistics",
.class_id = 1,
@@ -74,7 +71,7 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
/* ensure that the generated conn_id is unique
* for this endpoint */
if (!mgcp_endp_get_conn_rtp(endp, id_hex)) {
if (!mgcp_conn_get_rtp(endp, id_hex)) {
osmo_strlcpy(id, id_hex, MGCP_CONN_ID_MAXLEN);
return 0;
}
@@ -88,31 +85,42 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
/* Initialize rtp connection struct with default values */
static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
{
struct mgcp_rtp_end *end = &conn_rtp->end;
/* FIXME: Each new rate counter group requires an unique index. At the
* moment we generate this index using this counter, but perhaps there
* is a more concious way to assign the indexes. */
static atomic_uint rate_ctr_index = 0;
static unsigned int rate_ctr_index = 0;
conn_rtp->type = MGCP_RTP_DEFAULT;
/* Osmux specific defaults, only used if conn is later on Osmux-enabled: */
conn_rtp->osmux.state = OSMUX_STATE_DISABLED;
conn_rtp->osmux.local_cid_allocated = false;
conn_rtp->osmux.local_cid = 0;
conn_rtp->osmux.remote_cid_present = false;
conn_rtp->osmux.remote_cid = 0;
conn_rtp->osmux.cid_allocated = false;
conn_rtp->osmux.cid = 0;
/* backpointer to the generic part of the connection */
conn->u.rtp.conn = conn;
conn_rtp->ctrg = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
if (!conn_rtp->ctrg)
end->rtp.fd = -1;
end->rtcp.fd = -1;
end->rtp_port = end->rtcp_port = 0;
talloc_free(end->fmtp_extra);
end->fmtp_extra = NULL;
/* Set default values */
end->frames_per_packet = 0; /* unknown */
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
end->output_enabled = 0;
end->maximum_packet_time = -1;
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index);
if (!conn_rtp->rate_ctr_group)
return -1;
conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, IN_STREAM_ERR_TSTMP_CTR);
conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, OUT_STREAM_ERR_TSTMP_CTR);
conn_rtp->state.in_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[IN_STREAM_ERR_TSTMP_CTR];
conn_rtp->state.out_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[OUT_STREAM_ERR_TSTMP_CTR];
rate_ctr_index++;
/* Make sure codec table is reset */
mgcp_codec_reset_all(conn_rtp);
mgcp_rtp_end_init(&conn_rtp->end, conn_rtp);
return 0;
}
@@ -121,22 +129,21 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
{
if (mgcp_conn_rtp_is_osmux(conn_rtp))
conn_osmux_disable(conn_rtp);
if (mgcp_conn_rtp_is_iuup(conn_rtp))
mgcp_conn_iuup_cleanup(conn_rtp);
mgcp_rtp_end_cleanup(&conn_rtp->end);
rate_ctr_group_free(conn_rtp->ctrg);
mgcp_free_rtp_port(&conn_rtp->end);
rate_ctr_group_free(conn_rtp->rate_ctr_group);
mgcp_codec_reset_all(conn_rtp);
}
void mgcp_conn_watchdog_cb(void *data)
{
struct mgcp_conn *conn = data;
LOGPCONN(conn, DLMGCP, LOGL_ERROR, "connection timed out!\n");
mgcp_conn_free(conn);
mgcp_conn_free(conn->endp, conn->id);
}
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn)
{
int timeout = conn->endp->trunk->cfg->conn_timeout;
int timeout = conn->endp->cfg->conn_timeout;
if (!timeout)
return;
@@ -156,8 +163,8 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
struct mgcp_conn *conn;
int rc;
/* Do not allow more than the maximum number of connections */
if (endp->type->max_conns > 0 && llist_count(&endp->conns) >= endp->type->max_conns)
/* Do not allow more then two connections */
if (llist_count(&endp->conns) >= endp->type->max_conns)
return NULL;
/* Create new connection and add it to the list */
@@ -197,10 +204,62 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
return conn;
}
/*! find a connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection
* \returns pointer to allocated connection, NULL if not found */
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id)
{
struct mgcp_conn *conn;
const char *id_upper;
const char *conn_id;
if (!id || !*id)
return NULL;
/* Ignore leading zeros in needle */
while (*id == '0')
id++;
/* Use uppercase to compare identifiers, to avoid mismatches: RFC3435 2.1.3.2 "Names of
* Connections" defines the id as a hex string, so clients may return lower case hex even though
* we sent upper case hex in the CRCX response. */
id_upper = osmo_str_toupper(id);
llist_for_each_entry(conn, &endp->conns, entry) {
/* Ignore leading zeros in haystack */
for (conn_id=conn->id; *conn_id == '0'; conn_id++);
if (strcmp(conn_id, id_upper) == 0)
return conn;
}
return NULL;
}
/*! find an RTP connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection
* \returns pointer to allocated connection, NULL if not found */
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
const char *id)
{
struct mgcp_conn *conn;
conn = mgcp_conn_get(endp, id);
if (!conn)
return NULL;
if (conn->type == MGCP_CONN_TYPE_RTP)
return &conn->u.rtp;
return NULL;
}
static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
{
struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
struct rate_ctr_group *conn_stats = conn_rtp->ctrg;
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
if (all_stats == NULL || conn_stats == NULL)
return;
@@ -211,29 +270,29 @@ static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_con
OSMO_ASSERT(conn_stats->desc->num_ctr + 1 == all_stats->desc->num_ctr);
/* all other counters are [now] updated in real-time */
rate_ctr_add(rate_ctr_group_get_ctr(all_stats, IN_STREAM_ERR_TSTMP_CTR),
rate_ctr_group_get_ctr(conn_stats, IN_STREAM_ERR_TSTMP_CTR)->current);
rate_ctr_add(rate_ctr_group_get_ctr(all_stats, OUT_STREAM_ERR_TSTMP_CTR),
rate_ctr_group_get_ctr(conn_stats, OUT_STREAM_ERR_TSTMP_CTR)->current);
rate_ctr_add(&all_stats->ctr[IN_STREAM_ERR_TSTMP_CTR],
conn_stats->ctr[IN_STREAM_ERR_TSTMP_CTR].current);
rate_ctr_add(&all_stats->ctr[OUT_STREAM_ERR_TSTMP_CTR],
conn_stats->ctr[OUT_STREAM_ERR_TSTMP_CTR].current);
rate_ctr_inc(rate_ctr_group_get_ctr(all_stats, RTP_NUM_CONNECTIONS));
rate_ctr_inc(&all_stats->ctr[RTP_NUM_CONNECTIONS]);
}
/*! free a connection
* \param[in] conn the conn to free. May be NULL.
*/
void mgcp_conn_free(struct mgcp_conn *conn)
/*! free a connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection */
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
{
struct mgcp_conn_rtp *conn_rtp;
struct mgcp_conn *conn;
conn = mgcp_conn_get(endp, id);
if (!conn)
return;
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
conn_rtp = mgcp_conn_get_conn_rtp(conn);
aggregate_rtp_conn_stats(conn->endp, conn_rtp);
mgcp_rtp_conn_cleanup(conn_rtp);
aggregate_rtp_conn_stats(endp, &conn->u.rtp);
mgcp_rtp_conn_cleanup(&conn->u.rtp);
break;
default:
/* NOTE: This should never be called with an
@@ -243,40 +302,40 @@ void mgcp_conn_free(struct mgcp_conn *conn)
}
osmo_timer_del(&conn->watchdog);
mgcp_endp_remove_conn(conn->endp, conn);
mgcp_endp_remove_conn(endp, conn);
/* WARN: endp may have be freed after call to mgcp_endp_remove_conn */
talloc_free(conn);
}
/*! Parse connection mode.
* \param[in] conn Connection whose mode is being set
* \param[in] mode Mode to set
* \returns 0 on success, -1 on error */
int mgcp_conn_set_mode(struct mgcp_conn *conn, enum mgcp_connection_mode mode)
/*! free oldest connection in the list.
* \param[in] endp associated endpoint */
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
{
OSMO_ASSERT(conn);
if (mode == MGCP_CONN_NONE) {
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"missing connection mode\n");
return -1;
}
conn->mode = mode;
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
mgcp_cmode_name(mode), conn->mode);
struct mgcp_conn *conn;
/* Special handling for RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
conn_rtp->end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n",
conn_rtp->end.output_enabled);
if (llist_empty(&endp->conns))
return;
conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
if (!conn)
return;
mgcp_conn_free(endp, conn->id);
}
/*! free all connections at once.
* \param[in] endp associated endpoint */
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
{
struct mgcp_conn *conn;
struct mgcp_conn *conn_tmp;
/* Drop all items in the list */
llist_for_each_entry_safe(conn, conn_tmp, &endp->conns, entry) {
mgcp_conn_free(endp, conn->id);
}
/* The VTY might change the connection mode at any time, so we have
* to hold a copy of the original connection mode */
conn->mode_orig = conn->mode;
return 0;
return;
}
/*! dump basic connection information to human readable string.
@@ -286,39 +345,29 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
{
static char str[sizeof(conn->name)+sizeof(conn->id)+256];
char ipbuf[INET6_ADDRSTRLEN];
struct osmo_strbuf sb = { .buf = str, .len = sizeof(str) };
struct mgcp_conn_rtp *conn_rtp;
if (!conn)
return "NULL";
if (!conn) {
snprintf(str, sizeof(str), "(null connection)");
return str;
}
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
conn_rtp = mgcp_conn_get_conn_rtp(conn);
OSMO_STRBUF_PRINTF(sb, "(%s/%s C:%s r=%s:%u<->l=%s:%u",
conn->name,
mgcp_conn_rtp_type_name(conn->type),
conn->id,
osmo_sockaddr_ntop(&conn_rtp->end.addr.u.sa, ipbuf) ? : "NULL",
osmo_sockaddr_port(&conn_rtp->end.addr.u.sa),
conn_rtp->end.local_addr ? : "NULL",
conn_rtp->end.local_port);
switch (conn_rtp->type) {
case MGCP_RTP_OSMUX:
OSMO_STRBUF_PRINTF(sb, " CID=%u", conn_rtp->osmux.local_cid);
break;
default:
break;
}
OSMO_STRBUF_PRINTF(sb, ")");
/* Dump RTP connection */
snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
"rtp:%u rtcp:%u)",
conn->name,
conn->id,
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
ntohs(conn->u.rtp.end.rtp_port),
ntohs(conn->u.rtp.end.rtcp_port));
break;
default:
/* Should not happen, we should be able to dump
* every possible connection type. */
return "(unknown connection type)";
snprintf(str, sizeof(str), "(unknown connection type)");
break;
}
return str;
@@ -346,9 +395,12 @@ struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
return NULL;
}
const struct value_string mgcp_conn_rtp_type_names[] = {
{ MGCP_RTP_DEFAULT, "rtp" },
{ MGCP_RTP_OSMUX, "osmux" },
{ MGCP_RTP_IUUP, "iuup" },
{}
};
/*! get oldest connection in the list.
* \param[in] endp associated endpoint */
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp)
{
if (llist_empty(&endp->conns))
return NULL;
return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
}

View File

@@ -0,0 +1,36 @@
/*
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/ctrl/control_if.h>
#include <osmocom/mgcp/mgcp.h>
static int mgw_ctrl_node_lookup(void *data, vector vline, int *node_type,
void **node_data, int *i)
{
return 0;
}
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
const char *bind_addr, uint16_t port)
{
return ctrl_interface_setup_dynip2(cfg, bind_addr, port, mgw_ctrl_node_lookup,
_LAST_CTRL_NODE);
}

View File

@@ -40,7 +40,6 @@
#include <osmocom/mgcp/debug.h>
#include <osmocom/mgcp/mgcp_e1.h>
#include <osmocom/codec/codec.h>
#include <osmocom/gsm/rtp_extensions.h>
#define DEBUG_BITS_MAX 80
#define DEBUG_BYTES_MAX 40
@@ -57,6 +56,135 @@ static const struct e1inp_line_ops dummy_e1_line_ops = {
.sign_link = NULL,
};
/* EFR idle frame */
static const ubit_t idle_tf_efr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1,
};
/* FR idle frame */
static const ubit_t idle_tf_fr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1,
};
/* Idle speech frame, see also GSM 08.60, chapter 3.4 */
static const ubit_t idle_tf_spch[] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1,
};
/* If the RTP transmission has dropouts for some reason the I.460 TX-Queue may
* run empty. In order to make sure that the TRAU frame transmission continues
* we generate idle TRAU frames here. */
@@ -64,63 +192,37 @@ static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_dat
{
struct mgcp_endpoint *endp = user_data;
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
struct msgb *msg = msgb_alloc_c(endp->trunk, E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame");
const uint8_t *dummy_fill_pl;
unsigned dummy_fill_pl_len;
struct osmo_trau_frame tf;
int rc;
struct msgb *msg = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame");
uint8_t *ptr;
const uint8_t *ptr_ft;
enum osmo_trau_frame_type ft;
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_MUX_EMPTY_CTR));
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_MUX_EMPTY_CTR]);
/* choose dummy fill frame payload based on current codec */
switch (endp->e1.trau_rtp_st->type) {
/* Choose an appropiate idle frame type */
ft = endp->e1.trau_rtp_st->type;
switch (ft) {
case OSMO_TRAU16_FT_FR:
dummy_fill_pl = osmo_gsm611_silence_frame;
dummy_fill_pl_len = GSM_FR_BYTES;
ptr_ft = idle_tf_fr;
break;
case OSMO_TRAU16_FT_EFR:
dummy_fill_pl = osmo_gsm660_homing_frame;
dummy_fill_pl_len = GSM_EFR_BYTES;
break;
case OSMO_TRAU16_FT_HR:
case OSMO_TRAU8_SPEECH:
dummy_fill_pl = osmo_gsm620_silence_frame;
dummy_fill_pl_len = GSM_HR_BYTES;
ptr_ft = idle_tf_efr;
break;
default:
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-I.460-IDLE-TX: unsupported frame type\n");
goto skip;
/* FIXME: What about 8k subslots and AMR frames? */
ptr_ft = idle_tf_spch;
}
/* turn it into a TRAU-DL frame */
memset(&tf, 0, sizeof(tf));
tf.dir = OSMO_TRAU_DIR_DL;
rc = osmo_rtp2trau(&tf, dummy_fill_pl, dummy_fill_pl_len, endp->e1.trau_rtp_st);
if (rc < 0) {
LOGPENDP(endp, DE1, LOGL_ERROR,
"E1-I.460-IDLE-TX: error converting dummy fill frame!\n");
goto skip;
}
rc = osmo_trau_frame_encode(msgb_data(msg), msg->data_len, &tf);
if (rc < 0) {
LOGPENDP(endp, DE1, LOGL_ERROR,
"E1-I.460-IDLE-TX: error encoding dummy fill frame!\n");
goto skip;
}
msgb_put(msg, rc);
/* enqueue it into the I.460 multiplexer */
/* Put the replacement into a message buffer and enqueue it into the
* I.460 multiplexer */
ptr = msgb_put(msg, E1_TRAU_BITS);
memcpy(ptr, ptr_ft, E1_TRAU_BITS);
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-IDLE-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg),
osmo_ubit_dump(msgb_data(msg), msgb_length(msg) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg)));
osmo_i460_mux_enqueue(endp->e1.schan, msg);
return;
skip:
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_TX_FAIL_CTR));
msgb_free(msg);
}
/* called by I.460 de-multiplexer; feed output of I.460 demux into TRAU frame sync */
/* called by I.460 de-multeiplexer; feed output of I.460 demux into TRAU frame sync */
static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits,
unsigned int num_bits)
{
@@ -136,9 +238,9 @@ static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_da
* (the resulting frame will be prepended with an all-zero (12-byte) rtp header) */
static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
{
struct msgb *msg = msgb_alloc(RTP_BUF_SIZE, "RTP-rx-from-E1");
unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
struct mgcp_endpoint *endp = user_data;
struct msgb *msg = msgb_alloc_c(endp->trunk, RTP_BUF_SIZE, "RTP-rx-from-E1");
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
struct mgcp_conn *conn_dst;
struct osmo_trau_frame fr;
@@ -178,7 +280,6 @@ static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int
/* Convert decoded trau frame to RTP frame */
struct osmo_trau2rtp_state t2rs = {
.type = fr.type,
.rtp_extensions = endp->e1.rtp_extensions,
};
rc = osmo_trau2rtp(msgb_data(msg) + rtp_hdr_len, msg->data_len - rtp_hdr_len, &fr, &t2rs);
if (rc <= 0) {
@@ -200,17 +301,18 @@ static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int
mgcp_send(endp, 1, NULL, msg, &conn_dst->u.rtp, &conn_dst->u.rtp);
msgb_free(msg);
return;
skip:
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_RX_FAIL_CTR));
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_RX_FAIL_CTR]);
msgb_free(msg);
return;
}
/* handle outgoing E1 traffic */
static void e1_send_ts_frame(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
/* Function to handle outgoing E1 traffic */
static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
{
struct msgb *msg = msgb_alloc_c(trunk, E1_TS_BYTES, "E1-TX-timeslot-bytes");
struct msgb *msg = msgb_alloc(E1_TS_BYTES, "E1-TX-timeslot-bytes");
uint8_t *ptr;
/* Get E1 frame from I.460 multiplexer */
@@ -223,7 +325,7 @@ static void e1_send_ts_frame(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
msgb_length(msg) > DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
#endif
/* Hand data over to the E1 stack */
e1inp_ts_send_raw(ts, msg);
msgb_enqueue(&ts->raw.tx_queue, msg);
return;
}
@@ -235,8 +337,7 @@ static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
/* Find associated trunk */
trunk = mgcp_trunk_by_line_num(cfg, ts->line->num);
if (!trunk) {
LOGP(DE1, LOGL_ERROR, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
msgb_free(msg);
LOGP(DE1, LOGL_DEBUG, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
return;
}
@@ -257,17 +358,16 @@ static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
osmo_i460_demux_in(&trunk->e1.i460_ts[ts->num - 1], msgb_data(msg), msgb_length(msg));
/* Trigger sending of pending E1 traffic */
e1_send_ts_frame(ts, trunk);
/* e1inp_rx_ts(), the caller of this callback does not free() msgb. */
msgb_free(msg);
e1_send(ts, trunk);
}
static int e1_open(struct mgcp_trunk *trunk, uint8_t ts_nr)
static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
{
/*! One E1 timeslot may serve multiple I.460 subslots. The timeslot is opened as soon as an I.460 subslot is
* opened and will stay open until the last I.460 subslot is closed (see e1_close below). This function must
* be called any time a new I.460 subslot is opened in order to maintain constancy of the ts_usecount counter. */
/*! Each timeslot needs only to be configured once. The Timeslot then
* stays open and permanently receives data. It is then up to the
* I.460 demultiplexer to add/remove subchannels as needed. It is
* allowed to call this function multiple times since we check if the
* timeslot is already configured. */
struct e1inp_line *e1_line;
int rc;
@@ -275,17 +375,15 @@ static int e1_open(struct mgcp_trunk *trunk, uint8_t ts_nr)
OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
cfg = trunk->cfg;
if (trunk->e1.ts_usecount[ts_nr - 1] > 0) {
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u already set up and in use by %u subslot(s), using it as it is...\n",
ts_nr, trunk->e1.ts_usecount[ts_nr - 1]);
trunk->e1.ts_usecount[ts_nr - 1]++;
if (trunk->e1.ts_in_use[ts_nr - 1]) {
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u already set up, skipping...\n", ts_nr);
return 0;
}
/* Find E1 line */
/* Get E1 line */
e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
if (!e1_line) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n",
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "no such E1 line %u - check VTY config!\n",
trunk->e1.vty_line_nr);
return -EINVAL;
}
@@ -299,61 +397,12 @@ static int e1_open(struct mgcp_trunk *trunk, uint8_t ts_nr)
}
rc = e1inp_line_update(e1_line);
if (rc < 0) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 line %u.\n", ts_nr);
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 timeslot %u.\n", ts_nr);
return -EINVAL;
}
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u set up successfully.\n", ts_nr);
trunk->e1.ts_usecount[ts_nr - 1]++;
OSMO_ASSERT(trunk->e1.ts_usecount[ts_nr - 1] == 1);
return 0;
}
static int e1_close(struct mgcp_trunk *trunk, uint8_t ts_nr)
{
/* See also comment above (e1_open). This function must be called any time an I.460 subslot is closed */
struct e1inp_line *e1_line;
int rc;
OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
cfg = trunk->cfg;
if (trunk->e1.ts_usecount[ts_nr - 1] > 1) {
trunk->e1.ts_usecount[ts_nr - 1]--;
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u still in use by %u other subslot(s), leaving it open...\n",
ts_nr, trunk->e1.ts_usecount[ts_nr - 1]);
return 0;
} else if (trunk->e1.ts_usecount[ts_nr - 1] == 0) {
/* This should not be as it means we close the timeslot too often. */
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "E1 timeslot %u already closed, leaving it as it is...\n", ts_nr);
return -EINVAL;
}
/* Find E1 line */
e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
if (!e1_line) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n",
trunk->e1.vty_line_nr);
return -EINVAL;
}
/* Release E1 timeslot */
rc = e1inp_ts_config_none(&e1_line->ts[ts_nr - 1], e1_line);
if (rc < 0) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to disable E1 timeslot %u.\n", ts_nr);
return -EINVAL;
}
rc = e1inp_line_update(e1_line);
if (rc < 0) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 line %u.\n", trunk->e1.vty_line_nr);
return -EINVAL;
}
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u closed.\n", ts_nr);
trunk->e1.ts_usecount[ts_nr - 1]--;
OSMO_ASSERT(trunk->e1.ts_usecount[ts_nr - 1] == 0);
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u set up successfully.\n", ts_nr);
trunk->e1.ts_in_use[ts_nr - 1] = true;
return 0;
}
@@ -364,21 +413,11 @@ static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name,
{
if (strcmp(sdp_subtype_name, "GSM") == 0)
return OSMO_TRAU16_FT_FR;
if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
return OSMO_TRAU16_FT_EFR;
if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0) {
if (i460_rate == OSMO_I460_RATE_16k)
return OSMO_TRAU16_FT_HR;
if (i460_rate == OSMO_I460_RATE_8k)
return OSMO_TRAU8_SPEECH;
LOGPENDP(endp, DE1, LOGL_ERROR,
"E1-TRAU-TX: unsupported or illegal I.460 rate for HR\n");
return OSMO_TRAU_FT_NONE;
}
if (strcmp(sdp_subtype_name, "AMR") == 0) {
else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
return OSMO_TRAU16_FT_HR;
else if (strcmp(sdp_subtype_name, "AMR") == 0) {
if (i460_rate == OSMO_I460_RATE_8k) {
switch (amr_ft) {
case AMR_4_75:
@@ -396,11 +435,11 @@ static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name,
}
}
return OSMO_TRAU16_FT_AMR;
} else {
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
sdp_subtype_name);
return OSMO_TRAU_FT_NONE;
}
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
sdp_subtype_name);
return OSMO_TRAU_FT_NONE;
}
/* Determine a suitable TRAU frame type for a given codec */
@@ -409,21 +448,11 @@ static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name
{
if (strcmp(sdp_subtype_name, "GSM") == 0)
return OSMO_TRAU_SYNCP_16_FR_EFR;
if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
return OSMO_TRAU_SYNCP_16_FR_EFR;
if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0) {
if (i460_rate == OSMO_I460_RATE_16k)
return OSMO_TRAU_SYNCP_16_FR_EFR;
if (i460_rate == OSMO_I460_RATE_8k)
return OSMO_TRAU_SYNCP_8_HR;
LOGPENDP(endp, DE1, LOGL_ERROR,
"E1-TRAU-TX: unsupported or illegal I.460 rate for HR\n");
return OSMO_TRAU_SYNCP_16_FR_EFR;
}
if (strcmp(sdp_subtype_name, "AMR") == 0) {
else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
return OSMO_TRAU_SYNCP_8_HR;
else if (strcmp(sdp_subtype_name, "AMR") == 0) {
if (i460_rate == OSMO_I460_RATE_8k) {
switch (amr_ft) {
case AMR_4_75:
@@ -441,11 +470,11 @@ static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name
}
}
return OSMO_TRAU_SYNCP_16_FR_EFR;
} else {
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
sdp_subtype_name);
return OSMO_TRAU_SYNCP_16_FR_EFR;
}
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
sdp_subtype_name);
return OSMO_TRAU_SYNCP_16_FR_EFR;
}
/* Find out if a given TRAU frame type is AMR */
@@ -463,7 +492,7 @@ static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
}
}
/*! Equip E1 endpoint with I.460 mux and E1 timeslot resources.
/* !Equip E1 endpoint with I.460 mux resources.
* \param[in] endp endpoint to equip
* \param[in] ts E1 timeslot number.
* \param[in] ss E1 subslot number.
@@ -484,7 +513,7 @@ int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8
endp->e1.last_amr_ft = AMR_4_75;
/* Set up E1 line / timeslot */
rc = e1_open(endp->trunk, ts);
rc = e1_init(endp->trunk, ts);
if (rc != 0)
return -EINVAL;
@@ -516,7 +545,7 @@ int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8
endp->e1.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb;
endp->e1.scd.mux.user_data = endp;
LOGPENDP(endp, DE1, LOGL_INFO, "adding I.460 subchannel: ts=%u, bit_offset=%u, rate=%uk, num_bits=%lu\n", ts,
LOGPENDP(endp, DE1, LOGL_DEBUG, "adding I.460 subchannel: ts=%u, bit_offset=%u, rate=%uk, num_bits=%lu\n", ts,
offs, e1_rates[ss], endp->e1.scd.demux.num_bits);
endp->e1.schan = osmo_i460_subchan_add(endp, &endp->trunk->e1.i460_ts[ts - 1], &endp->e1.scd);
if (!endp->e1.schan) {
@@ -546,20 +575,18 @@ int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8
/*! Update E1 related parameters (codec and sync pattern).
* \param[in] endp endpoint to update. */
int mgcp_e1_endp_update(struct mgcp_endpoint *endp)
void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
{
struct mgcp_conn *conn;
struct mgcp_conn_rtp *conn_rtp;
struct mgcp_rtp_codec *codec;
enum osmo_tray_sync_pat_id sync_pat_id;
/* In order to determine the codec, find the oldest connection on
* the endpoint and use its codec information. Normally on an E1
* endpoint no more than one connection should exist. */
conn = mgcp_endp_get_conn_oldest(endp);
conn = mgcp_conn_get_oldest(endp);
OSMO_ASSERT(conn);
conn_rtp = mgcp_conn_get_conn_rtp(conn);
codec = conn_rtp->end.cset.codec;
codec = conn->u.rtp.end.codec;
OSMO_ASSERT(codec);
/* Update codec information */
@@ -567,29 +594,15 @@ int mgcp_e1_endp_update(struct mgcp_endpoint *endp)
determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
endp->e1.last_codec = codec;
/* possible RTP extensions, codec-associated */
endp->e1.rtp_extensions = 0;
if (codec->param_present && codec->param.fr_efr_twts001)
endp->e1.rtp_extensions |= OSMO_RTP_EXT_TWTS001;
if (codec->param_present && codec->param.hr_twts002)
endp->e1.rtp_extensions |= OSMO_RTP_EXT_TWTS002;
/* Update sync pattern */
sync_pat_id = determine_trau_sync_pat(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
osmo_trau_sync_set_pat(endp->e1.trau_sync_fi, sync_pat_id);
return 0;
}
/*! Remove E1 resources from endpoint
* \param[in] endp endpoint to release.
* \param[in] ts E1 timeslot number. */
void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts)
* \param[in] endp endpoint to release. */
void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
{
/* Guard against multiple calls. In case we don't see a subchannel anymore we can safely assume that all work
* is done. */
if (!(endp->e1.schan || endp->e1.trau_rtp_st || endp->e1.trau_sync_fi))
return;
LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
if (endp->e1.schan)
@@ -598,10 +611,8 @@ void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts)
talloc_free(endp->e1.trau_rtp_st);
if (endp->e1.trau_sync_fi)
osmo_fsm_inst_term(endp->e1.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL);
memset(&endp->e1, 0, sizeof(endp->e1));
/* Close E1 timeslot */
e1_close(endp->trunk, ts);
memset(&endp->e1, 0, sizeof(endp->e1));
}
/*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit.
@@ -611,7 +622,7 @@ void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts)
* \returns 0 on success, -1 on ERROR. */
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg)
{
struct msgb *msg_tf = msgb_alloc_c(endp->trunk, E1_TRAU_BITS_MSGB, "E1-I.460-TX-TRAU-frame");
struct msgb *msg_tf = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-TX-TRAU-frame");
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
struct osmo_trau_frame tf;
@@ -668,7 +679,7 @@ int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, s
return 0;
skip:
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_TX_FAIL_CTR));
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_TX_FAIL_CTR]);
msgb_free(msg_tf);
return -1;
}

View File

@@ -24,13 +24,11 @@
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_iuup.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/mgcp/mgcp_e1.h>
#include <osmocom/core/stat_item.h>
#define E1_RATE_MAX 64
#define E1_OFFS_MAX 8
@@ -39,6 +37,7 @@
const struct mgcp_endpoint_typeset ep_typeset = {
/* Specify endpoint properties for RTP endpoint */
.rtp = {
.max_conns = 2,
.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
.cleanup_cb = mgcp_cleanup_rtp_bridge_cb,
},
@@ -59,7 +58,7 @@ static char *gen_virtual_epname(void *ctx, const char *domain,
}
/* Generate E1 endpoint name from given numeric parameters */
static char *gen_e1_epname(const void *ctx, const char *domain, unsigned int trunk_nr,
static char *gen_e1_epname(void *ctx, const char *domain, uint8_t trunk_nr,
uint8_t ts_nr, uint8_t ss_nr)
{
unsigned int rate;
@@ -89,6 +88,7 @@ struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
return NULL;
INIT_LLIST_HEAD(&endp->conns);
endp->cfg = trunk->cfg;
endp->trunk = trunk;
switch (trunk->trunk_type) {
@@ -110,6 +110,31 @@ struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
return endp;
}
/*! release endpoint, all open connections are closed.
* \param[in] endp endpoint to release */
void mgcp_endp_release(struct mgcp_endpoint *endp)
{
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
/* Normally this function should only be called when
* all connections have been removed already. In case
* that there are still connections open (e.g. when
* RSIP is executed), free them all at once. */
mgcp_conn_free_all(endp);
/* Reset endpoint parameters and states */
talloc_free(endp->callid);
endp->callid = NULL;
talloc_free(endp->local_options.string);
endp->local_options.string = NULL;
talloc_free(endp->local_options.codec);
endp->local_options.codec = NULL;
endp->wildcarded_req = false;
if (endp->trunk->trunk_type == MGCP_TRUNK_E1)
mgcp_e1_endp_release(endp);
}
/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
* "ds/e1-") and write the epname without the prefix back to the memory
* pointed at by epname. (per trunk the prefix is the same for all endpoints,
@@ -157,15 +182,12 @@ static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
}
}
/*! Convert all characters in epname to lowercase and strip trunk prefix and
/* Convert all characters in epname to lowercase and strip trunk prefix and
* endpoint name suffix (domain name) from epname. The result is written to
* to the memory pointed at by epname_stripped. The expected size of the
* result is either equal or lower then the length of the input string
* (epname)
* \param[out] epname_stripped pointer to store the stripped ep name.
* \param[in] epname endpoint name to lookup.
* \param[in] trunk where the endpoint is located. */
void mgcp_endp_strip_name(char *epname_stripped, const char *epname,
* (epname) */
static void strip_epname(char *epname_stripped, const char *epname,
const struct mgcp_trunk *trunk)
{
osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname);
@@ -192,11 +214,9 @@ static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
return NULL;
}
/*! Find an endpoint of a trunk specified by its name.
* \param[in] epname endpoint name to check.
* \param[in] trunk mgcp_trunk that might have this endpoint.
* \returns NULL if no ep found, else endpoint. */
struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
/* Find an endpoint specified by its name. If the endpoint can not be found,
* return NULL */
static struct mgcp_endpoint *find_specific_endpoint(const char *epname,
const struct mgcp_trunk *trunk)
{
char epname_stripped[MGCP_ENDPOINT_MAXLEN];
@@ -205,11 +225,11 @@ struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
unsigned int i;
/* Strip irrelevant information from the endpoint name */
mgcp_endp_strip_name(epname_stripped, epname, trunk);
strip_epname(epname_stripped, epname, trunk);
for (i = 0; i < trunk->number_endpoints; i++) {
endp = trunk->endpoints[i];
mgcp_endp_strip_name(epname_stripped_endp, endp->name, trunk);
strip_epname(epname_stripped_endp, endp->name, trunk);
if (strcmp(epname_stripped_endp, epname_stripped) == 0)
return endp;
}
@@ -217,29 +237,6 @@ struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
return NULL;
}
/*! Check if the given epname refers to a wildcarded request or to a specific
* endpoint.
* \param[in] epname endpoint name to check
* \returns true if epname refers to wildcarded request, else false. */
bool mgcp_endp_is_wildcarded(const char *epname)
{
if (strstr(epname, "*"))
return true;
return false;
}
/*! Check if the given epname refers to a "null" endpoint.
* \param[in] epname endpoint name to check
* \returns true if epname refers to "null"" endpoint, else false. */
bool mgcp_endp_is_null(const char *epname)
{
if (strncasecmp(epname, "null@", 5) == 0)
return true;
return false;
}
/*! Find an endpoint by its name on a specified trunk.
* \param[out] cause pointer to store cause code, can be NULL.
* \param[in] epname endpoint name to lookup.
@@ -256,12 +253,13 @@ struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
/* At the moment we only support a primitive ('*'-only) method of
* wildcarded endpoint searches that picks the next free endpoint on
* a trunk. */
if (mgcp_endp_is_wildcarded(epname)) {
if (strstr(epname, "*")) {
endp = find_free_endpoint(trunk);
if (endp) {
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
"(trunk:%d) found free endpoint: %s\n",
trunk->trunk_nr, endp->name);
endp->wildcarded_req = true;
return endp;
}
@@ -275,11 +273,12 @@ struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
/* Find an endpoint by its name (if wildcarded request is not
* applicable) */
endp = mgcp_endp_find_specific(epname, trunk);
endp = find_specific_endpoint(epname, trunk);
if (endp) {
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
"(trunk:%d) found endpoint: %s\n",
trunk->trunk_nr, endp->name);
endp->wildcarded_req = false;
return endp;
}
@@ -292,6 +291,32 @@ struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
return NULL;
}
/* Check if the domain name, which is supplied with the endpoint name
* matches the configuration. */
static int check_domain_name(const char *epname, struct mgcp_config *cfg)
{
char *domain_to_check;
domain_to_check = strstr(epname, "@");
if (!domain_to_check) {
LOGP(DLMGCP, LOGL_ERROR, "missing domain name in endpoint name \"%s\", expecting \"%s\"\n",
epname, cfg->domain);
return -EINVAL;
}
/* Accept any domain if configured as "*" */
if (!strcmp(cfg->domain, "*"))
return 0;
if (strcmp(domain_to_check+1, cfg->domain) != 0) {
LOGP(DLMGCP, LOGL_ERROR, "wrong domain name in endpoint name \"%s\", expecting \"%s\"\n",
epname, cfg->domain);
return -EINVAL;
}
return 0;
}
/*! Find an endpoint by its name, search at all trunks.
* \param[out] cause, pointer to store cause code, can be NULL.
* \param[in] epname, must contain trunk prefix.
@@ -315,6 +340,10 @@ struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
if (!trunk)
return NULL;
/* All endpoint names require a domain as suffix */
if (check_domain_name(epname, cfg))
return NULL;
/* Identify the endpoint on the trunk */
endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
if (!endp) {
@@ -447,7 +476,7 @@ static uint8_t e1_ss_nr_from_epname(const char *epname)
/* Check if the selected E1 endpoint is avalable, which means that none of
* the overlapping endpoints are currently serving a call. (if the system
* is properly configured such a situation should never ocurr!) */
static bool endp_avail_e1(const struct mgcp_endpoint *endp)
static bool endp_avail_e1(struct mgcp_endpoint *endp)
{
/* The following map shows the overlapping of the subslots and their
* respective rates. The numbers on the right running from top to bottom
@@ -526,7 +555,7 @@ static bool endp_avail_e1(const struct mgcp_endpoint *endp)
epname_check = gen_e1_epname(endp, endp->trunk->cfg->domain,
endp->trunk->trunk_nr, ts_nr,
interlock[i]);
endp_check = mgcp_endp_find_specific(epname_check, endp->trunk);
endp_check = find_specific_endpoint(epname_check, endp->trunk);
if (!endp_check) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"cannot check endpoint availability, overlapping endpoint:%s not found!\n",
@@ -553,7 +582,7 @@ static bool endp_avail_e1(const struct mgcp_endpoint *endp)
/*! check if an endpoint is available for any kind of operation.
* \param[in] endp endpoint to check.
* \returns true if endpoint is avalable, false it is blocked for any reason. */
bool mgcp_endp_avail(const struct mgcp_endpoint *endp)
bool mgcp_endp_avail(struct mgcp_endpoint *endp)
{
switch (endp->trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
@@ -570,24 +599,6 @@ bool mgcp_endp_avail(const struct mgcp_endpoint *endp)
return false;
}
/*! Get number of conns in an endpoint.
* \param[in] endp endpoint to check.
* \returns Number of connections present in the endpoint. */
unsigned int mgcp_endp_num_conns(const struct mgcp_endpoint *endp)
{
return llist_count(&endp->conns);
}
/*! check if an endpoint can in current state allocate new conns.
* \param[in] endp endpoint to check.
* \returns true if more connections can be allowed on endpoint, false if it is already busy. */
bool mgcp_endp_is_full(const struct mgcp_endpoint *endp)
{
if (endp->type->max_conns == 0)
return false;
return mgcp_endp_num_conns(endp) >= endp->type->max_conns;
}
/*! claim endpoint, sets callid and activates endpoint, should be called at the
* beginning of the CRCX procedure when it is clear that a new call should be
* created.
@@ -608,8 +619,6 @@ int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid)
* connection ids) */
endp->callid = talloc_strdup(endp, callid);
OSMO_ASSERT(endp->callid);
osmo_stat_item_inc(osmo_stat_item_group_get_item(endp->trunk->stats.common,
TRUNK_STAT_ENDPOINTS_USED), 1);
/* Allocate resources */
switch (endp->trunk->trunk_type) {
@@ -641,85 +650,20 @@ int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid)
/*! update endpoint, updates internal endpoint specific data, should be
* after when MDCX or CRCX has been executed successuflly.
* \param[in] endp endpoint to update.
* \returns zero on success, mgcp negative error on failure. */
static int mgcp_endp_update_virtual(struct mgcp_endpoint *endp, struct mgcp_conn *conn, enum mgcp_verb verb)
* \param[in] endp endpoint to update. */
void mgcp_endp_update(struct mgcp_endpoint *endp)
{
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->type == MGCP_CONN_TYPE_RTP);
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
switch (conn_rtp->type) {
case MGCP_RTP_DEFAULT:
break;
case MGCP_RTP_OSMUX:
if (conn_osmux_event_rx_crcx_mdcx(conn_rtp) < 0) {
LOGPCONN(conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n");
return -500;
}
break;
case MGCP_RTP_IUUP:
return mgcp_conn_iuup_event_rx_crcx_mdcx(conn_rtp);
default:
return -523;
}
return 0;
}
/*! update endpoint, updates internal endpoint specific data, should be
* after when MDCX or CRCX has been executed successuflly.
* \param[in] endp endpoint to update.
* \returns zero on success, mgcp negative error on failure. */
int mgcp_endp_update(struct mgcp_endpoint *endp, struct mgcp_conn *conn, enum mgcp_verb verb)
{
OSMO_ASSERT(conn);
struct mgcp_trunk *trunk = endp->trunk;
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
char new_local_addr[INET6_ADDRSTRLEN];
/* CRCX: Find a local address for conn based on policy and initial SDP remote
* information, then find a free port for it.
* MDCX: msg may have provided a new remote address, which means we may need
* to update our announced IP addr and re-bind our local end. This can
* happen for instance if MGW initially provided an IPv4 during CRCX
* ACK, and now MDCX tells us the remote has an IPv6 address.
*/
if (mgcp_get_local_addr(new_local_addr, conn_rtp) < 0)
goto fail_bind_port_ret;
if (strcmp(new_local_addr, conn_rtp->end.local_addr)) {
osmo_strlcpy(conn_rtp->end.local_addr, new_local_addr, sizeof(conn_rtp->end.local_addr));
mgcp_rtp_end_free_port(&conn_rtp->end);
if (mgcp_trunk_allocate_conn_rtp_ports(trunk, conn_rtp) != 0)
goto fail_bind_port_ret;
}
/* Allocate resources */
switch (endp->trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
return mgcp_endp_update_virtual(endp, conn, verb);
/* No updating initaliziation required for virtual endpoints. */
break;
case MGCP_TRUNK_E1:
return mgcp_e1_endp_update(endp);
mgcp_e1_endp_update(endp);
break;
default:
OSMO_ASSERT(false);
}
return 0;
fail_bind_port_ret:
switch (verb) {
case MGCP_VERB_CRCX:
rate_ctr_inc(rate_ctr_group_get_ctr(trunk->ratectr.mgcp_crcx_ctr_group,
MGCP_CRCX_FAIL_BIND_PORT));
break;
case MGCP_VERB_MDCX:
rate_ctr_inc(rate_ctr_group_get_ctr(trunk->ratectr.mgcp_mdcx_ctr_group,
MGCP_MDCX_FAIL_BIND_PORT));
break;
default:
break;
}
return -500;
}
void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
@@ -738,140 +682,3 @@ void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
if (llist_empty(&endp->conns))
mgcp_endp_release(endp);
}
/*! free oldest connection in the list.
* \param[in] endp associated endpoint */
void mgcp_endp_free_conn_oldest(struct mgcp_endpoint *endp)
{
struct mgcp_conn *conn;
if (llist_empty(&endp->conns))
return;
conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
mgcp_conn_free(conn);
}
/*! free all connections at once.
* \param[in] endp associated endpoint */
#if defined(__has_attribute)
#if __has_attribute(no_sanitize)
__attribute__((no_sanitize("undefined"))) /* ubsan detects a misaligned load */
#endif
#endif
void mgcp_endp_free_conn_all(struct mgcp_endpoint *endp)
{
struct mgcp_conn *conn;
/* Drop all items in the list, might be consecutive! */
while ((conn = llist_first_entry_or_null(&endp->conns, struct mgcp_conn, entry)))
mgcp_conn_free(conn);
}
/*! find a connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection
* \returns pointer to allocated connection, NULL if not found */
struct mgcp_conn *mgcp_endp_get_conn(struct mgcp_endpoint *endp, const char *id)
{
struct mgcp_conn *conn;
const char *id_upper;
const char *conn_id;
if (!id || !*id)
return NULL;
/* Ignore leading zeros in needle */
while (*id == '0')
id++;
/* Use uppercase to compare identifiers, to avoid mismatches: RFC3435 2.1.3.2 "Names of
* Connections" defines the id as a hex string, so clients may return lower case hex even though
* we sent upper case hex in the CRCX response. */
id_upper = osmo_str_toupper(id);
llist_for_each_entry(conn, &endp->conns, entry) {
/* Ignore leading zeros in haystack */
for (conn_id = conn->id; *conn_id == '0'; conn_id++);
if (strcmp(conn_id, id_upper) == 0)
return conn;
}
return NULL;
}
/*! get oldest connection in the list.
* \param[in] endp associated endpoint */
struct mgcp_conn *mgcp_endp_get_conn_oldest(struct mgcp_endpoint *endp)
{
if (llist_empty(&endp->conns))
return NULL;
return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
}
/*! find an RTP connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection
* \returns pointer to allocated connection, NULL if not found */
struct mgcp_conn_rtp *mgcp_endp_get_conn_rtp(struct mgcp_endpoint *endp,
const char *id)
{
struct mgcp_conn *conn;
conn = mgcp_endp_get_conn(endp, id);
if (!conn)
return NULL;
if (conn->type == MGCP_CONN_TYPE_RTP)
return mgcp_conn_get_conn_rtp(conn);
return NULL;
}
/* Helps assigning a new lco structure, since "codec" is talloc allocated. */
void mgcp_endp_update_lco(struct mgcp_endpoint *endp, const struct mgcp_lco *lco)
{
/* First free old talloc allocated codec string: */
talloc_free(endp->local_options.codec);
endp->local_options.codec = NULL;
if (lco) {
endp->local_options = *lco;
if (lco->codec)
endp->local_options.codec = talloc_strdup(endp, lco->codec);
} else {
endp->local_options = (struct mgcp_lco){0};
}
}
/*! release endpoint, all open connections are closed.
* \param[in] endp endpoint to release */
void mgcp_endp_release(struct mgcp_endpoint *endp)
{
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
/* Normally this function should only be called when
* all connections have been removed already. In case
* that there are still connections open (e.g. when
* RSIP is executed), free them all at once. */
mgcp_endp_free_conn_all(endp);
/* We must only decrement the stat item when the endpoint as actually
* claimed. An endpoint is claimed when a call-id is set */
if (endp->callid)
osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common,
TRUNK_STAT_ENDPOINTS_USED), 1);
/* Reset endpoint parameters and states */
talloc_free(endp->callid);
endp->callid = NULL;
mgcp_endp_update_lco(endp, NULL);
if (endp->trunk->trunk_type == MGCP_TRUNK_E1) {
uint8_t ts = e1_ts_nr_from_epname(endp->name);
mgcp_e1_endp_release(endp, ts);
}
}

View File

@@ -1,796 +0,0 @@
/*
* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All rights not specifically granted under this license are reserved.
*
* Author: Pau Espin Pedrol
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*/
#include <stdint.h>
#include <osmocom/core/byteswap.h>
#include <osmocom/gsm/iuup.h>
#include <osmocom/netif/rtp.h>
#include <osmocom/netif/amr.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_iuup.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_network.h>
#include <osmocom/mgcp/debug.h>
#define MGW_IUUP_MSGB_SIZE 4096
static const struct osmo_iuup_rnl_config def_configure_req = {
.transparent = false,
.active = true,
.supported_versions_mask = 0x0003,
.num_rfci = 0,
.num_subflows = 0,
.IPTIs_present = false,
.t_init = { .t_ms = IUUP_TIMER_INIT_T_DEFAULT, .n_max = IUUP_TIMER_INIT_N_DEFAULT },
.t_ta = { .t_ms = IUUP_TIMER_TA_T_DEFAULT, .n_max = IUUP_TIMER_TA_N_DEFAULT },
.t_rc = { .t_ms = IUUP_TIMER_RC_T_DEFAULT, .n_max = IUUP_TIMER_RC_N_DEFAULT },
};
/* Find a destination connection. */
static struct mgcp_conn *_find_dst_conn(struct mgcp_conn *conn)
{
/* NOTE: This code path runs every time an RTP packet is received. The
* function mgcp_find_dst_conn() we use to determine the detination
* connection will iterate the connection list inside the endpoint.
* Since list iterations are quite costly, we will figure out the
* destination only once and use the optional private data pointer of
* the connection to cache the destination connection pointer. */
struct mgcp_conn *conn_dst;
if (!conn->priv) {
conn_dst = mgcp_find_dst_conn(conn);
conn->priv = conn_dst;
} else {
conn_dst = (struct mgcp_conn *)conn->priv;
}
return conn_dst;
}
/* Find RFCI containing all 0 sizes, -1 if not found. irp is an Initialization.ind prim */
static int _find_rfci_no_data(struct osmo_iuup_rnl_prim *irp)
{
int i;
uint8_t rfci_cnt = 0;
/* Find RFCI containing NO_DATA: */
for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) {
struct osmo_iuup_rfci *rfci = &irp->u.status.u.initialization.rfci[i];
int j;
bool is_no_data;
if (!rfci->used)
continue;
rfci_cnt++;
is_no_data = true;
for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++) {
if (rfci->subflow_sizes[j]) {
is_no_data = false;
break;
}
}
if (is_no_data)
return rfci->id;
/* early loop termination: */
if (rfci_cnt == irp->u.status.u.initialization.num_subflows)
break;
}
return -1;
}
/* Lookup RFCI to use for specific AMR codec type. -1 if none found */
static int8_t _conn_iuup_amr_ft_2_rfci(struct mgcp_conn_rtp *conn_rtp, uint8_t ft)
{
int8_t i;
uint8_t rfci_cnt = 0;
unsigned match_bytes = (unsigned)osmo_amr_bytes(ft);
struct osmo_iuup_rnl_prim *irp = conn_rtp->iuup.init_ind;
if (!irp) {
/* No IuUP Initialization has occured on the IuUP side yet. Return error and drop the RTP data, until
* the IuUP Initialization has configured the link. */
return -1;
}
/* TODO: cache this somehow */
for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) {
struct osmo_iuup_rfci *rfci = &irp->u.status.u.initialization.rfci[i];
int j;
unsigned num_bits;
if (!rfci->used)
continue;
rfci_cnt++;
num_bits = 0;
for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++)
num_bits += rfci->subflow_sizes[j];
if (match_bytes == (num_bits + 7)/8)
return rfci->id;
/* early loop termination: */
if (rfci_cnt == irp->u.status.u.initialization.num_subflows)
break;
}
return -1;
}
/* Helper function to configure IuUP layer FSM as Init-Passive, based on default config */
static int _conn_iuup_configure_as_passive(struct mgcp_conn_rtp *conn_rtp)
{
struct osmo_iuup_rnl_prim *irp;
int rc;
conn_rtp->iuup.active_init = false;
/* Tx CONFIG.req */
irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
irp->u.config = def_configure_req;
irp->u.config.active = conn_rtp->iuup.active_init;
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) == 0)
conn_rtp->iuup.configured = true;
else
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n");
return rc;
}
/* Helper function to configure IuUP layer FSM as Init-Active, based on received
* RNL Status-Init primitive from the sister IuUP connection we will bridge to. */
static int _conn_iuup_configure_as_active(struct mgcp_conn_rtp *conn_rtp, struct osmo_iuup_rnl_prim *init_ind)
{
struct osmo_iuup_rnl_prim *irp = init_ind;
struct osmo_iuup_rnl_prim *irp2;
struct msgb *msg;
bool prev_output_enabled;
int rc;
conn_rtp->iuup.active_init = true;
/* Find RFCI containing NO_DATA: */
conn_rtp->iuup.rfci_id_no_data = _find_rfci_no_data(init_ind);
/* Copy over the rfci_id_no_data, since we reuse the same subflow set: */
msg = msgb_copy_c(conn_rtp->conn, irp->oph.msg, "iuup-init-copy");
conn_rtp->iuup.init_ind = (struct osmo_iuup_rnl_prim *)msgb_data(msg);
conn_rtp->iuup.init_ind->oph.msg = msg;
/* Tx CONFIG.req */
irp2 = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
irp2->u.config.transparent = false;
irp2->u.config.active = conn_rtp->iuup.active_init;
irp2->u.config.data_pdu_type = irp->u.status.u.initialization.data_pdu_type;
irp2->u.config.supported_versions_mask = def_configure_req.supported_versions_mask;
irp2->u.config.num_rfci = irp->u.status.u.initialization.num_rfci;
irp2->u.config.num_subflows = irp->u.status.u.initialization.num_subflows;
irp2->u.config.IPTIs_present = irp->u.status.u.initialization.IPTIs_present;
memcpy(irp2->u.config.rfci, irp->u.status.u.initialization.rfci, sizeof(irp2->u.config.rfci));
irp2->u.config.t_init = def_configure_req.t_init;
irp2->u.config.t_ta = def_configure_req.t_ta;
irp2->u.config.t_rc = def_configure_req.t_rc;
/* We need to force allowance of RTP containing Init-ACK back: */
prev_output_enabled = conn_rtp->end.output_enabled;
conn_rtp->end.output_enabled = true;
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp2)) == 0)
conn_rtp->iuup.configured = true;
else
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n");
conn_rtp->end.output_enabled = prev_output_enabled;
return rc;
}
/* Helper function to push an RTP+IuUP pkt up to the IuUP layer FSM through the
* TNL primitive interface. */
static int _conn_iuup_rtp_pl_up(struct mgcp_conn_rtp *conn_rtp, struct msgb *msg)
{
/* Send RTP payload (IuUP) up the stack: */
struct osmo_iuup_tnl_prim *itp;
int rc;
msg->l2h = msgb_data(msg) + sizeof(struct rtp_hdr);
itp = osmo_iuup_tnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, MGW_IUUP_MSGB_SIZE);
itp->oph.msg->l2h = msgb_put(itp->oph.msg, msgb_l2len(msg));
memcpy(itp->oph.msg->l2h, msgb_l2(msg), msgb_l2len(msg));
if ((rc = osmo_iuup_tnl_prim_up(conn_rtp->iuup.iui, itp)) != 0) {
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed passing IuUP-Init to IuUP layer\n");
}
return rc;
}
static int check_rtp_iuup(const struct mgcp_conn_rtp *conn_rtp, struct msgb *msg)
{
size_t min_size = sizeof(struct rtp_hdr);
/* Check there's at least 2 bytes of RTP payload (IuUP header). This is
** mainly to avoid 0-byte payload copy cases */
if (msgb_length(msg) < sizeof(struct rtp_hdr) + 2) {
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "RTP-IuUP packet too short (%u < %zu)\n",
msgb_length(msg), min_size);
return -1;
}
return 0;
}
/* Bridge received IuUP packet in conn_rtp_src to conn_rtp_dst, an IuUP sister
* conn in the endpoint. The function takes ownsership of the irp */
static int bridge_iuup_to_iuup_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mgcp_conn_rtp *conn_rtp_dst, struct osmo_iuup_rnl_prim *irp)
{
int rc;
/* If we are not configured and we received bridged data, it means
* conn_rtp_src is already configured and INITed, and we can infer
* conn_rtp_src is Init-passive (RNC side), so conn_rtp_dst needs to be
* configured as INIT-active: */
if (!conn_rtp_dst->iuup.configured) {
OSMO_ASSERT(conn_rtp_src->iuup.init_ind);
rc = _conn_iuup_configure_as_active(conn_rtp_dst, conn_rtp_src->iuup.init_ind);
if (rc < 0) {
msgb_free(irp->oph.msg);
return rc;
}
}
/* We simply forward the msg, without freeing it: */
talloc_steal(conn_rtp_dst->conn, irp->oph.msg);
irp->oph.operation = PRIM_OP_REQUEST;
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp_dst->iuup.iui, irp)) != 0)
LOG_CONN_RTP(conn_rtp_dst, LOGL_ERROR, "Failed Tx data down to IuUP layer\n");
return rc;
}
/* Bridge received IuUP packet in conn_rtp_src to conn_rtp_dst, an RTP (no IuUP)
* sister conn in the endpoint. The function takes ownsership of the irp */
static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mgcp_conn_rtp *conn_rtp_dst, struct osmo_iuup_rnl_prim *irp)
{
/* FIXME: We probably need transcoding here?! Or at least look up AMR modes and translate to related RFCI */
uint8_t frame_nr = irp->u.data.frame_nr;
uint8_t fqc = irp->u.data.fqc;
struct msgb *msg = irp->oph.msg;
ssize_t amr_length = 0;
int ft;
uint8_t *amr_data;
struct rtp_hdr *rtp_hdr;
struct amr_hdr *amr_hdr;
struct mgcp_rtp_codec *dst_codec;
int rc;
ft = osmo_amr_bytes_to_ft(msgb_l3len(msg));
if (ft < 0) {
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
"Unknown AMR format for size %u\n", msgb_l3len(msg));
msgb_free(msg);
return ft;
}
msgb_pull_to_l3(msg);
dst_codec = conn_rtp_dst->end.cset.codec;
if (mgcp_codec_amr_is_octet_aligned(dst_codec)) {
LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR OA: ft %d, len %d\n", ft, msgb_length(msg));
amr_hdr = (struct amr_hdr *) msgb_push(msg, sizeof(struct amr_hdr));
amr_hdr->cmr = 15; /* no change */
amr_hdr->f = 0;
amr_hdr->q = !fqc;
amr_hdr->ft = ft & 0xff;
amr_hdr->pad1 = 0;
amr_hdr->pad2 = 0;
} else {
OSMO_ASSERT(msgb_tailroom(msg) >= 2);
msgb_put(msg, 2);
osmo_amr_iuup_to_bwe(msgb_data(msg), msgb_length(msg) - 2, msgb_length(msg) + 2);
/* fill bwe header */
amr_data = msgb_data(msg);
/* CMR no change | follow bit | ft (3 of 4 bits) */
amr_data[0] = 15 << 4 | (0 << 3) | (ft >> 1);
amr_data[1] |= ((ft & 0x1) << 7) | (((!fqc) & 0x1) << 6);
amr_length = (osmo_amr_bits(ft) + 10 + 7) / 8;
msgb_trim(msg, amr_length);
LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR BE: ft %d, len %zd\n", ft, amr_length);
}
rtp_hdr = (struct rtp_hdr *) msgb_push(msg, sizeof(*rtp_hdr));
*rtp_hdr = (struct rtp_hdr){
.csrc_count = 0,
.extension = 0,
.padding = 0,
.version = 0,
.payload_type = dst_codec->payload_type,
.marker = 0,
.sequence = frame_nr,
.timestamp = 0,
.ssrc = 0
};
rc = mgcp_send(conn_rtp_dst->conn->endp, true, NULL, msg, conn_rtp_src, conn_rtp_dst);
return rc;
}
/* Handle RNL Data primitive received from the IuUP layer FSM: Bridge it to the
* sister connection in the endpoint: */
static int _conn_iuup_rx_rnl_data(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
{
struct mgcp_conn *conn_dst;
struct mgcp_conn_rtp *conn_rtp_dst;
int rc;
conn_dst = _find_dst_conn(conn_rtp_src->conn);
/* There is no destination conn, stop here */
if (!conn_dst) {
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_DEBUG,
"no connection to forward an incoming IuUP payload to\n");
rc = -1;
goto free_ret;
}
/* The destination conn is not an RTP/IuUP connection */
if (conn_dst->type != MGCP_CONN_TYPE_RTP) {
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
"unable to find suitable destination conn\n");
rc = -1;
goto free_ret;
}
conn_rtp_dst = &conn_dst->u.rtp;
switch (conn_rtp_dst->type) {
case MGCP_RTP_IUUP:
return bridge_iuup_to_iuup_peer(conn_rtp_src, conn_rtp_dst, irp);
case MGCP_RTP_DEFAULT:
return bridge_iuup_to_rtp_peer(conn_rtp_src, conn_rtp_dst, irp);
case MGCP_RTP_OSMUX:
default:
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
"Forward of IuUP payload to RTP connection type %u not supported!\n",
conn_rtp_dst->type);
rc = 0;
}
free_ret:
msgb_free(irp->oph.msg);
return rc;
}
/* Handle RNL Status-Init primitive received from the IuUP layer FSM.
* Potentially configure sister conn as IuUP Init-Active: */
static int _conn_iuup_rx_rnl_status_init(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
{
struct mgcp_conn *conn_dst;
struct mgcp_conn_rtp *conn_rtp_dst;
int rc = 0;
struct msgb *msg;
if (conn_rtp_src->iuup.init_ind) {
/* We received more than one IuUP Initialization. It's probably
* a retransmission, so simply ignore it (lower layers take care
* of ACKing it). */
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_INFO,
"Ignoring potential IuUP Initialization retrans\n");
return 0;
}
msg = msgb_copy_c(conn_rtp_src->conn, irp->oph.msg, "iuup-init-copy");
conn_rtp_src->iuup.init_ind = (struct osmo_iuup_rnl_prim *)msgb_data(msg);
conn_rtp_src->iuup.init_ind->oph.msg = msg;
/* Find RFCI containing NO_DATA: */
conn_rtp_src->iuup.rfci_id_no_data = _find_rfci_no_data(irp);
conn_dst = _find_dst_conn(conn_rtp_src->conn);
/* If not yet there, peer will potentially be IuUP-Initialized later
* when we attempt to bridge audio towards it. See bridge_iuup_to_iuup_peer() */
if (!conn_dst)
return 0;
conn_rtp_dst = &conn_dst->u.rtp;
if (!mgcp_conn_rtp_is_iuup(conn_rtp_dst))
return 0; /* Nothing to do */
/* We received IuUP parameters on the peer (RNC), Init actively this conn (against CN): */
if (!conn_rtp_dst->iuup.configured)
rc = _conn_iuup_configure_as_active(conn_rtp_dst, irp);
return rc;
}
/* Handle RNL Status primitives received from the IuUP layer FSM: */
static int _conn_iuup_rx_rnl_status(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
{
int rc;
switch (irp->u.status.procedure) {
case IUUP_PROC_INIT:
rc = _conn_iuup_rx_rnl_status_init(conn_rtp_src, irp);
break;
case IUUP_PROC_RATE_CTRL:
case IUUP_PROC_TIME_ALIGN:
case IUUP_PROC_ERR_EVENT:
default:
LOG_CONN_RTP(conn_rtp_src, LOGL_ERROR,
"Received IuUP RNL STATUS procedure type %u not handled\n",
irp->u.status.procedure);
rc = 0;
}
return rc;
}
/* Received RNL primitive from the IuUP layer FSM containing IuUP Status or
* data. Continue pushing it up the stack, either IuUP Status or Data: */
static int _conn_iuup_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
struct mgcp_conn_rtp *conn_rtp_src = ctx;
struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
struct msgb *msg = oph->msg;
int rc;
switch (OSMO_PRIM_HDR(&irp->oph)) {
case OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION):
/* we pass ownsership of msg here: */
rc = _conn_iuup_rx_rnl_data(conn_rtp_src, irp);
break;
case OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION):
rc = _conn_iuup_rx_rnl_status(conn_rtp_src, irp);
msgb_free(msg);
break;
default:
msgb_free(msg);
OSMO_ASSERT(false);
}
return rc;
}
/*! Send |RTP+IuUP| data down the stack of the specified destination connection.
* \param[in] endp associated endpoint (for configuration, logging).
* \param[in] buf buffer that contains the |RTP+IuUP| data.
* \param[in] len length of the buffer that contains the |RTP+IuUP| data.
* \param[in] conn_src associated source connection.
* \param[in] conn_dst associated destination connection.
* \returns 0 on success, -1 on ERROR. */
static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst)
{
/*! When no destination connection is available (e.g. when only one
* connection in loopback mode exists), then the source connection
* shall be specified as destination connection */
struct mgcp_rtp_end *rtp_end;
struct mgcp_rtp_state *rtp_state;
char ipbuf[INET6_ADDRSTRLEN];
struct rtp_hdr *hdr = (struct rtp_hdr *)msgb_data(msg);
int buflen = msgb_length(msg);
char *dest_name;
int rc;
OSMO_ASSERT(conn_src);
OSMO_ASSERT(conn_dst);
LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering IuUP packet...\n");
/* Note: In case of loopback configuration, both, the source and the
* destination will point to the same connection. */
rtp_end = &conn_dst->end;
rtp_state = &conn_src->state;
dest_name = conn_dst->conn->name;
/* Ensure we have an alternative SSRC in case we need it, see also
* gen_rtp_header() */
if (rtp_state->alt_rtp_tx_ssrc == 0)
rtp_state->alt_rtp_tx_ssrc = rand();
if (!rtp_end->output_enabled) {
rtpconn_rate_ctr_add(conn_dst, endp, RTP_DROPPED_PACKETS_CTR, 1);
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"output disabled, drop to %s %s "
"rtp_port:%u rtcp_port:%u\n",
dest_name,
osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
);
return 0;
}
/* Specs say, in IuUP, the RTP seqnum and timestamp should actually be
* ignored by the receiver, but still it's useful for debug purposes
* to set it. Moreover, it seems ip.access nano3g produces much worse
* audio output on the air side if timestamp is not set properly. */
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->cset.codec->rate));
hdr->sequence = osmo_htons(rtp_state->alt_rtp_tx_sequence);
hdr->ssrc = rtp_state->alt_rtp_tx_ssrc;
rtp_state->alt_rtp_tx_sequence++;
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"process/send IuUP to %s %s rtp_port:%u rtcp_port:%u\n",
dest_name, osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port));
/* Forward a copy of the RTP data to a debug ip/port */
forward_data_tap(rtp_end->rtp, &conn_src->tap_out, msg);
rc = mgcp_udp_send(rtp_end->rtp, &rtp_end->addr, (char *)hdr, buflen);
if (rc < 0)
return rc;
rtpconn_rate_ctr_add(conn_dst, endp, RTP_PACKETS_TX_CTR, 1);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, buflen);
return 0;
}
/* Received TNL primitive from IuUP layer FSM, transmit it further down to the
* socket towards destination peer. */
static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
struct mgcp_conn_rtp *conn_rtp_dst = ctx;
struct mgcp_conn *conn_dst = conn_rtp_dst->conn;
struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
struct mgcp_conn *conn_src;
struct msgb *msg;
struct rtp_hdr *rtph;
OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
msg = oph->msg;
talloc_steal(conn_rtp_dst->conn, msg);
msgb_pull_to_l2(msg);
rtph = (struct rtp_hdr *)msgb_push(msg, sizeof(*rtph));
/* rtph is further filled in mgcp_send_iuup() below. */
*rtph = (struct rtp_hdr){
.csrc_count = 0,
.extension = 0,
.padding = 0,
.version = 2,
.payload_type = conn_rtp_dst->end.cset.codec->payload_type,
.marker = 0,
.sequence = 0,
.timestamp = 0,
.ssrc = 0
};
/* The destination of the destination conn is the source conn, right? */
conn_src = _find_dst_conn(conn_dst);
if (!conn_src) {
LOG_CONN_RTP(conn_rtp_dst, LOGL_NOTICE,
"Couldn't find source conn for IuUP dst conn\n");
/* If there's no sister connection we are either still
* initializing (so we want to send back Init (ACK)), or we are
* probably in loopback mode anyway, so use dst as src. */
conn_src = conn_dst;
}
return mgcp_send_iuup(conn_dst->endp, msg, &conn_src->u.rtp, conn_rtp_dst);
}
/* Used to upgrade a regular RTP connection (MGCP_RTP_DEFAULT) to become a IuUP
* connection (MGCP_RTP_IUUP) */
int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp)
{
conn_rtp->type = MGCP_RTP_IUUP;
conn_rtp->iuup.iui = osmo_iuup_instance_alloc(conn_rtp->conn, conn_rtp->conn->id);
OSMO_ASSERT(conn_rtp->iuup.iui);
osmo_iuup_instance_set_user_prim_cb(conn_rtp->iuup.iui, _conn_iuup_user_prim_cb, conn_rtp);
osmo_iuup_instance_set_transport_prim_cb(conn_rtp->iuup.iui, _conn_iuup_transport_prim_cb, conn_rtp);
conn_rtp->iuup.rfci_id_no_data = -1;
return 0;
}
/* Cleanup specific IuUP connection (MGCP_RTP_IUUP) state, allocated by mgcp_conn_iuup_init() */
void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp)
{
osmo_iuup_instance_free(conn_rtp->iuup.iui);
conn_rtp->iuup.iui = NULL;
}
/* Received RTP+IuUP pkt from socket of conn_rtp_src, build a TNL primitive to
* push it further up the stack to the IuUP layer FSM to handle and/or bridge it */
int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg)
{
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
struct mgcp_conn_rtp *conn_rtp_src = mc->conn_src;
int rc = 0;
bool force_output_enabled = false;
bool prev_output_enabled;
struct osmo_sockaddr prev_rem_addr;
uint16_t prev_rem_rtp_port;
OSMO_ASSERT(mgcp_conn_rtp_is_iuup(conn_rtp_src));
if ((rc = check_rtp_iuup(conn_rtp_src, msg)) < 0)
goto free_ret;
if (!conn_rtp_src->iuup.configured) {
/* We received the first message without sending any, the peer is the active side (RNC). */
rc = _conn_iuup_configure_as_passive(conn_rtp_src);
if (rc < 0)
goto free_ret;
/* We need to force allowance of RTP containing Init-ACK back: */
prev_output_enabled = conn_rtp_src->end.output_enabled;
conn_rtp_src->end.output_enabled = true;
force_output_enabled = true;
/* Fill in the peer address so that we can send Init-ACK back: */
prev_rem_addr = conn_rtp_src->end.addr;
prev_rem_rtp_port = osmo_sockaddr_port(&conn_rtp_src->end.addr.u.sa);
conn_rtp_src->end.addr = *mc->from_addr;
}
rc = _conn_iuup_rtp_pl_up(conn_rtp_src, msg);
if (force_output_enabled) {
conn_rtp_src->end.output_enabled = prev_output_enabled;
conn_rtp_src->end.addr = prev_rem_addr;
osmo_sockaddr_set_port(&conn_rtp_src->end.addr.u.sa, prev_rem_rtp_port);
}
return rc;
free_ret:
msgb_free(msg);
return rc;
}
/* Build IuUP RNL Data primitive from msg containing an incoming RTP pkt from
* peer and send it down the IuUP layer towards the destination as IuUP/RTP. Takes ownership of msg. */
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg)
{
struct osmo_iuup_rnl_prim *irp;
struct rtp_hdr *rtph;
int rc = -1;
int iuup_length = 0;
struct mgcp_rtp_codec *src_codec;
int8_t rfci;
/* Tx RNL-DATA.req */
rtph = (struct rtp_hdr *)msgb_data(msg);
msgb_pull(msg, sizeof(*rtph));
/* FIXME: validate amr packets */
irp = osmo_iuup_rnl_prim_alloc(conn_dest_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
irp->u.data.frame_nr = htons(rtph->sequence) % 16;
/* TODO: CMR handling & multiple frames handling */
src_codec = conn_src_rtp->end.cset.codec;
if (strcmp(src_codec->subtype_name, "AMR") != 0) {
LOG_CONN_RTP(conn_src_rtp, LOGL_ERROR,
"Bridge RTP=>IuUP: Bridging src codec %s to IuUP AMR not supported\n",
src_codec->subtype_name);
goto free_ret;
}
if (mgcp_codec_amr_is_octet_aligned(src_codec)) {
struct amr_hdr *amr_hdr = (struct amr_hdr *) msgb_data(msg);
if (msgb_length(msg) < (sizeof(*amr_hdr))) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE,
"Bridge RTP=>IuUP: too short for AMR OA hdr (%u)\n", msgb_length(msg));
goto free_ret;
}
if (!osmo_amr_ft_valid(amr_hdr->ft)) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR OA ft=%u\n", amr_hdr->ft);
goto free_ret;
}
if ((rfci = _conn_iuup_amr_ft_2_rfci(conn_dest_rtp, amr_hdr->ft)) < 0) {
LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR OA ft=%u\n", amr_hdr->ft);
goto free_ret;
}
irp->u.data.fqc = amr_hdr->q ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD;
irp->u.data.rfci = rfci;
msgb_pull(msg, 2);
LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR OA -> IuUP: ft %d -> rfci %d len %d\n",
amr_hdr->ft, rfci, msgb_length(msg));
} else {
uint8_t *amr_bwe_hdr = (uint8_t *) msgb_data(msg);
int8_t ft;
uint8_t q;
if (msgb_length(msg) < 2) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE,
"Bridge RTP=>IuUP: too short for AMR BE hdr (%u)\n", msgb_length(msg));
goto free_ret;
}
ft = ((amr_bwe_hdr[0] & 0x07) << 1) | ((amr_bwe_hdr[1] & 0x80) >> 7);
if (!osmo_amr_ft_valid(ft)) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR BE ft=%u\n", ft);
goto free_ret;
}
if ((rfci = _conn_iuup_amr_ft_2_rfci(conn_dest_rtp, ft)) < 0) {
LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR BE ft=%u\n", ft);
goto free_ret;
}
q = amr_bwe_hdr[1] & 0x40;
irp->u.data.fqc = q ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD;
irp->u.data.rfci = rfci;
rc = iuup_length = osmo_amr_bwe_to_iuup(msgb_data(msg), msgb_length(msg));
if (rc < 0) {
LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Bridge RTP=>IuUP: Failed convert the RTP/AMR to IuUP payload\n");
return rc;
}
msgb_trim(msg, iuup_length);
LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR BE -> IuUP: ft %d -> rfci %d len %d\n",
ft, rfci, msgb_length(msg));
}
irp->oph.msg->l3h = msgb_put(irp->oph.msg, msgb_length(msg));
memcpy(irp->oph.msg->l3h, msgb_data(msg), msgb_length(msg));
if ((rc = osmo_iuup_rnl_prim_down(conn_dest_rtp->iuup.iui, irp)) != 0)
LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Bridge RTP=>IuUP: Failed Tx RTP payload down the IuUP layer\n");
return rc;
free_ret:
msgb_free(irp->oph.msg);
return -1;
}
/* Build IuUP RNL Data primitive from msg containing dummy content and send it
* down the IuUP layer towards the destination as IuUP/RTP: */
int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp)
{
struct osmo_iuup_rnl_prim *irp;
int rc;
if (conn_rtp->iuup.rfci_id_no_data == -1) {
LOG_CONN_RTP(conn_rtp, LOGL_NOTICE, "No RFCI NO_DATA found, unable to send dummy packet\n");
return -ENOTSUP;
}
irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
irp->u.data.frame_nr = 0;
irp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
irp->u.data.rfci = conn_rtp->iuup.rfci_id_no_data;
irp->oph.msg->l3h = irp->oph.msg->tail;
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) != 0) {
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed Tx RTP dummy payload down the IuUP layer\n");
return -EINVAL;
}
return 0;
}
/* To be called every time an CRCX/MDCX is received.
* returns: 0 if conn can continue, MGCP negative code if an error ocurred during setup */
int mgcp_conn_iuup_event_rx_crcx_mdcx(struct mgcp_conn_rtp *conn_rtp)
{
struct mgcp_conn *peer_conn;
struct mgcp_conn_rtp *peer_conn_rtp;
OSMO_ASSERT(mgcp_conn_rtp_is_iuup(conn_rtp));
/* keep waiting to receive remote address through CRCX/MDCX */
if (!mgcp_rtp_end_remote_addr_available(&conn_rtp->end))
return 0;
/* Conn already IuUP-configured/initialized, nothing to be done. */
if (conn_rtp->iuup.configured)
return 0;
/* Reached this point, conn_rtp is an IuUP conn which can be configured
* and was not yet configured. If its sister conn in the endpoint was
* already configured as IuUP "passive", then we know this one can be
* configured as IuUP "active" right now. In that case, do so.
* This usually happens on an HNBGW co-located MGW, where during
* RAB-ASS-REQ the RAN-side conn (sister conn here) was already
* configured and then upon rx of RAB-ASS-RESP it sets up (CRCX) the
* CN-side IuUP conn (rt_conn here).
*/
peer_conn = mgcp_find_dst_conn(conn_rtp->conn);
/* No peer conn yet, nothing to be done. */
if (!peer_conn)
return 0;
peer_conn_rtp = mgcp_conn_get_conn_rtp(peer_conn);
if (mgcp_conn_rtp_is_iuup(peer_conn_rtp) &&
peer_conn_rtp->iuup.configured &&
!conn_rtp->iuup.active_init) {
LOG_CONN_RTP(conn_rtp, LOGL_INFO, "Sister IuUP conn in endp configured as passive, init this one as active\n");
OSMO_ASSERT(peer_conn_rtp->iuup.init_ind);
_conn_iuup_configure_as_active(conn_rtp, peer_conn_rtp->iuup.init_ind);
}
return 0;
}

View File

@@ -23,7 +23,6 @@
*/
#include <limits.h>
#include <ctype.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/osmux.h>
@@ -32,11 +31,6 @@
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
/* (same fmt as LOGPENDP()) */
#define LOG_MGCP_PDATA(PDATA, LEVEL, FMT, ARGS...) \
LOGP(DLMGCP, LEVEL, "%s: endpoint(%s) " FMT, (PDATA)->rq->name, (PDATA)->epname ? : "null-epname", ##ARGS)
/*! Display an mgcp message on the log output.
* \param[in] message mgcp message string
@@ -80,232 +74,82 @@ void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
}
/*! Parse connection mode.
* \param[in] mode as string (recvonly, sendrecv, sendonly confecho or loopback)
* \returns MGCP_CONN_* on success, MGCP_CONN_NONE on error */
enum mgcp_connection_mode mgcp_parse_conn_mode(const char *mode)
* \param[in] mode as string (recvonly, sendrecv, sendonly or loopback)
* \param[in] endp pointer to endpoint (only used for log output)
* \param[out] associated connection to be modified accordingly
* \returns 0 on success, -1 on error */
int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
struct mgcp_conn *conn)
{
int ret = 0;
if (!mode)
return MGCP_CONN_NONE;
if (strcasecmp(mode, "recvonly") == 0)
return MGCP_CONN_RECV_ONLY;
if (strcasecmp(mode, "sendrecv") == 0)
return MGCP_CONN_RECV_SEND;
if (strcasecmp(mode, "sendonly") == 0)
return MGCP_CONN_SEND_ONLY;
if (strcasecmp(mode, "confecho") == 0)
return MGCP_CONN_CONFECHO;
if (strcasecmp(mode, "loopback") == 0)
return MGCP_CONN_LOOPBACK;
return MGCP_CONN_NONE;
}
/*! Helper function for check_local_cx_options() to get a pointer of the next
* lco option identifier
* \param[in] lco string
* \returns pointer to the beginning of the LCO identifier, NULL on failure */
char *get_lco_identifier(const char *options)
{
char *ptr;
unsigned int count = 0;
/* Jump to the end of the lco identifier */
ptr = strstr(options, ":");
if (!ptr)
return NULL;
/* Walk backwards until the pointer points to the beginning of the
* lco identifier. We know that we stand at the beginning when we
* are either at the beginning of the memory or see a space or
* comma. (this is tolerant, it will accept a:10, b:11 as well as
* a:10,b:11) */
while (1) {
/* Endless loop protection */
if (count > 10000)
return NULL;
else if (ptr < options || *ptr == ' ' || *ptr == ',') {
ptr++;
break;
}
ptr--;
count++;
if (!mode) {
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"missing connection mode\n");
return -1;
}
/* Check if we got any result */
if (*ptr == ':')
return NULL;
return ptr;
}
/*! Check the LCO option. This function checks for multiple appearance of LCO
* options, which is illegal
* \param[in] ctx talloc context
* \param[in] lco string
* \returns 0 on success, -1 on failure */
int check_local_cx_options(void *ctx, const char *options)
{
int i;
char *options_copy;
char *lco_identifier;
char *lco_identifier_end;
char *next_lco_identifier;
char **lco_seen;
unsigned int lco_seen_n = 0;
if (!options)
if (!conn)
return -1;
if (!endp)
return -1;
lco_seen =
(char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
options_copy = talloc_strdup(ctx, options);
lco_identifier = options_copy;
do {
/* Move the lco_identifier pointer to the beginning of the
* current lco option identifier */
lco_identifier = get_lco_identifier(lco_identifier);
if (!lco_identifier)
goto error;
/* Look ahead to the next LCO option early, since we
* will parse destructively */
next_lco_identifier = strstr(lco_identifier + 1, ",");
/* Pinch off the end of the lco field identifier name
* and see if we still got something, also check if
* there is some value after the colon. */
lco_identifier_end = strstr(lco_identifier, ":");
if (!lco_identifier_end)
goto error;
if (*(lco_identifier_end + 1) == ' '
|| *(lco_identifier_end + 1) == ','
|| *(lco_identifier_end + 1) == '\0')
goto error;
*lco_identifier_end = '\0';
if (strlen(lco_identifier) == 0)
goto error;
/* Check if we have already seen the current field identifier
* before. If yes, we must bail, an LCO must only appear once
* in the LCO string */
for (i = 0; i < lco_seen_n; i++) {
if (strcasecmp(lco_seen[i], lco_identifier) == 0)
goto error;
}
lco_seen[lco_seen_n] = lco_identifier;
lco_seen_n++;
/* The first identifier must always be found at the beginnning
* of the LCO string */
if (lco_seen[0] != options_copy)
goto error;
/* Go to the next lco option */
lco_identifier = next_lco_identifier;
} while (lco_identifier);
talloc_free(lco_seen);
talloc_free(options_copy);
return 0;
error:
talloc_free(lco_seen);
talloc_free(options_copy);
return -1;
}
/* Set the LCO from a string (see RFC 3435).
* The string is stored in the 'string' field. A NULL string is handled exactly
* like an empty string, the 'string' field is never NULL after this function
* has been called. */
static int mgcp_parse_lco(void *ctx, struct mgcp_lco *lco, const char *options)
{
const char *lco_id;
char codec[17];
char nt[17];
int len;
if (!options)
return 0;
if (strlen(options) == 0)
return 0;
lco->present = true;
/* Make sure the encoding of the LCO is consistent before we proceed */
if (check_local_cx_options(ctx, options) != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"local CX options: Internal inconsistency in Local Connection Options!\n");
return -524;
if (strcasecmp(mode, "recvonly") == 0)
conn->mode = MGCP_CONN_RECV_ONLY;
else if (strcasecmp(mode, "sendrecv") == 0)
conn->mode = MGCP_CONN_RECV_SEND;
else if (strcasecmp(mode, "sendonly") == 0)
conn->mode = MGCP_CONN_SEND_ONLY;
else if (strcasecmp(mode, "loopback") == 0)
conn->mode = MGCP_CONN_LOOPBACK;
else {
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"unknown connection mode: '%s'\n", mode);
ret = -1;
}
lco_id = options;
while ((lco_id = get_lco_identifier(lco_id))) {
switch (tolower(lco_id[0])) {
case 'p':
if (sscanf(lco_id + 1, ":%d-%d",
&lco->pkt_period_min, &lco->pkt_period_max) == 1)
lco->pkt_period_max = lco->pkt_period_min;
break;
case 'a':
/* FIXME: LCO also supports the negotiation of more than one codec.
* (e.g. a:PCMU;G726-32) But this implementation only supports a single
* codec only. Ignoring all but the first codec. */
if (sscanf(lco_id + 1, ":%16[^,;]", codec) == 1) {
talloc_free(lco->codec);
/* MGCP header is case insensive, and we'll need
codec in uppercase when using it later: */
len = strlen(codec);
lco->codec = talloc_size(ctx, len + 1);
osmo_str_toupper_buf(lco->codec, len + 1, codec);
}
break;
case 'n':
if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
break;
/* else: fall through to print notice log */
default:
LOGP(DLMGCP, LOGL_NOTICE,
"LCO: unhandled option: '%c'/%d in \"%s\"\n",
*lco_id, *lco_id, options);
break;
}
lco_id = strchr(lco_id, ',');
if (!lco_id)
break;
/* Special handling for RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
conn->u.rtp.end.output_enabled =
conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
}
LOGP(DLMGCP, LOGL_DEBUG,
"local CX options: lco->pkt_period_max: %d, lco->codec: %s\n",
lco->pkt_period_max, lco->codec);
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
/* Check if the packetization fits the 20ms raster */
if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
LOGP(DLMGCP, LOGL_ERROR,
"local CX options: packetization interval is not a multiple of 20ms!\n");
return -535;
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
mode, conn->mode);
/* Special handling für RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %d\n",
conn->u.rtp.end.output_enabled);
}
return 0;
/* The VTY might change the connection mode at any time, so we have
* to hold a copy of the original connection mode */
conn->mode_orig = conn->mode;
return ret;
}
/*! Analyze and parse the the hader of an MGCP messeage string.
* \param[out] pdata caller provided memory to store the parsing results.
* \param[in] data mgcp message string.
* \returns 0 when the status line was complete and parseable, negative (MGCP
* cause code) on error. */
* \param[out] pdata caller provided memory to store the parsing results
* \param[in] data mgcp message string
* \returns when the status line was complete and transaction_id and
* endp out parameters are set, -1 on error */
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
{
int i = 0;
char *elem, *save = NULL;
int cause;
/*! This function will parse the header part of the received
* MGCP message. The parsing results are stored in pdata. */
* MGCP message. The parsing results are stored in pdata.
* The function will also automatically search the pool with
* available endpoints in order to find an endpoint that matches
* the endpoint string in in the header */
OSMO_ASSERT(data);
pdata->trans = "000000";
for (elem = strtok_r(data, " ", &save); elem;
elem = strtok_r(NULL, " ", &save)) {
@@ -314,138 +158,42 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
pdata->trans = elem;
break;
case 1:
pdata->epname = elem;
pdata->endp = mgcp_endp_by_name(&cause, elem, pdata->cfg);
if (!pdata->endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Unable to find Endpoint `%s'\n", elem);
OSMO_ASSERT(cause < 0);
return cause;
}
break;
case 2:
if (strcasecmp("MGCP", elem)) {
LOG_MGCP_PDATA(pdata, LOGL_ERROR, "MGCP header parsing error\n");
LOGP(DLMGCP, LOGL_ERROR,
"MGCP header parsing error\n");
return -510;
}
break;
case 3:
if (strcmp("1.0", elem))
if (strcmp("1.0", elem)) {
LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
"not supported\n", elem);
return -528;
}
break;
}
i++;
}
if (i != 4) {
LOG_MGCP_PDATA(pdata, LOGL_ERROR, "MGCP status line too short.\n");
LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
pdata->trans = "000000";
pdata->endp = NULL;
return -510;
}
return 0;
}
static bool parse_x_osmo_ign(struct mgcp_parse_data *pdata, char *line)
{
char *saveptr = NULL;
if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
return false;
line += strlen(MGCP_X_OSMO_IGN_HEADER);
while (1) {
char *token = strtok_r(line, " ", &saveptr);
line = NULL;
if (!token)
break;
if (!strcasecmp(token, "C"))
pdata->hpars.x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
else
LOG_MGCP_PDATA(pdata, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
}
return true;
}
/*! Analyze and parse the the header of an MGCP message string.
* \param[inout] pdata caller provided memory to store the parsing results.
* \returns 0 when parsing was successful, negative (MGCP cause code) on error. */
int mgcp_parse_hdr_pars(struct mgcp_parse_data *pdata)
{
struct mgcp_parse_hdr_pars *hp = &pdata->hpars;
char *line;
int rc;
mgcp_parse_hdr_pars_init(hp);
for_each_line(line, pdata->save) {
if (!mgcp_check_param(line)) {
LOG_MGCP_PDATA(pdata, LOGL_NOTICE, "wrong MGCP option format: '%s'\n", line);
continue;
}
switch (toupper(line[0])) {
case 'L':
hp->lco_string = (const char *)line + 3;
rc = mgcp_parse_lco(pdata, &hp->lco, hp->lco_string);
if (rc < 0) {
LOG_MGCP_PDATA(pdata, LOGL_NOTICE, "Invalid LocalConnectionOptions line: '%s'", line);
switch (pdata->rq->verb) {
case MGCP_VERB_CRCX:
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_crcx_ctr_group,
MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
break;
case MGCP_VERB_MDCX:
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_mdcx_ctr_group,
MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
break;
default:
break;
}
return rc;
}
break;
case 'C':
hp->callid = (const char *)line + 3;
break;
case 'I':
hp->connid = (const char *)line + 3;
break;
case 'M':
hp->mode = mgcp_parse_conn_mode((const char *)line + 3);
break;
case 'X':
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
hp->remote_osmux_cid = mgcp_parse_osmux_cid(line);
break;
}
if (parse_x_osmo_ign(pdata, line))
break;
/* Ignore unknown X-headers */
break;
case '\0':
hp->have_sdp = true;
goto mgcp_header_done;
default:
LOG_MGCP_PDATA(pdata, LOGL_NOTICE, "unhandled option: '%c'/%d\n", *line, *line);
switch (pdata->rq->verb) {
case MGCP_VERB_CRCX:
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_crcx_ctr_group,
MGCP_CRCX_FAIL_UNHANDLED_PARAM));
break;
case MGCP_VERB_MDCX:
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_mdcx_ctr_group,
MGCP_MDCX_FAIL_UNHANDLED_PARAM));
break;
case MGCP_VERB_DLCX:
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_dlcx_ctr_group,
MGCP_DLCX_FAIL_UNHANDLED_PARAM));
break;
default:
break;
}
return -539;
}
}
mgcp_header_done:
return 0;
}
/*! Extract OSMUX CID from an MGCP parameter line (string).
* \param[in] line single parameter line from the MGCP message
* \returns OSMUX CID, -1 wildcard, -2 on error */
@@ -456,37 +204,42 @@ int mgcp_parse_osmux_cid(const char *line)
if (strcasecmp(line + 2, "Osmux: *") == 0) {
LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n");
return MGCP_PARSE_HDR_PARS_OSMUX_CID_WILDCARD;
return -1;
}
if (sscanf(line + 2 + 7, "%u", &osmux_cid) != 1) {
LOGP(DLMGCP, LOGL_ERROR, "Failed parsing Osmux in MGCP msg line: %s\n",
line);
return MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET;
return -2;
}
if (osmux_cid > OSMUX_CID_MAX) {
LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
osmux_cid, OSMUX_CID_MAX);
return MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET;
return -2;
}
LOGP(DLMGCP, LOGL_DEBUG, "MGCP client offered Osmux CID %u\n", osmux_cid);
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
return osmux_cid;
}
/*! Check MGCP parameter line (string) for plausibility.
* \param[in] endp pointer to endpoint (only used for log output)
* \param[in] line single parameter line from the MGCP message
* \returns true when line seems plausible, false on error */
bool mgcp_check_param(const char *line)
* \returns 1 when line seems plausible, 0 on error */
int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
{
const size_t line_len = strlen(line);
if (line[0] != '\0' && line_len < 2)
return false;
if (line[0] != '\0' && line_len < 2) {
LOGP(DLMGCP, LOGL_ERROR,
"Wrong MGCP option format: '%s' on %s\n",
line, endp->name);
return 0;
}
/* FIXME: A couple more checks wouldn't hurt... */
return true;
return 1;
}
/*! Check if the specified callid seems plausible.
@@ -501,6 +254,10 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
if (!endp)
return -1;
/* Accept any CallID for "X-Osmo-IGN: C" */
if (endp->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID)
return 0;
if (!callid)
return -1;
if (!endp->callid)
@@ -549,7 +306,7 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
}
/* Check if connection exists */
if (mgcp_endp_get_conn(endp, conn_id))
if (mgcp_conn_get(endp, conn_id))
return 0;
LOGPENDP(endp, DLMGCP, LOGL_ERROR,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -22,14 +22,9 @@
*
*/
#include <stdatomic.h>
#include <errno.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/stat_item.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_ratectr.h>
static const struct rate_ctr_desc mgcp_general_ctr_desc[] = {
@@ -43,7 +38,7 @@ static const struct rate_ctr_desc mgcp_general_ctr_desc[] = {
{ "mgcp:err_rx_no_endpoint", "can't find MGCP endpoint, probably we've used all allocated endpoints." },
};
static const struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
const static struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
.group_name_prefix = "mgcp",
.group_description = "mgcp general statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@@ -63,6 +58,7 @@ static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC] =
{ "crcx:no_remote_conn_desc", "no opposite end specified for connection." },
[MGCP_CRCX_FAIL_START_RTP] = { "crcx:start_rtp_failure", "failure to start RTP processing." },
[MGCP_CRCX_FAIL_REJECTED_BY_POLICY] = { "crcx:conn_rejected", "connection rejected by policy." },
[MGCP_CRCX_FAIL_NO_OSMUX] = { "crcx:no_osmux", "no osmux offered by peer." },
[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS] = { "crcx:conn_opt", "connection options invalid." },
[MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = { "crcx:codec_nego", "codec negotiation failure." },
@@ -71,7 +67,7 @@ static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
[MGCP_CRCX_FAIL_CLAIM] = { "crcx:claim", "endpoint can not be claimed." },
};
static const struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
.group_name_prefix = "crcx",
.group_description = "crxc statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@@ -93,11 +89,13 @@ static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = { "mdcx:conn_opt", "connection options invalid." },
[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] =
{ "mdcx:no_remote_conn_desc", "no opposite end specified for connection." },
[MGCP_MDCX_FAIL_BIND_PORT] = { "mdcx:bind_port", "port bind failure." },
[MGCP_MDCX_FAIL_START_RTP] = { "mdcx:start_rtp_failure", "failure to start RTP processing." },
[MGCP_MDCX_FAIL_REJECTED_BY_POLICY] = { "mdcx:conn_rejected", "connection rejected by policy." },
[MGCP_MDCX_DEFERRED_BY_POLICY] = { "mdcx:conn_deferred", "connection deferred by policy." },
[MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." },
};
static const struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
.group_name_prefix = "mdcx",
.group_description = "mdcx statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@@ -107,16 +105,19 @@ static const struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
[MGCP_DLCX_SUCCESS] = { "dlcx:success", "DLCX command processed successfully." },
[MGCP_DLCX_FAIL_WILDCARD] = { "dlcx:wildcard", "wildcard names in DLCX commands are unsupported." },
[MGCP_DLCX_FAIL_NO_CONN] = { "dlcx:no_conn", "endpoint specified in DLCX command has no active connections." },
[MGCP_DLCX_FAIL_INVALID_CALLID] =
{ "dlcx:callid", "CallId specified in DLCX command mismatches endpoint's CallId ." },
[MGCP_DLCX_FAIL_INVALID_CONNID] =
{ "dlcx:connid", "connection ID specified in DLCX command does not exist on endpoint." },
[MGCP_DLCX_FAIL_UNHANDLED_PARAM] = { "dlcx:unhandled_param", "unhandled parameter in DLCX command." },
[MGCP_DLCX_FAIL_REJECTED_BY_POLICY] = { "dlcx:rejected", "connection deletion rejected by policy." },
[MGCP_DLCX_DEFERRED_BY_POLICY] = { "dlcx:deferred", "connection deletion deferred by policy." },
[MGCP_DLCX_FAIL_AVAIL] = { "dlcx:unavailable", "endpoint unavailable." },
};
static const struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
.group_name_prefix = "dlcx",
.group_description = "dlcx statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@@ -125,12 +126,12 @@ static const struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
};
static const struct rate_ctr_desc e1_rate_ctr_desc[] = {
[E1_I460_TRAU_RX_FAIL_CTR] = { "e1:rx_fail", "Inbound I.460 TRAU failures." },
[E1_I460_TRAU_TX_FAIL_CTR] = { "e1:tx_fail", "Outbound I.460 TRAU failures." },
[E1_I460_TRAU_MUX_EMPTY_CTR] = { "e1:i460", "Outbound I.460 MUX queue empty." }
[E1_I460_TRAU_RX_FAIL_CTR] = {"e1:rx_fail", "Inbound I.460 TRAU failures."},
[E1_I460_TRAU_TX_FAIL_CTR] = {"e1:tx_fail", "Outbound I.460 TRAU failures."},
[E1_I460_TRAU_MUX_EMPTY_CTR] = {"e1:i460", "Outbound I.460 MUX queue empty."}
};
static const struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
const static struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
.group_name_prefix = "e1",
.group_description = "e1 statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@@ -138,7 +139,7 @@ static const struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
.ctr_desc = e1_rate_ctr_desc
};
static const struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
.group_name_prefix = "all_rtp_conn",
.group_description = "aggregated statistics for all rtp connections",
.class_id = 1,
@@ -146,202 +147,83 @@ static const struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
.ctr_desc = all_rtp_conn_rate_ctr_desc
};
static const struct rate_ctr_group_desc all_osmux_conn_rate_ctr_group_desc = {
.group_name_prefix = "all_osmux_conn",
.group_description = "aggregated statistics for all osmux connections",
.class_id = 1,
.num_ctr = ARRAY_SIZE(all_osmux_conn_rate_ctr_desc),
.ctr_desc = all_osmux_conn_rate_ctr_desc
};
/*! allocate global rate counters
* (called once at startup).
* \param[in] cfg mgw configuration for which the rate counters are allocated.
* \returns 0 on success, -EINVAL on failure. */
int mgcp_ratectr_global_alloc(struct mgcp_config *cfg)
static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group)
{
struct mgcp_ratectr_global *ratectr = &cfg->ratectr;
static atomic_uint general_rate_ctr_index = 0;
char ctr_name[512];
rate_ctr_group_free(rate_ctr_group);
return 0;
}
/*! allocate global rate counters into a given rate counter struct
* (called once at startup)
* \param[in] ctx talloc context.
* \param[out] ratectr struct that holds the counters
* \returns 0 on success, -EINVAL on failure */
int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr)
{
/* FIXME: Each new rate counter group requires a unique index. At the
* moment we generate an index using a counter, but perhaps there is
* a better way of assigning indices? */
static unsigned int general_rate_ctr_index = 0;
if (ratectr->mgcp_general_ctr_group == NULL) {
ratectr->mgcp_general_ctr_group =
rate_ctr_group_alloc(cfg, &mgcp_general_ctr_group_desc, general_rate_ctr_index++);
rate_ctr_group_alloc(ctx, &mgcp_general_ctr_group_desc, general_rate_ctr_index);
if (!ratectr->mgcp_general_ctr_group)
return -EINVAL;
snprintf(ctr_name, sizeof(ctr_name), "%s:general", cfg->domain);
rate_ctr_group_set_name(ratectr->mgcp_general_ctr_group, ctr_name);
talloc_set_destructor(ratectr->mgcp_general_ctr_group, free_rate_counter_group);
general_rate_ctr_index++;
}
return 0;
}
/*! free global rate counters
* (called once at process shutdown).
* \param[in] cfg mgw configuration for which the rate counters are allocated. */
void mgcp_ratectr_global_free(struct mgcp_config *cfg)
{
struct mgcp_ratectr_global *ratectr = &cfg->ratectr;
if (ratectr->mgcp_general_ctr_group) {
rate_ctr_group_free(ratectr->mgcp_general_ctr_group);
ratectr->mgcp_general_ctr_group = NULL;
}
}
/*! allocate trunk specific rate counters
* (called once on trunk initialization).
* \param[in] trunk mgw trunk for which the rate counters are allocated.
/*! allocate trunk specific rate counters into a given rate counter struct
* (called once on trunk initialization)
* \param[in] ctx talloc context.
* \param[out] ratectr struct that holds the counters
* \returns 0 on success, -EINVAL on failure */
int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk)
int mgcp_ratectr_trunk_alloc(void *ctx, struct mgcp_ratectr_trunk *ratectr)
{
struct mgcp_ratectr_trunk *ratectr = &trunk->ratectr;
static atomic_uint crcx_rate_ctr_index = 0;
static atomic_uint mdcx_rate_ctr_index = 0;
static atomic_uint dlcx_rate_ctr_index = 0;
static atomic_uint all_rtp_conn_rate_ctr_index = 0;
static atomic_uint all_osmux_conn_rate_ctr_index = 0;
char ctr_name[256];
/* FIXME: see comment in mgcp_ratectr_global_alloc() */
static unsigned int crcx_rate_ctr_index = 0;
static unsigned int mdcx_rate_ctr_index = 0;
static unsigned int dlcx_rate_ctr_index = 0;
static unsigned int all_rtp_conn_rate_ctr_index = 0;
if (ratectr->mgcp_crcx_ctr_group == NULL) {
ratectr->mgcp_crcx_ctr_group =
rate_ctr_group_alloc(trunk, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index++);
ratectr->mgcp_crcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index);
if (!ratectr->mgcp_crcx_ctr_group)
return -EINVAL;
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:crcx", mgcp_trunk_type_strs_str(trunk->trunk_type),
trunk->trunk_nr);
rate_ctr_group_set_name(ratectr->mgcp_crcx_ctr_group, ctr_name);
talloc_set_destructor(ratectr->mgcp_crcx_ctr_group, free_rate_counter_group);
crcx_rate_ctr_index++;
}
if (ratectr->mgcp_mdcx_ctr_group == NULL) {
ratectr->mgcp_mdcx_ctr_group =
rate_ctr_group_alloc(trunk, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index++);
ratectr->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index);
if (!ratectr->mgcp_mdcx_ctr_group)
return -EINVAL;
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:mdcx", mgcp_trunk_type_strs_str(trunk->trunk_type),
trunk->trunk_nr);
rate_ctr_group_set_name(ratectr->mgcp_mdcx_ctr_group, ctr_name);
talloc_set_destructor(ratectr->mgcp_mdcx_ctr_group, free_rate_counter_group);
mdcx_rate_ctr_index++;
}
if (ratectr->mgcp_dlcx_ctr_group == NULL) {
ratectr->mgcp_dlcx_ctr_group =
rate_ctr_group_alloc(trunk, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index++);
ratectr->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index);
if (!ratectr->mgcp_dlcx_ctr_group)
return -EINVAL;
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:dlcx", mgcp_trunk_type_strs_str(trunk->trunk_type),
trunk->trunk_nr);
rate_ctr_group_set_name(ratectr->mgcp_dlcx_ctr_group, ctr_name);
talloc_set_destructor(ratectr->mgcp_dlcx_ctr_group, free_rate_counter_group);
dlcx_rate_ctr_index++;
}
if (ratectr->all_rtp_conn_stats == NULL) {
ratectr->all_rtp_conn_stats = rate_ctr_group_alloc(trunk, &all_rtp_conn_rate_ctr_group_desc,
all_rtp_conn_rate_ctr_index++);
ratectr->all_rtp_conn_stats = rate_ctr_group_alloc(ctx, &all_rtp_conn_rate_ctr_group_desc,
all_rtp_conn_rate_ctr_index);
if (!ratectr->all_rtp_conn_stats)
return -EINVAL;
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:rtp_conn", mgcp_trunk_type_strs_str(trunk->trunk_type),
trunk->trunk_nr);
rate_ctr_group_set_name(ratectr->all_rtp_conn_stats, ctr_name);
talloc_set_destructor(ratectr->all_rtp_conn_stats, free_rate_counter_group);
all_rtp_conn_rate_ctr_index++;
}
if (ratectr->all_osmux_conn_stats == NULL) {
ratectr->all_osmux_conn_stats = rate_ctr_group_alloc(trunk, &all_osmux_conn_rate_ctr_group_desc,
all_osmux_conn_rate_ctr_index++);
if (!ratectr->all_osmux_conn_stats)
return -EINVAL;
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:osmux_conn", mgcp_trunk_type_strs_str(trunk->trunk_type),
trunk->trunk_nr);
rate_ctr_group_set_name(ratectr->all_osmux_conn_stats, ctr_name);
}
/* E1 specific */
if (trunk->trunk_type == MGCP_TRUNK_E1 && ratectr->e1_stats == NULL) {
ratectr->e1_stats = rate_ctr_group_alloc(trunk, &e1_rate_ctr_group_desc, mdcx_rate_ctr_index++);
if (ratectr->e1_stats == NULL) {
ratectr->e1_stats = rate_ctr_group_alloc(ctx, &e1_rate_ctr_group_desc, mdcx_rate_ctr_index);
if (!ratectr->e1_stats)
return -EINVAL;
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:e1", mgcp_trunk_type_strs_str(trunk->trunk_type),
trunk->trunk_nr);
rate_ctr_group_set_name(ratectr->e1_stats, ctr_name);
talloc_set_destructor(ratectr->e1_stats, free_rate_counter_group);
mdcx_rate_ctr_index++;
}
return 0;
}
/*! free trunk specific rate counters
* (called once when trunk is freed).
* \param[in] trunk mgw trunk on which the rate counters are allocated. */
void mgcp_ratectr_trunk_free(struct mgcp_trunk *trunk)
{
struct mgcp_ratectr_trunk *ratectr = &trunk->ratectr;
if (ratectr->mgcp_crcx_ctr_group) {
rate_ctr_group_free(ratectr->mgcp_crcx_ctr_group);
ratectr->mgcp_crcx_ctr_group = NULL;
}
if (ratectr->mgcp_mdcx_ctr_group) {
rate_ctr_group_free(ratectr->mgcp_mdcx_ctr_group);
ratectr->mgcp_mdcx_ctr_group = NULL;
}
if (ratectr->mgcp_dlcx_ctr_group) {
rate_ctr_group_free(ratectr->mgcp_dlcx_ctr_group);
ratectr->mgcp_dlcx_ctr_group = NULL;
}
if (ratectr->all_rtp_conn_stats) {
rate_ctr_group_free(ratectr->all_rtp_conn_stats);
ratectr->all_rtp_conn_stats = NULL;
}
if (ratectr->all_osmux_conn_stats) {
rate_ctr_group_free(ratectr->all_osmux_conn_stats);
ratectr->all_osmux_conn_stats = NULL;
}
/* E1 specific */
if (ratectr->e1_stats) {
rate_ctr_group_free(ratectr->e1_stats);
ratectr->e1_stats = NULL;
}
}
const struct osmo_stat_item_desc trunk_stat_desc[] = {
[TRUNK_STAT_ENDPOINTS_TOTAL] = { "endpoints:total",
"Number of endpoints that exist on the trunk",
"", 60, 0 },
[TRUNK_STAT_ENDPOINTS_USED] = { "endpoints:used",
"Number of endpoints in use",
"", 60, 0 },
};
const struct osmo_stat_item_group_desc trunk_statg_desc = {
.group_name_prefix = "trunk",
.group_description = "mgw trunk",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_items = ARRAY_SIZE(trunk_stat_desc),
.item_desc = trunk_stat_desc,
};
/*! allocate trunk specific stat items
* (called once on trunk initialization).
* \param[in] trunk for which the stat items are allocated.
* \returns 0 on success, -EINVAL on failure. */
int mgcp_stat_trunk_alloc(struct mgcp_trunk *trunk)
{
struct mgcp_stat_trunk *stats = &trunk->stats;
static unsigned int common_stat_index = 0;
char stat_name[256];
stats->common = osmo_stat_item_group_alloc(trunk, &trunk_statg_desc, common_stat_index);
if (!stats->common)
return -EINVAL;
snprintf(stat_name, sizeof(stat_name), "%s-%u:common", mgcp_trunk_type_strs_str(trunk->trunk_type),
trunk->trunk_nr);
osmo_stat_item_group_set_name(stats->common, stat_name);
common_stat_index++;
return 0;
}
/*! free trunk specific stat items
* (called once when trunk is freed).
* \param[in] trunk on which the stat items are allocated. */
void mgcp_stat_trunk_free(struct mgcp_trunk *trunk)
{
struct mgcp_stat_trunk *stats = &trunk->stats;
if (stats->common) {
osmo_stat_item_group_free(stats->common);
stats->common = NULL;
}
}

View File

@@ -1,104 +0,0 @@
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2013-2024 by sysmocom - s.f.m.c. GmbH
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/mgcp/mgcp_rtp_end.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
/***********************
* mgcp_rtp_end
**********************/
void mgcp_rtp_end_init(struct mgcp_rtp_end *end, struct mgcp_conn_rtp *conn_rtp)
{
struct mgcp_trunk *trunk = conn_rtp->conn->endp->trunk;
struct mgcp_config *cfg = trunk->cfg;
end->conn_rtp = conn_rtp;
end->rtp = NULL;
end->rtcp = NULL;
memset(&end->addr, 0, sizeof(end->addr));
end->rtcp_port = 0;
/* Set default values */
end->frames_per_packet = 0; /* unknown */
end->output_enabled = false;
end->maximum_packet_time = -1;
end->force_aligned_timing = trunk->force_aligned_timing;
end->force_constant_ssrc = trunk->force_constant_ssrc;
end->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
if (cfg->force_ptime) {
end->packet_duration_ms = cfg->force_ptime;
end->force_output_ptime = 1;
} else {
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
}
/* Make sure codec table is reset */
mgcp_codecset_reset(&end->cset);
}
void mgcp_rtp_end_cleanup(struct mgcp_rtp_end *end)
{
mgcp_rtp_end_free_port(end);
mgcp_codecset_reset(&end->cset);
}
void mgcp_rtp_end_set_packet_duration_ms(struct mgcp_rtp_end *end, uint32_t packet_duration_ms)
{
if (end->force_output_ptime)
return;
end->packet_duration_ms = packet_duration_ms;
}
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end)
{
return (osmo_sockaddr_port(&rtp_end->addr.u.sa) != 0) &&
(osmo_sockaddr_is_any(&rtp_end->addr) == 0);
}
/*! free allocated RTP and RTCP ports.
* \param[in] end RTP end */
void mgcp_rtp_end_free_port(struct mgcp_rtp_end *end)
{
if (end->rtp) {
osmo_iofd_free(end->rtp);
end->rtp = NULL;
}
if (end->rtcp) {
osmo_iofd_free(end->rtcp);
end->rtcp = NULL;
}
}

View File

@@ -98,7 +98,7 @@ static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
/* Helper function to update codec map information with additional data from
* SDP, called from: mgcp_parse_sdp_data() */
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
int payload_type, const char *audio_name)
int payload, const char *audio_name)
{
int i;
@@ -110,7 +110,7 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
/* Note: We can only update payload codecs that already exist
* in our codec list. If we get an unexpected payload type,
* we just drop it */
if (codecs[i].payload_type != payload_type)
if (codecs[i].payload_type != payload)
continue;
if (sscanf(audio_name, "%63[^/]/%d/%d",
@@ -127,7 +127,7 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
return;
}
LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload_type,
LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload,
audio_name);
}
@@ -199,7 +199,7 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
unsigned int pt;
unsigned int count = 0;
char delimiter;
unsigned int param_val;
unsigned int amr_octet_aligned;
memset(fmtp_param, 0, sizeof(*fmtp_param));
@@ -239,30 +239,13 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
if (delimiter == ';' || delimiter == ',')
str_ptr[strlen(str_ptr) - 1] = '\0';
/* We support the following codec parameters:
*
* AMR: octet-align parameter of RFC 4867 section 8.3;
* FR & EFR: tw-ts-001 parameter, Osmocom private;
* HR: tw-ts-002 parameter, Osmocom private.
*
* tw-ts-001 and tw-ts-002 Osmocom-private parameters are used
* only in the context of OsmoBSC driving an E1 MGW endpoint.
*/
if (sscanf(param_str, "octet-align=%d", &param_val) == 1) {
/* AMR octet aligned parameter */
if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
fmtp_param->param.amr_octet_aligned_present = true;
fmtp_param->param.amr_octet_aligned = false;
if (param_val == 1)
if (amr_octet_aligned == 1)
fmtp_param->param.amr_octet_aligned = true;
} else if (sscanf(param_str, "tw-ts-001=%d", &param_val) == 1) {
fmtp_param->param.fr_efr_twts001_present = true;
fmtp_param->param.fr_efr_twts001 = false;
if (param_val == 1)
fmtp_param->param.fr_efr_twts001 = true;
} else if (sscanf(param_str, "tw-ts-002=%d", &param_val) == 1) {
fmtp_param->param.hr_twts002_present = true;
fmtp_param->param.hr_twts002 = false;
if (param_val == 1)
fmtp_param->param.hr_twts002 = true;
}
param_str = strtok(NULL, " ");
@@ -330,13 +313,16 @@ static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_
}
/*! Analyze SDP input string.
* \param[inout] p provided memory to store the parsing results.
* \param[in] endp trunk endpoint.
* \param[out] conn associated rtp connection.
* \param[out] caller provided memory to store the parsing results.
*
* Note: In conn (conn->end) the function returns the packet duration,
* rtp port, rtcp port and the codec information.
* \returns 0 on success, -1 on failure. */
int mgcp_parse_sdp_data(struct mgcp_parse_data *p)
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
{
OSMO_ASSERT(p);
struct mgcp_parse_sdp *sdp = &p->sdp;
struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
unsigned int codecs_used = 0;
struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
@@ -346,14 +332,19 @@ int mgcp_parse_sdp_data(struct mgcp_parse_data *p)
char *line;
unsigned int i;
void *tmp_ctx = talloc_new(NULL);
struct mgcp_rtp_end *rtp;
int payload_type;
int payload;
int ptime, ptime2 = 0;
char audio_name[64];
int port, rc;
OSMO_ASSERT(endp);
OSMO_ASSERT(conn);
OSMO_ASSERT(p);
rtp = &conn->end;
memset(&codecs, 0, sizeof(codecs));
mgcp_parse_sdp_init(sdp);
for_each_line(line, p->save) {
switch (line[0]) {
@@ -364,26 +355,26 @@ int mgcp_parse_sdp_data(struct mgcp_parse_data *p)
/* skip these SDP attributes */
break;
case 'a':
if (sscanf(line, "a=rtpmap:%d %63s", &payload_type, audio_name) == 2) {
codecs_update(tmp_ctx, codecs, codecs_used, payload_type, audio_name);
if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) {
codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
break;
}
if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
if (ptime2 > 0 && ptime2 != ptime)
sdp->ptime = 0;
rtp->packet_duration_ms = 0;
else
sdp->ptime = ptime;
rtp->packet_duration_ms = ptime;
break;
}
if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
sdp->maxptime = ptime2;
rtp->maximum_packet_time = ptime2;
break;
}
if (strncmp("a=fmtp:", line, 6) == 0) {
rc = fmtp_from_sdp(tmp_ctx, &fmtp_params[fmtp_used], line);
rc = fmtp_from_sdp(conn->conn, &fmtp_params[fmtp_used], line);
if (rc >= 0)
fmtp_used++;
break;
@@ -392,23 +383,31 @@ int mgcp_parse_sdp_data(struct mgcp_parse_data *p)
break;
case 'm':
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
if (rc == 1)
sdp->rtp_port = port;
if (rc == 1) {
rtp->rtp_port = htons(port);
rtp->rtcp_port = htons(port + 1);
}
rc = pt_from_sdp(tmp_ctx, codecs, ARRAY_SIZE(codecs), line);
rc = pt_from_sdp(conn->conn, codecs,
ARRAY_SIZE(codecs), line);
if (rc > 0)
codecs_used = rc;
break;
case 'c':
if (audio_ip_from_sdp(&sdp->rem_addr, line) < 0) {
talloc_free(tmp_ctx);
if (audio_ip_from_sdp(&rtp->addr, line) < 0)
return -1;
}
break;
default:
LOGP(DLMGCP, LOGL_NOTICE,
"Unhandled SDP option: '%c'/%d on %s\n",
line[0], line[0], p->epname);
if (p->endp)
/* TODO: Check spec: We used the bare endpoint number before,
* now we use the endpoint name as a whole? Is this allowed? */
LOGP(DLMGCP, LOGL_NOTICE,
"Unhandled SDP option: '%c'/%d on %s\n",
line[0], line[0], endp->name);
else
LOGP(DLMGCP, LOGL_NOTICE,
"Unhandled SDP option: '%c'/%d\n",
line[0], line[0]);
break;
}
}
@@ -422,22 +421,23 @@ int mgcp_parse_sdp_data(struct mgcp_parse_data *p)
/* Store parsed codec information */
for (i = 0; i < codecs_used; i++) {
codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
rc = mgcp_codecset_add_codec(&sdp->cset, codecs[i].payload_type, codecs[i].map_line, codec_param);
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
if (rc < 0)
LOGP(DLMGCP, LOGL_NOTICE, "%s: failed to add codec\n", p->epname);
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
}
talloc_free(tmp_ctx);
LOGP(DLMGCP, LOGL_NOTICE,
"%s: Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
p->epname, sdp->rtp_port, osmo_sockaddr_ntop(&sdp->rem_addr.u.sa, ipbuf), sdp->ptime);
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
"Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
ntohs(rtp->rtp_port), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
rtp->packet_duration_ms);
if (codecs_used == 0)
LOGPC(DLMGCP, LOGL_NOTICE, "none");
for (i = 0; i < codecs_used; i++) {
LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
sdp->cset.codecs[i].payload_type,
strlen(sdp->cset.codecs[i].subtype_name) ? sdp->cset.codecs[i].subtype_name : "unknown");
rtp->codecs[i].payload_type,
rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown");
LOGPC(DLMGCP, LOGL_NOTICE, " ");
}
LOGPC(DLMGCP, LOGL_NOTICE, "\n");
@@ -487,10 +487,34 @@ static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_
}
/* Add fmtp strings to sdp payload */
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len,
const char *fmtp_extra)
{
unsigned int i;
int rc;
int fmtp_extra_pt = -1;
char *fmtp_extra_pars = "";
/* When no fmtp parameters ara available but an fmtp extra string
* is configured, just add the fmtp extra string */
if (fmtp_params_len == 0 && fmtp_extra) {
return msgb_printf(sdp, "%s\r\n", fmtp_extra);
}
/* When there is fmtp extra configured we dissect it in order to drop
* in the configured extra parameters at the right place when
* generating the fmtp strings. */
if (fmtp_extra) {
if (sscanf(fmtp_extra, "a=fmtp:%d ", &fmtp_extra_pt) != 1)
fmtp_extra_pt = -1;
fmtp_extra_pars = strstr(fmtp_extra, " ");
if (!fmtp_extra_pars)
fmtp_extra_pars = "";
else
fmtp_extra_pars++;
}
for (i = 0; i < fmtp_params_len; i++) {
rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
@@ -507,6 +531,13 @@ static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsign
return -EINVAL;
}
/* Append extra parameters from fmtp extra */
if (fmtp_params[i].payload_type == fmtp_extra_pt) {
rc = msgb_printf(sdp, " %s", fmtp_extra_pars);
if (rc < 0)
return -EINVAL;
}
rc = msgb_printf(sdp, "\r\n");
if (rc < 0)
return -EINVAL;
@@ -526,6 +557,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
const char *addr)
{
const struct mgcp_rtp_codec *codec;
const char *fmtp_extra;
const char *audio_name;
int payload_type;
struct sdp_fmtp_param fmtp_param;
@@ -541,7 +573,10 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
OSMO_ASSERT(sdp);
OSMO_ASSERT(addr);
codec = conn->end.cset.codec;
/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
&codec, &fmtp_extra,
(struct mgcp_conn_rtp *)conn);
audio_name = codec->audio_name;
payload_type = codec->payload_type;
@@ -564,7 +599,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
payload_types[0] = payload_type;
if (mgcp_conn_rtp_is_osmux(conn))
local_port = endp->trunk->cfg->osmux.local_port;
local_port = endp->cfg->osmux_port;
else
local_port = conn->end.local_port;
rc = add_audio(sdp, payload_types, 1, local_port);
@@ -577,29 +612,13 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
goto buffer_too_small;
}
/* Include a=fmtp line in MGCP response only for AMR
* octet-align parameter, not for tw-ts-* parameters.
* Rationale:
*
* - tw-ts-* parameters exist meaningfully only for E1
* endpoints driven by OsmoBSC, not in any other
* context. libosmo-mgcp-client used by OsmoBSC
* completely ignores all fmtp lines, has no code
* to parse them at all.
*
* - The SDP we return is supposed to indicate what
* _we_ accept, conceptually independent from what
* the remote accepts. And we always accept TW-TS-001
* and TW-TS-002 RTP formats going to E1 DL, whether
* or not we send them in UL per client request.
*/
if (codec->param_present && codec->param.amr_octet_aligned_present) {
if (codec->param_present) {
fmtp_param.payload_type = payload_type;
fmtp_param.param = codec->param;
fmtp_params[0] = fmtp_param;
fmtp_params_len = 1;
}
rc = add_fmtp(sdp, fmtp_params, fmtp_params_len);
rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra);
if (rc < 0)
goto buffer_too_small;
}

View File

@@ -28,18 +28,12 @@
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
#if defined(__has_attribute)
#if __has_attribute(no_sanitize)
__attribute__((no_sanitize("undefined")))
#endif
#endif
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)
{
struct mgcp_rtp_state *state = &conn->state;
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
*expected = state->stats.cycles + state->stats.max_seq;
*expected = *expected - state->stats.base_seq + 1;
@@ -80,10 +74,10 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
int ploss;
int nchars;
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR);
struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR);
struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR);
struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
struct rate_ctr *octets_rx = &conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR];
struct rate_ctr *packets_tx = &conn->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR];
struct rate_ctr *octets_tx = &conn->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR];
calc_loss(conn, &expected, &ploss);
jitter = calc_jitter(&conn->state);
@@ -99,7 +93,7 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
str += nchars;
str_len -= nchars;
if (conn->conn->endp->trunk->cfg->osmux.usage != OSMUX_USAGE_OFF) {
if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) {
/* Error Counter */
nchars = snprintf(str, str_len,
"\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64,
@@ -112,12 +106,9 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
str_len -= nchars;
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
struct rate_ctr *osmux_chunks_rx, *osmux_octets_rx;
osmux_chunks_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_CHUNKS_RX_CTR);
osmux_octets_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_OCTETS_RX_CTR);
snprintf(str, str_len,
"\r\nX-Osmux-ST: CR=%" PRIu64 ", BR=%" PRIu64,
osmux_chunks_rx->current, osmux_octets_rx->current);
"\r\nX-Osmux-ST: CR=%u, BR=%u",
conn->osmux.stats.chunks, conn->osmux.stats.octets);
}
}
@@ -141,7 +132,7 @@ void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn)
* keep this option open: */
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
mgcp_format_stats_rtp(str, str_len, mgcp_conn_get_conn_rtp(conn));
mgcp_format_stats_rtp(str, str_len, &conn->u.rtp);
break;
default:
break;

View File

@@ -1,207 +0,0 @@
/*
* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <inttypes.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/eventfd.h>
#include <sys/types.h>
#include <unistd.h>
#include <talloc.h>
#include <osmocom/mgcp/mgcp_threads_queue.h>
/*
classic lamport circular lockfree spsc queue:
every "side" only writes its own ptr, but may read the other sides ptr
notify reader using eventfd as soon as element is added, reader then reads until
read fails
-> reader pops in a loop until FALSE and might get spurious events because it
read before it was notified, which is fine
-> writing pushes *the same data* in a loop until TRUE, blocks
shutting this down requires
1) to stop reading and pushing
2) ONE side to take care of the eventfds
*/
static struct spsc *spsc_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr, bool blockw)
{
struct spsc *q = talloc_zero_size(talloc_ctx, sizeof(struct spsc) + sizeof(uintptr_t) * count);
atomic_init(&q->readptr, 0);
atomic_init(&q->writeptr, 0);
q->efd_r = eventfd(0, blockr ? 0 : EFD_NONBLOCK);
q->efd_w = eventfd(1, blockw ? 0 : EFD_NONBLOCK);
q->count = count;
q->size_per_buf = size_per_buf;
q->buf = talloc_zero_size(q, size_per_buf * count);
for (int i = 0; i < count; i++)
q->data[i] = (uintptr_t)q->buf + i * size_per_buf;
return q;
}
static void spsc_deinit(struct spsc *q)
{
talloc_free(q->buf);
close(q->efd_r);
close(q->efd_w);
talloc_free(q);
}
static ssize_t spsc_check_r(struct spsc *q)
{
uint64_t efdr;
return read(q->efd_r, &efdr, sizeof(uint64_t));
}
static ssize_t spsc_check_w(struct spsc *q)
{
uint64_t efdr;
return read(q->efd_w, &efdr, sizeof(uint64_t));
}
static void spsc_notify_r(struct spsc *q)
{
uint64_t efdu = 1;
write(q->efd_r, &efdu, sizeof(uint64_t));
}
static void spsc_notify_w(struct spsc *q)
{
uint64_t efdu = 1;
write(q->efd_w, &efdu, sizeof(uint64_t));
}
/*! Adds element to the queue by copying the data.
* \param[in] q queue.
* \param[in] elem input buffer, must match the originally configured queue buffer size!.
* \returns true if queue was not full and element was successfully pushed */
bool spsc_push(struct spsc *q, void *elem)
{
size_t cur_wp, cur_rp;
cur_wp = atomic_load_explicit(&q->writeptr, memory_order_relaxed);
cur_rp = atomic_load_explicit(&q->readptr, memory_order_acquire);
if ((cur_wp + 1) % q->count == cur_rp) {
spsc_check_w(q); /* blocks, ensures next (!) call succeeds */
return false;
}
memcpy((void *)q->data[cur_wp], elem, q->size_per_buf);
atomic_store_explicit(&q->writeptr, (cur_wp + 1) % q->count, memory_order_release);
spsc_notify_r(q); /* fine after release */
return true;
}
/*! Reads the read-fd of the queue, which, depending on settings passed on queue creation, blocks.
* This function can be used to deliberately wait for a non-empty queue on the read side.
* \param[in] q queue.
* \returns result of reading the fd. */
ssize_t spsc_prep_pop(struct spsc *q)
{
return spsc_check_r(q);
}
/*! Removes element from the queue by copying the data.
* \param[in] q queue.
* \param[in] elem output buffer, must match the originally configured queue buffer size!.
* \returns true if queue was not empty and element was successfully removed */
bool spsc_pop(struct spsc *q, void *elem)
{
size_t cur_wp, cur_rp;
cur_wp = atomic_load_explicit(&q->writeptr, memory_order_acquire);
cur_rp = atomic_load_explicit(&q->readptr, memory_order_relaxed);
if (cur_wp == cur_rp) /* blocks via prep_pop */
return false;
memcpy(elem, (void *)q->data[cur_rp], q->size_per_buf);
atomic_store_explicit(&q->readptr, (cur_rp + 1) % q->count, memory_order_release);
spsc_notify_w(q);
return true;
}
/*! Creates a bidirectional queue channel that consists of two queues, one in each direction,
* commonly referred to as a and b side.
* \param[in] talloc_ctx allocation context.
* \param[in] count number of buffers per queue.
* \param[in] size_per_buf size of buffers per queue.
* \param[in] blockr_a should reading the a-side read fd block?.
* \param[in] blockw_a should reading the a-side write fd block?.
* \param[in] blockr_b should reading the b-side read fd block?.
* \param[in] blockw_b should reading the b-side write fd block?.
* \returns queue channel */
struct qchan spsc_chan_init_ex(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr_a,
bool blockw_a, bool blockr_b, bool blockw_b)
{
struct qchan q;
q.a = spsc_init(talloc_ctx, count, size_per_buf, blockr_a, blockw_a);
q.b = spsc_init(talloc_ctx, count, size_per_buf, blockr_b, blockw_b);
return q;
}
/*! Creates a bidirectional queue channel that consists of two queues, one in each direction,
* commonly referred to as a and b side.
* \param[in] talloc_ctx allocation context.
* \param[in] count number of buffers per queue.
* \param[in] size_per_buf size of buffers per queue.
* \returns queue channel */
struct qchan spsc_chan_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf)
{
return spsc_chan_init_ex(talloc_ctx, count, size_per_buf, false, true, false, true);
}
/*! Closes a bidirectional queue channel.
* \param[in] q queue */
void spsc_chan_close(struct qchan *q)
{
spsc_deinit(q->a);
spsc_deinit(q->b);
free(q);
}
/*! Gets queue channel read/write fd for a/b side according to function name.
* \param[in] q queue channel.
* \returns fd */
int spsc_get_a_rdfd(struct qchan *q)
{
return q->a->efd_r;
}
/*! Gets queue channel read/write fd for a/b side according to function name.
* \param[in] q queue channel.
* \returns fd */
int spsc_get_b_rdfd(struct qchan *q)
{
return q->b->efd_r;
}
/*! Gets queue channel read/write fd for a/b side according to function name.
* \param[in] q queue channel.
* \returns fd */
int spsc_get_a_wrfd(struct qchan *q)
{
return q->a->efd_w;
}
/*! Gets queue channel read/write fd for a/b side according to function name.
* \param[in] q queue channel.
* \returns fd */
int spsc_get_b_wrfd(struct qchan *q)
{
return q->b->efd_w;
}

View File

@@ -27,31 +27,14 @@
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_e1.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/core/stat_item.h>
const struct value_string mgcp_trunk_type_strs[] = {
{ MGCP_TRUNK_VIRTUAL, "virtual" },
{ MGCP_TRUNK_E1, "e1" },
{ 0, NULL }
};
/* Free trunk, this function is automatically called by talloc_free when the trunk is freed. It does not free the
* endpoints on the trunk, this must be done separately before freeing the trunk. */
static int trunk_free_talloc_destructor(struct mgcp_trunk *trunk)
{
llist_del(&trunk->entry);
mgcp_ratectr_trunk_free(trunk);
mgcp_stat_trunk_free(trunk);
return 0;
}
/*! allocate trunk and add it to the trunk list.
/*! allocate trunk and add it (if required) to the trunk list.
* (called once at startup by VTY).
* \param[in] cfg mgcp configuration.
* \param[in] ttype trunk type.
* \param[in] nr trunk number.
* \returns pointer to allocated trunk, NULL on failure. */
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, unsigned int nr)
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
{
struct mgcp_trunk *trunk;
@@ -74,9 +57,7 @@ struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_typ
llist_add_tail(&trunk->entry, &cfg->trunks);
mgcp_ratectr_trunk_alloc(trunk);
mgcp_stat_trunk_alloc(trunk);
talloc_set_destructor(trunk, trunk_free_talloc_destructor);
mgcp_ratectr_trunk_alloc(cfg, &trunk->ratectr);
return trunk;
}
@@ -123,7 +104,7 @@ int mgcp_trunk_alloc_endpts(struct mgcp_trunk *trunk)
OSMO_ASSERT(number_endpoints < 65534);
/* allocate pointer array for the endpoints */
trunk->endpoints = talloc_zero_array(trunk, struct mgcp_endpoint*,
trunk->endpoints = talloc_zero_array(trunk->cfg, struct mgcp_endpoint*,
number_endpoints);
if (!trunk->endpoints)
return -1;
@@ -140,8 +121,7 @@ int mgcp_trunk_alloc_endpts(struct mgcp_trunk *trunk)
/* make the endpoints we just created available to the MGW code */
trunk->number_endpoints = number_endpoints;
osmo_stat_item_set(osmo_stat_item_group_get_item(trunk->stats.common, TRUNK_STAT_ENDPOINTS_TOTAL),
trunk->number_endpoints);
return 0;
}
@@ -185,7 +165,7 @@ int mgcp_trunk_equip(struct mgcp_trunk *trunk)
* \param[in] ttype trunk type.
* \param[in] nr trunk number.
* \returns pointer to trunk configuration, NULL on error. */
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, unsigned int nr)
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
{
struct mgcp_trunk *trunk;
@@ -198,9 +178,9 @@ struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_tr
}
/* Made public for unit-testing, do not use from outside this file */
int e1_trunk_nr_from_epname(unsigned int *trunk_nr, const char *epname)
int e1_trunk_nr_from_epname(const char *epname)
{
unsigned long trunk_nr_temp;
unsigned long int trunk_nr;
size_t prefix_len;
char *str_trunk_nr_end;
@@ -209,41 +189,13 @@ int e1_trunk_nr_from_epname(unsigned int *trunk_nr, const char *epname)
return -EINVAL;
errno = 0;
trunk_nr_temp = strtoul(epname + prefix_len, &str_trunk_nr_end, 10);
if (errno == ERANGE || trunk_nr_temp > 64
trunk_nr = strtoul(epname + prefix_len, &str_trunk_nr_end, 10);
if (errno == ERANGE || trunk_nr > 64
|| epname + prefix_len == str_trunk_nr_end
|| str_trunk_nr_end[0] != '/')
return -EINVAL;
else {
*trunk_nr = (unsigned int)trunk_nr_temp;
return 0;
}
}
/* Check if the domain name, which is supplied with the endpoint name
* matches the configuration. */
static int check_domain_name(const char *epname, const struct mgcp_config *cfg)
{
char *domain_to_check;
domain_to_check = strstr(epname, "@");
if (!domain_to_check) {
LOGP(DLMGCP, LOGL_ERROR, "missing domain name in endpoint name \"%s\", expecting \"%s\"\n",
epname, cfg->domain);
return -EINVAL;
}
/* Accept any domain if configured as "*" */
if (!strcmp(cfg->domain, "*"))
return 0;
if (strcmp(domain_to_check+1, cfg->domain) != 0) {
LOGP(DLMGCP, LOGL_ERROR, "wrong domain name in endpoint name \"%s\", expecting \"%s\"\n",
epname, cfg->domain);
return -EINVAL;
}
return 0;
else
return trunk_nr;
}
/*! Find a trunk by the trunk prefix in the endpoint name.
@@ -254,23 +206,18 @@ struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char
{
size_t prefix_len;
char epname_lc[MGCP_ENDPOINT_MAXLEN];
unsigned int trunk_nr;
int rc;
int trunk_nr;
osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
epname = epname_lc;
/* All endpoint names require a domain as suffix */
if (check_domain_name(epname, cfg))
return NULL;
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, prefix_len) == 0) {
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
}
rc = e1_trunk_nr_from_epname(&trunk_nr, epname);
if (rc == 0)
trunk_nr = e1_trunk_nr_from_epname(epname);
if (trunk_nr >= 0)
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_E1, trunk_nr);
/* Earlier versions of osmo-mgw were accepting endpoint names
@@ -306,43 +253,3 @@ struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigne
return NULL;
}
/* Try to find a free port by attempting to bind on it. Also handle the
* counter that points on the next free port. Since we have a pointer
* to the next free port, binding should in work on the first attempt in
* general. In case of failure the next port is tried until the whole port
* range is tried once. */
int mgcp_trunk_allocate_conn_rtp_ports(struct mgcp_trunk *trunk, struct mgcp_conn_rtp *conn_rtp)
{
int i;
struct mgcp_port_range *range;
unsigned int tries;
OSMO_ASSERT(trunk);
OSMO_ASSERT(conn_rtp);
range = &trunk->cfg->net_ports;
pthread_mutex_lock(&range->lock);
/* attempt to find a port */
tries = (range->range_end - range->range_start) / 2;
for (i = 0; i < tries; ++i) {
int rc;
if (range->last_port >= range->range_end)
range->last_port = range->range_start;
rc = mgcp_conn_rtp_bind_rtp_ports(conn_rtp, range->last_port);
range->last_port += 2;
if (rc == 0) {
pthread_mutex_unlock(&range->lock);
return 0;
}
}
pthread_mutex_unlock(&range->lock);
LOGPCONN(conn_rtp->conn, DLMGCP, LOGL_ERROR,
"Allocating a RTP/RTCP port failed %u times.\n", tries);
return -1;
}

View File

@@ -65,17 +65,17 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, "mgcp%s", VTY_NEWLINE);
vty_out(vty, " domain %s%s", g_cfg->domain, VTY_NEWLINE);
if (strlen(g_cfg->local_ip))
if (g_cfg->local_ip)
vty_out(vty, " local ip %s%s", g_cfg->local_ip, VTY_NEWLINE);
vty_out(vty, " bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE);
vty_out(vty, " bind port %u%s", g_cfg->source_port, VTY_NEWLINE);
vty_out(vty, " rtp port-range %u %u%s",
g_cfg->net_ports.range_start, g_cfg->net_ports.range_end,
VTY_NEWLINE);
if (strlen(g_cfg->net_ports.bind_addr_v4))
if (g_cfg->net_ports.bind_addr_v4)
vty_out(vty, " rtp bind-ip %s%s",
g_cfg->net_ports.bind_addr_v4, VTY_NEWLINE);
if (strlen(g_cfg->net_ports.bind_addr_v6))
if (g_cfg->net_ports.bind_addr_v6)
vty_out(vty, " rtp bind-ip-v6 %s%s",
g_cfg->net_ports.bind_addr_v6, VTY_NEWLINE);
if (g_cfg->net_ports.bind_addr_probe)
@@ -83,8 +83,6 @@ static int config_write_mgcp(struct vty *vty)
else
vty_out(vty, " no rtp ip-probing%s", VTY_NEWLINE);
vty_out(vty, " rtp ip-dscp %d%s", g_cfg->endp_dscp, VTY_NEWLINE);
if (g_cfg->endp_priority)
vty_out(vty, " rtp socket-priority %d%s", g_cfg->endp_priority, VTY_NEWLINE);
if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
vty_out(vty, " rtp keep-alive once%s", VTY_NEWLINE);
else if (trunk->keepalive_interval)
@@ -111,20 +109,25 @@ static int config_write_mgcp(struct vty *vty)
VTY_NEWLINE);
} else
vty_out(vty, " no rtp-patch%s", VTY_NEWLINE);
if (trunk->audio_fmtp_extra)
vty_out(vty, " sdp audio fmtp-extra %s%s",
trunk->audio_fmtp_extra, VTY_NEWLINE);
vty_out(vty, " %ssdp audio-payload send-ptime%s",
trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE);
vty_out(vty, " %ssdp audio-payload send-name%s",
trunk->audio_send_name ? "" : "no ", VTY_NEWLINE);
vty_out(vty, " number endpoints %u%s",
trunk->v.vty_number_endpoints, VTY_NEWLINE);
if (strlen(g_cfg->call_agent_addr))
vty_out(vty, " %sallow-transcoding%s",
trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE);
if (g_cfg->call_agent_addr)
vty_out(vty, " call-agent ip %s%s", g_cfg->call_agent_addr,
VTY_NEWLINE);
if (g_cfg->force_ptime > 0)
vty_out(vty, " rtp force-ptime %d%s", g_cfg->force_ptime,
VTY_NEWLINE);
switch (g_cfg->osmux.usage) {
switch (g_cfg->osmux) {
case OSMUX_USAGE_ON:
vty_out(vty, " osmux on%s", VTY_NEWLINE);
break;
@@ -136,23 +139,17 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " osmux off%s", VTY_NEWLINE);
break;
}
if (g_cfg->osmux.usage != OSMUX_USAGE_OFF) {
if (g_cfg->osmux.local_addr_v4)
vty_out(vty, " osmux bind-ip %s%s",
g_cfg->osmux.local_addr_v4, VTY_NEWLINE);
if (g_cfg->osmux.local_addr_v6)
vty_out(vty, " osmux bind-ip-v6 %s%s",
g_cfg->osmux.local_addr_v6, VTY_NEWLINE);
if (g_cfg->osmux) {
vty_out(vty, " osmux bind-ip %s%s",
g_cfg->osmux_addr, VTY_NEWLINE);
vty_out(vty, " osmux batch-factor %d%s",
g_cfg->osmux.batch_factor, VTY_NEWLINE);
g_cfg->osmux_batch, VTY_NEWLINE);
vty_out(vty, " osmux batch-size %u%s",
g_cfg->osmux.batch_size, VTY_NEWLINE);
g_cfg->osmux_batch_size, VTY_NEWLINE);
vty_out(vty, " osmux port %u%s",
g_cfg->osmux.local_port, VTY_NEWLINE);
g_cfg->osmux_port, VTY_NEWLINE);
vty_out(vty, " osmux dummy %s%s",
g_cfg->osmux.dummy_padding ? "on" : "off", VTY_NEWLINE);
vty_out(vty, " osmux peer-behind-nat %s%s",
g_cfg->osmux.peer_behind_nat ? "on" : "off", VTY_NEWLINE);
g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
}
if (g_cfg->conn_timeout)
@@ -165,16 +162,16 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
{
struct mgcp_rtp_state *state = &conn->state;
struct mgcp_rtp_end *end = &conn->end;
struct mgcp_rtp_codec *codec = end->cset.codec;
struct mgcp_rtp_codec *codec = end->codec;
struct rate_ctr *tx_packets, *tx_bytes;
struct rate_ctr *rx_packets, *rx_bytes;
struct rate_ctr *dropped_packets;
tx_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR);
tx_bytes = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR);
rx_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
rx_bytes = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR);
dropped_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_DROPPED_PACKETS_CTR);
tx_packets = &conn->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR];
tx_bytes = &conn->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR];
rx_packets = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
rx_bytes = &conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR];
dropped_packets = &conn->rate_ctr_group->ctr[RTP_DROPPED_PACKETS_CTR];
vty_out(vty,
" Packets Sent: %" PRIu64 " (%" PRIu64 " bytes total)%s"
@@ -184,52 +181,28 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
" Payload Type: %d Rate: %u Channels: %d %s"
" Frame Duration: %u Frame Denominator: %u%s"
" FPP: %d Packet Duration: %u%s"
" Audio-Name: %s Sub-Type: %s%s"
" FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"
" Output-Enabled: %d Force-PTIME: %d%s",
tx_packets->current, tx_bytes->current, VTY_NEWLINE,
rx_packets->current, rx_bytes->current, VTY_NEWLINE,
state->in_stream.err_ts_ctr->current,
state->out_stream.err_ts_ctr->current,
VTY_NEWLINE,
VTY_NEWLINE,
dropped_packets->current, VTY_NEWLINE,
codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
codec->frame_duration_num, codec->frame_duration_den,
VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms,
VTY_NEWLINE, codec->audio_name,
VTY_NEWLINE, end->fmtp_extra, codec->audio_name,
codec->subtype_name, VTY_NEWLINE, end->output_enabled,
end->force_output_ptime, VTY_NEWLINE);
if (mgcp_conn_rtp_is_osmux(conn)) {
struct rate_ctr *rx_chunks, *rx_octets, *rtp_tx, *rtp_tx_dropped, *octets_tx;
rx_chunks = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_CHUNKS_RX_CTR);
rx_octets = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_OCTETS_RX_CTR);
rtp_tx = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_RTP_PACKETS_TX_CTR);
rtp_tx_dropped = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
octets_tx = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_AMR_OCTETS_TX_CTR);
vty_out(vty,
" Osmux:%s"
" State: %s%s"
" Local CID: %d%s"
" Remote CID: %d%s"
" Chunks received: %" PRIu64 " (%" PRIu64 " bytes total)%s"
" RTP Packets encoded (Tx): %" PRIu64 " (%" PRIu64 " AMR octets total)%s"
" AMR payloads Dropped (Tx): %" PRIu64 "%s",
VTY_NEWLINE, osmux_state_str(conn->osmux.state), VTY_NEWLINE,
conn->osmux.local_cid_allocated ? conn->osmux.local_cid : -1, VTY_NEWLINE,
conn->osmux.remote_cid_present ? conn->osmux.remote_cid : -1, VTY_NEWLINE,
rx_chunks->current, rx_octets->current, VTY_NEWLINE,
rtp_tx->current, octets_tx->current, VTY_NEWLINE,
rtp_tx_dropped->current, VTY_NEWLINE
);
}
}
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
unsigned int trunk_nr, enum mgcp_trunk_type trunk_type, int show_stats)
int trunk_nr, enum mgcp_trunk_type trunk_type, int show_stats)
{
struct mgcp_conn *conn;
vty_out(vty, "%s trunk %u endpoint %s:%s",
vty_out(vty, "%s trunk %d endpoint %s:%s",
trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", trunk_nr, endp->name, VTY_NEWLINE);
vty_out(vty, " Availability: %s%s",
mgcp_endp_avail(endp) ? "available" : "not in service", VTY_NEWLINE);
@@ -243,7 +216,7 @@ static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
vty_out(vty, " CONN: %s%s", mgcp_conn_dump(conn), VTY_NEWLINE);
if (show_stats) {
if (endp->trunk->cfg->conn_timeout) {
if (endp->cfg->conn_timeout) {
struct timeval remaining;
osmo_timer_remaining(&conn->watchdog, NULL, &remaining);
vty_out(vty, " Currently remaining timeout (seconds): %d.%06d%s",
@@ -254,8 +227,7 @@ static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
* connection types (E1) as soon as
* the implementation is available */
if (conn->type == MGCP_CONN_TYPE_RTP) {
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
dump_rtp_end(vty, conn_rtp);
dump_rtp_end(vty, &conn->u.rtp);
}
}
}
@@ -315,14 +287,6 @@ static void dump_ratectr_trunk(struct vty *vty, struct mgcp_trunk *trunk)
" %25n: %10c (%S/s %M/m %H/h %D/d) %d",
ratectr->all_rtp_conn_stats);
}
if (ratectr->all_osmux_conn_stats) {
vty_out(vty, " %s:%s",
ratectr->all_osmux_conn_stats->desc->group_description,
VTY_NEWLINE);
vty_out_rate_ctr_group_fmt(vty,
" %25n: %10c (%S/s %M/m %H/h %D/d) %d",
ratectr->all_osmux_conn_stats);
}
if (ratectr->e1_stats && trunk->trunk_type == MGCP_TRUNK_E1) {
vty_out(vty, " %s:%s",
@@ -335,10 +299,9 @@ static void dump_ratectr_trunk(struct vty *vty, struct mgcp_trunk *trunk)
}
static void dump_trunk(struct vty *vty, struct mgcp_trunk *trunk, int show_stats, int active_only)
static void dump_trunk(struct vty *vty, struct mgcp_trunk *trunk, int show_stats)
{
int i;
int active_count = 0;
vty_out(vty, "%s trunk %d with %d endpoints:%s",
trunk->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
@@ -351,32 +314,31 @@ static void dump_trunk(struct vty *vty, struct mgcp_trunk *trunk, int show_stats
for (i = 0; i < trunk->number_endpoints; ++i) {
struct mgcp_endpoint *endp = trunk->endpoints[i];
if (!active_only || !llist_empty(&endp->conns)) {
dump_endpoint(vty, endp, trunk->trunk_nr, trunk->trunk_type,
show_stats);
if (i < trunk->number_endpoints - 1)
vty_out(vty, "%s", VTY_NEWLINE);
}
if (!llist_empty(&endp->conns))
active_count++;
dump_endpoint(vty, endp, trunk->trunk_nr, trunk->trunk_type,
show_stats);
if (i < trunk->number_endpoints - 1)
vty_out(vty, "%s", VTY_NEWLINE);
}
if (active_count == 0)
vty_out(vty, "No endpoints in use.%s", VTY_NEWLINE);
if (show_stats)
dump_ratectr_trunk(vty, trunk);
}
static int mgcp_show(struct vty *vty, int argc, const char **argv,
int show_stats, int active_only)
#define SHOW_MGCP_STR "Display information about the MGCP Media Gateway\n"
DEFUN(show_mcgp, show_mgcp_cmd,
"show mgcp [stats]",
SHOW_STR
SHOW_MGCP_STR
"Include Statistics\n")
{
struct mgcp_trunk *trunk;
int show_stats = argc >= 1;
llist_for_each_entry(trunk, &g_cfg->trunks, entry)
dump_trunk(vty, trunk, show_stats, active_only);
dump_trunk(vty, trunk, show_stats);
if (g_cfg->osmux.usage != OSMUX_USAGE_OFF)
if (g_cfg->osmux)
vty_out(vty, "Osmux used CID: %d%s", osmux_cid_pool_count_used(),
VTY_NEWLINE);
@@ -386,27 +348,6 @@ static int mgcp_show(struct vty *vty, int argc, const char **argv,
return CMD_SUCCESS;
}
#define SHOW_MGCP_STR "Display information about the MGCP Media Gateway\n"
DEFUN(show_mgcp, show_mgcp_cmd,
"show mgcp [stats]",
SHOW_STR
SHOW_MGCP_STR
"Include statistics\n")
{
int show_stats = argc >= 1;
return mgcp_show(vty, argc, argv, show_stats, 0);
}
DEFUN(show_mgcp_active, show_mgcp_active_cmd,
"show mgcp active",
SHOW_STR
SHOW_MGCP_STR
"Show only endpoints with active connections\n")
{
return mgcp_show(vty, argc, argv, 0, 1);
}
static void
dump_mgcp_endpoint(struct vty *vty, struct mgcp_trunk *trunk, const char *epname)
{
@@ -416,7 +357,7 @@ dump_mgcp_endpoint(struct vty *vty, struct mgcp_trunk *trunk, const char *epname
/* If a trunk is given, search on that specific trunk only */
endp = mgcp_endp_by_name_trunk(NULL, epname, trunk);
if (!endp) {
vty_out(vty, "endpoint %s not configured on trunk %u%s", epname, trunk->trunk_nr, VTY_NEWLINE);
vty_out(vty, "endpoint %s not configured on trunk %d%s", epname, trunk->trunk_nr, VTY_NEWLINE);
return;
}
} else {
@@ -477,7 +418,7 @@ DEFUN_USRATTR(cfg_mgcp_local_ip,
"IPv4 Address to use in SDP record\n"
"IPv6 Address to use in SDP record\n")
{
osmo_strlcpy(g_cfg->local_ip, argv[0], sizeof(g_cfg->local_ip));
osmo_talloc_replace_string(g_cfg, &g_cfg->local_ip, argv[0]);
return CMD_SUCCESS;
}
@@ -489,7 +430,7 @@ DEFUN(cfg_mgcp_bind_ip,
"IPv4 Address to bind to\n"
"IPv6 Address to bind to\n")
{
osmo_strlcpy(g_cfg->source_addr, argv[0], sizeof(g_cfg->source_addr));
osmo_talloc_replace_string(g_cfg, &g_cfg->source_addr, argv[0]);
return CMD_SUCCESS;
}
@@ -509,7 +450,6 @@ DEFUN_DEPRECATED(cfg_mgcp_bind_early,
BIND_STR
"Bind local ports on start up\n" "Bind on demand\n" "Bind on startup\n")
{
vty_out(vty, "%% Deprecated 'bind early (0|1)' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -568,7 +508,7 @@ DEFUN_USRATTR(cfg_mgcp_rtp_bind_ip,
RTP_STR "Bind endpoints facing the Network\n"
"IPv4 Address to bind to\n")
{
osmo_strlcpy(g_cfg->net_ports.bind_addr_v4, argv[0], sizeof(g_cfg->net_ports.bind_addr_v4));
osmo_talloc_replace_string(g_cfg, &g_cfg->net_ports.bind_addr_v4, argv[0]);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgcp_rtp_bind_ip,
@@ -583,7 +523,8 @@ DEFUN_USRATTR(cfg_mgcp_rtp_no_bind_ip,
NO_STR RTP_STR "Bind endpoints facing the Network\n"
"Address to bind to\n")
{
osmo_strlcpy(g_cfg->net_ports.bind_addr_v4, "", sizeof(g_cfg->net_ports.bind_addr_v4));
talloc_free(g_cfg->net_ports.bind_addr_v4);
g_cfg->net_ports.bind_addr_v4 = NULL;
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgcp_rtp_no_bind_ip,
@@ -599,7 +540,7 @@ DEFUN_USRATTR(cfg_mgcp_rtp_bind_ip_v6,
RTP_STR "Bind endpoints facing the Network\n"
"IPv6 Address to bind to\n")
{
osmo_strlcpy(g_cfg->net_ports.bind_addr_v6, argv[0], sizeof(g_cfg->net_ports.bind_addr_v6));
osmo_talloc_replace_string(g_cfg, &g_cfg->net_ports.bind_addr_v6, argv[0]);
return CMD_SUCCESS;
}
@@ -610,7 +551,8 @@ DEFUN_USRATTR(cfg_mgcp_rtp_no_bind_ip_v6,
NO_STR RTP_STR "Bind endpoints facing the Network\n"
"Address to bind to\n")
{
osmo_strlcpy(g_cfg->net_ports.bind_addr_v6, "", sizeof(g_cfg->net_ports.bind_addr_v6));
talloc_free(g_cfg->net_ports.bind_addr_v6);
g_cfg->net_ports.bind_addr_v6 = NULL;
return CMD_SUCCESS;
}
@@ -637,27 +579,19 @@ DEFUN_USRATTR(cfg_mgcp_rtp_no_net_bind_ip_probing,
DEFUN_USRATTR(cfg_mgcp_rtp_ip_dscp,
cfg_mgcp_rtp_ip_dscp_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"rtp ip-dscp <0-63>",
"rtp ip-dscp <0-255>",
RTP_STR
"Use specified DSCP for the audio stream (including Osmux)\n" "The DSCP value\n")
"Apply IP_TOS to the audio stream (including Osmux)\n" "The DSCP value\n")
{
int dscp = atoi(argv[0]);
g_cfg->endp_dscp = dscp;
return CMD_SUCCESS;
}
DEFUN_USRATTR(cfg_mgcp_rtp_priority,
cfg_mgcp_rtp_priority_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"rtp socket-priority <0-255>",
RTP_STR
"socket priority (values > 6 require CAP_NET_ADMIN)\n" "socket priority value\n")
{
int prio = atoi(argv[0]);
g_cfg->endp_priority = prio;
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgcp_rtp_ip_dscp, cfg_mgcp_rtp_ip_tos_cmd,
"rtp ip-tos <0-255>",
RTP_STR
"Apply IP_TOS to the audio stream\n" "The DSCP value\n")
#define FORCE_PTIME_STR "Force a fixed ptime for packets sent"
DEFUN_USRATTR(cfg_mgcp_rtp_force_ptime,
cfg_mgcp_rtp_force_ptime_cmd,
@@ -679,40 +613,53 @@ DEFUN_USRATTR(cfg_mgcp_no_rtp_force_ptime,
return CMD_SUCCESS;
}
DEFUN_USRATTR(cfg_mgcp_sdp_fmtp_extra,
cfg_mgcp_sdp_fmtp_extra_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"sdp audio fmtp-extra .NAME",
"Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
"Extra Information\n")
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
char *txt = argv_concat(argv, argc, 0);
if (!txt)
return CMD_WARNING;
osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt);
talloc_free(txt);
return CMD_SUCCESS;
}
DEFUN_USRATTR(cfg_mgcp_allow_transcoding,
cfg_mgcp_allow_transcoding_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"allow-transcoding", "Allow transcoding\n")
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
trunk->no_audio_transcoding = 0;
return CMD_SUCCESS;
}
DEFUN_USRATTR(cfg_mgcp_no_allow_transcoding,
cfg_mgcp_no_allow_transcoding_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"no allow-transcoding", NO_STR "Allow transcoding\n")
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
trunk->no_audio_transcoding = 1;
return CMD_SUCCESS;
}
#define SDP_STR "SDP File related options\n"
#define AUDIO_STR "Audio payload options\n"
DEFUN_DEPRECATED(cfg_mgcp_sdp_fmtp_extra,
cfg_mgcp_sdp_fmtp_extra_cmd,
"sdp audio fmtp-extra .NAME",
SDP_STR AUDIO_STR "Deprecated, without effect since osmo-mgw v1.13\n" "Deprecated, without effect\n")
{
vty_out(vty, "%% deprecated: the config option 'sdp audio fmtp-extra' has been removed.%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_mgcp_allow_transcoding,
cfg_mgcp_allow_transcoding_cmd,
"allow-transcoding", "Allow transcoding\n")
{
vty_out(vty, "%% Deprecated 'allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_mgcp_no_allow_transcoding,
cfg_mgcp_no_allow_transcoding_cmd,
"no allow-transcoding", NO_STR "Allow transcoding\n")
{
vty_out(vty, "%% Deprecated 'no allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_mgcp_sdp_payload_number,
cfg_mgcp_sdp_payload_number_cmd,
"sdp audio-payload number <0-255>",
SDP_STR AUDIO_STR "Number\n" "Payload number\n")
{
vty_out(vty, "%% Deprecated 'sdp audio-payload number <0-255>' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -726,7 +673,6 @@ DEFUN_DEPRECATED(cfg_mgcp_sdp_payload_name,
"sdp audio-payload name NAME",
SDP_STR AUDIO_STR "Name\n" "Payload name\n")
{
vty_out(vty, "%% Deprecated 'sdp audio-payload name NAME' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -787,7 +733,6 @@ DEFUN_DEPRECATED(cfg_mgcp_loop,
"loop (0|1)",
"Loop audio for all endpoints on main trunk\n" "Don't Loop\n" "Loop\n")
{
vty_out(vty, "%% Deprecated 'loop (0|1)' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -857,7 +802,7 @@ DEFUN_USRATTR(cfg_mgcp_patch_rtp_ssrc,
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
trunk->force_constant_ssrc = true;
trunk->force_constant_ssrc = 1;
return CMD_SUCCESS;
}
@@ -868,7 +813,7 @@ DEFUN_USRATTR(cfg_mgcp_no_patch_rtp_ssrc,
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
trunk->force_constant_ssrc = false;
trunk->force_constant_ssrc = 0;
return CMD_SUCCESS;
}
@@ -923,7 +868,7 @@ DEFUN_USRATTR(cfg_mgcp_no_patch_rtp,
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
trunk->force_constant_ssrc = false;
trunk->force_constant_ssrc = 0;
trunk->force_aligned_timing = 0;
trunk->rfc5993_hr_convert = false;
return CMD_SUCCESS;
@@ -972,7 +917,7 @@ DEFUN(cfg_mgcp_agent_addr,
"IPv4 Address of the call agent\n"
"IPv6 Address of the call agent\n")
{
osmo_strlcpy(g_cfg->call_agent_addr, argv[0], sizeof(g_cfg->call_agent_addr));
osmo_talloc_replace_string(g_cfg, &g_cfg->call_agent_addr, argv[0]);
return CMD_SUCCESS;
}
@@ -985,7 +930,7 @@ DEFUN(cfg_mgcp_trunk, cfg_mgcp_trunk_cmd,
"trunk <0-64>", "Configure a SS7 trunk\n" "Trunk Nr\n")
{
struct mgcp_trunk *trunk;
unsigned int index = atoi(argv[0]);
int index = atoi(argv[0]);
trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_E1, index);
if (!trunk) {
@@ -1013,11 +958,10 @@ static int config_write_trunk(struct vty *vty)
config of trunk 0 here. The configuration for the virtual
trunk is written by config_write_mgcp(). */
if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL
&& trunk->trunk_nr == MGCP_VIRT_TRUNK_ID)
if (trunk->trunk_nr == MGCP_VIRT_TRUNK_ID)
continue;
vty_out(vty, " trunk %u%s", trunk->trunk_nr, VTY_NEWLINE);
vty_out(vty, " trunk %d%s", trunk->trunk_nr, VTY_NEWLINE);
vty_out(vty, " line %u%s", trunk->e1.vty_line_nr, VTY_NEWLINE);
vty_out(vty, " %ssdp audio-payload send-ptime%s",
trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE);
@@ -1052,17 +996,30 @@ static int config_write_trunk(struct vty *vty)
VTY_NEWLINE);
} else
vty_out(vty, " no rtp-patch%s", VTY_NEWLINE);
if (trunk->audio_fmtp_extra)
vty_out(vty, " sdp audio fmtp-extra %s%s",
trunk->audio_fmtp_extra, VTY_NEWLINE);
vty_out(vty, " %sallow-transcoding%s",
trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE);
}
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_trunk_sdp_fmtp_extra,
DEFUN_USRATTR(cfg_trunk_sdp_fmtp_extra,
cfg_trunk_sdp_fmtp_extra_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"sdp audio fmtp-extra .NAME",
SDP_STR AUDIO_STR "Deprecated, without effect since osmo-mgw v1.13\n" "Deprecated, without effect\n")
"Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
"Extra Information\n")
{
vty_out(vty, "%% deprecated: the config option 'sdp audio fmtp-extra' has been removed.%s", VTY_NEWLINE);
struct mgcp_trunk *trunk = vty->index;
char *txt = argv_concat(argv, argc, 0);
if (!txt)
return CMD_WARNING;
osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt);
talloc_free(txt);
return CMD_SUCCESS;
}
@@ -1071,7 +1028,6 @@ DEFUN_DEPRECATED(cfg_trunk_payload_number,
"sdp audio-payload number <0-255>",
SDP_STR AUDIO_STR "Number\n" "Payload Number\n")
{
vty_out(vty, "%% Deprecated 'sdp audio-payload number <0-255>' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1084,7 +1040,6 @@ DEFUN_DEPRECATED(cfg_trunk_payload_name,
"sdp audio-payload name NAME",
SDP_STR AUDIO_STR "Payload\n" "Payload Name\n")
{
vty_out(vty, "%% Deprecated 'sdp audio-payload name NAME' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1097,33 +1052,6 @@ DEFUN_DEPRECATED(cfg_trunk_loop,
"loop (0|1)",
"Loop audio for all endpoints on this trunk\n" "Don't Loop\n" "Loop\n")
{
vty_out(vty, "%% Deprecated 'loop (0|1)' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN_USRATTR(cfg_trunk_force_realloc,
cfg_trunk_force_realloc_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"force-realloc (0|1)",
"Force endpoint reallocation when the endpoint is still seized\n"
"Don't force reallocation\n" "force reallocation\n")
{
struct mgcp_trunk *trunk = vty->index;
OSMO_ASSERT(trunk);
trunk->force_realloc = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_trunk_rtp_accept_all,
cfg_trunk_rtp_accept_all_cmd,
"rtp-accept-all (0|1)",
"Accept all RTP packets, even when the originating IP/Port does not match\n"
"enable filter\n" "disable filter\n",
CMD_ATTR_IMMEDIATE)
{
struct mgcp_trunk *trunk = vty->index;
OSMO_ASSERT(trunk);
trunk->rtp_accept_all = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -1197,7 +1125,7 @@ DEFUN_USRATTR(cfg_trunk_patch_rtp_ssrc,
"rtp-patch ssrc", RTP_PATCH_STR "Force a fixed SSRC\n")
{
struct mgcp_trunk *trunk = vty->index;
trunk->force_constant_ssrc = true;
trunk->force_constant_ssrc = 1;
return CMD_SUCCESS;
}
@@ -1207,7 +1135,7 @@ DEFUN_USRATTR(cfg_trunk_no_patch_rtp_ssrc,
"no rtp-patch ssrc", NO_STR RTP_PATCH_STR "Force a fixed SSRC\n")
{
struct mgcp_trunk *trunk = vty->index;
trunk->force_constant_ssrc = false;
trunk->force_constant_ssrc = 0;
return CMD_SUCCESS;
}
@@ -1257,7 +1185,7 @@ DEFUN_USRATTR(cfg_trunk_no_patch_rtp,
"no rtp-patch", NO_STR RTP_PATCH_STR)
{
struct mgcp_trunk *trunk = vty->index;
trunk->force_constant_ssrc = false;
trunk->force_constant_ssrc = 0;
trunk->force_aligned_timing = 0;
trunk->rfc5993_hr_convert = false;
return CMD_SUCCESS;
@@ -1295,19 +1223,23 @@ DEFUN_ATTR(cfg_trunk_no_rtp_keepalive,
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_trunk_allow_transcoding,
cfg_trunk_allow_transcoding_cmd,
"allow-transcoding", "Allow transcoding\n")
DEFUN_USRATTR(cfg_trunk_allow_transcoding,
cfg_trunk_allow_transcoding_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"allow-transcoding", "Allow transcoding\n")
{
vty_out(vty, "%% Deprecated 'allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
struct mgcp_trunk *trunk = vty->index;
trunk->no_audio_transcoding = 0;
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_trunk_no_allow_transcoding,
cfg_trunk_no_allow_transcoding_cmd,
"no allow-transcoding", NO_STR "Allow transcoding\n")
DEFUN_USRATTR(cfg_trunk_no_allow_transcoding,
cfg_trunk_no_allow_transcoding_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"no allow-transcoding", NO_STR "Allow transcoding\n")
{
vty_out(vty, "%% Deprecated 'no allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
struct mgcp_trunk *trunk = vty->index;
trunk->no_audio_transcoding = 1;
return CMD_SUCCESS;
}
@@ -1343,7 +1275,7 @@ DEFUN(loop_conn,
}
if (!trunk->endpoints) {
vty_out(vty, "%%Trunk %u has no endpoints allocated.%s",
vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
trunk->trunk_nr, VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1358,11 +1290,10 @@ DEFUN(loop_conn,
endp = trunk->endpoints[endp_no];
int loop = atoi(argv[2]);
llist_for_each_entry(conn, &endp->conns, entry) {
if (conn->type == MGCP_CONN_TYPE_RTP) {
if (conn->type == MGCP_CONN_TYPE_RTP)
/* Handle it like a MDCX, switch on SSRC patching if enabled */
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
conn_rtp->state.patch.patch_ssrc = true;
} else {
mgcp_rtp_end_config(endp, 1, &conn->u.rtp.end);
else {
/* FIXME: Introduce support for other connection (E1)
* types when implementation is available */
vty_out(vty, "%%Can't enable SSRC patching,"
@@ -1405,7 +1336,7 @@ DEFUN(tap_rtp,
}
if (!trunk->endpoints) {
vty_out(vty, "%%Trunk %u has no endpoints allocated.%s",
vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
trunk->trunk_nr, VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1420,7 +1351,7 @@ DEFUN(tap_rtp,
endp = trunk->endpoints[endp_no];
conn_id = argv[2];
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
conn = mgcp_conn_get_rtp(endp, conn_id);
if (!conn) {
vty_out(vty, "Conn ID %s is invalid.%s",
conn_id, VTY_NEWLINE);
@@ -1472,7 +1403,7 @@ DEFUN(free_endp, free_endp_cmd,
}
if (!trunk->endpoints) {
vty_out(vty, "%%Trunk %u has no endpoints allocated.%s",
vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
trunk->trunk_nr, VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1505,7 +1436,7 @@ DEFUN(reset_endp, reset_endp_cmd,
}
if (!trunk->endpoints) {
vty_out(vty, "%%Trunk %u has no endpoints allocated.%s",
vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
trunk->trunk_nr, VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1550,12 +1481,12 @@ DEFUN(cfg_mgcp_osmux,
OSMO_ASSERT(trunk);
if (strcmp(argv[0], "off") == 0) {
g_cfg->osmux.usage = OSMUX_USAGE_OFF;
g_cfg->osmux = OSMUX_USAGE_OFF;
return CMD_SUCCESS;
} else if (strcmp(argv[0], "on") == 0)
g_cfg->osmux.usage = OSMUX_USAGE_ON;
g_cfg->osmux = OSMUX_USAGE_ON;
else if (strcmp(argv[0], "only") == 0)
g_cfg->osmux.usage = OSMUX_USAGE_ONLY;
g_cfg->osmux = OSMUX_USAGE_ONLY;
return CMD_SUCCESS;
@@ -1563,29 +1494,12 @@ DEFUN(cfg_mgcp_osmux,
DEFUN(cfg_mgcp_osmux_ip,
cfg_mgcp_osmux_ip_cmd,
"osmux bind-ip " VTY_IPV4_CMD,
OSMUX_STR IP_STR
"IPv4 Address to bind to\n")
{
osmo_talloc_replace_string(g_cfg, &g_cfg->osmux.local_addr_v4, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_ip_v6,
cfg_mgcp_osmux_ip_v6_cmd,
"osmux bind-ip-v6 " VTY_IPV6_CMD,
"osmux bind-ip " VTY_IPV46_CMD,
OSMUX_STR IP_STR
"IPv4 Address to bind to\n"
"IPv6 Address to bind to\n")
{
osmo_talloc_replace_string(g_cfg, &g_cfg->osmux.local_addr_v6, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_port,
cfg_mgcp_osmux_port_cmd,
"osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n")
{
g_cfg->osmux.local_port = atoi(argv[0]);
osmo_talloc_replace_string(g_cfg, &g_cfg->osmux_addr, argv[0]);
return CMD_SUCCESS;
}
@@ -1594,7 +1508,7 @@ DEFUN(cfg_mgcp_osmux_batch_factor,
"osmux batch-factor <1-8>",
OSMUX_STR "Batching factor\n" "Number of messages in the batch\n")
{
g_cfg->osmux.batch_factor = atoi(argv[0]);
g_cfg->osmux_batch = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -1603,7 +1517,15 @@ DEFUN(cfg_mgcp_osmux_batch_size,
"osmux batch-size <1-65535>",
OSMUX_STR "batch size\n" "Batch size in bytes\n")
{
g_cfg->osmux.batch_size = atoi(argv[0]);
g_cfg->osmux_batch_size = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_port,
cfg_mgcp_osmux_port_cmd,
"osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n")
{
g_cfg->osmux_port = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -1614,24 +1536,9 @@ DEFUN(cfg_mgcp_osmux_dummy,
"Disable dummy padding\n")
{
if (strcmp(argv[0], "on") == 0)
g_cfg->osmux.dummy_padding = true;
g_cfg->osmux_dummy = 1;
else if (strcmp(argv[0], "off") == 0)
g_cfg->osmux.dummy_padding = false;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_peer_behind_nat,
cfg_mgcp_osmux_peer_behind_nat_cmd,
"osmux peer-behind-nat (on|off)",
OSMUX_STR "Define whether peer is behind NAT\n"
"Peer is behind NAT\n"
"Peer is NOT behind NAT\n")
{
if (strcmp(argv[0], "on") == 0)
g_cfg->osmux.peer_behind_nat = true;
else if (strcmp(argv[0], "off") == 0)
g_cfg->osmux.peer_behind_nat = false;
g_cfg->osmux_dummy = 0;
return CMD_SUCCESS;
}
@@ -1662,7 +1569,6 @@ DEFUN(cfg_mgcp_conn_timeout,
int mgcp_vty_init(void)
{
install_element_ve(&show_mgcp_cmd);
install_element_ve(&show_mgcp_active_cmd);
install_element_ve(&show_mgcp_endpoint_cmd);
install_element_ve(&show_mgcp_trunk_endpoint_cmd);
install_element(ENABLE_NODE, &loop_conn_cmd);
@@ -1689,7 +1595,7 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_rtp_net_bind_ip_probing_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_no_net_bind_ip_probing_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_priority_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_tos_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_force_ptime_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_rtp_force_ptime_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_cmd);
@@ -1721,12 +1627,10 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_name_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_v6_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_factor_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_size_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_peer_behind_nat_cmd);
install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_domain_cmd);
@@ -1742,8 +1646,6 @@ int mgcp_vty_init(void)
install_element(TRUNK_NODE, &cfg_trunk_payload_number_cmd_old);
install_element(TRUNK_NODE, &cfg_trunk_payload_name_cmd_old);
install_element(TRUNK_NODE, &cfg_trunk_loop_cmd);
install_element(TRUNK_NODE, &cfg_trunk_force_realloc_cmd);
install_element(TRUNK_NODE, &cfg_trunk_rtp_accept_all_cmd);
install_element(TRUNK_NODE, &cfg_trunk_omit_rtcp_cmd);
install_element(TRUNK_NODE, &cfg_trunk_no_omit_rtcp_cmd);
install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ssrc_cmd);
@@ -1771,9 +1673,9 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
int rc;
struct mgcp_trunk *trunk;
cfg->osmux.local_port = OSMUX_DEFAULT_PORT;
cfg->osmux.batch_factor = 4;
cfg->osmux.batch_size = OSMUX_BATCH_DEFAULT_MAX;
cfg->osmux_port = OSMUX_PORT;
cfg->osmux_batch = 4;
cfg->osmux_batch_size = OSMUX_BATCH_DEFAULT_MAX;
g_cfg = cfg;
rc = vty_read_config_file(config_file, NULL);
@@ -1783,7 +1685,7 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
return rc;
}
if (!strlen(g_cfg->source_addr)) {
if (!g_cfg->source_addr) {
fprintf(stderr, "You need to specify a bind address.\n");
return -1;
}
@@ -1791,7 +1693,7 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
if (mgcp_trunk_equip(trunk) != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Failed to initialize trunk %u (%d endpoints)\n",
"Failed to initialize trunk %d (%d endpoints)\n",
trunk->trunk_nr, trunk->number_endpoints);
return -1;
}

View File

@@ -27,7 +27,6 @@ osmo_mgw_SOURCES = \
osmo_mgw_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCODEC_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCTRL_LIBS) \

View File

@@ -41,6 +41,7 @@
#include <osmocom/mgcp/debug.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_ctrl.h>
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
@@ -50,7 +51,7 @@
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/socket.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/ctrl/control_vty.h>
#include <osmocom/vty/telnet_interface.h>
@@ -82,7 +83,7 @@ static int daemonize = 0;
const char *osmomgw_copyright =
"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
"Copyright (C) 2017-2022 by sysmocom s.f.m.c. GmbH\r\n"
"Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
"Contributions by Pablo Neira Ayuso, Jacob Erlbeck, Neels Hofmeyr\r\n"
"Philipp Maier\r\n\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
@@ -94,43 +95,15 @@ static char *config_file = "osmo-mgw.cfg";
/* used by msgb and mgcp */
void *tall_mgw_ctx = NULL;
static void print_help(void)
static void print_help()
{
printf("Some useful options:\n");
printf("Some useful help...\n");
printf(" -h --help is printing this text.\n");
printf(" -c --config-file filename The config file to use.\n");
printf(" -s --disable-color\n");
printf(" -D --daemonize Fork the process into a background daemon\n");
printf(" -V --version Print the version number\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);
}
printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
}
static void handle_options(int argc, char **argv)
@@ -144,8 +117,7 @@ static void handle_options(int argc, char **argv)
{"daemonize", 0, 0, 'D'},
{"version", 0, 0, 'V'},
{"disable-color", 0, 0, 's'},
{"vty-ref-mode", 1, &long_option, 1},
{"vty-ref-xml", 0, &long_option, 2},
{"vty-ref-xml", 0, &long_option, 1},
{0, 0, 0, 0},
};
@@ -160,8 +132,14 @@ static void handle_options(int argc, char **argv)
exit(0);
break;
case 0:
handle_long_options(argv[0], long_option);
break;
switch (long_option) {
case 1:
vty_dump_xml_ref(stdout);
exit(0);
default:
fprintf(stderr, "error parsing cmdline options\n");
exit(2);
}
case 'c':
config_file = talloc_strdup(tall_mgw_ctx, optarg);
break;
@@ -234,7 +212,7 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
/* reset endpoints */
if (reset_endpoints) {
LOGP(DLMGCP, LOGL_NOTICE,
"Asked to reset endpoints: %u/%d\n",
"Asked to reset endpoints: %d/%d\n",
reset_trunk->trunk_nr, reset_trunk->trunk_type);
/* reset flag */
@@ -242,13 +220,24 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
/* Walk over all endpoints and trigger a release, this will release all
* endpoints, possible open connections are forcefully dropped */
for (i = 0; i < reset_trunk->number_endpoints; ++i)
for (i = 1; i < reset_trunk->number_endpoints; ++i)
mgcp_endp_release(reset_trunk->endpoints[i]);
}
return 0;
}
int mgcp_vty_is_config_node(struct vty *vty, int node)
{
switch (node) {
case CONFIG_NODE:
return 0;
default:
return 1;
}
}
int mgcp_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
@@ -258,7 +247,7 @@ int mgcp_vty_go_parent(struct vty *vty)
break;
case MGCP_NODE:
default:
if (vty->node != CONFIG_NODE)
if (mgcp_vty_is_config_node(vty, vty->node))
vty->node = CONFIG_NODE;
else
vty->node = ENABLE_NODE;
@@ -269,36 +258,12 @@ int mgcp_vty_go_parent(struct vty *vty)
return vty->node;
}
static void signal_handler(int signum)
{
fprintf(stdout, "signal %u received\n", signum);
switch (signum) {
case SIGABRT:
/* 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_mgw_ctx, stderr);
signal(SIGABRT, SIG_DFL);
raise(SIGABRT);
break;
case SIGUSR1:
talloc_report(tall_vty_ctx, stderr);
talloc_report_full(tall_mgw_ctx, stderr);
break;
default:
break;
}
}
static struct vty_app_info vty_info = {
.name = "OsmoMGW",
.version = PACKAGE_VERSION,
.go_parent_cb = mgcp_vty_go_parent,
.is_config_node = mgcp_vty_is_config_node,
};
static const struct log_info_cat log_categories[] = {
@@ -307,22 +272,13 @@ static const struct log_info_cat log_categories[] = {
.name = "DRTP",
.description = "RTP stream handling",
.color = "\033[1;30m",
.enabled = 1,
.loglevel = LOGL_NOTICE,
.enabled = 1,.loglevel = LOGL_NOTICE,
},
[DE1] = {
.name = "DE1",
.description = "E1 line handling",
.color = "\033[1;31m",
.enabled = 1,
.loglevel = LOGL_NOTICE,
},
[DOSMUX] = {
.name = "DOSMUX",
.description = "Osmux (Osmocom RTP multiplexing)",
.color = "\033[1;32m",
.enabled = 1,
.loglevel = LOGL_NOTICE,
.enabled = 1,.loglevel = LOGL_NOTICE,
},
};
@@ -341,8 +297,6 @@ int main(int argc, char **argv)
msgb_talloc_ctx_init(tall_mgw_ctx, 0);
signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
osmo_init_ignore_signals();
osmo_init_logging2(tall_mgw_ctx, &log_info);
libosmo_abis_init(tall_mgw_ctx);
@@ -375,11 +329,12 @@ int main(int argc, char **argv)
return rc;
/* start telnet after reading config for vty_get_bind_addr() */
rc = telnet_init_default(tall_mgw_ctx, NULL, OSMO_VTY_PORT_MGW);
rc = telnet_init_dynif(tall_mgw_ctx, NULL,
vty_get_bind_addr(), OSMO_VTY_PORT_MGW);
if (rc < 0)
return rc;
cfg->ctrl = ctrl_interface_setup(cfg, OSMO_CTRL_PORT_MGW, NULL);
cfg->ctrl = mgw_ctrl_interface_setup(cfg, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
if (!cfg->ctrl) {
fprintf(stderr, "Failed to init the control interface on %s:%u. Exiting\n",
ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
@@ -391,12 +346,12 @@ int main(int argc, char **argv)
/* we need to bind a socket */
flags = OSMO_SOCK_F_BIND;
if (strlen(cfg->call_agent_addr))
if (cfg->call_agent_addr)
flags |= OSMO_SOCK_F_CONNECT;
rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
cfg->source_addr, cfg->source_port,
cfg->call_agent_addr, strlen(cfg->call_agent_addr) ? 2727 : 0, flags);
cfg->call_agent_addr, cfg->call_agent_addr ? 2727 : 0, flags);
if (rc < 0) {
perror("Gateway failed to bind");
return -1;

View File

@@ -34,11 +34,11 @@ DISTCLEANFILES = \
$(NULL)
if ENABLE_EXT_TESTS
python-tests: $(top_builddir)/src/osmo-mgw/osmo-mgw
python-tests: $(BUILT_SOURCES)
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
else
python-tests:
python-tests: $(BUILT_SOURCES)
echo "Not running python-based tests (determined at configure-time)"
endif

View File

@@ -18,14 +18,13 @@ AM_CFLAGS = \
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
-no-install \
$(NULL)
EXTRA_DIST = \
mgcp_test.ok \
$(NULL)
check_PROGRAMS = \
noinst_PROGRAMS = \
mgcp_test \
$(NULL)
@@ -36,7 +35,6 @@ mgcp_test_SOURCES = \
mgcp_test_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCODEC_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
@@ -46,6 +44,3 @@ mgcp_test_LDADD = \
$(LIBOSMONETIF_LIBS) \
-lm \
$(NULL)
update_exp:
$(builddir)/mgcp_test >$(srcdir)/mgcp_test.ok

File diff suppressed because it is too large Load Diff

View File

@@ -73,7 +73,7 @@ v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:20
a=ptime:40
---------8<---------
checking response:
@@ -97,46 +97,21 @@ Response matches our expectations.
Dummy packets: 2
================================================
Testing MDCX4_ADDR000
Testing MDCX4
creating message from statically defined input:
---------8<---------
MDCX 18983216 1@mgw MGCP 1.0
M: sendrecv
C: 2
M: sendrecv
C: 2
I: %s
L: p:20, a:AMR, nt:IN
v=0
o=- %s 23 IN IP4 0.0.0.0
c=IN IP4 0.0.0.0
t=0 0
m=audio 4441 RTP/AVP 99
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
================================================
Testing MDCX4
creating message from statically defined input:
---------8<---------
MDCX 18983217 1@mgw MGCP 1.0
M: sendrecv
C: 2
I: %s
L: p:20, a:AMR, nt:IN
v=0
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
t=0 0
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
a=ptime:20
a=rtpmap:99 AMR/8000
a=ptime:40
---------8<---------
@@ -149,19 +124,18 @@ Dummy packets: 2
================================================
Testing MDCX4_PT1
creating message from statically defined input:
MDCX 18983218 1@mgw MGCP 1.0
M: SENDRECV
C: 2
---------8<---------
MDCX 18983217 1@mgw MGCP 1.0
M: SENDRECV
C: 2
I: %s
L: p:20-40, a:AMR, nt:IN
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
v=0
o=- %s 23 IN IP4 0.0.0.0
c=IN IP4 0.0.0.0
t=0 0
a=ptime:20
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
a=ptime:40
@@ -174,19 +148,18 @@ Dummy packets: 2
================================================
Testing MDCX4_PT2
MDCX 18983219 1@mgw MGCP 1.0
M: sendrecv
C: 2
creating message from statically defined input:
---------8<---------
MDCX 18983218 1@mgw MGCP 1.0
M: sendrecv
C: 2
I: %s
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
L: p:20-20, a:AMR, nt:IN
v=0
o=- %s 23 IN IP4 0.0.0.0
c=IN IP4 0.0.0.0
a=ptime:20
t=0 0
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
a=ptime:40
@@ -199,19 +172,18 @@ Dummy packets: 2
Dummy packets: 2
================================================
MDCX 18983220 1@mgw MGCP 1.0
M: sendrecv
C: 2
Testing MDCX4_PT3
creating message from statically defined input:
---------8<---------
MDCX 18983219 1@mgw MGCP 1.0
M: sendrecv
C: 2
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
I: %s
L: a:AMR, nt:IN
v=0
o=- %s 23 IN IP4 0.0.0.0
a=ptime:20
c=IN IP4 0.0.0.0
t=0 0
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
@@ -224,19 +196,18 @@ Dummy packets: 2
(response contains a connection id)
Dummy packets: 2
MDCX 18983221 1@mgw MGCP 1.0
m: sendrecv
c: 2
================================================
Testing MDCX4_PT4
creating message from statically defined input:
---------8<---------
MDCX 18983220 1@mgw MGCP 1.0
m: sendrecv
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
c: 2
i: %s
l: A:amr, NT:IN
v=0
a=ptime:20
o=- %s 23 IN IP4 0.0.0.0
c=IN IP4 0.0.0.0
t=0 0
m=audio 4441 RTP/AVP 99
@@ -249,19 +220,18 @@ Dummy packets: 2
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
MDCX 18983222 1@mgw MGCP 1.0
M: sendonly
C: 2
================================================
Testing MDCX4_SO
creating message from statically defined input:
---------8<---------
MDCX 18983221 1@mgw MGCP 1.0
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
M: sendonly
C: 2
I: %s
L: p:20, a:AMR, nt:IN
a=ptime:20
v=0
o=- %s 23 IN IP4 0.0.0.0
c=IN IP4 0.0.0.0
t=0 0
@@ -273,9 +243,8 @@ Response matches our expectations.
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
MDCX 18983223 1@mgw MGCP 1.0
M: recvonly
C: 2
(response contains a connection id)
================================================
Testing MDCX4_RO
creating message from statically defined input:
@@ -304,15 +273,9 @@ Response matches our expectations.
---------8<---------
checking response:
using message as statically defined for comparison
CRCX 2 1@mgw MGCP 1.0
M: recvonly
C: 2
v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing CRCX_ZYN
creating message from statically defined input:
@@ -428,7 +391,7 @@ v=0
(response does not contain a connection id)
================================================
a=ptime:20
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
@@ -478,6 +441,7 @@ checking response:
(response does not contain a connection id)
================================================
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 6@mgw MGCP 1.0
@@ -493,7 +457,7 @@ v=0
================================================
Testing CRCX
a=ptime:20
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
M: recvonly
@@ -506,7 +470,7 @@ Dummy packets: 2
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:40
MDCX 18983224 1@mgw MGCP 1.0
---------8<---------
checking response:
using message with patched conn_id for comparison
@@ -539,184 +503,6 @@ Response matches our expectations.
v=0
c=IN IP4 123.12.12.123
================================================
Testing AUEP_NULL
creating message from statically defined input:
---------8<---------
AUEP 18983215 null@mgw MGCP 1.0
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing CRCX_NULL
creating message from statically defined input:
---------8<---------
CRCX 2 null@mgw MGCP 1.0
m: recvonly
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:20
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing MDCX_NULL
creating message from statically defined input:
---------8<---------
MDCX 9 null@mgw MGCP 1.0
I: %s
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing DLCX_NULL
creating message from statically defined input:
---------8<---------
DLCX 8 null@mgw MGCP 1.0
I: %s
C: 2
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing RQNT_NULL
creating message from statically defined input:
---------8<---------
RQNT 186908782 null@mgw MGCP 1.0
X: B244F267488
S: D/9
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing CRCX_PORT_0
creating message from statically defined input:
---------8<---------
CRCX 3 1@mgw MGCP 1.0
m: recvonly
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
================================================
Testing CRCX_PORT_0_IUFP
creating message from statically defined input:
---------8<---------
CRCX 4 1@mgw MGCP 1.0
m: recvonly
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
================================================
Testing CRCX_PORT_0_IUFP_SENDRECV
creating message from statically defined input:
---------8<---------
CRCX 4 1@mgw MGCP 1.0
M: sendrecv
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
================================================
Testing CRCX_PORT_0_IUFP_SENDRECV2
creating message from statically defined input:
---------8<---------
CRCX 4 1@mgw MGCP 1.0
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
a=sendrecv
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
================================================
Testing CRCX_PORT_0_IUFP_SENDRECV3
creating message from statically defined input:
---------8<---------
CRCX 4 1@mgw MGCP 1.0
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
m=audio 5904 RTP/AVP 111
a=rtpmap:111 AMR/8000/1
a=ptime:20
@@ -730,7 +516,7 @@ v=0
Dummy packets: 2
================================================
a=ptime:20
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
@@ -748,7 +534,7 @@ v=0
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
a=ptime:20
Re-transmitting CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
@@ -860,7 +646,7 @@ v=0
checking response:
using message as statically defined for comparison
Response matches our expectations.
a=ptime:20
Testing packet loss calculation.
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
@@ -984,15 +770,6 @@ Stats: Jitter = 0, Transit = -144000
In TS: 36728, dTS: 160, Seq: 24
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 22, Transit = -32888
In TS: 180320, dTS: 0, Seq: 1003
Out TS change: 20000, dTS: 0, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -163840
In TS: 180480, dTS: 160, Seq: 1004
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -163840
In TS: 180640, dTS: 160, Seq: 1005
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 5, Transit = -163760
In TS: 36888, dTS: 160, Seq: 25
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 21, Transit = -32888
@@ -1087,15 +864,6 @@ Stats: Jitter = 0, Transit = -144000
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 22, Transit = -32888
In TS: 36888, dTS: 160, Seq: 25
In TS: 180320, dTS: 0, Seq: 1003
Out TS change: 20000, dTS: 0, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -163840
In TS: 180480, dTS: 160, Seq: 1004
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -163840
In TS: 180640, dTS: 160, Seq: 1005
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 5, Transit = -163760
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 21, Transit = -32888
Output SSRC changed to 50607080
@@ -1190,15 +958,6 @@ Stats: Jitter = 0, Transit = -144000
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 22, Transit = -32888
In TS: 36888, dTS: 160, Seq: 25
In TS: 180320, dTS: 0, Seq: 1003
Out TS change: 20000, dTS: 0, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -163840
In TS: 180480, dTS: 160, Seq: 1004
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -163840
In TS: 180640, dTS: 160, Seq: 1005
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 5, Transit = -163760
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 21, Transit = -32888
Output SSRC changed to 50607080
@@ -1291,15 +1050,6 @@ Stats: Jitter = 0, Transit = -144000
In TS: 36728, dTS: 160, Seq: 24
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 22, Transit = -32888
In TS: 180320, dTS: 0, Seq: 1003
Out TS change: 20000, dTS: 0, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -163840
In TS: 180480, dTS: 160, Seq: 1004
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 0, Transit = -163840
In TS: 180640, dTS: 160, Seq: 1005
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 5, Transit = -163760
In TS: 36888, dTS: 160, Seq: 25
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 21, Transit = -32888
@@ -1314,7 +1064,7 @@ c=IN IP4 123.12.12.123
Stats: Jitter = 0, Transit = -144000
Testing multiple payload types
creating message from statically defined input:
a=ptime:20
---------8<---------
CRCX 2 1@mgw MGCP 1.0
M: recvonly
C: 2
@@ -1331,7 +1081,7 @@ m=audio 5904 RTP/AVP 18 97 101
---------8<---------
creating message from statically defined input:
---------8<---------
a=ptime:20
CRCX 2 2@mgw MGCP 1.0
M: recvonly
C: 2
X
@@ -1348,7 +1098,7 @@ m=audio 5904 RTP/AVP
---------8<---------
creating message from statically defined input:
---------8<---------
a=ptime:20
CRCX 2 3@mgw MGCP 1.0
M: recvonly
C: 2
X
@@ -1365,7 +1115,7 @@ m=audio 5904 RTP/AVP 18
---------8<---------
creating message from statically defined input:
---------8<---------
a=ptime:20
CRCX 2 4@mgw MGCP 1.0
M: recvonly
C: 2
X
@@ -1447,7 +1197,7 @@ v=0
---------8<---------
Testing no sequence flow on initial packet
a=ptime:20
Testing no rtpmap name
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
@@ -1468,7 +1218,7 @@ p:10, a:PCMU -> p:10, a:PCMU
Testing get_lco_identifier()
p:10, a:PCMU -> p:10, a:PCMU
p:10, a:PCMU -> p:10, a:PCMU
Testing mgcp_codec_find_convertible()
'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU'
'XXXX,p:10,a:PCMU' -> 'p:10,a:PCMU'
'10,a:PCMU' -> 'a:PCMU'
'10, a:PCMU' -> 'a:PCMU'
@@ -1478,15 +1228,13 @@ Testing mgcp_codec_find_convertible()
' a:PCMU' -> 'a:PCMU'
'' -> '(null)'
p10, aPCMU -> (null)
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:AMR, pt:96
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:96
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
'10,a :PCMU' -> '(null)'
Testing mgcp_codec_pt_translate()
#0: same order, but differing payload type numbers
- add codecs on conn0:
0: 112 AMR/8000/1 octet-aligned=1 -> rc=0
1: 0 PCMU/8000/1 -> rc=0
2: 111 GSM-HR-08/8000/1 -> rc=0
- add codecs on conn1:
0: 96 AMR/8000/1 octet-aligned=1 -> rc=0
@@ -1496,15 +1244,13 @@ Testing mgcp_codec_find_convertible()
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:PCMU, pt:0
conn[1]: codec:PCMU, pt:0
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:97
conn[0]: codec:GSM-HR-08, pt:111
===> SUCCESS: codec decision as expected!
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
#1: different order and different payload type numbers
- add codecs on conn0:
0: 0 PCMU/8000/1 -> rc=0
1: 111 GSM-HR-08/8000/1 -> rc=0
2: 112 AMR/8000/1 octet-aligned=1 -> rc=0
- add codecs on conn1:
0: 97 GSM-HR-08/8000/1 -> rc=0
@@ -1514,15 +1260,12 @@ Testing mgcp_codec_find_convertible()
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:PCMU, pt:0
conn[1]: codec:PCMU, pt:0
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:97
conn[0]: codec:GSM-HR-08, pt:96
===> SUCCESS: codec decision as expected!
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
#2: both sides have the same payload_type numbers assigned to differing codecs
- add codecs on conn0:
0: 0 PCMU/8000/1 -> rc=0
1: 96 GSM-HR-08/8000/1 -> rc=0
2: 97 AMR/8000/1 octet-aligned=1 -> rc=0
- add codecs on conn1:
@@ -1530,13 +1273,9 @@ Testing mgcp_codec_find_convertible()
1: 0 PCMU/8000/1 -> rc=0
2: 96 AMR/8000/1 octet-aligned=1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 96) -> 97
- mgcp_codec_decide(&conn[0], &conn[1]):
codec decision failed (expected)!
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:96
conn[0]: codec:none, pt:none
===> SUCCESS: codec decision as expected!
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 96
- mgcp_codec_pt_translate(conn0, conn1, 97) -> 96
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 97
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
#3: conn0 has no codecs
@@ -1544,116 +1283,39 @@ Testing mgcp_codec_find_convertible()
(none)
- add codecs on conn1:
0: 96 AMR/8000/1 octet-aligned=1 -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:none, pt:none
- mgcp_codec_decide(&conn[1], &conn[0]):
codec decision failed (expected)!
===> SUCCESS: codec decision as expected!
#5: test AMR with differing octet-aligned settings (both <-> both)
1: 0 PCMU/8000/1 -> rc=0
2: 97 GSM-HR-08/8000/1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
#4: conn1 has no codecs
- add codecs on conn0:
0: 112 AMR/8000/1 octet-aligned=1 -> rc=0
1: 0 PCMU/8000/1 -> rc=0
2: 111 GSM-HR-08/8000/1 -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:111
conn[1]: codec:AMR, pt:121
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
#6: test AMR with differing octet-aligned settings (oa <-> both)
- add codecs on conn1:
(none)
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
#5: test AMR with differing octet-aligned settings
- add codecs on conn0:
0: 111 AMR/8000 octet-aligned=1 -> rc=0
1: 112 AMR/8000 octet-aligned=0 -> rc=0
0: 122 AMR/8000 octet-aligned=0 -> rc=0
1: 121 AMR/8000 octet-aligned=1 -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:111
conn[1]: codec:AMR, pt:121
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:121
conn[0]: codec:AMR, pt:111
===> SUCCESS: codec decision as expected!
#7: test AMR with differing octet-aligned settings (bwe <-> both)
- add codecs on conn0:
0: 112 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=0 -> rc=0
1: 121 AMR/8000 octet-aligned=1 -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
#8: test AMR with missing octet-aligned settings (oa <-> unset)
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=0 -> rc=0
1: 121 AMR/8000 octet-aligned=1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 121
- mgcp_codec_pt_translate(conn1, conn0, 121) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=unset -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:111
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:111
===> SUCCESS: codec decision as expected!
#9: test AMR with missing octet-aligned settings (bwe <-> unset)
- add codecs on conn0:
0: 111 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=unset -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:111
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:111
===> SUCCESS: codec decision as expected!
#10: test AMR with NULL param (oa <-> null)
- add codecs on conn0:
0: 112 AMR/8000 octet-aligned=1 -> rc=0
#6: test AMR with missing octet-aligned settings (defaults to 0)
- add codecs on conn0:
0: 111 AMR/8000 octet-aligned=1 -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
#11: test AMR with NULL param (bwe <-> null)
- add codecs on conn0:
0: 112 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
#12: match FOO/8000/1 and FOO/8000 as identical, single channel is implicit
1: 112 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=unset -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
#7: test AMR with NULL param (defaults to 0)
@@ -1662,16 +1324,14 @@ Testing mgcp_codec_find_convertible()
1: 112 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:PCMU, pt:0
conn[1]: codec:PCMU, pt:0
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:97
conn[0]: codec:GSM-HR-08, pt:111
===> SUCCESS: codec decision as expected!
#13: match FOO/8000/1 and FOO as identical, 8k and single channel are implicit
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
#8: match FOO/8000/1 and FOO/8000 as identical, single channel is implicit
- add codecs on conn0:
0: 0 PCMU/8000/1 -> rc=0
1: 111 GSM-HR-08/8000/1 -> rc=0
2: 112 AMR/8000/1 octet-aligned=1 -> rc=0
- add codecs on conn1:
0: 97 GSM-HR-08/8000 -> rc=0
1: 0 PCMU/8000 -> rc=0
@@ -1680,16 +1340,14 @@ Testing mgcp_codec_find_convertible()
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:PCMU, pt:0
conn[1]: codec:PCMU, pt:0
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:97
conn[0]: codec:GSM-HR-08, pt:111
===> SUCCESS: codec decision as expected!
#14: test whether channel number matching is waterproof
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
#9: match FOO/8000/1 and FOO as identical, 8k and single channel are implicit
- add codecs on conn0:
0: 0 PCMU/8000/1 -> rc=0
1: 111 GSM-HR-08/8000/1 -> rc=0
2: 112 AMR/8000/1 octet-aligned=1 -> rc=0
- add codecs on conn1:
0: 97 GSM-HR-08 -> rc=0
1: 0 PCMU -> rc=0
@@ -1697,15 +1355,10 @@ Testing mgcp_codec_find_convertible()
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:GSM-HR-08, pt:111
conn[1]: codec:GSM-HR-08, pt:121
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:121
conn[0]: codec:GSM-HR-08, pt:111
===> SUCCESS: codec decision as expected!
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
#10: test whether channel number matching is waterproof
- add codecs on conn0:
0: 111 GSM-HR-08/8000 -> rc=0

View File

@@ -15,7 +15,6 @@ AM_CFLAGS = \
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
-no-install \
$(NULL)
EXTRA_DIST = \
@@ -23,7 +22,7 @@ EXTRA_DIST = \
mgcp_client_test.err \
$(NULL)
check_PROGRAMS = \
noinst_PROGRAMS = \
mgcp_client_test \
$(NULL)

View File

@@ -73,7 +73,7 @@ static struct msgb *from_str(const char *str)
return msg;
}
static struct mgcp_client_conf *conf;
static struct mgcp_client_conf conf;
struct mgcp_client *mgcp = NULL;
static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
@@ -107,6 +107,9 @@ void test_response_cb(struct mgcp_response *response, void *priv)
printf(" audio_port = %u\n", response->audio_port);
printf(" audio_ip = %s\n", response->audio_ip);
printf(" ptime = %u\n", response->ptime);
printf(" codecs_len = %u\n", response->codecs_len);
for(i=0;i<response->codecs_len;i++)
printf(" codecs[%u] = %u\n", i, response->codecs[i]);
printf(" ptmap_len = %u\n", response->ptmap_len);
for(i=0;i<response->ptmap_len;i++) {
printf(" ptmap[%u].codec = %u\n", i, response->ptmap[i].codec);
@@ -146,11 +149,12 @@ void test_mgcp_msg(void)
.conn_id = "11",
.conn_mode = MGCP_CONN_RECV_SEND,
.ptime = 20,
.ptmap = {
{ .codec = CODEC_GSM_8000_1, .pt = CODEC_GSM_8000_1 },
{ .codec = CODEC_AMR_8000_1, .pt = CODEC_AMR_8000_1 },
{ .codec = CODEC_GSMEFR_8000_1, .pt = 96 },
},
.codecs[0] = CODEC_GSM_8000_1,
.codecs[1] = CODEC_AMR_8000_1,
.codecs[2] = CODEC_GSMEFR_8000_1,
.codecs_len = 1,
.ptmap[0].codec = CODEC_GSMEFR_8000_1,
.ptmap[0].pt = 96,
.ptmap_len = 1,
.x_osmo_ign = MGCP_X_OSMO_IGN_CALLID,
.x_osmo_osmux_cid = -1, /* wildcard */
@@ -158,7 +162,7 @@ void test_mgcp_msg(void)
if (mgcp)
talloc_free(mgcp);
mgcp = mgcp_client_init(ctx, conf);
mgcp = mgcp_client_init(ctx, &conf);
printf("\n");
@@ -175,9 +179,9 @@ void test_mgcp_msg(void)
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
mgcp_msg.ptmap_len = 2;
mgcp_msg.codecs_len = 2;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.ptmap_len = 1;
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated CRCX message (three codecs, one with custom pt):\n");
@@ -185,9 +189,9 @@ void test_mgcp_msg(void)
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
mgcp_msg.ptmap_len = 3;
mgcp_msg.codecs_len = 3;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.ptmap_len = 1;
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated MDCX message:\n");
@@ -205,9 +209,9 @@ void test_mgcp_msg(void)
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
mgcp_msg.ptmap_len = 2;
mgcp_msg.codecs_len = 2;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.ptmap_len = 1;
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated MDCX message (three codecs, one with custom pt):\n");
@@ -216,9 +220,9 @@ void test_mgcp_msg(void)
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
mgcp_msg.ptmap_len = 3;
mgcp_msg.codecs_len = 3;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.ptmap_len = 1;
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated DLCX message:\n");
@@ -311,7 +315,7 @@ void test_mgcp_msg(void)
msgb_free(msg);
}
void test_mgcp_client_cancel(void)
void test_mgcp_client_cancel()
{
mgcp_trans_id_t trans_id;
struct msgb *msg;
@@ -326,10 +330,8 @@ void test_mgcp_client_cancel(void)
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
| MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE),
.ptime = 20,
.ptmap = {
{ .codec = CODEC_AMR_8000_1, .pt = CODEC_AMR_8000_1 },
},
.ptmap_len = 1
.codecs[0] = CODEC_AMR_8000_1,
.codecs_len = 1
};
printf("\n%s():\n", __func__);
@@ -337,7 +339,7 @@ void test_mgcp_client_cancel(void)
if (mgcp)
talloc_free(mgcp);
mgcp = mgcp_client_init(ctx, conf);
mgcp = mgcp_client_init(ctx, &conf);
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
trans_id = mgcp_msg_trans_id(msg);
@@ -485,7 +487,7 @@ static struct sdp_section_start_test sdp_section_start_tests[] = {
},
};
void test_sdp_section_start(void)
void test_sdp_section_start()
{
int i;
int failures = 0;
@@ -529,7 +531,7 @@ void test_sdp_section_start(void)
OSMO_ASSERT(!failures);
}
static void test_map_str_to_codec(void)
static void test_map_pt_to_codec(void)
{
/* Full form */
OSMO_ASSERT(map_str_to_codec("PCMU/8000/1") == CODEC_PCMU_8000_1);
@@ -628,7 +630,7 @@ void test_mgcp_client_e1_epname(void)
if (mgcp)
talloc_free(mgcp);
mgcp = mgcp_client_init(ctx, conf);
mgcp = mgcp_client_init(ctx, &conf);
/* Valid endpoint names */
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 1, 15, 64, 0);
@@ -673,214 +675,6 @@ void test_mgcp_client_e1_epname(void)
OSMO_ASSERT(epname == NULL);
}
struct parse_response_test {
const char *body;
int expect_rc;
struct mgcp_response expect_params;
};
static struct parse_response_test parse_response_tests[] = {
{
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 112 3\r\n" /* <-- implicit: 3 = GSM-FR */
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n",
.expect_rc = 0,
.expect_params = {
.audio_port = 23,
.audio_ip = "1.2.3.4",
.ptmap = {
{ .codec = CODEC_AMR_8000_1, .pt = 112 },
{ .codec = CODEC_GSM_8000_1, .pt = 3 },
},
.ptmap_len = 2,
},
},
{
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 112 3\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=rtpmap:3 GSM/8000\r\n" /* 3 == GSM-FR implicitly, is an explicit entry a problem? */
"a=ptime:20\r\n",
.expect_rc = 0,
.expect_params = {
.audio_port = 23,
.audio_ip = "1.2.3.4",
.ptmap = {
{ .codec = CODEC_AMR_8000_1, .pt = 112 },
{ .codec = CODEC_GSM_8000_1, .pt = 3 }, /* no, not a problem */
},
.ptmap_len = 2,
},
},
{
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 3\r\n" /* <-- 112 is missing here. Will it still appear? */
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n",
.expect_rc = 0,
.expect_params = {
.audio_port = 23,
.audio_ip = "1.2.3.4",
.ptmap = {
{ .codec = CODEC_GSM_8000_1, .pt = 3 },
{ .codec = CODEC_AMR_8000_1, .pt = 112 }, /* <-- yes, it was added to the end. */
},
.ptmap_len = 2,
},
},
{
/* test MGCP_MAX_CODECS */
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110\r\n" /* <-- 10 codecs max */
"a=rtpmap:101 AMR/8000\r\n"
"a=rtpmap:102 AMR/8000\r\n"
"a=rtpmap:103 AMR/8000\r\n"
"a=rtpmap:104 AMR/8000\r\n"
"a=rtpmap:105 AMR/8000\r\n"
"a=rtpmap:106 AMR/8000\r\n"
"a=rtpmap:107 AMR/8000\r\n"
"a=rtpmap:108 AMR/8000\r\n"
"a=rtpmap:109 AMR/8000\r\n"
"a=rtpmap:110 AMR/8000\r\n"
"a=ptime:20\r\n",
.expect_rc = 0,
.expect_params = {
.audio_port = 23,
.audio_ip = "1.2.3.4",
.ptmap = {
{ .codec = CODEC_AMR_8000_1, .pt = 101 },
{ .codec = CODEC_AMR_8000_1, .pt = 102 },
{ .codec = CODEC_AMR_8000_1, .pt = 103 },
{ .codec = CODEC_AMR_8000_1, .pt = 104 },
{ .codec = CODEC_AMR_8000_1, .pt = 105 },
{ .codec = CODEC_AMR_8000_1, .pt = 106 },
{ .codec = CODEC_AMR_8000_1, .pt = 107 },
{ .codec = CODEC_AMR_8000_1, .pt = 108 },
{ .codec = CODEC_AMR_8000_1, .pt = 109 },
{ .codec = CODEC_AMR_8000_1, .pt = 110 },
},
.ptmap_len = 10,
},
},
{
/* test MGCP_MAX_CODECS */
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110 3\r\n" /* <-- 11 > MGCP_MAX_CODECS */
"a=rtpmap:101 AMR/8000\r\n"
"a=rtpmap:102 AMR/8000\r\n"
"a=rtpmap:103 AMR/8000\r\n"
"a=rtpmap:104 AMR/8000\r\n"
"a=rtpmap:105 AMR/8000\r\n"
"a=rtpmap:106 AMR/8000\r\n"
"a=rtpmap:107 AMR/8000\r\n"
"a=rtpmap:108 AMR/8000\r\n"
"a=rtpmap:109 AMR/8000\r\n"
"a=rtpmap:110 AMR/8000\r\n"
"a=ptime:20\r\n",
.expect_rc = -EINVAL,
},
};
static void test_parse_response(void)
{
int i;
int failures = 0;
for (i = 0; i < ARRAY_SIZE(parse_response_tests); i++) {
int rc;
struct parse_response_test *t = &parse_response_tests[i];
struct mgcp_response *r = talloc_zero(ctx, struct mgcp_response);
int p;
r->body = talloc_strdup(r, t->body);
//printf("\n%s() test [%d]:\n", __func__, i);
fprintf(stderr, "\n%s() test [%d]:\n", __func__, i);
fprintf(stderr, "body: \"%s\"\n", osmo_escape_str(r->body, -1));
rc = mgcp_response_parse_params(r);
fprintf(stderr, "got rc=%d\n", rc);
if (rc != t->expect_rc) {
fprintf(stderr, "FAIL: Expected rc=%d\n", t->expect_rc);
failures++;
}
if (rc) {
talloc_free(r);
continue;
}
fprintf(stderr, "got audio_ip=\"%s\"\n", r->audio_ip);
if (strcmp(r->audio_ip, t->expect_params.audio_ip)) {
fprintf(stderr, "FAIL: Expected audio_ip=\"%s\"\n", t->expect_params.audio_ip);
failures++;
}
fprintf(stderr, "got audio_port=%u\n", r->audio_port);
if (r->audio_port != t->expect_params.audio_port) {
fprintf(stderr, "FAIL: Expected audio_port=%u\n", t->expect_params.audio_port);
failures++;
}
for (p = 0; p < r->ptmap_len; p++) {
struct ptmap *got = &r->ptmap[p];
struct ptmap *expect = NULL;
fprintf(stderr, " %d %s\n", got->pt, osmo_mgcpc_codec_name(got->codec));
if (p >= t->expect_params.ptmap_len) {
fprintf(stderr, " - ERROR: too many codec entries\n");
failures++;
continue;
}
expect = &t->expect_params.ptmap[p];
if (ptmap_cmp(got, expect)) {
fprintf(stderr, " - ERROR: expected: %d %s\n",
expect->pt, osmo_mgcpc_codec_name(expect->codec));
failures++;
}
}
talloc_free(r);
}
OSMO_ASSERT(!failures);
}
static const struct log_info_cat log_categories[] = {
};
@@ -895,25 +689,22 @@ int main(int argc, char **argv)
ctx = talloc_named_const(NULL, 1, "mgcp_client_test");
msgb_talloc_ctx_init(ctx, 0);
osmo_init_logging2(ctx, &log_info);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_filename(osmo_stderr_target, 0);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
log_set_category_filter(osmo_stderr_target, DLMGCP, 1, LOGL_DEBUG);
conf = mgcp_client_conf_alloc(ctx);
mgcp_client_conf_init(&conf);
test_mgcp_msg();
test_mgcp_client_cancel();
test_sdp_section_start();
test_map_codec_to_pt_and_map_pt_to_codec();
test_map_str_to_codec();
test_map_pt_to_codec();
test_mgcp_client_e1_epname();
test_parse_response();
printf("Done\n");
fprintf(stderr, "Done\n");
return EXIT_SUCCESS;

View File

@@ -1,21 +1,18 @@
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
DLMGCP MGW(mgw) Message buffer too small, can not generate MGCP message (SDP)
DLMGCP MGW(mgw) Failed to add SDP, can not generate MGCP message
DLMGCP MGCP client: using endpoint domain '@mgw'
DLMGCP message buffer to small, can not generate MGCP message
test_mgcp_client_cancel():
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
DLMGCP MGCP client: using endpoint domain '@mgw'
- composed msg with trans_id=1
- not in queue yet, cannot cancel yet
DLMGCP MGW(mgw) Cannot cancel, no such transaction: 1
DLMGCP Cannot cancel, no such transaction: 1
- enqueue
- cancel succeeds
DLMGCP MGW(mgw) Canceled transaction 1
DLMGCP Canceled transaction 1
- late response gets discarded
DLMGCP MGW(mgw) MGCP link to MGW now considered UP
DLMGCP MGW(mgw) MGCP client: Rx 200 1 OK
DLMGCP MGW(mgw) Cannot find matching MGCP transaction for trans_id 1
DLMGCP Cannot find matching MGCP transaction for trans_id 1
- canceling again does nothing
DLMGCP MGW(mgw) Cannot cancel, no such transaction: 1
DLMGCP Cannot cancel, no such transaction: 1
test_mgcp_client_cancel() done
test_sdp_section_start() test [0]:
@@ -132,56 +129,9 @@ DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw), rate(128)/offset(0) combination is invalid!
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-0/su8-2@mgw), E1-timeslot number (0) is invalid!
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-64/su8-2@mgw), E1-timeslot number (64) is invalid!
test_parse_response() test [0]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 112 3\r\na=rtpmap:112 AMR/8000\r\na=ptime:20\r\n"
got rc=0
got audio_ip="1.2.3.4"
got audio_port=23
112 AMR/8000/1
3 GSM/8000/1
test_parse_response() test [1]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 112 3\r\na=rtpmap:112 AMR/8000\r\na=rtpmap:3 GSM/8000\r\na=ptime:20\r\n"
got rc=0
got audio_ip="1.2.3.4"
got audio_port=23
112 AMR/8000/1
3 GSM/8000/1
test_parse_response() test [2]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 3\r\na=rtpmap:112 AMR/8000\r\na=ptime:20\r\n"
DLMGCP error in MGCP message: 'a=rtpmap:112' has no matching entry in 'm=audio ... 112'
got rc=0
got audio_ip="1.2.3.4"
got audio_port=23
3 GSM/8000/1
112 AMR/8000/1
test_parse_response() test [3]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110\r\na=rtpmap:101 AMR/8000\r\na=rtpmap:102 AMR/8000\r\na=rtpmap:103 AMR/8000\r\na=rtpmap:104 AMR/8"
got rc=0
got audio_ip="1.2.3.4"
got audio_port=23
101 AMR/8000/1
102 AMR/8000/1
103 AMR/8000/1
104 AMR/8000/1
105 AMR/8000/1
106 AMR/8000/1
107 AMR/8000/1
108 AMR/8000/1
109 AMR/8000/1
110 AMR/8000/1
test_parse_response() test [4]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110 3\r\na=rtpmap:101 AMR/8000\r\na=rtpmap:102 AMR/8000\r\na=rtpmap:103 AMR/8000\r\na=rtpmap:104 AMR"
DLMGCP SDP: can parse only up to 10 payload type numbers
DLMGCP Failed to parse SDP parameter payload types (RTP/AVP)
got rc=-22
DLMGCP MGCP client: using endpoint domain '@mgw'
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw), rate(128)/offset(0) combination is invalid!
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-0/su8-2@mgw), E1-timeslot number (0) is invalid!
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-64/su8-2@mgw), E1-timeslot number (64) is invalid!
Done