mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-24 16:43:39 +00:00
Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e81c1176aa | ||
|
|
ed0c1aa9d5 | ||
|
|
ac69ea9cdf | ||
|
|
0a40379214 | ||
|
|
72bc8da537 | ||
|
|
c3132fd508 | ||
|
|
96c3107daf | ||
|
|
ad21a0e166 | ||
|
|
32d15cc8ba | ||
|
|
1fd50e5306 | ||
|
|
6802374574 | ||
|
|
82c1d1eb20 | ||
|
|
5a5169e15c | ||
|
|
7c0b70a94e | ||
|
|
ca6a8495e1 | ||
|
|
c0a2196f91 | ||
|
|
da026abc27 | ||
|
|
27780313ac | ||
|
|
e5ed946bdb | ||
|
|
c319b79baa | ||
|
|
2818db8b33 | ||
|
|
0415de74b8 | ||
|
|
4075b8805d | ||
|
|
8ab3fbbaf2 | ||
|
|
cc39218dce | ||
|
|
8dbc9ed408 | ||
|
|
ba25eab0fa | ||
|
|
aa823bf24b | ||
|
|
a714abfc91 | ||
|
|
9270e91c09 | ||
|
|
b7974e2fa4 | ||
|
|
1e174875bf | ||
|
|
c26b665c0c | ||
|
|
0479b2a15f | ||
|
|
ff6606cacb | ||
|
|
407b1f186d | ||
|
|
de2a4d7c22 | ||
|
|
48aff62341 | ||
|
|
852ba86949 | ||
|
|
662fa421c6 | ||
|
|
d14163e74f | ||
|
|
11b4810142 | ||
|
|
9ecceb651b | ||
|
|
426a9d9103 | ||
|
|
17bf603222 | ||
|
|
bcd52e5724 | ||
|
|
956242dcec | ||
|
|
e686675f7d | ||
|
|
4ca0b0d1fa | ||
|
|
efd645e5a8 | ||
|
|
f0504e86b3 | ||
|
|
e28b673fba | ||
|
|
a77eade744 | ||
|
|
6531726a02 | ||
|
|
31b4729f27 | ||
|
|
a729db62a9 | ||
|
|
5336f57f05 | ||
|
|
eb72ff058f | ||
|
|
8a91d2c04e | ||
|
|
55e0dcf254 | ||
|
|
23e7bf1c00 | ||
|
|
10d487e13f | ||
|
|
40f5033cf9 | ||
|
|
b1bb1fa187 | ||
|
|
1d121483f6 | ||
|
|
124441af1f | ||
|
|
b861db97ea | ||
|
|
08e07046f9 | ||
|
|
a5e0cf09a6 | ||
|
|
f2388eab71 | ||
|
|
120865833c | ||
|
|
e6d8e91b3a | ||
|
|
475f868b7f | ||
|
|
0063ca2fb0 | ||
|
|
610fda6108 | ||
|
|
352eed09ae | ||
|
|
0a89e92a5e | ||
|
|
dde80f3f61 | ||
|
|
9a7ccc3746 | ||
|
|
910189d0a1 | ||
|
|
8482e8374c | ||
|
|
fee4fa9492 | ||
|
|
4dba769577 | ||
|
|
da895b1151 | ||
|
|
a74c0ea2db | ||
|
|
6a26c16eb0 | ||
|
|
af8e00ffe8 | ||
|
|
6931f9a7a4 | ||
|
|
544448abea | ||
|
|
e547bdd1ce | ||
|
|
d1562a7766 | ||
|
|
304b3eb328 | ||
|
|
9cd5233609 | ||
|
|
35a382968c | ||
|
|
7066af825a | ||
|
|
acc10353fe | ||
|
|
bca0ef6cd9 | ||
|
|
cede2a4b7c | ||
|
|
337209a6ea | ||
|
|
56e0443e1c | ||
|
|
b1a1b4000e | ||
|
|
8838c62fe9 | ||
|
|
704c4f0adf | ||
|
|
bc0346e080 | ||
|
|
5928dc9345 | ||
|
|
04da5e5e98 | ||
|
|
54b4f82f91 | ||
|
|
3d7b58d77a | ||
|
|
604410cd13 | ||
|
|
b340f90c9e | ||
|
|
3c8ccb6724 | ||
|
|
d4e6aa42ca | ||
|
|
e6df0e47e7 | ||
|
|
54eb0e1204 | ||
|
|
b38fb8911f | ||
|
|
1b3a385b9d | ||
|
|
dbd70c7b68 | ||
|
|
a19547b7a1 | ||
|
|
06823731d8 | ||
|
|
ed1cff5ab9 | ||
|
|
f2321b7a72 | ||
|
|
4219904cb2 | ||
|
|
b2753f2044 | ||
|
|
ba61f68137 | ||
|
|
9e1d164469 | ||
|
|
0ec1d4e17c |
13
.gitignore
vendored
13
.gitignore
vendored
@@ -22,6 +22,7 @@ src/utils/smpp_mirror
|
|||||||
*.pyc
|
*.pyc
|
||||||
*.gcda
|
*.gcda
|
||||||
*.gcno
|
*.gcno
|
||||||
|
*.pc
|
||||||
|
|
||||||
#configure
|
#configure
|
||||||
aclocal.m4
|
aclocal.m4
|
||||||
@@ -38,6 +39,7 @@ missing
|
|||||||
stamp-h1
|
stamp-h1
|
||||||
libtool
|
libtool
|
||||||
ltmain.sh
|
ltmain.sh
|
||||||
|
m4/*.m4
|
||||||
|
|
||||||
# git-version-gen magic
|
# git-version-gen magic
|
||||||
.tarball-version
|
.tarball-version
|
||||||
@@ -81,3 +83,14 @@ gsn_restart
|
|||||||
src/openbsc.cfg*
|
src/openbsc.cfg*
|
||||||
writtenconfig/
|
writtenconfig/
|
||||||
gtphub_restart_count
|
gtphub_restart_count
|
||||||
|
|
||||||
|
# manuals
|
||||||
|
doc/manuals/*.html
|
||||||
|
doc/manuals/*.svg
|
||||||
|
doc/manuals/*.pdf
|
||||||
|
doc/manuals/*__*.png
|
||||||
|
doc/manuals/*.check
|
||||||
|
doc/manuals/generated/
|
||||||
|
doc/manuals/osmomsc-usermanual.xml
|
||||||
|
doc/manuals/common
|
||||||
|
doc/manuals/build
|
||||||
|
|||||||
@@ -18,13 +18,15 @@ SUBDIRS = \
|
|||||||
|
|
||||||
pkgconfigdir = $(libdir)/pkgconfig
|
pkgconfigdir = $(libdir)/pkgconfig
|
||||||
pkgconfig_DATA = \
|
pkgconfig_DATA = \
|
||||||
libosmo-legacy-mgcp.pc \
|
|
||||||
libosmo-mgcp-client.pc \
|
libosmo-mgcp-client.pc \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
BUILT_SOURCES = $(top_srcdir)/.version
|
BUILT_SOURCES = $(top_srcdir)/.version
|
||||||
EXTRA_DIST = git-version-gen osmoappdesc.py .version
|
EXTRA_DIST = git-version-gen osmoappdesc.py .version
|
||||||
|
|
||||||
|
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||||
|
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||||
|
|
||||||
@RELMAKE@
|
@RELMAKE@
|
||||||
|
|
||||||
$(top_srcdir)/.version:
|
$(top_srcdir)/.version:
|
||||||
|
|||||||
4
README
4
README
@@ -19,10 +19,6 @@ OsmoBSC, and receives and sends RTP streams as configured via MGCP.
|
|||||||
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC (found
|
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC (found
|
||||||
in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
|
in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
|
||||||
|
|
||||||
The libosmo-mgcp library exposes MGCP server utilities used by e.g. OsmoBSC-NAT
|
|
||||||
(found in osmo-bsc.git) to navigate RTP streams through a NAT.
|
|
||||||
(At time of writing, this is still called libosmo-legacy-mgcp.)
|
|
||||||
|
|
||||||
Find OsmoMGW issue tracker and wiki online at
|
Find OsmoMGW issue tracker and wiki online at
|
||||||
https://osmocom.org/projects/osmo-mgw
|
https://osmocom.org/projects/osmo-mgw
|
||||||
https://osmocom.org/projects/osmo-mgw/wiki
|
https://osmocom.org/projects/osmo-mgw/wiki
|
||||||
|
|||||||
89
configure.ac
89
configure.ac
@@ -39,10 +39,10 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
|
|||||||
AC_SUBST(LIBRARY_DL)
|
AC_SUBST(LIBRARY_DL)
|
||||||
|
|
||||||
|
|
||||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
|
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.11.0)
|
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.0.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
|
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.0.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.2.0)
|
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.4.0)
|
||||||
|
|
||||||
AC_ARG_ENABLE(sanitize,
|
AC_ARG_ENABLE(sanitize,
|
||||||
[AS_HELP_STRING(
|
[AS_HELP_STRING(
|
||||||
@@ -74,22 +74,6 @@ then
|
|||||||
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
|
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Enable/disable transcoding within osmo-bsc_mgcp?
|
|
||||||
AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [Build the MGCP gateway with internal transcoding enabled.])],
|
|
||||||
[osmo_ac_mgcp_transcoding="$enableval"],[osmo_ac_mgcp_transcoding="no"])
|
|
||||||
AC_ARG_WITH([g729], [AS_HELP_STRING([--with-g729], [Enable G.729 encoding/decoding.])], [osmo_ac_with_g729="$withval"],[osmo_ac_with_g729="no"])
|
|
||||||
|
|
||||||
if test "$osmo_ac_mgcp_transcoding" = "yes" ; then
|
|
||||||
AC_SEARCH_LIBS([gsm_create], [gsm], [LIBRARY_GSM="$LIBS";LIBS=""], [AC_MSG_ERROR([--enable-mgcp-transcoding: cannot find usable libgsm])])
|
|
||||||
AC_SUBST(LIBRARY_GSM)
|
|
||||||
if test "$osmo_ac_with_g729" = "yes" ; then
|
|
||||||
PKG_CHECK_MODULES(LIBBCG729, libbcg729 >= 0.1, [AC_DEFINE([HAVE_BCG729], [1], [Use bgc729 decoder/encoder])])
|
|
||||||
fi
|
|
||||||
AC_DEFINE(BUILD_MGCP_TRANSCODING, 1, [Define if we want to build the MGCP gateway with transcoding support])
|
|
||||||
fi
|
|
||||||
AM_CONDITIONAL(BUILD_MGCP_TRANSCODING, test "x$osmo_ac_mgcp_transcoding" = "xyes")
|
|
||||||
AC_SUBST(osmo_ac_mgcp_transcoding)
|
|
||||||
|
|
||||||
dnl Checks for typedefs, structures and compiler characteristics
|
dnl Checks for typedefs, structures and compiler characteristics
|
||||||
|
|
||||||
# The following test is taken from WebKit's webkit.m4
|
# The following test is taken from WebKit's webkit.m4
|
||||||
@@ -144,6 +128,64 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
|
|||||||
AC_MSG_RESULT([$enable_ext_tests])
|
AC_MSG_RESULT([$enable_ext_tests])
|
||||||
AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
|
AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
|
||||||
|
|
||||||
|
# Generate manuals
|
||||||
|
AC_ARG_ENABLE(manuals,
|
||||||
|
[AS_HELP_STRING(
|
||||||
|
[--enable-manuals],
|
||||||
|
[Generate manual PDFs [default=no]],
|
||||||
|
)],
|
||||||
|
[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"])
|
||||||
|
AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"])
|
||||||
|
AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals"
|
||||||
|
fallback])
|
||||||
|
if test x"$osmo_ac_build_manuals" = x"yes"
|
||||||
|
then
|
||||||
|
# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback)
|
||||||
|
if test -n "$OSMO_GSM_MANUALS_DIR"; then
|
||||||
|
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)"
|
||||||
|
else
|
||||||
|
OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)"
|
||||||
|
if test -n "$OSMO_GSM_MANUALS_DIR"; then
|
||||||
|
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)"
|
||||||
|
else
|
||||||
|
OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals"
|
||||||
|
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if ! test -d "$OSMO_GSM_MANUALS_DIR"; then
|
||||||
|
AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find and run check-depends
|
||||||
|
CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh"
|
||||||
|
if ! test -x "$CHECK_DEPENDS"; then
|
||||||
|
CHECK_DEPENDS="osmo-gsm-manuals-check-depends"
|
||||||
|
fi
|
||||||
|
if ! $CHECK_DEPENDS; then
|
||||||
|
AC_MSG_ERROR("missing dependencies for --enable-manuals")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Put in Makefile with absolute path
|
||||||
|
OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")"
|
||||||
|
AC_SUBST([OSMO_GSM_MANUALS_DIR])
|
||||||
|
fi
|
||||||
|
|
||||||
|
# https://www.freedesktop.org/software/systemd/man/daemon.html
|
||||||
|
AC_ARG_WITH([systemdsystemunitdir],
|
||||||
|
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
|
||||||
|
[with_systemdsystemunitdir=auto])
|
||||||
|
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
|
||||||
|
def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
|
||||||
|
|
||||||
|
AS_IF([test "x$def_systemdsystemunitdir" = "x"],
|
||||||
|
[AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
|
||||||
|
[AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
|
||||||
|
with_systemdsystemunitdir=no],
|
||||||
|
[with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
|
||||||
|
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
|
||||||
|
[AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
|
||||||
|
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
|
||||||
|
|
||||||
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
|
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
|
||||||
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
|
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
|
||||||
|
|
||||||
@@ -151,25 +193,22 @@ dnl Generate the output
|
|||||||
AM_CONFIG_HEADER(bscconfig.h)
|
AM_CONFIG_HEADER(bscconfig.h)
|
||||||
|
|
||||||
AC_OUTPUT(
|
AC_OUTPUT(
|
||||||
libosmo-legacy-mgcp.pc
|
|
||||||
libosmo-mgcp-client.pc
|
libosmo-mgcp-client.pc
|
||||||
include/Makefile
|
include/Makefile
|
||||||
include/osmocom/Makefile
|
include/osmocom/Makefile
|
||||||
include/osmocom/legacy_mgcp/Makefile
|
|
||||||
include/osmocom/mgcp_client/Makefile
|
include/osmocom/mgcp_client/Makefile
|
||||||
include/osmocom/mgcp/Makefile
|
include/osmocom/mgcp/Makefile
|
||||||
src/Makefile
|
src/Makefile
|
||||||
src/libosmo-legacy-mgcp/Makefile
|
|
||||||
src/libosmo-mgcp-client/Makefile
|
src/libosmo-mgcp-client/Makefile
|
||||||
src/libosmo-mgcp/Makefile
|
src/libosmo-mgcp/Makefile
|
||||||
src/osmo-bsc_mgcp/Makefile
|
|
||||||
src/osmo-mgw/Makefile
|
src/osmo-mgw/Makefile
|
||||||
tests/Makefile
|
tests/Makefile
|
||||||
tests/atlocal
|
tests/atlocal
|
||||||
tests/legacy_mgcp/Makefile
|
|
||||||
tests/mgcp_client/Makefile
|
tests/mgcp_client/Makefile
|
||||||
tests/mgcp/Makefile
|
tests/mgcp/Makefile
|
||||||
doc/Makefile
|
doc/Makefile
|
||||||
doc/examples/Makefile
|
doc/examples/Makefile
|
||||||
|
doc/manuals/Makefile
|
||||||
contrib/Makefile
|
contrib/Makefile
|
||||||
|
contrib/systemd/Makefile
|
||||||
Makefile)
|
Makefile)
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
|
SUBDIRS = systemd
|
||||||
|
|
||||||
EXTRA_DIST = ipa.py
|
EXTRA_DIST = ipa.py
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# jenkins build helper script for openbsc. This is how we build on jenkins.osmocom.org
|
# jenkins build helper script for openbsc. This is how we build on jenkins.osmocom.org
|
||||||
|
#
|
||||||
|
# environment variables:
|
||||||
|
# * WITH_MANUALS: build manual PDFs if set to "1"
|
||||||
|
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
|
||||||
|
#
|
||||||
|
|
||||||
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
|
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
|
||||||
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
|
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
|
||||||
@@ -23,10 +28,18 @@ verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
|
|||||||
|
|
||||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||||
export LD_LIBRARY_PATH="$inst/lib"
|
export LD_LIBRARY_PATH="$inst/lib"
|
||||||
|
export PATH="$inst/bin:$PATH"
|
||||||
|
|
||||||
osmo-build-dep.sh libosmo-abis
|
osmo-build-dep.sh libosmo-abis
|
||||||
osmo-build-dep.sh libosmo-netif
|
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
|
||||||
|
|
||||||
set +x
|
set +x
|
||||||
echo
|
echo
|
||||||
echo
|
echo
|
||||||
@@ -37,13 +50,17 @@ set -x
|
|||||||
|
|
||||||
cd "$base"
|
cd "$base"
|
||||||
autoreconf --install --force
|
autoreconf --install --force
|
||||||
./configure $MGCP --enable-vty-tests --enable-external-tests --enable-werror
|
./configure --enable-vty-tests --enable-external-tests --enable-werror $CONFIG
|
||||||
$MAKE $PARALLEL_MAKE
|
$MAKE $PARALLEL_MAKE
|
||||||
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
|
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
|
||||||
|| cat-testlogs.sh
|
|| cat-testlogs.sh
|
||||||
LD_LIBRARY_PATH="$inst/lib" \
|
LD_LIBRARY_PATH="$inst/lib" \
|
||||||
DISTCHECK_CONFIGURE_FLAGS="$MGCP --enable-vty-tests --enable-external-tests" \
|
DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests $CONFIG" \
|
||||||
$MAKE distcheck \
|
$MAKE distcheck \
|
||||||
|| cat-testlogs.sh
|
|| cat-testlogs.sh
|
||||||
|
|
||||||
|
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
|
||||||
|
make -C "$base/doc/manuals" publish
|
||||||
|
fi
|
||||||
|
|
||||||
osmo-clean-workspace.sh
|
osmo-clean-workspace.sh
|
||||||
|
|||||||
6
contrib/systemd/Makefile.am
Normal file
6
contrib/systemd/Makefile.am
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
EXTRA_DIST = osmo-mgw.service
|
||||||
|
|
||||||
|
if HAVE_SYSTEMD
|
||||||
|
systemdsystemunit_DATA = \
|
||||||
|
osmo-mgw.service
|
||||||
|
endif
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=OpenBSC MGCP
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
Restart=always
|
|
||||||
ExecStart=/usr/bin/osmo-bsc_mgcp -s -c /etc/osmocom/osmo-bsc-mgcp.cfg
|
|
||||||
RestartSec=2
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
155
debian/changelog
vendored
155
debian/changelog
vendored
@@ -1,3 +1,158 @@
|
|||||||
|
osmo-mgw (1.5.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
[ Pau Espin Pedrol ]
|
||||||
|
* gitignore: Filter *.pc
|
||||||
|
* configure: Find correct libgsm's gsm.h header
|
||||||
|
* vty: Fix typo writing bts-jitter-buffer-delay-{min,max}
|
||||||
|
* Remove libosmo-legacy-mgcp and osmo-bsc-mgcp
|
||||||
|
* debian: Remove dangling symlink to osmo-bsc-mgcp.service
|
||||||
|
* Install systemd services with autotools
|
||||||
|
* Install sample cfg file to /etc/osmocom
|
||||||
|
* mgcp: Fix osmux_cid_bitmap static array size calculation
|
||||||
|
* mgcp_osmux: Use define to calculate rtp_ssrc_winlen
|
||||||
|
* osmux: Avoid initing output without enabling osmux
|
||||||
|
* mgcp: Log endpoint nr consistently as hex
|
||||||
|
* osmux_send_dummy: Avoid logging incorrectly and sending if osmux not enabled
|
||||||
|
* osmux: Don't process regular osmux frames if disabled by cfg
|
||||||
|
* osmux: Move parse_cid of legacy dummy frames to own function
|
||||||
|
* osmux: Make func handling dummy frames independent of endp type
|
||||||
|
* osmux: allow enabling osmux only on correct activating state
|
||||||
|
* osmux: Improve checks around activating and using enabled osmux
|
||||||
|
* osmux.h: Document enum osmux_state
|
||||||
|
* osmux: Avoid processing further frames if conn not found
|
||||||
|
|
||||||
|
[ Philipp Maier ]
|
||||||
|
* mgcp_client_fsm: allow ptmap in mgcp_client_fsm as well
|
||||||
|
* mgcp_network: translate payload type numbers in RTP packets
|
||||||
|
* mgcp_client: use IETF source port as for MGCP
|
||||||
|
* mgcp_client: increment local port number when port is in use
|
||||||
|
* mgcp_test: release endpoints after use
|
||||||
|
* network: do not patch PT of RTCP packets
|
||||||
|
* network: check packets before further processing
|
||||||
|
* Cosmetic: remove misplaced line break
|
||||||
|
* mgcp_sdp: restructure mgcp_write_response_sdp() (rtpmap)
|
||||||
|
* mgcp_sdp: restructure mgcp_write_response_sdp() (audio)
|
||||||
|
* mgcp_client: check local port only once
|
||||||
|
* mgcp_client_fsm: switch to MGCP_CONN_RECV_SEND in add_audio()
|
||||||
|
* mgcp_protocol: increase buffer space for codec name in LCO
|
||||||
|
* osmo-mgw: Add vty reference manual
|
||||||
|
|
||||||
|
[ Neels Hofmeyr ]
|
||||||
|
* fix handling of "Wrong domain name" error
|
||||||
|
* interpret domain '*' as 'allow any domain'
|
||||||
|
* cosmetic: log: fix "CallIDs does not match"
|
||||||
|
* fix 3G hack: allow any IP for loopback and 0.0.0.0
|
||||||
|
* cosmetic: drop code dup in mgcp_client_fsm.c CRCX
|
||||||
|
* add X-Osmo-IGN MGCP header to ignore CallID
|
||||||
|
* X-Osmo-IGN: rather parse items by token, not char
|
||||||
|
* mgcp_test: fix log of conn_id presence
|
||||||
|
* mgcp_test: fix get_conn_id_from_response() CI length
|
||||||
|
* mgcp_client_test: cosmetically re-arrange reply_to() args
|
||||||
|
* mgcp_client_test: use "\r\n\r\n" instead of "\n\n"
|
||||||
|
* mgcp_client_test: also verify received conn_id
|
||||||
|
* mgcp_client_test: test long conn_id
|
||||||
|
* mgcp_client: error on too long conn id
|
||||||
|
* mgcp_common: rename to MGCP_CONN_ID_MAXLEN
|
||||||
|
* doc: fix mgcp_verify_ci() return val doc
|
||||||
|
* mgcp_verify_ci(): return meaningful error codes
|
||||||
|
* fix mgcp_verify_ci(): off-by-one in max len check
|
||||||
|
* generate shorter 'I:' conn IDs
|
||||||
|
* mgcp_conn_get(): compare conn Id ('I:') case insensitively
|
||||||
|
* mgcp_conn_get(): match conn Id ('I:') despite leading zeros
|
||||||
|
* cosmetic: mgcp_test: fix get_conn_id_from_response()
|
||||||
|
* comment: indicate struct type for mgcp_endpoint.conns
|
||||||
|
* log: avoid logging early media as error
|
||||||
|
* fix osmo-mgw -s; fixes osmo-mgw.service using -s
|
||||||
|
* Importing history from osmo-gsm-manuals.git
|
||||||
|
* OsmoMGW: update VTY reference
|
||||||
|
* OsmoMGW: document the 'X-Osmo-IGN' MGCP extension
|
||||||
|
* mgw: update vty reference
|
||||||
|
* drop/replace very weird logging in mgcp_client.c
|
||||||
|
* check_rtp: on IP:port errors, log the IP and port
|
||||||
|
* osmo-mgw: err-log: include expected domain name
|
||||||
|
* mgcp_client_vty: fix missing talloc_free
|
||||||
|
* mgcp_client: drop a bunch of dead code
|
||||||
|
* mgcp_client: logging tweaks
|
||||||
|
* mgcp_client: make domain part of endpoint configurable
|
||||||
|
* mgcp_client: tweak some log levels INFO -> {DEBUG,ERROR}
|
||||||
|
|
||||||
|
[ Stefan Sperling ]
|
||||||
|
* add VTY commands which show specific mgcp endpoints
|
||||||
|
* add MGCP CRCX command statistics to osmo-mgw
|
||||||
|
* show RTP TX/RX stats in 'mgcp show stats' output
|
||||||
|
* use local variable for rate counters in handle_create_con()
|
||||||
|
* add more mgcp crxc error counters
|
||||||
|
* add MDCX command statistics to osmo-mgw
|
||||||
|
* add aggregated rtp connection stats to osmo-mgw
|
||||||
|
* add DLCX command statistics to osmo-mgw
|
||||||
|
|
||||||
|
[ Harald Welte ]
|
||||||
|
* debian/rules: Don't overwrite .tarball-version
|
||||||
|
* check_rtp_origin(): Don't memcmp sockadd_in and in_addr
|
||||||
|
* check_rtp_origin(): Avoid using memcmp for comparing integer types
|
||||||
|
* vty-ref: Update URI of docbook 5.0 schema
|
||||||
|
|
||||||
|
[ Daniel Willmann ]
|
||||||
|
* mgw: Add new VTY reference
|
||||||
|
* Add initial OsmoMGW manual
|
||||||
|
|
||||||
|
[ Oliver Smith ]
|
||||||
|
* build manuals moved here from osmo-gsm-manuals.git
|
||||||
|
* jenkins.sh: remove leftover MGCP env variable
|
||||||
|
* Fix DISTCHECK_CONFIGURE_FLAGS override
|
||||||
|
* contrib/jenkins.sh: build and publish manuals
|
||||||
|
* contrib: fix makedistcheck with disabled systemd
|
||||||
|
|
||||||
|
-- Harald Welte <laforge@gnumonks.org> Sun, 20 Jan 2019 15:02:18 +0100
|
||||||
|
|
||||||
|
osmo-mgw (1.4.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
[ Philipp Maier ]
|
||||||
|
* network: independently initalize state->out_stream
|
||||||
|
* stats: use libosmocore rate counter for in/out_stream.err_ts_counter
|
||||||
|
* mgcp_sdp: correct apidoc of mgcp_parse_sdp_data
|
||||||
|
* vty: clean up rtp port-range command
|
||||||
|
* sdp: remove unused alt_codec field from struct mgcp_rtp_end
|
||||||
|
* sdp: remove circular inclusion
|
||||||
|
* protocol: Try whole port range on port allocation
|
||||||
|
* client: do not start connections in loopback mode
|
||||||
|
* mgcp_network: do not log destination invalid ip/port as error
|
||||||
|
* cosmetic: fix log output
|
||||||
|
* conn: call talloc_free before setting the pointer to NULL
|
||||||
|
* protocol: do not change LCO, when no LCO are present
|
||||||
|
* protocol: reject illegal lco options
|
||||||
|
* cosmetic: fix typo
|
||||||
|
* mgw: clean up codec negotiation (sdp)
|
||||||
|
* client: add features to generate and parse codec information
|
||||||
|
* mgcp_internal: remove unused struct member
|
||||||
|
* stats: replace packet statistic counters with libosmocore rate counters
|
||||||
|
* stat+vty: fix printing of rate counter values
|
||||||
|
* protocol: prevent unnecessary null pointer deref
|
||||||
|
|
||||||
|
[ Pau Espin Pedrol ]
|
||||||
|
* legacy-mgcp: Add jitter buffer on the uplink receiver
|
||||||
|
* legacy-mgcp: switch to new osmux output APIs
|
||||||
|
* mgcp: mgcp_osmux: use conn_bts when forwarding pkts from bsc_nat
|
||||||
|
* mgcp: switch to new osmux output APIs
|
||||||
|
* debian: Package installed example doc files
|
||||||
|
* gitignore: Add m4 scripts from m4 subdir
|
||||||
|
|
||||||
|
[ Neels Hofmeyr ]
|
||||||
|
* api doc: fix parameter name for mgcp_conn_create()
|
||||||
|
* mgcp-client: add mgcp_conn_get_ci()
|
||||||
|
* mgcp_client_fsm: improve error logging
|
||||||
|
* cosmetic: fix doxygen comment markers
|
||||||
|
* cosmetic: mgcp_network.c: merge one LOGPC to its preceding LOGP
|
||||||
|
* IuUP hack: make RTP patching less general
|
||||||
|
|
||||||
|
[ Harald Welte ]
|
||||||
|
* cosmetic: fix typo in log message: 'abrupt' instead of 'aprupt'
|
||||||
|
|
||||||
|
[ Daniel Willmann ]
|
||||||
|
* git-version-gen: Don't check for .git directory
|
||||||
|
|
||||||
|
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 27 Jul 2018 19:05:22 +0200
|
||||||
|
|
||||||
osmo-mgw (1.3.0) unstable; urgency=medium
|
osmo-mgw (1.3.0) unstable; urgency=medium
|
||||||
|
|
||||||
[ Pau Espin Pedrol ]
|
[ Pau Espin Pedrol ]
|
||||||
|
|||||||
25
debian/control
vendored
25
debian/control
vendored
@@ -19,7 +19,7 @@ Multi-Arch: foreign
|
|||||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||||
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
||||||
|
|
||||||
Package: libosmo-mgcp-client3
|
Package: libosmo-mgcp-client5
|
||||||
Section: libs
|
Section: libs
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Multi-Arch: same
|
Multi-Arch: same
|
||||||
@@ -31,26 +31,5 @@ Package: libosmo-mgcp-client-dev
|
|||||||
Section: libdevel
|
Section: libdevel
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Multi-Arch: same
|
Multi-Arch: same
|
||||||
Depends: libosmo-mgcp-client3 (= ${binary:Version}), ${misc:Depends}
|
Depends: libosmo-mgcp-client5 (= ${binary:Version}), ${misc:Depends}
|
||||||
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
|
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
|
||||||
|
|
||||||
Package: osmo-bsc-mgcp
|
|
||||||
Architecture: any
|
|
||||||
Multi-Arch: foreign
|
|
||||||
Depends: libosmo-legacy-mgcp0, ${misc:Depends}, ${shlibs:Depends}
|
|
||||||
Description: OsmoBSC-MGCP: Osmocom's Legacy Media Gateway; use osmo-mgw instead.
|
|
||||||
|
|
||||||
Package: libosmo-legacy-mgcp0
|
|
||||||
Section: libs
|
|
||||||
Architecture: any
|
|
||||||
Multi-Arch: same
|
|
||||||
Pre-Depends: ${misc:Pre-Depends}
|
|
||||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
|
||||||
Description: libosmo-legacy-mgcp: Osmocom's Legacy Media Gateway server library; use libosmo-mgcp instead.
|
|
||||||
|
|
||||||
Package: libosmo-legacy-mgcp-dev
|
|
||||||
Section: libdevel
|
|
||||||
Architecture: any
|
|
||||||
Multi-Arch: same
|
|
||||||
Depends: libosmo-legacy-mgcp0 (= ${binary:Version}), ${misc:Depends}
|
|
||||||
Description: libosmo-legacy-mgcp: Osmocom's Legacy Media Gateway server library; use libosmo-mgcp instead.
|
|
||||||
|
|||||||
36
debian/copyright
vendored
36
debian/copyright
vendored
@@ -40,30 +40,6 @@ License: GPL-2.0+
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
Files: src/libosmo-legacy-mgcp/g711common.h
|
|
||||||
Copyright: 2000 Abramo Bagnara <abramo@alsa-project.org>
|
|
||||||
License: GPL-2.0+
|
|
||||||
Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org>
|
|
||||||
.
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
.
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
.
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
.
|
|
||||||
The FSF address in the above text is the old one.
|
|
||||||
.
|
|
||||||
On Debian systems, the complete text of the GNU General Public License
|
|
||||||
Version 2 can be found in `/usr/share/common-licenses/GPL-2'.
|
|
||||||
|
|
||||||
Files: tests/vty_test_runner.py
|
Files: tests/vty_test_runner.py
|
||||||
Copyright: 2013 Holger Hans Peter Freyther
|
Copyright: 2013 Holger Hans Peter Freyther
|
||||||
2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
|
2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
|
||||||
@@ -87,15 +63,3 @@ License: GPL-3.0+
|
|||||||
Files: osmoappdesc.py
|
Files: osmoappdesc.py
|
||||||
Copyright: 2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
|
Copyright: 2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
|
||||||
License: GPL-3.0+
|
License: GPL-3.0+
|
||||||
|
|
||||||
Files: src/libosmo-legacy-mgcp/mgcp_osmux.c
|
|
||||||
Copyright: 2012-2013 On Waves ehf <http://www.on-waves.com>
|
|
||||||
2012-2013 Pablo Neira Ayuso <pablo@gnumonks.org>
|
|
||||||
License: AGPL-3.0+
|
|
||||||
All rights not specifically granted under this license are 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.
|
|
||||||
|
|
||||||
|
|||||||
4
debian/libosmo-legacy-mgcp-dev.install
vendored
4
debian/libosmo-legacy-mgcp-dev.install
vendored
@@ -1,4 +0,0 @@
|
|||||||
usr/include/osmocom/legacy_mgcp
|
|
||||||
usr/lib/*/libosmo-legacy-mgcp.so
|
|
||||||
usr/lib/*/libosmo-legacy-mgcp.a
|
|
||||||
usr/lib/*/pkgconfig/libosmo-legacy-mgcp.pc
|
|
||||||
1
debian/libosmo-legacy-mgcp0.install
vendored
1
debian/libosmo-legacy-mgcp0.install
vendored
@@ -1 +0,0 @@
|
|||||||
usr/lib/*/libosmo-legacy-mgcp.so.*
|
|
||||||
1
debian/osmo-bsc-mgcp.install
vendored
1
debian/osmo-bsc-mgcp.install
vendored
@@ -1 +0,0 @@
|
|||||||
usr/bin/osmo-bsc_mgcp
|
|
||||||
1
debian/osmo-bsc-mgcp.service
vendored
1
debian/osmo-bsc-mgcp.service
vendored
@@ -1 +0,0 @@
|
|||||||
../contrib/systemd/osmo-bsc-mgcp.service
|
|
||||||
3
debian/osmo-mgw.install
vendored
3
debian/osmo-mgw.install
vendored
@@ -1 +1,4 @@
|
|||||||
|
etc/osmocom/osmo-mgw.cfg
|
||||||
|
lib/systemd/system/osmo-mgw.service
|
||||||
usr/bin/osmo-mgw
|
usr/bin/osmo-mgw
|
||||||
|
usr/share/doc/osmo-mgw/examples/osmo-mgw/osmo-mgw.cfg
|
||||||
|
|||||||
1
debian/osmo-mgw.service
vendored
1
debian/osmo-mgw.service
vendored
@@ -1 +0,0 @@
|
|||||||
../contrib/systemd/osmo-mgw.service
|
|
||||||
7
debian/rules
vendored
7
debian/rules
vendored
@@ -14,7 +14,7 @@ CFLAGS += -g
|
|||||||
|
|
||||||
# main packaging script based on dh7 syntax
|
# main packaging script based on dh7 syntax
|
||||||
%:
|
%:
|
||||||
dh $@ --with autoreconf
|
dh $@ --with autoreconf
|
||||||
|
|
||||||
# debmake generated override targets
|
# debmake generated override targets
|
||||||
# Set options for ./configure
|
# Set options for ./configure
|
||||||
@@ -29,8 +29,7 @@ CFLAGS += -g
|
|||||||
override_dh_auto_test:
|
override_dh_auto_test:
|
||||||
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
|
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
|
||||||
|
|
||||||
override_dh_autoreconf:
|
override_dh_auto_configure:
|
||||||
echo $(VERSION) > .tarball-version
|
dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system
|
||||||
dh_autoreconf
|
|
||||||
|
|
||||||
# See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg
|
# See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
examples \
|
examples \
|
||||||
|
manuals \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
OSMOCONF_FILES = \
|
||||||
|
osmo-mgw/osmo-mgw.cfg
|
||||||
|
|
||||||
|
osmoconfdir = $(sysconfdir)/osmocom
|
||||||
|
osmoconf_DATA = $(OSMOCONF_FILES)
|
||||||
|
|
||||||
|
EXTRA_DIST = $(OSMOCONF_FILES)
|
||||||
|
|
||||||
CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
|
CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
|
||||||
|
|
||||||
dist-hook:
|
dist-hook:
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
!
|
|
||||||
! MGCP configuration example
|
|
||||||
!
|
|
||||||
mgcp
|
|
||||||
!local ip 10.23.24.2
|
|
||||||
!bts ip 10.24.24.1
|
|
||||||
!bind ip 10.23.24.1
|
|
||||||
bind port 2427
|
|
||||||
rtp base 4000
|
|
||||||
rtp force-ptime 20
|
|
||||||
sdp audio payload number 98
|
|
||||||
sdp audio payload name AMR/8000
|
|
||||||
number endpoints 31
|
|
||||||
no rtcp-omit
|
|
||||||
16
doc/manuals/Makefile.am
Normal file
16
doc/manuals/Makefile.am
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
EXTRA_DIST = osmomgw-usermanual.adoc \
|
||||||
|
osmomgw-usermanual-docinfo.xml \
|
||||||
|
osmomgw-vty-reference.xml \
|
||||||
|
chapters \
|
||||||
|
vty
|
||||||
|
|
||||||
|
if BUILD_MANUALS
|
||||||
|
ASCIIDOC = osmomgw-usermanual.adoc
|
||||||
|
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc
|
||||||
|
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
|
||||||
|
|
||||||
|
VTY_REFERENCE = osmomgw-vty-reference.xml
|
||||||
|
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
|
||||||
|
|
||||||
|
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
|
||||||
|
endif
|
||||||
57
doc/manuals/chapters/configuration.adoc
Normal file
57
doc/manuals/chapters/configuration.adoc
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
== Configuring OsmoMGW
|
||||||
|
|
||||||
|
A basic configation of OsmoMGW mainly consists of specifying the IP address
|
||||||
|
and port on which to listen to MGCP commands, but changing the port range at
|
||||||
|
which the RTP streams terminate as well as limiting operation to a single call
|
||||||
|
agent can be done as well as changing the number of endpoints.
|
||||||
|
|
||||||
|
=== Configuring MGCP
|
||||||
|
|
||||||
|
By default OsmoMGW listens for MGCP on port 2427 on any IP address. If a call
|
||||||
|
agent address is configured then OsmoMGW will only answer MGCP commands from
|
||||||
|
that IP port 2727, otherwise all sources are handled. A domain can be
|
||||||
|
specified
|
||||||
|
|
||||||
|
.Example: MGCP configuration
|
||||||
|
----
|
||||||
|
OsmoMGW(config-mgcp)# bind ip 127.0.0.1
|
||||||
|
OsmoMGW(config-mgcp)# bind port 2427
|
||||||
|
OsmoMGW(config-mgcp)# call-agent ip 127.0.0.1
|
||||||
|
OsmoMGW(config-mgcp)# domain mgw-bsc
|
||||||
|
OsmoMGW(config-mgcp)# local ip 127.0.0.1
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Configuring the trunk
|
||||||
|
|
||||||
|
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 which are not yet implemented. Don't use trunks other than the
|
||||||
|
"virtual" trunk 0.
|
||||||
|
|
||||||
|
.Example: MGCP trunk configuration
|
||||||
|
----
|
||||||
|
OsmoMGW(config-mgcp)# number endpoints 63 <1>
|
||||||
|
OsmoMGW(config-mgcp)# rtp bind-ip 10.0.0.1 <2>
|
||||||
|
OsmoMGW(config-mgcp)# rtp port-range 12000-14000 <3>
|
||||||
|
----
|
||||||
|
<1> Maximum number of endpoints that can be allocated at once
|
||||||
|
<2> Use this IP when binding RTP endpoints
|
||||||
|
<3> Use ports in this range when binding RTP endpoints
|
||||||
|
|
||||||
|
There are some options to tweak how RTP forwarding behaves in OsmoMGW:
|
||||||
|
|
||||||
|
.Example: MGCP trunk rtp options
|
||||||
|
----
|
||||||
|
OsmoMGW(config-mgcp)# rtp keep-alive 30 <1>
|
||||||
|
OsmoMGW(config-mgcp)# rtp-patch ssrc <2>
|
||||||
|
OsmoMGW(config-mgcp)# rtp-patch timestamp <3>
|
||||||
|
----
|
||||||
|
<1> Send dummy UDP packets periodically to RTP destination
|
||||||
|
<2> Hide SSRC changes
|
||||||
|
<3> Ensure RTP timestamp is aligned with frame duration
|
||||||
|
|
||||||
|
|
||||||
4
doc/manuals/chapters/counters.adoc
Normal file
4
doc/manuals/chapters/counters.adoc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[[counters]]
|
||||||
|
== Counters
|
||||||
|
|
||||||
|
include::./counters_generated.adoc[]
|
||||||
14
doc/manuals/chapters/counters_generated.adoc
Normal file
14
doc/manuals/chapters/counters_generated.adoc
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
// autogenerated by show asciidoc counters
|
||||||
|
These counters and their description based on OsmoMGW 1.3.0.34-9cd52 (OsmoMGW).
|
||||||
|
|
||||||
|
=== Rate Counters
|
||||||
|
|
||||||
|
// generating tables for rate_ctr_group
|
||||||
|
== Osmo Stat Items
|
||||||
|
|
||||||
|
// generating tables for osmo_stat_items
|
||||||
|
== Osmo Counters
|
||||||
|
|
||||||
|
// generating tables for osmo_counters
|
||||||
|
// there are no ungrouped osmo_counters
|
||||||
68
doc/manuals/chapters/mgcp_extensions.adoc
Normal file
68
doc/manuals/chapters/mgcp_extensions.adoc
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
== MGCP Extensions
|
||||||
|
|
||||||
|
The MGCP protocol is extendable. The following non-standard extensions are
|
||||||
|
understood by OsmoMGW.
|
||||||
|
|
||||||
|
=== `X-Osmo-IGN`
|
||||||
|
|
||||||
|
`X-Osmo-IGN` indicates to OsmoMGW that specific items of an endpoint should be
|
||||||
|
ignored, so that it is lenient on mismatching values that would normally
|
||||||
|
indicate collisions or configuration errors.
|
||||||
|
|
||||||
|
==== `X-Osmo-IGN` Format
|
||||||
|
|
||||||
|
The value part of X-Osmo-IGN must be one or more items separated by one or more
|
||||||
|
spaces. Each item consists of one or more non-whitespace characters.
|
||||||
|
|
||||||
|
.Example: `X-Osmo-IGN` format with three ficticious items "X", "abc" and "123".
|
||||||
|
----
|
||||||
|
X-Osmo-IGN: X abc 123
|
||||||
|
----
|
||||||
|
|
||||||
|
`X-Osmo-IGN` must be issued in the MGCP section (typically as its last item),
|
||||||
|
before the SDP section starts.
|
||||||
|
|
||||||
|
==== Supported `X-Osmo-IGN` Items
|
||||||
|
|
||||||
|
Currently, the following `X-Osmo-IGN` items are supported:
|
||||||
|
|
||||||
|
* `C`: ignore CallID mismatches, i.e. differing "C" values between connections
|
||||||
|
on the same endpoint.
|
||||||
|
|
||||||
|
.Note:
|
||||||
|
`X-Osmo-IGN` does not support ignoring mismatches on the domain part of
|
||||||
|
an endpoint name, e.g. ignoring a mismatch on "example.com" in
|
||||||
|
`rtpbridge/123abc@example.com`. Instead, you may globally configure OsmoMGW
|
||||||
|
with `mgcp` / `domain *` to permit all domain parts.
|
||||||
|
|
||||||
|
===== `X-Osmo-IGN: C`
|
||||||
|
|
||||||
|
By default, OsmoMGW verifies that all CallIDs ("C" values) match for all
|
||||||
|
connections on any one given endpoint. To ignore CallID mismatches, pass a `C`
|
||||||
|
in the `X-Osmo-IGN` header, for the first or the second `CRCX` on an endpoint.
|
||||||
|
When the `X-Osmo-IGN: C` is sent for any one `CRCX` on an endpoint, CallID
|
||||||
|
mismatches will be ignored for that and all subsequent messages for that
|
||||||
|
endpoint. Hence this header only needs to be included once per endpoint, in any
|
||||||
|
`CRCX` message that precedes or coincides with a CallID mismatch.
|
||||||
|
|
||||||
|
This is particularly useful for a BSC that is connected to an A/SCCPlite MSC,
|
||||||
|
where the BSC and MSC each are expected to configure their respective own
|
||||||
|
connection on a shared endpoint. For A/SCCPlite, it is impossible for the BSC
|
||||||
|
to know the CallID that the MSC will use, so CallID mismatches are inevitable.
|
||||||
|
See also OsmoBSC, which will by default pass the `X-Osmo-IGN: C` header for
|
||||||
|
endpoints associated with an A/SCCPlite MSC.
|
||||||
|
|
||||||
|
.Example: `CRCX` message that instructs OsmoMGW to ignore CallID mismatches
|
||||||
|
----
|
||||||
|
CRCX 2 rtpbridge/123abc@mgw MGCP 1.0
|
||||||
|
M: recvonly
|
||||||
|
C: 2
|
||||||
|
L: p:20
|
||||||
|
X-Osmo-IGN: C
|
||||||
|
|
||||||
|
v=0
|
||||||
|
c=IN IP4 123.12.12.123
|
||||||
|
m=audio 5904 RTP/AVP 97
|
||||||
|
a=rtpmap:97 GSM-EFR/8000
|
||||||
|
a=ptime:40
|
||||||
|
----
|
||||||
100
doc/manuals/chapters/overview.adoc
Normal file
100
doc/manuals/chapters/overview.adoc
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
[[overview]]
|
||||||
|
== Overview
|
||||||
|
|
||||||
|
This manual should help you getting started with OsmoMGW. It will cover
|
||||||
|
aspects of configuring and running the media gateway.
|
||||||
|
|
||||||
|
[[intro_overview]]
|
||||||
|
=== About OsmoMGW
|
||||||
|
|
||||||
|
OsmoMGW is the Osmocom implementation of a media gateway to handle user
|
||||||
|
plane (voice) traffic in cellular networks. It can connect and optionally
|
||||||
|
transcode RTP voice streams between different network elements such as BTSs
|
||||||
|
and external entities like SIP. It is typically co-located with both OsmoBSC
|
||||||
|
and OsmoMSC and controlled by them via MGCP, the Media Gateway Control
|
||||||
|
Protocol.
|
||||||
|
|
||||||
|
[[fig-bsc]]
|
||||||
|
.OsmoMGW used with OsmoBSC
|
||||||
|
[graphviz]
|
||||||
|
----
|
||||||
|
digraph G {
|
||||||
|
rankdir = LR;
|
||||||
|
OsmoBTS -> OsmoBSC [label="Abis/IP"];
|
||||||
|
OsmoBSC -> OsmoMSC [label="3GPP AoIP"];
|
||||||
|
OsmoBSC -> OsmoMGW [label="MGCP"];
|
||||||
|
OsmoBTS -> OsmoMGW [label="RTP",dir=both];
|
||||||
|
OsmoMGW -> OsmoMSC [label="RTP",dir=both];
|
||||||
|
{rank=same OsmoBSC OsmoMGW}
|
||||||
|
OsmoMGW [color=red];
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
[[fig-msc]]
|
||||||
|
.OsmoMGW used with OsmoMSC
|
||||||
|
[graphviz]
|
||||||
|
----
|
||||||
|
digraph G {
|
||||||
|
rankdir = LR;
|
||||||
|
BTS -> BSC [label="Abis"];
|
||||||
|
BSC -> OsmoMSC [label="3GPP AoIP"];
|
||||||
|
OsmoMSC -> OsmoMGW [label="MGCP"];
|
||||||
|
BSC -> OsmoMGW [label="RTP",dir=both];
|
||||||
|
OsmoMSC -> OsmoSIP [label="MNCC"];
|
||||||
|
OsmoSIP -> PBX [label="SIP Trunk"];
|
||||||
|
OsmoMGW -> PBX [label="RTP",dir=both];
|
||||||
|
{rank=same OsmoMSC OsmoMGW}
|
||||||
|
OsmoSIP [label="osmo-sip-connector"];
|
||||||
|
OsmoMGW [color=red];
|
||||||
|
|
||||||
|
hNodeB -> OsmoHNBGW [label="Iuh"];
|
||||||
|
OsmoHNBGW -> OsmoMSC [label="IuCS"];
|
||||||
|
hNodeB -> OsmoMGW [label="RTP",dir=both];
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Software Components
|
||||||
|
|
||||||
|
OsmoMGW contains a variety of different software components, which we’ll
|
||||||
|
quickly describe in this section.
|
||||||
|
|
||||||
|
==== MGCP Implementation
|
||||||
|
|
||||||
|
OsmoMGW brings its own MGCP implementation through which OsmoMGW is
|
||||||
|
controlled.
|
||||||
|
|
||||||
|
The commands implemented are CRCX, MDCX, DLCX and RSIP. The command AUEP is
|
||||||
|
implemented as a stub and will simply respond with OK.
|
||||||
|
|
||||||
|
==== RTP implementation
|
||||||
|
|
||||||
|
Support for IuUP which is used in 3G cells is quite lacking at the moment.
|
||||||
|
3G<->3G and 2G<->2G calls should work, but 3G<->2G does not.
|
||||||
|
|
||||||
|
==== Audio transcoder
|
||||||
|
|
||||||
|
Transcoding is currently not supported in OsmoMGW.
|
||||||
|
|
||||||
|
=== Limitations
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
=== Additional resources
|
||||||
|
|
||||||
|
You can find the OsmoMGW issue tracker and wiki online at
|
||||||
|
|
||||||
|
- https://osmocom.org/projects/osmomgw
|
||||||
|
- https://osmocom.org/projects/osmomgw/wiki
|
||||||
|
|
||||||
|
RFC 3435 for MGCP is located at
|
||||||
|
|
||||||
|
- https://tools.ietf.org/html/rfc3435
|
||||||
25
doc/manuals/chapters/running.adoc
Normal file
25
doc/manuals/chapters/running.adoc
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
== Running OsmoMGW
|
||||||
|
|
||||||
|
The OsmoMGW executable (`osmo-mgw`) offers the following command-line
|
||||||
|
arguments:
|
||||||
|
|
||||||
|
=== SYNOPSIS
|
||||||
|
|
||||||
|
*osmo-mgw* [-h|-V] [-D] [-c 'CONFIGFILE'] [-s]
|
||||||
|
|
||||||
|
=== OPTIONS
|
||||||
|
|
||||||
|
*-h, --help*::
|
||||||
|
Print a short help message about the supported options
|
||||||
|
*-V, --version*::
|
||||||
|
Print the compile-time version number of the OsmoBTS program
|
||||||
|
*-D, --daemonize*::
|
||||||
|
Fork the process as a daemon into background.
|
||||||
|
*-c, --config-file 'CONFIGFILE'*::
|
||||||
|
Specify the file and path name of the configuration file to be
|
||||||
|
used. If none is specified, use `osmo-mgw.cfg` in the current
|
||||||
|
working directory.
|
||||||
|
*-s, --disable-color*::
|
||||||
|
Disable colors for logging to stderr. This has mostly been
|
||||||
|
deprecated by VTY based logging configuration, see <<logging>>
|
||||||
|
for more information.
|
||||||
47
doc/manuals/osmomgw-usermanual-docinfo.xml
Normal file
47
doc/manuals/osmomgw-usermanual-docinfo.xml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<revhistory>
|
||||||
|
<revision>
|
||||||
|
<revnumber>1</revnumber>
|
||||||
|
<date>July 24th, 2018</date>
|
||||||
|
<authorinitials>DW</authorinitials>
|
||||||
|
<revremark>
|
||||||
|
Initial version
|
||||||
|
</revremark>
|
||||||
|
</revision>
|
||||||
|
</revhistory>
|
||||||
|
|
||||||
|
<authorgroup>
|
||||||
|
<author>
|
||||||
|
<firstname>Daniel</firstname>
|
||||||
|
<surname>Willmann</surname>
|
||||||
|
<email>dwillmann@sysmocom.de</email>
|
||||||
|
<authorinitials>DW</authorinitials>
|
||||||
|
<affiliation>
|
||||||
|
<shortaffil>sysmocom</shortaffil>
|
||||||
|
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
|
||||||
|
</affiliation>
|
||||||
|
</author>
|
||||||
|
</authorgroup>
|
||||||
|
|
||||||
|
<copyright>
|
||||||
|
<year>2018</year>
|
||||||
|
<holder>sysmocom - s.f.m.c. GmbH</holder>
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<legalnotice>
|
||||||
|
<para>
|
||||||
|
Permission is granted to copy, distribute and/or modify this
|
||||||
|
document under the terms of the GNU Free Documentation License,
|
||||||
|
Version 1.3 or any later version published by the Free Software
|
||||||
|
Foundation; with the Invariant Sections being just 'Foreword',
|
||||||
|
'Acknowledgements' and 'Preface', with no Front-Cover Texts,
|
||||||
|
and no Back-Cover Texts. A copy of the license is included in
|
||||||
|
the section entitled "GNU Free Documentation License".
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The Asciidoc source code of this manual can be found at
|
||||||
|
<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
|
||||||
|
http://git.osmocom.org/osmo-gsm-manuals/
|
||||||
|
</ulink>
|
||||||
|
</para>
|
||||||
|
</legalnotice>
|
||||||
|
|
||||||
33
doc/manuals/osmomgw-usermanual.adoc
Normal file
33
doc/manuals/osmomgw-usermanual.adoc
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
:gfdl-enabled:
|
||||||
|
:program-name: OsmoMGW
|
||||||
|
|
||||||
|
OsmoMGW User Manual
|
||||||
|
====================
|
||||||
|
Daniel Willmann <dwillmann@sysmocom.de>
|
||||||
|
|
||||||
|
|
||||||
|
include::./common/chapters/preface.adoc[]
|
||||||
|
|
||||||
|
include::{srcdir}/chapters/overview.adoc[]
|
||||||
|
|
||||||
|
include::{srcdir}/chapters/running.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/vty.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/logging.adoc[]
|
||||||
|
|
||||||
|
include::{srcdir}/chapters/configuration.adoc[]
|
||||||
|
|
||||||
|
include::{srcdir}/chapters/mgcp_extensions.adoc[]
|
||||||
|
|
||||||
|
//include::{srcdir}/chapters/counters.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/port_numbers.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/bibliography.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/glossary.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/gfdl.adoc[]
|
||||||
|
|
||||||
|
|
||||||
38
doc/manuals/osmomgw-vty-reference.xml
Normal file
38
doc/manuals/osmomgw-vty-reference.xml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
ex:ts=2:sw=42sts=2:et
|
||||||
|
-*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||||
|
-->
|
||||||
|
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
|
||||||
|
"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
|
||||||
|
<!ENTITY chapter-vty SYSTEM "./common/chapters/vty.xml" >
|
||||||
|
<!ENTITY sections-vty SYSTEM "generated/docbook_vty.xml" >
|
||||||
|
]>
|
||||||
|
|
||||||
|
<book>
|
||||||
|
<info>
|
||||||
|
<revhistory>
|
||||||
|
<revision>
|
||||||
|
<revnumber>v1</revnumber>
|
||||||
|
<date>12th December 2017</date>
|
||||||
|
<authorinitials>pm</authorinitials>
|
||||||
|
<revremark>Initial</revremark>
|
||||||
|
</revision>
|
||||||
|
</revhistory>
|
||||||
|
|
||||||
|
<title>OsmoMGW VTY Reference</title>
|
||||||
|
|
||||||
|
<copyright>
|
||||||
|
<year>2017</year>
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<legalnotice>
|
||||||
|
<para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved.
|
||||||
|
</para>
|
||||||
|
</legalnotice>
|
||||||
|
</info>
|
||||||
|
|
||||||
|
<!-- Main chapters-->
|
||||||
|
&chapter-vty;
|
||||||
|
</book>
|
||||||
|
|
||||||
2
doc/manuals/vty/mgw_vty_additions.xml
Normal file
2
doc/manuals/vty/mgw_vty_additions.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
|
||||||
|
</vtydoc>
|
||||||
1645
doc/manuals/vty/mgw_vty_reference.xml
Normal file
1645
doc/manuals/vty/mgw_vty_reference.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -92,8 +92,8 @@ fi
|
|||||||
if test -n "$v"
|
if test -n "$v"
|
||||||
then
|
then
|
||||||
: # use $v
|
: # use $v
|
||||||
elif test -d ./.git \
|
elif
|
||||||
&& v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|
v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|
||||||
|| git describe --abbrev=4 HEAD 2>/dev/null` \
|
|| git describe --abbrev=4 HEAD 2>/dev/null` \
|
||||||
&& case $v in
|
&& case $v in
|
||||||
[0-9]*) ;;
|
[0-9]*) ;;
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ SUBDIRS = \
|
|||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
nobase_include_HEADERS = \
|
nobase_include_HEADERS = \
|
||||||
osmocom/legacy_mgcp/mgcp.h \
|
|
||||||
osmocom/legacy_mgcp/mgcp_internal.h \
|
|
||||||
osmocom/legacy_mgcp/osmux.h \
|
|
||||||
osmocom/mgcp_client/mgcp_client.h \
|
osmocom/mgcp_client/mgcp_client.h \
|
||||||
osmocom/mgcp_client/mgcp_client_fsm.h \
|
osmocom/mgcp_client/mgcp_client_fsm.h \
|
||||||
osmocom/mgcp_client/mgcp_common.h \
|
osmocom/mgcp_client/mgcp_common.h \
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
legacy_mgcp \
|
|
||||||
mgcp_client \
|
mgcp_client \
|
||||||
mgcp \
|
mgcp \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
noinst_HEADERS = \
|
|
||||||
mgcp_transcode.h \
|
|
||||||
vty.h \
|
|
||||||
$(NULL)
|
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
|
||||||
* (C) 2009-2012 by On-Waves
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU 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/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef OPENBSC_MGCP_H
|
|
||||||
#define OPENBSC_MGCP_H
|
|
||||||
|
|
||||||
#include <osmocom/core/msgb.h>
|
|
||||||
#include <osmocom/core/write_queue.h>
|
|
||||||
#include <osmocom/core/timer.h>
|
|
||||||
#include <osmocom/core/logging.h>
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
|
|
||||||
#define RTP_PORT_DEFAULT 4000
|
|
||||||
#define RTP_PORT_NET_DEFAULT 16000
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the RTP audio port for the given multiplex
|
|
||||||
* and the direction. This allows a semi static endpoint
|
|
||||||
* to port calculation removing the need for the BSC
|
|
||||||
* and the MediaGateway to communicate.
|
|
||||||
*
|
|
||||||
* Port usage explained:
|
|
||||||
* base + (multiplex * 2) + 0 == local port to wait for network packets
|
|
||||||
* base + (multiplex * 2) + 1 == local port for rtcp
|
|
||||||
*
|
|
||||||
* The above port will receive packets from the BTS that need
|
|
||||||
* to be patched and forwarded to the network.
|
|
||||||
* The above port will receive packets from the network that
|
|
||||||
* need to be patched and forwarded to the BTS.
|
|
||||||
*
|
|
||||||
* We assume to have a static BTS IP address so we can differentiate
|
|
||||||
* network and BTS.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static inline int rtp_calculate_port(int multiplex, int base)
|
|
||||||
{
|
|
||||||
return base + (multiplex * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Handling of MGCP Endpoints and the MGCP Config
|
|
||||||
*/
|
|
||||||
struct mgcp_endpoint;
|
|
||||||
struct mgcp_config;
|
|
||||||
struct mgcp_trunk_config;
|
|
||||||
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?
|
|
||||||
* - reject and send a failure code?
|
|
||||||
* - defer? do not send anything
|
|
||||||
*/
|
|
||||||
#define MGCP_POLICY_CONT 4
|
|
||||||
#define MGCP_POLICY_REJECT 5
|
|
||||||
#define MGCP_POLICY_DEFER 6
|
|
||||||
|
|
||||||
typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint);
|
|
||||||
typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state);
|
|
||||||
typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id);
|
|
||||||
typedef int (*mgcp_reset)(struct mgcp_trunk_config *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. The remaining payload
|
|
||||||
* length will be returned.
|
|
||||||
*/
|
|
||||||
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
|
|
||||||
struct mgcp_rtp_end *dst_end,
|
|
||||||
char *data, int *len, int buf_size);
|
|
||||||
typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
|
|
||||||
struct mgcp_rtp_end *dst_end,
|
|
||||||
struct mgcp_rtp_end *src_end);
|
|
||||||
|
|
||||||
typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
|
|
||||||
int *payload_type,
|
|
||||||
const char**subtype_name,
|
|
||||||
const char**fmtp_extra);
|
|
||||||
|
|
||||||
#define PORT_ALLOC_STATIC 0
|
|
||||||
#define PORT_ALLOC_DYNAMIC 1
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This holds information on how to allocate ports
|
|
||||||
*/
|
|
||||||
struct mgcp_port_range {
|
|
||||||
int mode;
|
|
||||||
|
|
||||||
/* addr or NULL to fall-back to default */
|
|
||||||
char *bind_addr;
|
|
||||||
|
|
||||||
/* pre-allocated from a base? */
|
|
||||||
int base_port;
|
|
||||||
|
|
||||||
/* dynamically allocated */
|
|
||||||
int range_start;
|
|
||||||
int range_end;
|
|
||||||
int last_port;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MGCP_KEEPALIVE_ONCE (-1)
|
|
||||||
|
|
||||||
struct mgcp_trunk_config {
|
|
||||||
struct llist_head entry;
|
|
||||||
|
|
||||||
struct mgcp_config *cfg;
|
|
||||||
|
|
||||||
int trunk_nr;
|
|
||||||
int trunk_type;
|
|
||||||
|
|
||||||
char *audio_fmtp_extra;
|
|
||||||
char *audio_name;
|
|
||||||
int audio_payload;
|
|
||||||
int audio_send_ptime;
|
|
||||||
int audio_send_name;
|
|
||||||
int audio_loop;
|
|
||||||
|
|
||||||
int no_audio_transcoding;
|
|
||||||
|
|
||||||
int omit_rtcp;
|
|
||||||
int keepalive_interval;
|
|
||||||
|
|
||||||
/* RTP patching */
|
|
||||||
int force_constant_ssrc; /* 0: don't, 1: once */
|
|
||||||
int force_aligned_timing;
|
|
||||||
|
|
||||||
/* spec handling */
|
|
||||||
int force_realloc;
|
|
||||||
|
|
||||||
/* timer */
|
|
||||||
struct osmo_timer_list keepalive_timer;
|
|
||||||
|
|
||||||
unsigned int number_endpoints;
|
|
||||||
struct mgcp_endpoint *endpoints;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum mgcp_role {
|
|
||||||
MGCP_BSC = 0,
|
|
||||||
MGCP_BSC_NAT,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum mgcp_connection_mode {
|
|
||||||
MGCP_CONN_NONE = 0,
|
|
||||||
MGCP_CONN_RECV_ONLY = 1,
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcp_config {
|
|
||||||
int source_port;
|
|
||||||
char *local_ip;
|
|
||||||
char *source_addr;
|
|
||||||
char *bts_ip;
|
|
||||||
char *call_agent_addr;
|
|
||||||
|
|
||||||
struct in_addr bts_in;
|
|
||||||
|
|
||||||
/* transcoder handling */
|
|
||||||
char *transcoder_ip;
|
|
||||||
struct in_addr transcoder_in;
|
|
||||||
int transcoder_remote_base;
|
|
||||||
|
|
||||||
/* 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 bts_ports;
|
|
||||||
struct mgcp_port_range net_ports;
|
|
||||||
struct mgcp_port_range transcoder_ports;
|
|
||||||
int endp_dscp;
|
|
||||||
|
|
||||||
int bts_force_ptime;
|
|
||||||
|
|
||||||
mgcp_change change_cb;
|
|
||||||
mgcp_policy policy_cb;
|
|
||||||
mgcp_reset reset_cb;
|
|
||||||
mgcp_realloc realloc_cb;
|
|
||||||
mgcp_rqnt rqnt_cb;
|
|
||||||
void *data;
|
|
||||||
|
|
||||||
uint32_t last_call_id;
|
|
||||||
|
|
||||||
/* trunk handling */
|
|
||||||
struct mgcp_trunk_config trunk;
|
|
||||||
struct llist_head trunks;
|
|
||||||
|
|
||||||
/* only used for start with a static configuration */
|
|
||||||
int last_net_port;
|
|
||||||
int last_bts_port;
|
|
||||||
|
|
||||||
enum mgcp_role role;
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* config management */
|
|
||||||
struct mgcp_config *mgcp_config_alloc(void);
|
|
||||||
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
|
|
||||||
enum mgcp_role role);
|
|
||||||
int mgcp_vty_init(void);
|
|
||||||
int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg);
|
|
||||||
void mgcp_release_endp(struct mgcp_endpoint *endp);
|
|
||||||
void mgcp_initialize_endp(struct mgcp_endpoint *endp);
|
|
||||||
int mgcp_reset_transcoder(struct mgcp_config *cfg);
|
|
||||||
void mgcp_format_stats(struct mgcp_endpoint *endp, char *stats, size_t size);
|
|
||||||
int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os, uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter);
|
|
||||||
|
|
||||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* format helper functions
|
|
||||||
*/
|
|
||||||
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
|
|
||||||
|
|
||||||
/* adc helper */
|
|
||||||
static inline int mgcp_timeslot_to_endpoint(int multiplex, int timeslot)
|
|
||||||
{
|
|
||||||
if (timeslot == 0) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Timeslot should not be 0\n");
|
|
||||||
timeslot = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
return timeslot + (32 * multiplex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void mgcp_endpoint_to_timeslot(int endpoint, int *multiplex, int *timeslot)
|
|
||||||
{
|
|
||||||
*multiplex = endpoint / 32;
|
|
||||||
*timeslot = endpoint % 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint);
|
|
||||||
int mgcp_send_reset_all(struct mgcp_config *cfg);
|
|
||||||
|
|
||||||
|
|
||||||
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port);
|
|
||||||
int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, struct sockaddr_in *addr, char *buf, int rc);
|
|
||||||
int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,337 +0,0 @@
|
|||||||
/* MGCP Private Data */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
|
||||||
* (C) 2009-2012 by On-Waves
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <osmocom/core/select.h>
|
|
||||||
|
|
||||||
#define CI_UNUSED 0
|
|
||||||
|
|
||||||
enum mgcp_trunk_type {
|
|
||||||
MGCP_TRUNK_VIRTUAL,
|
|
||||||
MGCP_TRUNK_E1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcp_rtp_stream_state {
|
|
||||||
uint32_t ssrc;
|
|
||||||
uint16_t last_seq;
|
|
||||||
uint32_t last_timestamp;
|
|
||||||
uint32_t err_ts_counter;
|
|
||||||
int32_t last_tsdelta;
|
|
||||||
uint32_t last_arrival_time;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcp_rtp_state {
|
|
||||||
int initialized;
|
|
||||||
int patch_ssrc;
|
|
||||||
|
|
||||||
uint32_t orig_ssrc;
|
|
||||||
|
|
||||||
int seq_offset;
|
|
||||||
|
|
||||||
int32_t timestamp_offset;
|
|
||||||
uint32_t packet_duration;
|
|
||||||
|
|
||||||
struct mgcp_rtp_stream_state in_stream;
|
|
||||||
struct mgcp_rtp_stream_state out_stream;
|
|
||||||
|
|
||||||
/* jitter and packet loss calculation */
|
|
||||||
int stats_initialized;
|
|
||||||
uint16_t stats_base_seq;
|
|
||||||
uint16_t stats_max_seq;
|
|
||||||
uint32_t stats_ssrc;
|
|
||||||
uint32_t stats_jitter;
|
|
||||||
int32_t stats_transit;
|
|
||||||
int stats_cycles;
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcp_rtp_end {
|
|
||||||
/* statistics */
|
|
||||||
unsigned int packets;
|
|
||||||
unsigned int octets;
|
|
||||||
unsigned int dropped_packets;
|
|
||||||
struct in_addr addr;
|
|
||||||
|
|
||||||
/* in network byte order */
|
|
||||||
int rtp_port, rtcp_port;
|
|
||||||
|
|
||||||
/* audio codec information */
|
|
||||||
struct mgcp_rtp_codec codec;
|
|
||||||
struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */
|
|
||||||
|
|
||||||
/* per endpoint data */
|
|
||||||
int frames_per_packet;
|
|
||||||
uint32_t packet_duration_ms;
|
|
||||||
char *fmtp_extra;
|
|
||||||
int output_enabled;
|
|
||||||
int force_output_ptime;
|
|
||||||
|
|
||||||
/* RTP patching */
|
|
||||||
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
|
|
||||||
int force_aligned_timing;
|
|
||||||
void *rtp_process_data;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Each end has a socket...
|
|
||||||
*/
|
|
||||||
struct osmo_fd rtp;
|
|
||||||
struct osmo_fd rtcp;
|
|
||||||
|
|
||||||
int local_port;
|
|
||||||
int local_alloc;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
MGCP_TAP_BTS_IN,
|
|
||||||
MGCP_TAP_BTS_OUT,
|
|
||||||
MGCP_TAP_NET_IN,
|
|
||||||
MGCP_TAP_NET_OUT,
|
|
||||||
|
|
||||||
/* last element */
|
|
||||||
MGCP_TAP_COUNT
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcp_rtp_tap {
|
|
||||||
int enabled;
|
|
||||||
struct sockaddr_in forward;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcp_lco {
|
|
||||||
char *string;
|
|
||||||
char *codec;
|
|
||||||
int pkt_period_min; /* time in ms */
|
|
||||||
int pkt_period_max; /* time in ms */
|
|
||||||
};
|
|
||||||
|
|
||||||
enum mgcp_type {
|
|
||||||
MGCP_RTP_DEFAULT = 0,
|
|
||||||
MGCP_RTP_TRANSCODED,
|
|
||||||
MGCP_OSMUX_BSC,
|
|
||||||
MGCP_OSMUX_BSC_NAT,
|
|
||||||
};
|
|
||||||
|
|
||||||
#include <osmocom/legacy_mgcp/osmux.h>
|
|
||||||
|
|
||||||
struct mgcp_endpoint {
|
|
||||||
int allocated;
|
|
||||||
uint32_t ci;
|
|
||||||
char *callid;
|
|
||||||
struct mgcp_lco local_options;
|
|
||||||
int conn_mode;
|
|
||||||
int orig_mode;
|
|
||||||
|
|
||||||
/* backpointer */
|
|
||||||
struct mgcp_config *cfg;
|
|
||||||
struct mgcp_trunk_config *tcfg;
|
|
||||||
|
|
||||||
/* port status for bts/net */
|
|
||||||
struct mgcp_rtp_end bts_end;
|
|
||||||
struct mgcp_rtp_end net_end;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For transcoding we will send from the local_port
|
|
||||||
* of trans_bts and it will arrive at trans_net from
|
|
||||||
* where we will forward it to the network.
|
|
||||||
*/
|
|
||||||
struct mgcp_rtp_end trans_bts;
|
|
||||||
struct mgcp_rtp_end trans_net;
|
|
||||||
enum mgcp_type type;
|
|
||||||
|
|
||||||
/* sequence bits */
|
|
||||||
struct mgcp_rtp_state net_state;
|
|
||||||
struct mgcp_rtp_state bts_state;
|
|
||||||
|
|
||||||
/* fields for re-transmission */
|
|
||||||
char *last_trans;
|
|
||||||
char *last_response;
|
|
||||||
|
|
||||||
/* tap for the endpoint */
|
|
||||||
struct mgcp_rtp_tap taps[MGCP_TAP_COUNT];
|
|
||||||
|
|
||||||
struct {
|
|
||||||
/* Osmux state: disabled, activating, active */
|
|
||||||
enum osmux_state state;
|
|
||||||
/* Allocated Osmux circuit ID for this endpoint */
|
|
||||||
int allocated_cid;
|
|
||||||
/* Used Osmux circuit ID for this endpoint */
|
|
||||||
uint8_t cid;
|
|
||||||
/* handle to batch messages */
|
|
||||||
struct osmux_in_handle *in;
|
|
||||||
/* handle to unbatch messages */
|
|
||||||
struct osmux_out_handle out;
|
|
||||||
/* statistics */
|
|
||||||
struct {
|
|
||||||
uint32_t chunks;
|
|
||||||
uint32_t octets;
|
|
||||||
} stats;
|
|
||||||
} osmux;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define for_each_line(line, save) \
|
|
||||||
for (line = strline_r(NULL, &save); line;\
|
|
||||||
line = strline_r(NULL, &save))
|
|
||||||
|
|
||||||
static inline char *strline_r(char *str, char **saveptr)
|
|
||||||
{
|
|
||||||
char *result;
|
|
||||||
|
|
||||||
if (str)
|
|
||||||
*saveptr = str;
|
|
||||||
|
|
||||||
result = *saveptr;
|
|
||||||
|
|
||||||
if (*saveptr != NULL) {
|
|
||||||
*saveptr = strpbrk(*saveptr, "\r\n");
|
|
||||||
|
|
||||||
if (*saveptr != NULL) {
|
|
||||||
char *eos = *saveptr;
|
|
||||||
|
|
||||||
if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n')
|
|
||||||
(*saveptr)++;
|
|
||||||
(*saveptr)++;
|
|
||||||
if ((*saveptr)[0] == '\0')
|
|
||||||
*saveptr = NULL;
|
|
||||||
|
|
||||||
*eos = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal structure while parsing a request
|
|
||||||
*/
|
|
||||||
struct mgcp_parse_data {
|
|
||||||
struct mgcp_config *cfg;
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
char *trans;
|
|
||||||
char *save;
|
|
||||||
int found;
|
|
||||||
};
|
|
||||||
|
|
||||||
int mgcp_send_dummy(struct mgcp_endpoint *endp);
|
|
||||||
int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port);
|
|
||||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port);
|
|
||||||
int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *enp, int rtp_port);
|
|
||||||
int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *enp, int rtp_port);
|
|
||||||
int mgcp_free_rtp_port(struct mgcp_rtp_end *end);
|
|
||||||
|
|
||||||
/* For transcoding we need to manage an in and an output that are connected */
|
|
||||||
static inline int endp_back_channel(int endpoint)
|
|
||||||
{
|
|
||||||
return endpoint + 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
|
|
||||||
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
|
|
||||||
|
|
||||||
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
|
|
||||||
struct mgcp_rtp_end *rtp);
|
|
||||||
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
|
|
||||||
struct mgcp_rtp_end *rtp);
|
|
||||||
|
|
||||||
void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *,
|
|
||||||
uint32_t *expected, int *loss);
|
|
||||||
uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *);
|
|
||||||
|
|
||||||
/* 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_rtp_end *dst_end,
|
|
||||||
struct mgcp_rtp_end *src_end);
|
|
||||||
|
|
||||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
|
||||||
int *payload_type,
|
|
||||||
const char**subtype_name,
|
|
||||||
const char**fmtp_extra);
|
|
||||||
|
|
||||||
/* internal RTP Annex A counting */
|
|
||||||
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);
|
|
||||||
|
|
||||||
int mgcp_set_ip_tos(int fd, int tos);
|
|
||||||
|
|
||||||
enum {
|
|
||||||
MGCP_DEST_NET = 0,
|
|
||||||
MGCP_DEST_BTS,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#define MGCP_DUMMY_LOAD 0x23
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SDP related information
|
|
||||||
*/
|
|
||||||
/* Assume audio frame length of 20ms */
|
|
||||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
|
|
||||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
|
|
||||||
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
|
|
||||||
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
|
|
||||||
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
|
|
||||||
|
|
||||||
#define PTYPE_UNDEFINED (-1)
|
|
||||||
int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p);
|
|
||||||
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
|
|
||||||
int payload_type, const char *audio_name);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal network related
|
|
||||||
*/
|
|
||||||
static inline const char *mgcp_net_src_addr(struct mgcp_endpoint *endp)
|
|
||||||
{
|
|
||||||
if (endp->cfg->net_ports.bind_addr)
|
|
||||||
return endp->cfg->net_ports.bind_addr;
|
|
||||||
return endp->cfg->source_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline const char *mgcp_bts_src_addr(struct mgcp_endpoint *endp)
|
|
||||||
{
|
|
||||||
if (endp->cfg->bts_ports.bind_addr)
|
|
||||||
return endp->cfg->bts_ports.bind_addr;
|
|
||||||
return endp->cfg->source_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgcp_msg_terminate_nul(struct msgb *msg);
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
* (C) 2014 by On-Waves
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU 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/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef OPENBSC_MGCP_TRANSCODE_H
|
|
||||||
#define OPENBSC_MGCP_TRANSCODE_H
|
|
||||||
|
|
||||||
#include "bscconfig.h"
|
|
||||||
|
|
||||||
#include <gsm.h>
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
#include <bcg729/decoder.h>
|
|
||||||
#include <bcg729/encoder.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum audio_format {
|
|
||||||
AF_INVALID,
|
|
||||||
AF_S16,
|
|
||||||
AF_L16,
|
|
||||||
AF_GSM,
|
|
||||||
AF_G729,
|
|
||||||
AF_PCMA,
|
|
||||||
AF_PCMU
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct mgcp_process_rtp_state {
|
|
||||||
/* decoding */
|
|
||||||
enum audio_format src_fmt;
|
|
||||||
union {
|
|
||||||
gsm gsm_handle;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
bcg729DecoderChannelContextStruct *g729_dec;
|
|
||||||
#endif
|
|
||||||
} src;
|
|
||||||
size_t src_frame_size;
|
|
||||||
size_t src_samples_per_frame;
|
|
||||||
|
|
||||||
/* processing */
|
|
||||||
|
|
||||||
/* encoding */
|
|
||||||
enum audio_format dst_fmt;
|
|
||||||
union {
|
|
||||||
gsm gsm_handle;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
bcg729EncoderChannelContextStruct *g729_enc;
|
|
||||||
#endif
|
|
||||||
} dst;
|
|
||||||
size_t dst_frame_size;
|
|
||||||
size_t dst_samples_per_frame;
|
|
||||||
int dst_packet_duration;
|
|
||||||
|
|
||||||
int is_running;
|
|
||||||
uint16_t next_seq;
|
|
||||||
uint32_t next_time;
|
|
||||||
int16_t samples[10*160];
|
|
||||||
size_t sample_cnt;
|
|
||||||
size_t sample_offs;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
int mgcp_transcoding_setup(struct mgcp_endpoint *endp,
|
|
||||||
struct mgcp_rtp_end *dst_end,
|
|
||||||
struct mgcp_rtp_end *src_end);
|
|
||||||
|
|
||||||
void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp,
|
|
||||||
int *payload_type,
|
|
||||||
const char**audio_name,
|
|
||||||
const char**fmtp_extra);
|
|
||||||
|
|
||||||
int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
|
|
||||||
struct mgcp_rtp_end *dst_end,
|
|
||||||
char *data, int *len, int buf_size);
|
|
||||||
|
|
||||||
int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst);
|
|
||||||
#endif /* OPENBSC_MGCP_TRANSCODE_H */
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#ifndef _OPENBSC_OSMUX_H_
|
|
||||||
#define _OPENBSC_OSMUX_H_
|
|
||||||
|
|
||||||
#include <osmocom/netif/osmux.h>
|
|
||||||
|
|
||||||
#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_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port);
|
|
||||||
void osmux_disable_endpoint(struct mgcp_endpoint *endp);
|
|
||||||
void osmux_allocate_cid(struct mgcp_endpoint *endp);
|
|
||||||
void osmux_release_cid(struct mgcp_endpoint *endp);
|
|
||||||
|
|
||||||
int osmux_xfrm_to_rtp(struct mgcp_endpoint *endp, int type, char *buf, int rc);
|
|
||||||
int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp);
|
|
||||||
|
|
||||||
int osmux_send_dummy(struct mgcp_endpoint *endp);
|
|
||||||
|
|
||||||
int osmux_get_cid(void);
|
|
||||||
void osmux_put_cid(uint8_t osmux_cid);
|
|
||||||
int osmux_used_cid(void);
|
|
||||||
|
|
||||||
enum osmux_state {
|
|
||||||
OSMUX_STATE_DISABLED = 0,
|
|
||||||
OSMUX_STATE_NEGOTIATING,
|
|
||||||
OSMUX_STATE_ACTIVATING,
|
|
||||||
OSMUX_STATE_ENABLED,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum osmux_usage {
|
|
||||||
OSMUX_USAGE_OFF = 0,
|
|
||||||
OSMUX_USAGE_ON = 1,
|
|
||||||
OSMUX_USAGE_ONLY = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <osmocom/vty/command.h>
|
|
||||||
|
|
||||||
enum mgcp_vty_node {
|
|
||||||
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
|
|
||||||
TRUNK_NODE,
|
|
||||||
};
|
|
||||||
@@ -5,5 +5,6 @@ noinst_HEADERS = \
|
|||||||
mgcp_stat.h \
|
mgcp_stat.h \
|
||||||
mgcp_endp.h \
|
mgcp_endp.h \
|
||||||
mgcp_sdp.h \
|
mgcp_sdp.h \
|
||||||
|
mgcp_codec.h \
|
||||||
debug.h \
|
debug.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|||||||
@@ -74,11 +74,13 @@ typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
|
|||||||
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
|
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
|
||||||
struct mgcp_rtp_end *dst_end,
|
struct mgcp_rtp_end *dst_end,
|
||||||
char *data, int *len, int buf_size);
|
char *data, int *len, int buf_size);
|
||||||
typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
|
|
||||||
struct mgcp_rtp_end *dst_end,
|
|
||||||
struct mgcp_rtp_end *src_end);
|
|
||||||
|
|
||||||
struct mgcp_conn_rtp;
|
struct mgcp_conn_rtp;
|
||||||
|
|
||||||
|
typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
|
||||||
|
struct mgcp_conn_rtp *conn_dst,
|
||||||
|
struct mgcp_conn_rtp *conn_src);
|
||||||
|
|
||||||
typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
|
typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
|
||||||
int *payload_type,
|
int *payload_type,
|
||||||
const char**subtype_name,
|
const char**subtype_name,
|
||||||
@@ -116,6 +118,55 @@ struct mgcp_port_range {
|
|||||||
#define MGCP_KEEPALIVE_ONCE (-1)
|
#define MGCP_KEEPALIVE_ONCE (-1)
|
||||||
#define MGCP_KEEPALIVE_NEVER 0
|
#define MGCP_KEEPALIVE_NEVER 0
|
||||||
|
|
||||||
|
/* Global MCGP CRCX related rate counters */
|
||||||
|
enum {
|
||||||
|
MGCP_CRCX_SUCCESS,
|
||||||
|
MGCP_CRCX_FAIL_BAD_ACTION,
|
||||||
|
MGCP_CRCX_FAIL_UNHANDLED_PARAM,
|
||||||
|
MGCP_CRCX_FAIL_MISSING_CALLID,
|
||||||
|
MGCP_CRCX_FAIL_INVALID_MODE,
|
||||||
|
MGCP_CRCX_FAIL_LIMIT_EXCEEDED,
|
||||||
|
MGCP_CRCX_FAIL_UNKNOWN_CALLID,
|
||||||
|
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,
|
||||||
|
MGCP_CRCX_FAIL_BIND_PORT,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Global MCGP MDCX related rate counters */
|
||||||
|
enum {
|
||||||
|
MGCP_MDCX_SUCCESS,
|
||||||
|
MGCP_MDCX_FAIL_WILDCARD,
|
||||||
|
MGCP_MDCX_FAIL_NO_CONN,
|
||||||
|
MGCP_MDCX_FAIL_INVALID_CALLID,
|
||||||
|
MGCP_MDCX_FAIL_INVALID_CONNID,
|
||||||
|
MGCP_MDCX_FAIL_UNHANDLED_PARAM,
|
||||||
|
MGCP_MDCX_FAIL_NO_CONNID,
|
||||||
|
MGCP_MDCX_FAIL_CONN_NOT_FOUND,
|
||||||
|
MGCP_MDCX_FAIL_INVALID_MODE,
|
||||||
|
MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS,
|
||||||
|
MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
|
||||||
|
MGCP_MDCX_FAIL_START_RTP,
|
||||||
|
MGCP_MDCX_FAIL_REJECTED_BY_POLICY,
|
||||||
|
MGCP_MDCX_DEFERRED_BY_POLICY
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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,
|
||||||
|
};
|
||||||
|
|
||||||
struct mgcp_trunk_config {
|
struct mgcp_trunk_config {
|
||||||
struct llist_head entry;
|
struct llist_head entry;
|
||||||
|
|
||||||
@@ -153,6 +204,15 @@ struct mgcp_trunk_config {
|
|||||||
unsigned int number_endpoints;
|
unsigned int number_endpoints;
|
||||||
int vty_number_endpoints;
|
int vty_number_endpoints;
|
||||||
struct mgcp_endpoint *endpoints;
|
struct mgcp_endpoint *endpoints;
|
||||||
|
|
||||||
|
/* Rate counter group which contains stats for processed CRCX commands. */
|
||||||
|
struct rate_ctr_group *mgcp_crcx_ctr_group;
|
||||||
|
/* Rate counter group which contains stats for processed MDCX commands. */
|
||||||
|
struct rate_ctr_group *mgcp_mdcx_ctr_group;
|
||||||
|
/* Rate counter group which contains stats for processed DLCX commands. */
|
||||||
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum mgcp_role {
|
enum mgcp_role {
|
||||||
|
|||||||
7
include/osmocom/mgcp/mgcp_codec.h
Normal file
7
include/osmocom/mgcp/mgcp_codec.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
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);
|
||||||
|
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);
|
||||||
@@ -49,6 +49,14 @@ enum mgcp_connection_mode {
|
|||||||
MGCP_CONN_LOOPBACK = 4 | MGCP_CONN_RECV_SEND,
|
MGCP_CONN_LOOPBACK = 4 | MGCP_CONN_RECV_SEND,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:"
|
||||||
|
|
||||||
|
/* Values should be bitwise-OR-able */
|
||||||
|
enum mgcp_x_osmo_ign {
|
||||||
|
MGCP_X_OSMO_IGN_NONE = 0,
|
||||||
|
MGCP_X_OSMO_IGN_CALLID = 1,
|
||||||
|
};
|
||||||
|
|
||||||
/* Ensure that the msg->l2h is NUL terminated. */
|
/* Ensure that the msg->l2h is NUL terminated. */
|
||||||
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
||||||
{
|
{
|
||||||
@@ -71,9 +79,12 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
|||||||
/* Maximum length of the comment field */
|
/* Maximum length of the comment field */
|
||||||
#define MGCP_COMMENT_MAXLEN 256
|
#define MGCP_COMMENT_MAXLEN 256
|
||||||
|
|
||||||
/* String length of Connection Identifiers
|
/* Maximum allowed String length of Connection Identifiers as per spec
|
||||||
* (see also RFC3435 2.1.3.2 Names of Connections) */
|
* (see also RFC3435 2.1.3.2 Names of Connections), plus one for '\0'. */
|
||||||
#define MGCP_CONN_ID_LENGTH 32+1
|
#define MGCP_CONN_ID_MAXLEN 32+1
|
||||||
|
|
||||||
|
/* Deprecated: old name of MGCP_CONN_ID_MAXLEN. */
|
||||||
|
#define MGCP_CONN_ID_LENGTH MGCP_CONN_ID_MAXLEN
|
||||||
|
|
||||||
/* String length of Endpoint Identifiers.
|
/* String length of Endpoint Identifiers.
|
||||||
/ (see also RFC3435 section 3.2.1.3) */
|
/ (see also RFC3435 section 3.2.1.3) */
|
||||||
@@ -82,4 +93,8 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
|||||||
/* A prefix to denote the virtual trunk (RTP on both ends) */
|
/* A prefix to denote the virtual trunk (RTP on both ends) */
|
||||||
#define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/"
|
#define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/"
|
||||||
|
|
||||||
|
/* Maximal number of payload types / codecs that can be negotiated via SDP at
|
||||||
|
* at once. */
|
||||||
|
#define MGCP_MAX_CODECS 10
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -25,8 +25,49 @@
|
|||||||
|
|
||||||
#include <osmocom/mgcp/mgcp_internal.h>
|
#include <osmocom/mgcp/mgcp_internal.h>
|
||||||
#include <osmocom/core/linuxlist.h>
|
#include <osmocom/core/linuxlist.h>
|
||||||
|
#include <osmocom/core/rate_ctr.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
/* RTP connection related counters */
|
||||||
|
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 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_rate_ctr_desc[] = {
|
||||||
|
[IN_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:in", "Inbound rtp-stream timestamp errors."},
|
||||||
|
[OUT_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:out", "Outbound rtp-stream timestamp errors."},
|
||||||
|
[RTP_PACKETS_RX_CTR] = {"rtp:packets_rx", "Inbound rtp packets."},
|
||||||
|
[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."}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Aggregated RTP connection stats. These are updated when an RTP connection is freed.
|
||||||
|
* Must be kept in sync with mgcp_conn_rate_ctr_desc above */
|
||||||
|
static const struct rate_ctr_desc all_rtp_conn_rate_ctr_desc[] = {
|
||||||
|
[IN_STREAM_ERR_TSTMP_CTR] = {"all_rtp:err_tstmp_in", "Total inbound rtp-stream timestamp errors."},
|
||||||
|
[OUT_STREAM_ERR_TSTMP_CTR] = {"all_rtp:err_tstmp_out", "Total outbound rtp-stream timestamp errors."},
|
||||||
|
[RTP_PACKETS_RX_CTR] = {"all_rtp:packets_rx", "Total inbound rtp packets."},
|
||||||
|
[RTP_OCTETS_RX_CTR] = {"all_rtp:octets_rx", "Total inbound rtp octets."},
|
||||||
|
[RTP_PACKETS_TX_CTR] = {"all_rtp:packets_tx", "Total outbound rtp packets."},
|
||||||
|
[RTP_OCTETS_TX_CTR] = {"all_rtp:octets_tx", "Total outbound rtp octets."},
|
||||||
|
[RTP_DROPPED_PACKETS_CTR] = {"all_rtp:dropped", "Total dropped rtp packets."},
|
||||||
|
|
||||||
|
/* This last counter does not exist in per-connection stats, only here. */
|
||||||
|
[RTP_NUM_CONNECTIONS] = {"all_rtp:num_closed_conns", "Total number of rtp connections closed."}
|
||||||
|
};
|
||||||
|
|
||||||
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||||
enum mgcp_conn_type type, char *name);
|
enum mgcp_conn_type type, char *name);
|
||||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
|
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
|
||||||
|
|||||||
@@ -42,13 +42,13 @@ typedef void (*mgcp_cleanup_cp) (struct mgcp_endpoint *endp,
|
|||||||
|
|
||||||
/*! MGCP endpoint properties */
|
/*! MGCP endpoint properties */
|
||||||
struct mgcp_endpoint_type {
|
struct mgcp_endpoint_type {
|
||||||
/*!< maximum number of connections */
|
/*! maximum number of connections */
|
||||||
int max_conns;
|
int max_conns;
|
||||||
|
|
||||||
/*!< callback that defines how to dispatch incoming RTP data */
|
/*! callback that defines how to dispatch incoming RTP data */
|
||||||
mgcp_dispatch_rtp_cb dispatch_rtp_cb;
|
mgcp_dispatch_rtp_cb dispatch_rtp_cb;
|
||||||
|
|
||||||
/*!< callback that implements endpoint specific cleanup actions */
|
/*! callback that implements endpoint specific cleanup actions */
|
||||||
mgcp_cleanup_cp cleanup_cb;
|
mgcp_cleanup_cp cleanup_cb;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,33 +63,36 @@ extern const struct mgcp_endpoint_typeset ep_typeset;
|
|||||||
/*! MGCP endpoint model */
|
/*! MGCP endpoint model */
|
||||||
struct mgcp_endpoint {
|
struct mgcp_endpoint {
|
||||||
|
|
||||||
/*!< Call identifier string (as supplied by the call agant) */
|
/*! Call identifier string (as supplied by the call agant) */
|
||||||
char *callid;
|
char *callid;
|
||||||
|
|
||||||
/*!< Local connection options (see mgcp_intermal.h) */
|
/*! Local connection options (see mgcp_internal.h) */
|
||||||
struct mgcp_lco local_options;
|
struct mgcp_lco local_options;
|
||||||
|
|
||||||
/*!< List with connections active on this endpoint */
|
/*! List of struct mgcp_conn, of the connections active on this endpoint */
|
||||||
struct llist_head conns;
|
struct llist_head conns;
|
||||||
|
|
||||||
/*!< Backpointer to the MGW configuration */
|
/*! Backpointer to the MGW configuration */
|
||||||
struct mgcp_config *cfg;
|
struct mgcp_config *cfg;
|
||||||
|
|
||||||
/*!< Backpointer to the Trunk specific configuration */
|
/*! Backpointer to the Trunk specific configuration */
|
||||||
struct mgcp_trunk_config *tcfg;
|
struct mgcp_trunk_config *tcfg;
|
||||||
|
|
||||||
/*!< Endpoint properties (see above) */
|
/*! Endpoint properties (see above) */
|
||||||
const struct mgcp_endpoint_type *type;
|
const struct mgcp_endpoint_type *type;
|
||||||
|
|
||||||
/*!< Last MGCP transmission (in case re-transmission is required) */
|
/*! Last MGCP transmission (in case re-transmission is required) */
|
||||||
char *last_trans;
|
char *last_trans;
|
||||||
|
|
||||||
/*!< Last MGCP response (in case re-transmission is required) */
|
/*! Last MGCP response (in case re-transmission is required) */
|
||||||
char *last_response;
|
char *last_response;
|
||||||
|
|
||||||
/*!< Memorize if this endpoint was choosen by the MGW (wildcarded, true)
|
/*! Memorize if this endpoint was choosen by the MGW (wildcarded, true)
|
||||||
* or if the user has choosen the particular endpoint explicitly. */
|
* or if the user has choosen the particular endpoint explicitly. */
|
||||||
bool wildcarded_req;
|
bool wildcarded_req;
|
||||||
|
|
||||||
|
/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */
|
||||||
|
uint32_t x_osmo_ign;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Extract endpoint number for a given endpoint */
|
/*! Extract endpoint number for a given endpoint */
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include <osmocom/mgcp/mgcp.h>
|
#include <osmocom/mgcp/mgcp.h>
|
||||||
#include <osmocom/core/linuxlist.h>
|
#include <osmocom/core/linuxlist.h>
|
||||||
#include <osmocom/core/counter.h>
|
#include <osmocom/core/counter.h>
|
||||||
|
#include <osmocom/core/rate_ctr.h>
|
||||||
|
|
||||||
#define CI_UNUSED 0
|
#define CI_UNUSED 0
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ struct mgcp_rtp_stream_state {
|
|||||||
uint32_t ssrc;
|
uint32_t ssrc;
|
||||||
uint16_t last_seq;
|
uint16_t last_seq;
|
||||||
uint32_t last_timestamp;
|
uint32_t last_timestamp;
|
||||||
uint32_t err_ts_counter;
|
struct rate_ctr *err_ts_ctr;
|
||||||
int32_t last_tsdelta;
|
int32_t last_tsdelta;
|
||||||
uint32_t last_arrival_time;
|
uint32_t last_arrival_time;
|
||||||
};
|
};
|
||||||
@@ -98,28 +99,25 @@ struct mgcp_rtp_codec {
|
|||||||
|
|
||||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
|
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
|
||||||
struct mgcp_rtp_end {
|
struct mgcp_rtp_end {
|
||||||
/* statistics */
|
|
||||||
struct {
|
|
||||||
unsigned int packets_rx;
|
|
||||||
unsigned int octets_rx;
|
|
||||||
unsigned int packets_tx;
|
|
||||||
unsigned int octets_tx;
|
|
||||||
unsigned int dropped_packets;
|
|
||||||
} stats;
|
|
||||||
|
|
||||||
/* local IP address of the RTP socket */
|
/* local IP address of the RTP socket */
|
||||||
struct in_addr addr;
|
struct in_addr addr;
|
||||||
|
|
||||||
/* in network byte order */
|
/* in network byte order */
|
||||||
int rtp_port, rtcp_port;
|
int rtp_port, rtcp_port;
|
||||||
|
|
||||||
/* audio codec information */
|
/* currently selected audio codec */
|
||||||
struct mgcp_rtp_codec codec;
|
struct mgcp_rtp_codec *codec;
|
||||||
struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */
|
|
||||||
|
/* 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 */
|
/* per endpoint data */
|
||||||
int frames_per_packet;
|
int frames_per_packet;
|
||||||
uint32_t packet_duration_ms;
|
uint32_t packet_duration_ms;
|
||||||
|
int maximum_packet_time; /* -1: not set */
|
||||||
char *fmtp_extra;
|
char *fmtp_extra;
|
||||||
/* are we transmitting packets (1) or dropping (0) outbound packets */
|
/* are we transmitting packets (1) or dropping (0) outbound packets */
|
||||||
int output_enabled;
|
int output_enabled;
|
||||||
@@ -130,8 +128,6 @@ struct mgcp_rtp_end {
|
|||||||
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
|
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
|
||||||
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
|
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
|
||||||
int force_aligned_timing;
|
int force_aligned_timing;
|
||||||
/* FIXME: not used anymore, used to be [external] transcoding related */
|
|
||||||
void *rtp_process_data;
|
|
||||||
|
|
||||||
/* Each end has a separate socket for RTP and RTCP */
|
/* Each end has a separate socket for RTP and RTCP */
|
||||||
struct osmo_fd rtp;
|
struct osmo_fd rtp;
|
||||||
@@ -202,6 +198,8 @@ struct mgcp_conn_rtp {
|
|||||||
uint32_t octets;
|
uint32_t octets;
|
||||||
} stats;
|
} stats;
|
||||||
} osmux;
|
} osmux;
|
||||||
|
|
||||||
|
struct rate_ctr_group *rate_ctr_group;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
||||||
@@ -212,33 +210,33 @@ enum mgcp_conn_type {
|
|||||||
|
|
||||||
/*! MGCP connection (untyped) */
|
/*! MGCP connection (untyped) */
|
||||||
struct mgcp_conn {
|
struct mgcp_conn {
|
||||||
/*!< list head */
|
/*! list head */
|
||||||
struct llist_head entry;
|
struct llist_head entry;
|
||||||
|
|
||||||
/*!< Backpointer to the endpoint where the conn belongs to */
|
/*! Backpointer to the endpoint where the conn belongs to */
|
||||||
struct mgcp_endpoint *endp;
|
struct mgcp_endpoint *endp;
|
||||||
|
|
||||||
/*!< type of the connection (union) */
|
/*! type of the connection (union) */
|
||||||
enum mgcp_conn_type type;
|
enum mgcp_conn_type type;
|
||||||
|
|
||||||
/*!< mode of the connection */
|
/*! mode of the connection */
|
||||||
enum mgcp_connection_mode mode;
|
enum mgcp_connection_mode mode;
|
||||||
|
|
||||||
/*!< copy of the mode to restore the original setting (VTY) */
|
/*! copy of the mode to restore the original setting (VTY) */
|
||||||
enum mgcp_connection_mode mode_orig;
|
enum mgcp_connection_mode mode_orig;
|
||||||
|
|
||||||
/*!< connection id to identify the connection */
|
/*! connection id to identify the connection */
|
||||||
char id[MGCP_CONN_ID_LENGTH];
|
char id[MGCP_CONN_ID_MAXLEN];
|
||||||
|
|
||||||
/*!< human readable name (vty, logging) */
|
/*! human readable name (vty, logging) */
|
||||||
char name[256];
|
char name[256];
|
||||||
|
|
||||||
/*!< union with connection description */
|
/*! union with connection description */
|
||||||
union {
|
union {
|
||||||
struct mgcp_conn_rtp rtp;
|
struct mgcp_conn_rtp rtp;
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
/*!< pointer to optional private data */
|
/*! pointer to optional private data */
|
||||||
void *priv;
|
void *priv;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -280,6 +278,8 @@ static inline int endp_back_channel(int endpoint)
|
|||||||
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
|
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
|
||||||
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
|
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
|
||||||
|
|
||||||
|
char *get_lco_identifier(const char *options);
|
||||||
|
int check_local_cx_options(void *ctx, const char *options);
|
||||||
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
|
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
|
||||||
struct mgcp_rtp_end *rtp);
|
struct mgcp_rtp_end *rtp);
|
||||||
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
|
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
|
||||||
@@ -290,8 +290,8 @@ int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end
|
|||||||
char *data, int *len, int buf_size);
|
char *data, int *len, int buf_size);
|
||||||
|
|
||||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||||
struct mgcp_rtp_end *dst_end,
|
struct mgcp_conn_rtp *conn_dst,
|
||||||
struct mgcp_rtp_end *src_end);
|
struct mgcp_conn_rtp *conn_src);
|
||||||
|
|
||||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||||
int *payload_type,
|
int *payload_type,
|
||||||
|
|||||||
@@ -21,15 +21,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
|
||||||
|
|
||||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||||
struct mgcp_conn_rtp *conn,
|
struct mgcp_conn_rtp *conn,
|
||||||
struct mgcp_parse_data *p);
|
struct mgcp_parse_data *p);
|
||||||
|
|
||||||
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
|
|
||||||
int payload_type, const char *audio_name);
|
|
||||||
|
|
||||||
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||||
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
|
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
|
||||||
const char *addr);
|
const char *addr);
|
||||||
|
|||||||
@@ -30,8 +30,7 @@
|
|||||||
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn);
|
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn);
|
||||||
|
|
||||||
/* Exposed for test purposes only, do not use actively */
|
/* Exposed for test purposes only, do not use actively */
|
||||||
void calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *,
|
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss);
|
||||||
uint32_t *expected, int *loss);
|
|
||||||
|
|
||||||
/* Exposed for test purposes only, do not use actively */
|
/* Exposed for test purposes only, do not use actively */
|
||||||
uint32_t calc_jitter(struct mgcp_rtp_state *);
|
uint32_t calc_jitter(struct mgcp_rtp_state *);
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ void osmux_put_cid(uint8_t osmux_cid);
|
|||||||
int osmux_used_cid(void);
|
int osmux_used_cid(void);
|
||||||
|
|
||||||
enum osmux_state {
|
enum osmux_state {
|
||||||
OSMUX_STATE_DISABLED = 0,
|
OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */
|
||||||
OSMUX_STATE_NEGOTIATING,
|
OSMUX_STATE_NEGOTIATING, /* Osmux was locally requested in MGCP CRCX */
|
||||||
OSMUX_STATE_ACTIVATING,
|
OSMUX_STATE_ACTIVATING, /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref osmux_enable_endpoint. */
|
||||||
OSMUX_STATE_ENABLED,
|
OSMUX_STATE_ENABLED, /* Osmux was initialized by \ref osmux_enable_endpoint and can process frames */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum osmux_usage {
|
enum osmux_usage {
|
||||||
@@ -35,4 +35,3 @@ enum osmux_usage {
|
|||||||
OSMUX_USAGE_ON = 1,
|
OSMUX_USAGE_ON = 1,
|
||||||
OSMUX_USAGE_ONLY = 2,
|
OSMUX_USAGE_ONLY = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,14 @@
|
|||||||
|
|
||||||
#include <osmocom/mgcp_client/mgcp_common.h>
|
#include <osmocom/mgcp_client/mgcp_common.h>
|
||||||
|
|
||||||
|
/* See also: RFC 3435, chapter 3.5 Transmission over UDP */
|
||||||
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
|
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
|
||||||
#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_ADDR_DEFAULT "127.0.0.1"
|
||||||
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
|
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
|
||||||
|
|
||||||
|
#define MGCP_CLIENT_MGW_STR "Configure MGCP connection to Media Gateway\n"
|
||||||
|
|
||||||
struct msgb;
|
struct msgb;
|
||||||
struct vty;
|
struct vty;
|
||||||
struct mgcp_client;
|
struct mgcp_client;
|
||||||
@@ -19,18 +22,46 @@ struct mgcp_client_conf {
|
|||||||
int local_port;
|
int local_port;
|
||||||
const char *remote_addr;
|
const char *remote_addr;
|
||||||
int remote_port;
|
int remote_port;
|
||||||
uint16_t first_endpoint;
|
|
||||||
uint16_t last_endpoint;
|
/* By default, we are always addressing the MGW with e.g. 'rtpbridge/123@mgw'.
|
||||||
uint16_t bts_base;
|
* If this is nonempty, the contained name will be used instead of 'mgw'. */
|
||||||
|
char endpoint_domain_name[MGCP_ENDPOINT_MAXLEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef unsigned int mgcp_trans_id_t;
|
typedef unsigned int mgcp_trans_id_t;
|
||||||
|
|
||||||
|
/*! Enumeration of the codec types that mgcp_client is able to handle. */
|
||||||
|
enum mgcp_codecs {
|
||||||
|
CODEC_PCMU_8000_1 = 0,
|
||||||
|
CODEC_GSM_8000_1 = 3,
|
||||||
|
CODEC_PCMA_8000_1 = 8,
|
||||||
|
CODEC_G729_8000_1 = 18,
|
||||||
|
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
|
||||||
|
* in enum mgcp_codecs must correspond to a valid payload type. However,
|
||||||
|
* this is an internal assumption that is made to avoid lookup tables.
|
||||||
|
* The API-User should not rely on this coincidence! */
|
||||||
|
|
||||||
|
/*! Structure to build a payload type map to allow the defiition custom payload
|
||||||
|
* types. */
|
||||||
|
struct ptmap {
|
||||||
|
/*! codec for which a payload type number should be defined */
|
||||||
|
enum mgcp_codecs codec;
|
||||||
|
|
||||||
|
/*! payload type number (96-127) */
|
||||||
|
unsigned int pt;
|
||||||
|
};
|
||||||
|
|
||||||
struct mgcp_response_head {
|
struct mgcp_response_head {
|
||||||
int response_code;
|
int response_code;
|
||||||
mgcp_trans_id_t trans_id;
|
mgcp_trans_id_t trans_id;
|
||||||
char comment[MGCP_COMMENT_MAXLEN];
|
char comment[MGCP_COMMENT_MAXLEN];
|
||||||
char conn_id[MGCP_CONN_ID_LENGTH];
|
char conn_id[MGCP_CONN_ID_MAXLEN];
|
||||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,6 +70,11 @@ struct mgcp_response {
|
|||||||
struct mgcp_response_head head;
|
struct mgcp_response_head head;
|
||||||
uint16_t audio_port;
|
uint16_t audio_port;
|
||||||
char audio_ip[INET_ADDRSTRLEN];
|
char audio_ip[INET_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 {
|
enum mgcp_verb {
|
||||||
@@ -55,6 +91,7 @@ enum mgcp_verb {
|
|||||||
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
|
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
|
||||||
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
|
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
|
||||||
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
|
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
|
||||||
|
#define MGCP_MSG_PRESENCE_X_OSMO_IGN 0x8000
|
||||||
|
|
||||||
struct mgcp_msg {
|
struct mgcp_msg {
|
||||||
enum mgcp_verb verb;
|
enum mgcp_verb verb;
|
||||||
@@ -66,6 +103,12 @@ struct mgcp_msg {
|
|||||||
uint16_t audio_port;
|
uint16_t audio_port;
|
||||||
char *audio_ip;
|
char *audio_ip;
|
||||||
enum mgcp_connection_mode conn_mode;
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
|
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
|
||||||
@@ -81,8 +124,8 @@ const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp);
|
|||||||
uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp);
|
uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp);
|
||||||
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp);
|
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp);
|
||||||
|
|
||||||
int mgcp_client_next_endpoint(struct mgcp_client *client);
|
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp);
|
||||||
void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client);
|
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp);
|
||||||
|
|
||||||
/* Invoked when an MGCP response is received or sending failed. When the
|
/* Invoked when an MGCP response is received or sending failed. When the
|
||||||
* response is passed as NULL, this indicates failure during transmission. */
|
* response is passed as NULL, this indicates failure during transmission. */
|
||||||
@@ -95,20 +138,6 @@ int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
|
|||||||
|
|
||||||
enum mgcp_connection_mode;
|
enum mgcp_connection_mode;
|
||||||
|
|
||||||
struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
|
|
||||||
uint16_t rtp_endpoint, unsigned int call_id,
|
|
||||||
enum mgcp_connection_mode mode)
|
|
||||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
|
|
||||||
|
|
||||||
struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
|
|
||||||
uint16_t rtp_endpoint, const char *rtp_conn_addr,
|
|
||||||
uint16_t rtp_port, enum mgcp_connection_mode mode)
|
|
||||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
|
|
||||||
|
|
||||||
struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
|
|
||||||
unsigned int call_id)
|
|
||||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
|
|
||||||
|
|
||||||
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
|
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);
|
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
|
||||||
|
|
||||||
@@ -117,3 +146,9 @@ static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
|
|||||||
{
|
{
|
||||||
return get_value_string(mgcp_client_connection_mode_strs, mode);
|
return get_value_string(mgcp_client_connection_mode_strs, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum mgcp_codecs map_str_to_codec(const char *str);
|
||||||
|
unsigned int map_codec_to_pt(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);
|
||||||
|
|||||||
@@ -14,20 +14,44 @@
|
|||||||
* identifier is supplied it is checked against the internal state to make
|
* identifier is supplied it is checked against the internal state to make
|
||||||
* sure it is correct. */
|
* sure it is correct. */
|
||||||
struct mgcp_conn_peer {
|
struct mgcp_conn_peer {
|
||||||
/*!< RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
|
/*! RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
|
||||||
char addr[INET_ADDRSTRLEN];
|
char addr[INET_ADDRSTRLEN];
|
||||||
|
|
||||||
/*!< RTP connection IP-Port (optional) */
|
/*! RTP connection IP-Port (optional) */
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
|
|
||||||
/*!< RTP endpoint */
|
/*! RTP endpoint */
|
||||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||||
|
|
||||||
/*!< CALL ID (unique per connection) */
|
/*! CALL ID (unique per connection) */
|
||||||
unsigned int call_id;
|
unsigned int call_id;
|
||||||
|
|
||||||
|
/*! RTP packetization interval (optional) */
|
||||||
|
unsigned int ptime;
|
||||||
|
|
||||||
|
/*! 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) */
|
||||||
|
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||||
|
|
||||||
|
/*! RTP payload type map length (optional, only needed when payload
|
||||||
|
* types are used that differ from what IANA/3GPP defines) */
|
||||||
|
unsigned int ptmap_len;
|
||||||
|
|
||||||
|
/*! If nonzero, send 'X-Osmo-IGN:' header. This is useful e.g. for SCCPlite MSCs where the MSC is
|
||||||
|
* known to issue incoherent or unknown CallIDs / to issue CRCX commands with a different domain
|
||||||
|
* name than the BSC. An OsmoMGW will then ignore these and not fail on mismatches. */
|
||||||
|
uint32_t x_osmo_ign;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
|
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);
|
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);
|
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);
|
void mgcp_conn_delete(struct osmo_fsm_inst *fi);
|
||||||
|
|
||||||
|
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
prefix=@prefix@
|
|
||||||
exec_prefix=@exec_prefix@
|
|
||||||
libdir=@libdir@
|
|
||||||
includedir=@includedir@
|
|
||||||
|
|
||||||
Name: Osmocom legacy Media Gateway Control Protocol library
|
|
||||||
Description: C Utility Library
|
|
||||||
Version: @VERSION@
|
|
||||||
Libs: -L${libdir} -losmo-legacy-mgcp
|
|
||||||
Cflags: -I${includedir}/
|
|
||||||
@@ -17,11 +17,9 @@
|
|||||||
|
|
||||||
app_configs = {
|
app_configs = {
|
||||||
"osmo-mgw": ["doc/examples/osmo-mgw/osmo-mgw.cfg"],
|
"osmo-mgw": ["doc/examples/osmo-mgw/osmo-mgw.cfg"],
|
||||||
"osmo-bsc_mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apps = [(4243, "src/osmo-mgw/osmo-mgw", "OsmoMGW", "osmo-mgw"),
|
apps = [(4243, "src/osmo-mgw/osmo-mgw", "OsmoMGW", "osmo-mgw"),
|
||||||
(4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "osmo-bsc_mgcp"),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
vty_command = ["./src/osmo-mgw/osmo-mgw", "-c",
|
vty_command = ["./src/osmo-mgw/osmo-mgw", "-c",
|
||||||
|
|||||||
@@ -21,13 +21,11 @@ AM_LDFLAGS = \
|
|||||||
|
|
||||||
# Libraries
|
# Libraries
|
||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
libosmo-legacy-mgcp \
|
|
||||||
libosmo-mgcp-client \
|
libosmo-mgcp-client \
|
||||||
libosmo-mgcp \
|
libosmo-mgcp \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
# Programs
|
# Programs
|
||||||
SUBDIRS += \
|
SUBDIRS += \
|
||||||
osmo-bsc_mgcp \
|
|
||||||
osmo-mgw \
|
osmo-mgw \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
AM_CPPFLAGS = \
|
|
||||||
$(all_includes) \
|
|
||||||
-I$(top_srcdir)/include \
|
|
||||||
-I$(top_builddir) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
AM_CFLAGS = \
|
|
||||||
-Wall \
|
|
||||||
$(LIBOSMOCORE_CFLAGS) \
|
|
||||||
$(LIBOSMOVTY_CFLAGS) \
|
|
||||||
$(LIBOSMONETIF_CFLAGS) \
|
|
||||||
$(COVERAGE_CFLAGS) \
|
|
||||||
$(LIBBCG729_CFLAGS) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
AM_LDFLAGS = \
|
|
||||||
$(LIBOSMOCORE_LIBS) \
|
|
||||||
$(LIBOSMOVTY_LIBS) \
|
|
||||||
$(LIBOSMONETIF_LIBS) \
|
|
||||||
$(COVERAGE_LDFLAGS) \
|
|
||||||
$(LIBBCG729_LIBS) \
|
|
||||||
$(LIBRARY_GSM) \
|
|
||||||
$(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!
|
|
||||||
LEGACY_MGCP_LIBVERSION=0:1:0
|
|
||||||
|
|
||||||
lib_LTLIBRARIES = \
|
|
||||||
libosmo-legacy-mgcp.la \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
noinst_HEADERS = \
|
|
||||||
g711common.h \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
libosmo_legacy_mgcp_la_SOURCES = \
|
|
||||||
mgcp_common.c \
|
|
||||||
mgcp_protocol.c \
|
|
||||||
mgcp_network.c \
|
|
||||||
mgcp_vty.c \
|
|
||||||
mgcp_osmux.c \
|
|
||||||
mgcp_sdp.c \
|
|
||||||
$(NULL)
|
|
||||||
if BUILD_MGCP_TRANSCODING
|
|
||||||
libosmo_legacy_mgcp_la_SOURCES += \
|
|
||||||
mgcp_transcode.c \
|
|
||||||
$(NULL)
|
|
||||||
endif
|
|
||||||
|
|
||||||
libosmo_legacy_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LEGACY_MGCP_LIBVERSION)
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
/*
|
|
||||||
* PCM - A-Law conversion
|
|
||||||
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
|
||||||
*
|
|
||||||
* Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org>
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
static inline int val_seg(int val)
|
|
||||||
{
|
|
||||||
int r = 0;
|
|
||||||
val >>= 7; /*7 = 4 + 3*/
|
|
||||||
if (val & 0xf0) {
|
|
||||||
val >>= 4;
|
|
||||||
r += 4;
|
|
||||||
}
|
|
||||||
if (val & 0x0c) {
|
|
||||||
val >>= 2;
|
|
||||||
r += 2;
|
|
||||||
}
|
|
||||||
if (val & 0x02)
|
|
||||||
r += 1;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
|
|
||||||
*
|
|
||||||
* s16_to_alaw() 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.
|
|
||||||
* G711 is designed for 13 bits input signal, this function add extra shifting to take this into account.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static inline unsigned char s16_to_alaw(int pcm_val)
|
|
||||||
{
|
|
||||||
int mask;
|
|
||||||
int seg;
|
|
||||||
unsigned char aval;
|
|
||||||
|
|
||||||
if (pcm_val >= 0) {
|
|
||||||
mask = 0xD5;
|
|
||||||
} else {
|
|
||||||
mask = 0x55;
|
|
||||||
pcm_val = -pcm_val;
|
|
||||||
if (pcm_val > 0x7fff)
|
|
||||||
pcm_val = 0x7fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pcm_val < 256) /*256 = 32 << 3*/
|
|
||||||
aval = pcm_val >> 4; /*4 = 1 + 3*/
|
|
||||||
else {
|
|
||||||
/* Convert the scaled magnitude to segment number. */
|
|
||||||
seg = val_seg(pcm_val);
|
|
||||||
aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
|
|
||||||
}
|
|
||||||
return aval ^ mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static inline int alaw_to_s16(unsigned char a_val)
|
|
||||||
{
|
|
||||||
int t;
|
|
||||||
int seg;
|
|
||||||
|
|
||||||
a_val ^= 0x55;
|
|
||||||
t = a_val & 0x7f;
|
|
||||||
if (t < 16)
|
|
||||||
t = (t << 4) + 8;
|
|
||||||
else {
|
|
||||||
seg = (t >> 4) & 0x07;
|
|
||||||
t = ((t & 0x0f) << 4) + 0x108;
|
|
||||||
t <<= seg -1;
|
|
||||||
}
|
|
||||||
return ((a_val & 0x80) ? t : -t);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* s16_to_ulaw() - 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */
|
|
||||||
{
|
|
||||||
int mask;
|
|
||||||
int seg;
|
|
||||||
unsigned char uval;
|
|
||||||
|
|
||||||
if (pcm_val < 0) {
|
|
||||||
pcm_val = 0x84 - pcm_val;
|
|
||||||
mask = 0x7f;
|
|
||||||
} else {
|
|
||||||
pcm_val += 0x84;
|
|
||||||
mask = 0xff;
|
|
||||||
}
|
|
||||||
if (pcm_val > 0x7fff)
|
|
||||||
pcm_val = 0x7fff;
|
|
||||||
|
|
||||||
/* Convert the scaled magnitude to segment number. */
|
|
||||||
seg = val_seg(pcm_val);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Combine the sign, segment, quantization bits;
|
|
||||||
* and complement the code word.
|
|
||||||
*/
|
|
||||||
uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
|
|
||||||
return uval ^ mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ulaw_to_s16() - 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.
|
|
||||||
*/
|
|
||||||
static inline int ulaw_to_s16(unsigned char u_val)
|
|
||||||
{
|
|
||||||
int 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 & 0x0f) << 3) + 0x84;
|
|
||||||
t <<= (u_val & 0x70) >> 4;
|
|
||||||
|
|
||||||
return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84));
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
/* Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
|
||||||
/* Implementations useful both for the MGCP GW as well as MGCP GW clients */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
|
|
||||||
* 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 <errno.h>
|
|
||||||
|
|
||||||
#include <osmocom/core/utils.h>
|
|
||||||
#include <osmocom/legacy_mgcp/mgcp.h>
|
|
||||||
|
|
||||||
const struct value_string mgcp_connection_mode_strs[] = {
|
|
||||||
{ MGCP_CONN_NONE, "none" },
|
|
||||||
{ MGCP_CONN_RECV_SEND, "sendrecv" },
|
|
||||||
{ MGCP_CONN_SEND_ONLY, "sendonly" },
|
|
||||||
{ MGCP_CONN_RECV_ONLY, "recvonly" },
|
|
||||||
{ MGCP_CONN_LOOPBACK, "loopback" },
|
|
||||||
{ 0, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Ensure that the msg->l2h is NUL terminated. */
|
|
||||||
int mgcp_msg_terminate_nul(struct msgb *msg)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
tail[0] = '\0';
|
|
||||||
else if (tail[-1] == '\r' || tail[-1] == '\n')
|
|
||||||
tail[-1] = '\0';
|
|
||||||
else {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: "
|
|
||||||
"Length: %d, Buffer size: %d\n",
|
|
||||||
msgb_l2len(msg), msg->data_len);
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,583 +0,0 @@
|
|||||||
/*
|
|
||||||
* (C) 2012-2013 by Pablo Neira Ayuso <pablo@gnumonks.org>
|
|
||||||
* (C) 2012-2013 by On Waves ehf <http://www.on-waves.com>
|
|
||||||
* All rights not specifically granted under this license are 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h> /* for printf */
|
|
||||||
#include <string.h> /* for memcpy */
|
|
||||||
#include <stdlib.h> /* for abs */
|
|
||||||
#include <inttypes.h> /* for PRIu64 */
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <osmocom/core/msgb.h>
|
|
||||||
#include <osmocom/core/talloc.h>
|
|
||||||
|
|
||||||
#include <osmocom/netif/osmux.h>
|
|
||||||
#include <osmocom/netif/rtp.h>
|
|
||||||
|
|
||||||
#include <osmocom/legacy_mgcp/mgcp.h>
|
|
||||||
#include <osmocom/legacy_mgcp/mgcp_internal.h>
|
|
||||||
#include <osmocom/legacy_mgcp/osmux.h>
|
|
||||||
|
|
||||||
static struct osmo_fd osmux_fd;
|
|
||||||
|
|
||||||
static LLIST_HEAD(osmux_handle_list);
|
|
||||||
|
|
||||||
struct osmux_handle {
|
|
||||||
struct llist_head head;
|
|
||||||
struct osmux_in_handle *in;
|
|
||||||
struct in_addr rem_addr;
|
|
||||||
int rem_port;
|
|
||||||
int refcnt;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void *osmux;
|
|
||||||
|
|
||||||
static void osmux_deliver(struct msgb *batch_msg, void *data)
|
|
||||||
{
|
|
||||||
struct osmux_handle *handle = data;
|
|
||||||
struct sockaddr_in out = {
|
|
||||||
.sin_family = AF_INET,
|
|
||||||
.sin_port = handle->rem_port,
|
|
||||||
};
|
|
||||||
|
|
||||||
memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr));
|
|
||||||
sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0,
|
|
||||||
(struct sockaddr *)&out, sizeof(out));
|
|
||||||
msgb_free(batch_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct osmux_handle *
|
|
||||||
osmux_handle_find_get(struct in_addr *addr, int rem_port)
|
|
||||||
{
|
|
||||||
struct osmux_handle *h;
|
|
||||||
|
|
||||||
/* Lookup for existing OSMUX handle for this destination address. */
|
|
||||||
llist_for_each_entry(h, &osmux_handle_list, head) {
|
|
||||||
if (memcmp(&h->rem_addr, addr, sizeof(struct in_addr)) == 0 &&
|
|
||||||
h->rem_port == rem_port) {
|
|
||||||
LOGP(DLMGCP, LOGL_DEBUG, "using existing OSMUX handle "
|
|
||||||
"for addr=%s:%d\n",
|
|
||||||
inet_ntoa(*addr), ntohs(rem_port));
|
|
||||||
h->refcnt++;
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void osmux_handle_put(struct osmux_in_handle *in)
|
|
||||||
{
|
|
||||||
struct osmux_handle *h;
|
|
||||||
|
|
||||||
/* Lookup for existing OSMUX handle for this destination address. */
|
|
||||||
llist_for_each_entry(h, &osmux_handle_list, head) {
|
|
||||||
if (h->in == in) {
|
|
||||||
if (--h->refcnt == 0) {
|
|
||||||
LOGP(DLMGCP, LOGL_INFO,
|
|
||||||
"Releasing unused osmux handle for %s:%d\n",
|
|
||||||
inet_ntoa(h->rem_addr),
|
|
||||||
ntohs(h->rem_port));
|
|
||||||
LOGP(DLMGCP, LOGL_INFO, "Stats: "
|
|
||||||
"input RTP msgs: %u bytes: %"PRIu64" "
|
|
||||||
"output osmux msgs: %u bytes: %"PRIu64"\n",
|
|
||||||
in->stats.input_rtp_msgs,
|
|
||||||
in->stats.input_rtp_bytes,
|
|
||||||
in->stats.output_osmux_msgs,
|
|
||||||
in->stats.output_osmux_bytes);
|
|
||||||
llist_del(&h->head);
|
|
||||||
osmux_xfrm_input_fini(h->in);
|
|
||||||
talloc_free(h);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "cannot find Osmux input handle %p\n", in);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct osmux_handle *
|
|
||||||
osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
|
|
||||||
{
|
|
||||||
struct osmux_handle *h;
|
|
||||||
|
|
||||||
h = talloc_zero(osmux, struct osmux_handle);
|
|
||||||
if (!h)
|
|
||||||
return NULL;
|
|
||||||
h->rem_addr = *addr;
|
|
||||||
h->rem_port = rem_port;
|
|
||||||
h->refcnt++;
|
|
||||||
|
|
||||||
h->in = talloc_zero(h, struct osmux_in_handle);
|
|
||||||
if (!h->in) {
|
|
||||||
talloc_free(h);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
h->in->osmux_seq = 0; /* sequence number to start OSmux message from */
|
|
||||||
h->in->batch_factor = cfg->osmux_batch;
|
|
||||||
/* If batch size is zero, the library defaults to 1470 bytes. */
|
|
||||||
h->in->batch_size = cfg->osmux_batch_size;
|
|
||||||
h->in->deliver = osmux_deliver;
|
|
||||||
osmux_xfrm_input_init(h->in);
|
|
||||||
h->in->data = h;
|
|
||||||
|
|
||||||
llist_add(&h->head, &osmux_handle_list);
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_DEBUG, "created new OSMUX handle for addr=%s:%d\n",
|
|
||||||
inet_ntoa(*addr), ntohs(rem_port));
|
|
||||||
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct osmux_in_handle *
|
|
||||||
osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
|
|
||||||
{
|
|
||||||
struct osmux_handle *h;
|
|
||||||
|
|
||||||
h = osmux_handle_find_get(addr, rem_port);
|
|
||||||
if (h != NULL)
|
|
||||||
return h->in;
|
|
||||||
|
|
||||||
h = osmux_handle_alloc(cfg, addr, rem_port);
|
|
||||||
if (h == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return h->in;
|
|
||||||
}
|
|
||||||
|
|
||||||
int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct msgb *msg;
|
|
||||||
|
|
||||||
msg = msgb_alloc(4096, "RTP");
|
|
||||||
if (!msg)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
memcpy(msg->data, buf, rc);
|
|
||||||
msgb_put(msg, rc);
|
|
||||||
|
|
||||||
while ((ret = osmux_xfrm_input(endp->osmux.in, msg, endp->osmux.cid)) > 0) {
|
|
||||||
/* batch full, build and deliver it */
|
|
||||||
osmux_xfrm_input_deliver(endp->osmux.in);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct mgcp_endpoint *
|
|
||||||
endpoint_lookup(struct mgcp_config *cfg, int cid,
|
|
||||||
struct in_addr *from_addr, int type)
|
|
||||||
{
|
|
||||||
struct mgcp_endpoint *tmp = NULL;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Lookup for the endpoint that corresponds to this port */
|
|
||||||
for (i=0; i<cfg->trunk.number_endpoints; i++) {
|
|
||||||
struct in_addr *this;
|
|
||||||
|
|
||||||
tmp = &cfg->trunk.endpoints[i];
|
|
||||||
|
|
||||||
if (!tmp->allocated)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
case MGCP_DEST_NET:
|
|
||||||
this = &tmp->net_end.addr;
|
|
||||||
break;
|
|
||||||
case MGCP_DEST_BTS:
|
|
||||||
this = &tmp->bts_end.addr;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* Should not ever happen */
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tmp->osmux.cid == cid && this->s_addr == from_addr->s_addr)
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void scheduled_tx_net_cb(struct msgb *msg, void *data)
|
|
||||||
{
|
|
||||||
struct mgcp_endpoint *endp = data;
|
|
||||||
struct sockaddr_in addr = {
|
|
||||||
.sin_addr = endp->net_end.addr,
|
|
||||||
.sin_port = endp->net_end.rtp_port,
|
|
||||||
};
|
|
||||||
|
|
||||||
endp->bts_end.octets += msg->len;
|
|
||||||
endp->bts_end.packets++;
|
|
||||||
|
|
||||||
mgcp_send(endp, MGCP_DEST_NET, 1, &addr, (char *)msg->data, msg->len);
|
|
||||||
msgb_free(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
|
|
||||||
{
|
|
||||||
struct mgcp_endpoint *endp = data;
|
|
||||||
struct sockaddr_in addr = {
|
|
||||||
.sin_addr = endp->bts_end.addr,
|
|
||||||
.sin_port = endp->bts_end.rtp_port,
|
|
||||||
};
|
|
||||||
|
|
||||||
endp->net_end.octets += msg->len;
|
|
||||||
endp->net_end.packets++;
|
|
||||||
|
|
||||||
mgcp_send(endp, MGCP_DEST_BTS, 1, &addr, (char *)msg->data, msg->len);
|
|
||||||
msgb_free(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr)
|
|
||||||
{
|
|
||||||
struct msgb *msg;
|
|
||||||
socklen_t slen = sizeof(*addr);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
msg = msgb_alloc(4096, "OSMUX");
|
|
||||||
if (!msg) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "cannot allocate message\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0,
|
|
||||||
(struct sockaddr *)addr, &slen);
|
|
||||||
if (ret <= 0) {
|
|
||||||
msgb_free(msg);
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "cannot receive message\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
msgb_put(msg, ret);
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define osmux_chunk_length(msg, rem) (rem - msg->len);
|
|
||||||
|
|
||||||
int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
|
||||||
{
|
|
||||||
struct msgb *msg;
|
|
||||||
struct osmux_hdr *osmuxh;
|
|
||||||
struct llist_head list;
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
struct mgcp_config *cfg = ofd->data;
|
|
||||||
uint32_t rem;
|
|
||||||
|
|
||||||
msg = osmux_recv(ofd, &addr);
|
|
||||||
if (!msg)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* not any further processing dummy messages */
|
|
||||||
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
rem = msg->len;
|
|
||||||
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
|
|
||||||
/* Yes, we use MGCP_DEST_NET to locate the origin */
|
|
||||||
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
|
|
||||||
&addr.sin_addr, MGCP_DEST_NET);
|
|
||||||
if (!endp) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Cannot find an endpoint for circuit_id=%d\n",
|
|
||||||
osmuxh->circuit_id);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
endp->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
|
||||||
endp->osmux.stats.chunks++;
|
|
||||||
rem = msg->len;
|
|
||||||
|
|
||||||
osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
|
|
||||||
osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
msgb_free(msg);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This is called from the bsc-nat */
|
|
||||||
static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
|
|
||||||
struct msgb *msg)
|
|
||||||
{
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
uint8_t osmux_cid;
|
|
||||||
|
|
||||||
if (msg->len < 1 + sizeof(osmux_cid)) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Discarding truncated Osmux dummy load\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n",
|
|
||||||
inet_ntoa(addr->sin_addr));
|
|
||||||
|
|
||||||
if (!cfg->osmux) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"bsc wants to use Osmux but bsc-nat did not request it\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* extract the osmux CID from the dummy message */
|
|
||||||
memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid));
|
|
||||||
|
|
||||||
endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS);
|
|
||||||
if (!endp) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Cannot find endpoint for Osmux CID %d\n", osmux_cid);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endp->osmux.state == OSMUX_STATE_ENABLED)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (osmux_enable_endpoint(endp, &addr->sin_addr, addr->sin_port) < 0 ) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Could not enable osmux in endpoint %d\n",
|
|
||||||
ENDPOINT_NUMBER(endp));
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n",
|
|
||||||
ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr),
|
|
||||||
ntohs(addr->sin_port));
|
|
||||||
out:
|
|
||||||
msgb_free(msg);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
|
||||||
{
|
|
||||||
struct msgb *msg;
|
|
||||||
struct osmux_hdr *osmuxh;
|
|
||||||
struct llist_head list;
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
struct mgcp_config *cfg = ofd->data;
|
|
||||||
uint32_t rem;
|
|
||||||
|
|
||||||
msg = osmux_recv(ofd, &addr);
|
|
||||||
if (!msg)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* not any further processing dummy messages */
|
|
||||||
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
|
||||||
return osmux_handle_dummy(cfg, &addr, msg);
|
|
||||||
|
|
||||||
rem = msg->len;
|
|
||||||
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
|
|
||||||
/* Yes, we use MGCP_DEST_BTS to locate the origin */
|
|
||||||
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
|
|
||||||
&addr.sin_addr, MGCP_DEST_BTS);
|
|
||||||
if (!endp) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Cannot find an endpoint for circuit_id=%d\n",
|
|
||||||
osmuxh->circuit_id);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
endp->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
|
||||||
endp->osmux.stats.chunks++;
|
|
||||||
rem = msg->len;
|
|
||||||
|
|
||||||
osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
|
|
||||||
osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
msgb_free(msg);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int osmux_init(int role, struct mgcp_config *cfg)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
switch(role) {
|
|
||||||
case OSMUX_ROLE_BSC:
|
|
||||||
osmux_fd.cb = osmux_read_from_bsc_nat_cb;
|
|
||||||
break;
|
|
||||||
case OSMUX_ROLE_BSC_NAT:
|
|
||||||
osmux_fd.cb = osmux_read_from_bsc_cb;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "wrong role for OSMUX\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
osmux_fd.data = cfg;
|
|
||||||
|
|
||||||
ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp);
|
|
||||||
osmux_fd.when |= BSC_FD_READ;
|
|
||||||
|
|
||||||
ret = osmo_fd_register(&osmux_fd);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
cfg->osmux_init = 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port)
|
|
||||||
{
|
|
||||||
/* If osmux is enabled, initialize the output handler. This handler is
|
|
||||||
* used to reconstruct the RTP flow from osmux. The RTP SSRC is
|
|
||||||
* allocated based on the circuit ID (endp->osmux.cid), which is unique
|
|
||||||
* in the local scope to the BSC/BSC-NAT. We use it to divide the RTP
|
|
||||||
* SSRC space (2^32) by the 256 possible circuit IDs, then randomly
|
|
||||||
* select one value from that window. Thus, we have no chance to have
|
|
||||||
* overlapping RTP SSRC traveling to the BTSes behind the BSC,
|
|
||||||
* similarly, for flows traveling to the MSC.
|
|
||||||
*/
|
|
||||||
static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256;
|
|
||||||
|
|
||||||
if (endp->osmux.state == OSMUX_STATE_DISABLED) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Endpoint %u didn't request Osmux\n",
|
|
||||||
ENDPOINT_NUMBER(endp));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
osmux_xfrm_output_init(&endp->osmux.out,
|
|
||||||
(endp->osmux.cid * rtp_ssrc_winlen) +
|
|
||||||
(random() % rtp_ssrc_winlen));
|
|
||||||
|
|
||||||
endp->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
|
|
||||||
if (!endp->osmux.in) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (!osmux_xfrm_input_open_circuit(endp->osmux.in, endp->osmux.cid,
|
|
||||||
endp->cfg->osmux_dummy)) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot open osmux circuit %u\n",
|
|
||||||
endp->osmux.cid);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (endp->cfg->role) {
|
|
||||||
case MGCP_BSC_NAT:
|
|
||||||
endp->type = MGCP_OSMUX_BSC_NAT;
|
|
||||||
break;
|
|
||||||
case MGCP_BSC:
|
|
||||||
endp->type = MGCP_OSMUX_BSC;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
endp->osmux.state = OSMUX_STATE_ENABLED;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void osmux_disable_endpoint(struct mgcp_endpoint *endp)
|
|
||||||
{
|
|
||||||
LOGP(DLMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n",
|
|
||||||
ENDPOINT_NUMBER(endp), endp->osmux.cid);
|
|
||||||
osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid);
|
|
||||||
endp->osmux.state = OSMUX_STATE_DISABLED;
|
|
||||||
endp->osmux.cid = -1;
|
|
||||||
osmux_handle_put(endp->osmux.in);
|
|
||||||
}
|
|
||||||
|
|
||||||
void osmux_release_cid(struct mgcp_endpoint *endp)
|
|
||||||
{
|
|
||||||
if (endp->osmux.allocated_cid >= 0)
|
|
||||||
osmux_put_cid(endp->osmux.allocated_cid);
|
|
||||||
endp->osmux.allocated_cid = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void osmux_allocate_cid(struct mgcp_endpoint *endp)
|
|
||||||
{
|
|
||||||
osmux_release_cid(endp);
|
|
||||||
endp->osmux.allocated_cid = osmux_get_cid();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We don't need to send the dummy load for osmux so often as another endpoint
|
|
||||||
* may have already punched the hole in the firewall. This approach is simple
|
|
||||||
* though.
|
|
||||||
*/
|
|
||||||
int osmux_send_dummy(struct mgcp_endpoint *endp)
|
|
||||||
{
|
|
||||||
char buf[1 + sizeof(uint8_t)];
|
|
||||||
struct in_addr addr_unset = {};
|
|
||||||
|
|
||||||
buf[0] = MGCP_DUMMY_LOAD;
|
|
||||||
memcpy(&buf[1], &endp->osmux.cid, sizeof(endp->osmux.cid));
|
|
||||||
|
|
||||||
/* Wait until we have the connection information from MDCX */
|
|
||||||
if (memcmp(&endp->net_end.addr, &addr_unset, sizeof(addr_unset)) == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (endp->osmux.state == OSMUX_STATE_ACTIVATING) {
|
|
||||||
if (osmux_enable_endpoint(endp, &endp->net_end.addr,
|
|
||||||
htons(endp->cfg->osmux_port)) < 0) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Could not activate osmux in endpoint %d\n",
|
|
||||||
ENDPOINT_NUMBER(endp));
|
|
||||||
}
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Osmux CID %u for %s:%u is now enabled\n",
|
|
||||||
endp->osmux.cid, inet_ntoa(endp->net_end.addr),
|
|
||||||
endp->cfg->osmux_port);
|
|
||||||
}
|
|
||||||
LOGP(DLMGCP, LOGL_DEBUG,
|
|
||||||
"sending OSMUX dummy load to %s CID %u\n",
|
|
||||||
inet_ntoa(endp->net_end.addr), endp->osmux.cid);
|
|
||||||
|
|
||||||
return mgcp_udp_send(osmux_fd.fd, &endp->net_end.addr,
|
|
||||||
htons(endp->cfg->osmux_port), buf, sizeof(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* bsc-nat allocates/releases the Osmux circuit ID */
|
|
||||||
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8];
|
|
||||||
|
|
||||||
int osmux_used_cid(void)
|
|
||||||
{
|
|
||||||
int i, j, used = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
|
|
||||||
for (j = 0; j < 8; j++) {
|
|
||||||
if (osmux_cid_bitmap[i] & (1 << j))
|
|
||||||
used += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return used;
|
|
||||||
}
|
|
||||||
|
|
||||||
int osmux_get_cid(void)
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
|
|
||||||
for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
|
|
||||||
for (j = 0; j < 8; j++) {
|
|
||||||
if (osmux_cid_bitmap[i] & (1 << j))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
osmux_cid_bitmap[i] |= (1 << j);
|
|
||||||
LOGP(DLMGCP, LOGL_DEBUG,
|
|
||||||
"Allocating Osmux CID %u from pool\n", (i * 8) + j);
|
|
||||||
return (i * 8) + j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "All Osmux circuits are in use!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void osmux_put_cid(uint8_t osmux_cid)
|
|
||||||
{
|
|
||||||
LOGP(DLMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid);
|
|
||||||
osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8));
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,305 +0,0 @@
|
|||||||
/*
|
|
||||||
* Some SDP file parsing...
|
|
||||||
*
|
|
||||||
* (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
|
|
||||||
* (C) 2009-2014 by On-Waves
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU 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/legacy_mgcp/mgcp.h>
|
|
||||||
#include <osmocom/legacy_mgcp/mgcp_internal.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
struct sdp_rtp_map {
|
|
||||||
/* the type */
|
|
||||||
int payload_type;
|
|
||||||
/* null, static or later dynamic codec name */
|
|
||||||
char *codec_name;
|
|
||||||
/* A pointer to the original line for later parsing */
|
|
||||||
char *map_line;
|
|
||||||
|
|
||||||
int rate;
|
|
||||||
int channels;
|
|
||||||
};
|
|
||||||
|
|
||||||
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
|
|
||||||
int payload_type, const char *audio_name)
|
|
||||||
{
|
|
||||||
int rate = codec->rate;
|
|
||||||
int channels = codec->channels;
|
|
||||||
char audio_codec[64];
|
|
||||||
|
|
||||||
talloc_free(codec->subtype_name);
|
|
||||||
codec->subtype_name = NULL;
|
|
||||||
talloc_free(codec->audio_name);
|
|
||||||
codec->audio_name = NULL;
|
|
||||||
|
|
||||||
if (payload_type != PTYPE_UNDEFINED)
|
|
||||||
codec->payload_type = payload_type;
|
|
||||||
|
|
||||||
if (!audio_name) {
|
|
||||||
switch (payload_type) {
|
|
||||||
case 0: audio_name = "PCMU/8000/1"; break;
|
|
||||||
case 3: audio_name = "GSM/8000/1"; break;
|
|
||||||
case 8: audio_name = "PCMA/8000/1"; break;
|
|
||||||
case 18: audio_name = "G729/8000/1"; break;
|
|
||||||
default:
|
|
||||||
/* Payload type is unknown, don't change rate and
|
|
||||||
* channels. */
|
|
||||||
/* TODO: return value? */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
|
||||||
audio_codec, &rate, &channels) < 1)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
codec->rate = rate;
|
|
||||||
codec->channels = channels;
|
|
||||||
codec->subtype_name = talloc_strdup(ctx, audio_codec);
|
|
||||||
codec->audio_name = talloc_strdup(ctx, audio_name);
|
|
||||||
|
|
||||||
if (!strcmp(audio_codec, "G729")) {
|
|
||||||
codec->frame_duration_num = 10;
|
|
||||||
codec->frame_duration_den = 1000;
|
|
||||||
} else {
|
|
||||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
|
||||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payload_type < 0) {
|
|
||||||
payload_type = 96;
|
|
||||||
if (rate == 8000 && channels == 1) {
|
|
||||||
if (!strcmp(audio_codec, "GSM"))
|
|
||||||
payload_type = 3;
|
|
||||||
else if (!strcmp(audio_codec, "PCMA"))
|
|
||||||
payload_type = 8;
|
|
||||||
else if (!strcmp(audio_codec, "PCMU"))
|
|
||||||
payload_type = 0;
|
|
||||||
else if (!strcmp(audio_codec, "G729"))
|
|
||||||
payload_type = 18;
|
|
||||||
}
|
|
||||||
|
|
||||||
codec->payload_type = payload_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channels != 1)
|
|
||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
|
||||||
"Channels != 1 in SDP: '%s'\n", audio_name);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < used; ++i) {
|
|
||||||
switch (codecs[i].payload_type) {
|
|
||||||
case 0:
|
|
||||||
codecs[i].codec_name = "PCMU";
|
|
||||||
codecs[i].rate = 8000;
|
|
||||||
codecs[i].channels = 1;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
codecs[i].codec_name = "GSM";
|
|
||||||
codecs[i].rate = 8000;
|
|
||||||
codecs[i].channels = 1;
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
codecs[i].codec_name = "PCMA";
|
|
||||||
codecs[i].rate = 8000;
|
|
||||||
codecs[i].channels = 1;
|
|
||||||
break;
|
|
||||||
case 18:
|
|
||||||
codecs[i].codec_name = "G729";
|
|
||||||
codecs[i].rate = 8000;
|
|
||||||
codecs[i].channels = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, int payload, char *audio_name)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < used; ++i) {
|
|
||||||
char audio_codec[64];
|
|
||||||
int rate = -1;
|
|
||||||
int channels = -1;
|
|
||||||
if (codecs[i].payload_type != payload)
|
|
||||||
continue;
|
|
||||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
|
||||||
audio_codec, &rate, &channels) < 1) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n", audio_name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
codecs[i].map_line = talloc_strdup(ctx, audio_name);
|
|
||||||
codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
|
|
||||||
codecs[i].rate = rate;
|
|
||||||
codecs[i].channels = channels;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, audio_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_codec_compatible(struct mgcp_endpoint *endp, struct sdp_rtp_map *codec)
|
|
||||||
{
|
|
||||||
char *bts_codec;
|
|
||||||
char audio_codec[64];
|
|
||||||
|
|
||||||
if (!codec->codec_name)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* GSM, GSM/8000 and GSM/8000/1 should all be compatible.. let's go
|
|
||||||
* by name first.
|
|
||||||
*/
|
|
||||||
bts_codec = endp->tcfg->audio_name;
|
|
||||||
if (sscanf(bts_codec, "%63[^/]/%*d/%*d", audio_codec) < 1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return strcasecmp(audio_codec, codec->codec_name) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
|
|
||||||
{
|
|
||||||
struct sdp_rtp_map codecs[10];
|
|
||||||
int codecs_used = 0;
|
|
||||||
char *line;
|
|
||||||
int maxptime = -1;
|
|
||||||
int i;
|
|
||||||
int codecs_assigned = 0;
|
|
||||||
void *tmp_ctx = talloc_new(NULL);
|
|
||||||
|
|
||||||
memset(&codecs, 0, sizeof(codecs));
|
|
||||||
|
|
||||||
for_each_line(line, p->save) {
|
|
||||||
switch (line[0]) {
|
|
||||||
case 'o':
|
|
||||||
case 's':
|
|
||||||
case 't':
|
|
||||||
case 'v':
|
|
||||||
/* skip these SDP attributes */
|
|
||||||
break;
|
|
||||||
case 'a': {
|
|
||||||
int payload;
|
|
||||||
int ptime, ptime2 = 0;
|
|
||||||
char audio_name[64];
|
|
||||||
|
|
||||||
|
|
||||||
if (sscanf(line, "a=rtpmap:%d %63s",
|
|
||||||
&payload, audio_name) == 2) {
|
|
||||||
codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
|
|
||||||
} else if (sscanf(line, "a=ptime:%d-%d",
|
|
||||||
&ptime, &ptime2) >= 1) {
|
|
||||||
if (ptime2 > 0 && ptime2 != ptime)
|
|
||||||
rtp->packet_duration_ms = 0;
|
|
||||||
else
|
|
||||||
rtp->packet_duration_ms = ptime;
|
|
||||||
} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
|
|
||||||
maxptime = ptime2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'm': {
|
|
||||||
int port, rc;
|
|
||||||
|
|
||||||
rc = sscanf(line, "m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
|
|
||||||
&port,
|
|
||||||
&codecs[0].payload_type,
|
|
||||||
&codecs[1].payload_type,
|
|
||||||
&codecs[2].payload_type,
|
|
||||||
&codecs[3].payload_type,
|
|
||||||
&codecs[4].payload_type,
|
|
||||||
&codecs[5].payload_type,
|
|
||||||
&codecs[6].payload_type,
|
|
||||||
&codecs[7].payload_type,
|
|
||||||
&codecs[8].payload_type,
|
|
||||||
&codecs[9].payload_type);
|
|
||||||
if (rc >= 2) {
|
|
||||||
rtp->rtp_port = htons(port);
|
|
||||||
rtp->rtcp_port = htons(port + 1);
|
|
||||||
codecs_used = rc - 1;
|
|
||||||
codecs_initialize(tmp_ctx, codecs, codecs_used);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'c': {
|
|
||||||
char ipv4[16];
|
|
||||||
|
|
||||||
if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
|
|
||||||
inet_aton(ipv4, &rtp->addr);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if (p->endp)
|
|
||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
|
||||||
"Unhandled SDP option: '%c'/%d on 0x%x\n",
|
|
||||||
line[0], line[0], ENDPOINT_NUMBER(p->endp));
|
|
||||||
else
|
|
||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
|
||||||
"Unhandled SDP option: '%c'/%d\n",
|
|
||||||
line[0], line[0]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now select the primary and alt_codec */
|
|
||||||
for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
|
|
||||||
struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
|
|
||||||
&rtp->codec : &rtp->alt_codec;
|
|
||||||
|
|
||||||
if (endp->tcfg->no_audio_transcoding &&
|
|
||||||
!is_codec_compatible(endp, &codecs[i])) {
|
|
||||||
LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n",
|
|
||||||
codecs[i].codec_name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
mgcp_set_audio_info(p->cfg, codec,
|
|
||||||
codecs[i].payload_type,
|
|
||||||
codecs[i].map_line);
|
|
||||||
codecs_assigned += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codecs_assigned > 0) {
|
|
||||||
/* TODO/XXX: Store this per codec and derive it on use */
|
|
||||||
if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
|
|
||||||
rtp->codec.frame_duration_num * 1500) {
|
|
||||||
/* more than 1 frame */
|
|
||||||
rtp->packet_duration_ms = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
|
||||||
"Got media info via SDP: port %d, payload %d (%s), "
|
|
||||||
"duration %d, addr %s\n",
|
|
||||||
ntohs(rtp->rtp_port), rtp->codec.payload_type,
|
|
||||||
rtp->codec.subtype_name ? rtp->codec.subtype_name : "unknown",
|
|
||||||
rtp->packet_duration_ms, inet_ntoa(rtp->addr));
|
|
||||||
}
|
|
||||||
|
|
||||||
talloc_free(tmp_ctx);
|
|
||||||
return codecs_assigned > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,611 +0,0 @@
|
|||||||
/*
|
|
||||||
* (C) 2014 by On-Waves
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU 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 <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
|
|
||||||
#include "g711common.h"
|
|
||||||
|
|
||||||
#include <osmocom/legacy_mgcp/mgcp.h>
|
|
||||||
#include <osmocom/legacy_mgcp/mgcp_internal.h>
|
|
||||||
#include <osmocom/legacy_mgcp/mgcp_transcode.h>
|
|
||||||
|
|
||||||
#include <osmocom/core/talloc.h>
|
|
||||||
#include <osmocom/netif/rtp.h>
|
|
||||||
|
|
||||||
int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst)
|
|
||||||
{
|
|
||||||
struct mgcp_process_rtp_state *state = state_;
|
|
||||||
if (dst)
|
|
||||||
return (nsamples >= 0 ?
|
|
||||||
nsamples / state->dst_samples_per_frame :
|
|
||||||
1) * state->dst_frame_size;
|
|
||||||
else
|
|
||||||
return (nsamples >= 0 ?
|
|
||||||
nsamples / state->src_samples_per_frame :
|
|
||||||
1) * state->src_frame_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum audio_format get_audio_format(const struct mgcp_rtp_codec *codec)
|
|
||||||
{
|
|
||||||
if (codec->subtype_name) {
|
|
||||||
if (!strcasecmp("GSM", codec->subtype_name))
|
|
||||||
return AF_GSM;
|
|
||||||
if (!strcasecmp("PCMA", codec->subtype_name))
|
|
||||||
return AF_PCMA;
|
|
||||||
if (!strcasecmp("PCMU", codec->subtype_name))
|
|
||||||
return AF_PCMU;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
if (!strcasecmp("G729", codec->subtype_name))
|
|
||||||
return AF_G729;
|
|
||||||
#endif
|
|
||||||
if (!strcasecmp("L16", codec->subtype_name))
|
|
||||||
return AF_L16;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (codec->payload_type) {
|
|
||||||
case 0 /* PCMU */:
|
|
||||||
return AF_PCMU;
|
|
||||||
case 3 /* GSM */:
|
|
||||||
return AF_GSM;
|
|
||||||
case 8 /* PCMA */:
|
|
||||||
return AF_PCMA;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
case 18 /* G.729 */:
|
|
||||||
return AF_G729;
|
|
||||||
#endif
|
|
||||||
case 11 /* L16 */:
|
|
||||||
return AF_L16;
|
|
||||||
default:
|
|
||||||
return AF_INVALID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void l16_encode(short *sample, unsigned char *buf, size_t n)
|
|
||||||
{
|
|
||||||
for (; n > 0; --n, ++sample, buf += 2) {
|
|
||||||
buf[0] = sample[0] >> 8;
|
|
||||||
buf[1] = sample[0] & 0xff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void l16_decode(unsigned char *buf, short *sample, size_t n)
|
|
||||||
{
|
|
||||||
for (; n > 0; --n, ++sample, buf += 2)
|
|
||||||
sample[0] = ((short)buf[0] << 8) | buf[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void alaw_encode(short *sample, unsigned char *buf, size_t n)
|
|
||||||
{
|
|
||||||
for (; n > 0; --n)
|
|
||||||
*(buf++) = s16_to_alaw(*(sample++));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void alaw_decode(unsigned char *buf, short *sample, size_t n)
|
|
||||||
{
|
|
||||||
for (; n > 0; --n)
|
|
||||||
*(sample++) = alaw_to_s16(*(buf++));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ulaw_encode(short *sample, unsigned char *buf, size_t n)
|
|
||||||
{
|
|
||||||
for (; n > 0; --n)
|
|
||||||
*(buf++) = s16_to_ulaw(*(sample++));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ulaw_decode(unsigned char *buf, short *sample, size_t n)
|
|
||||||
{
|
|
||||||
for (; n > 0; --n)
|
|
||||||
*(sample++) = ulaw_to_s16(*(buf++));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int processing_state_destructor(struct mgcp_process_rtp_state *state)
|
|
||||||
{
|
|
||||||
switch (state->src_fmt) {
|
|
||||||
case AF_GSM:
|
|
||||||
if (state->src.gsm_handle)
|
|
||||||
gsm_destroy(state->src.gsm_handle);
|
|
||||||
break;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
case AF_G729:
|
|
||||||
if (state->src.g729_dec)
|
|
||||||
closeBcg729DecoderChannel(state->src.g729_dec);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (state->dst_fmt) {
|
|
||||||
case AF_GSM:
|
|
||||||
if (state->dst.gsm_handle)
|
|
||||||
gsm_destroy(state->dst.gsm_handle);
|
|
||||||
break;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
case AF_G729:
|
|
||||||
if (state->dst.g729_enc)
|
|
||||||
closeBcg729EncoderChannel(state->dst.g729_enc);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgcp_transcoding_setup(struct mgcp_endpoint *endp,
|
|
||||||
struct mgcp_rtp_end *dst_end,
|
|
||||||
struct mgcp_rtp_end *src_end)
|
|
||||||
{
|
|
||||||
struct mgcp_process_rtp_state *state;
|
|
||||||
enum audio_format src_fmt, dst_fmt;
|
|
||||||
const struct mgcp_rtp_codec *dst_codec = &dst_end->codec;
|
|
||||||
|
|
||||||
/* cleanup first */
|
|
||||||
if (dst_end->rtp_process_data) {
|
|
||||||
talloc_free(dst_end->rtp_process_data);
|
|
||||||
dst_end->rtp_process_data = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!src_end)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
const struct mgcp_rtp_codec *src_codec = &src_end->codec;
|
|
||||||
|
|
||||||
if (endp->tcfg->no_audio_transcoding) {
|
|
||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
|
||||||
"Transcoding disabled on endpoint 0x%x\n",
|
|
||||||
ENDPOINT_NUMBER(endp));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
src_fmt = get_audio_format(src_codec);
|
|
||||||
dst_fmt = get_audio_format(dst_codec);
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Checking transcoding: %s (%d) -> %s (%d)\n",
|
|
||||||
src_codec->subtype_name, src_codec->payload_type,
|
|
||||||
dst_codec->subtype_name, dst_codec->payload_type);
|
|
||||||
|
|
||||||
if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) {
|
|
||||||
if (!src_codec->subtype_name || !dst_codec->subtype_name)
|
|
||||||
/* Not enough info, do nothing */
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (strcasecmp(src_codec->subtype_name, dst_codec->subtype_name) == 0)
|
|
||||||
/* Nothing to do */
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Cannot transcode: %s codec not supported (%s -> %s).\n",
|
|
||||||
src_fmt != AF_INVALID ? "destination" : "source",
|
|
||||||
src_codec->audio_name, dst_codec->audio_name);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (src_codec->rate && dst_codec->rate && src_codec->rate != dst_codec->rate) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Cannot transcode: rate conversion (%d -> %d) not supported.\n",
|
|
||||||
src_codec->rate, dst_codec->rate);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
state = talloc_zero(endp->tcfg->cfg, struct mgcp_process_rtp_state);
|
|
||||||
talloc_set_destructor(state, processing_state_destructor);
|
|
||||||
dst_end->rtp_process_data = state;
|
|
||||||
|
|
||||||
state->src_fmt = src_fmt;
|
|
||||||
|
|
||||||
switch (state->src_fmt) {
|
|
||||||
case AF_L16:
|
|
||||||
case AF_S16:
|
|
||||||
state->src_frame_size = 80 * sizeof(short);
|
|
||||||
state->src_samples_per_frame = 80;
|
|
||||||
break;
|
|
||||||
case AF_GSM:
|
|
||||||
state->src_frame_size = sizeof(gsm_frame);
|
|
||||||
state->src_samples_per_frame = 160;
|
|
||||||
state->src.gsm_handle = gsm_create();
|
|
||||||
if (!state->src.gsm_handle) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Failed to initialize GSM decoder.\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
case AF_G729:
|
|
||||||
state->src_frame_size = 10;
|
|
||||||
state->src_samples_per_frame = 80;
|
|
||||||
state->src.g729_dec = initBcg729DecoderChannel();
|
|
||||||
if (!state->src.g729_dec) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Failed to initialize G.729 decoder.\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case AF_PCMU:
|
|
||||||
case AF_PCMA:
|
|
||||||
state->src_frame_size = 80;
|
|
||||||
state->src_samples_per_frame = 80;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->dst_fmt = dst_fmt;
|
|
||||||
|
|
||||||
switch (state->dst_fmt) {
|
|
||||||
case AF_L16:
|
|
||||||
case AF_S16:
|
|
||||||
state->dst_frame_size = 80*sizeof(short);
|
|
||||||
state->dst_samples_per_frame = 80;
|
|
||||||
break;
|
|
||||||
case AF_GSM:
|
|
||||||
state->dst_frame_size = sizeof(gsm_frame);
|
|
||||||
state->dst_samples_per_frame = 160;
|
|
||||||
state->dst.gsm_handle = gsm_create();
|
|
||||||
if (!state->dst.gsm_handle) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Failed to initialize GSM encoder.\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
case AF_G729:
|
|
||||||
state->dst_frame_size = 10;
|
|
||||||
state->dst_samples_per_frame = 80;
|
|
||||||
state->dst.g729_enc = initBcg729EncoderChannel();
|
|
||||||
if (!state->dst.g729_enc) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Failed to initialize G.729 decoder.\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case AF_PCMU:
|
|
||||||
case AF_PCMA:
|
|
||||||
state->dst_frame_size = 80;
|
|
||||||
state->dst_samples_per_frame = 80;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dst_end->force_output_ptime)
|
|
||||||
state->dst_packet_duration = mgcp_rtp_packet_duration(endp, dst_end);
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_INFO,
|
|
||||||
"Initialized RTP processing on: 0x%x "
|
|
||||||
"conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n",
|
|
||||||
ENDPOINT_NUMBER(endp),
|
|
||||||
src_fmt, src_codec->payload_type, src_codec->rate, src_end->fmtp_extra,
|
|
||||||
dst_fmt, dst_codec->payload_type, dst_codec->rate, dst_end->fmtp_extra);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp,
|
|
||||||
int *payload_type,
|
|
||||||
const char**audio_name,
|
|
||||||
const char**fmtp_extra)
|
|
||||||
{
|
|
||||||
struct mgcp_process_rtp_state *state = endp->net_end.rtp_process_data;
|
|
||||||
struct mgcp_rtp_codec *net_codec = &endp->net_end.codec;
|
|
||||||
struct mgcp_rtp_codec *bts_codec = &endp->bts_end.codec;
|
|
||||||
|
|
||||||
if (!state || net_codec->payload_type < 0) {
|
|
||||||
*payload_type = bts_codec->payload_type;
|
|
||||||
*audio_name = bts_codec->audio_name;
|
|
||||||
*fmtp_extra = endp->bts_end.fmtp_extra;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
*payload_type = net_codec->payload_type;
|
|
||||||
*audio_name = net_codec->audio_name;
|
|
||||||
*fmtp_extra = endp->net_end.fmtp_extra;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int decode_audio(struct mgcp_process_rtp_state *state,
|
|
||||||
uint8_t **src, size_t *nbytes)
|
|
||||||
{
|
|
||||||
while (*nbytes >= state->src_frame_size) {
|
|
||||||
if (state->sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(state->samples)) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Sample buffer too small: %zu > %zu.\n",
|
|
||||||
state->sample_cnt + state->src_samples_per_frame,
|
|
||||||
ARRAY_SIZE(state->samples));
|
|
||||||
return -ENOSPC;
|
|
||||||
}
|
|
||||||
switch (state->src_fmt) {
|
|
||||||
case AF_GSM:
|
|
||||||
if (gsm_decode(state->src.gsm_handle,
|
|
||||||
(gsm_byte *)*src, state->samples + state->sample_cnt) < 0) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Failed to decode GSM.\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
case AF_G729:
|
|
||||||
bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case AF_PCMU:
|
|
||||||
ulaw_decode(*src, state->samples + state->sample_cnt,
|
|
||||||
state->src_samples_per_frame);
|
|
||||||
break;
|
|
||||||
case AF_PCMA:
|
|
||||||
alaw_decode(*src, state->samples + state->sample_cnt,
|
|
||||||
state->src_samples_per_frame);
|
|
||||||
break;
|
|
||||||
case AF_S16:
|
|
||||||
memmove(state->samples + state->sample_cnt, *src,
|
|
||||||
state->src_frame_size);
|
|
||||||
break;
|
|
||||||
case AF_L16:
|
|
||||||
l16_decode(*src, state->samples + state->sample_cnt,
|
|
||||||
state->src_samples_per_frame);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*src += state->src_frame_size;
|
|
||||||
*nbytes -= state->src_frame_size;
|
|
||||||
state->sample_cnt += state->src_samples_per_frame;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int encode_audio(struct mgcp_process_rtp_state *state,
|
|
||||||
uint8_t *dst, size_t buf_size, size_t max_samples)
|
|
||||||
{
|
|
||||||
int nbytes = 0;
|
|
||||||
size_t nsamples = 0;
|
|
||||||
/* Encode samples into dst */
|
|
||||||
while (nsamples + state->dst_samples_per_frame <= max_samples) {
|
|
||||||
if (nbytes + state->dst_frame_size > buf_size) {
|
|
||||||
if (nbytes > 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Not even one frame fits into the buffer */
|
|
||||||
LOGP(DLMGCP, LOGL_INFO,
|
|
||||||
"Encoding (RTP) buffer too small: %zu > %zu.\n",
|
|
||||||
nbytes + state->dst_frame_size, buf_size);
|
|
||||||
return -ENOSPC;
|
|
||||||
}
|
|
||||||
switch (state->dst_fmt) {
|
|
||||||
case AF_GSM:
|
|
||||||
gsm_encode(state->dst.gsm_handle,
|
|
||||||
state->samples + state->sample_offs, dst);
|
|
||||||
break;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
case AF_G729:
|
|
||||||
bcg729Encoder(state->dst.g729_enc,
|
|
||||||
state->samples + state->sample_offs, dst);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case AF_PCMU:
|
|
||||||
ulaw_encode(state->samples + state->sample_offs, dst,
|
|
||||||
state->src_samples_per_frame);
|
|
||||||
break;
|
|
||||||
case AF_PCMA:
|
|
||||||
alaw_encode(state->samples + state->sample_offs, dst,
|
|
||||||
state->src_samples_per_frame);
|
|
||||||
break;
|
|
||||||
case AF_S16:
|
|
||||||
memmove(dst, state->samples + state->sample_offs,
|
|
||||||
state->dst_frame_size);
|
|
||||||
break;
|
|
||||||
case AF_L16:
|
|
||||||
l16_encode(state->samples + state->sample_offs, dst,
|
|
||||||
state->src_samples_per_frame);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
dst += state->dst_frame_size;
|
|
||||||
nbytes += state->dst_frame_size;
|
|
||||||
state->sample_offs += state->dst_samples_per_frame;
|
|
||||||
nsamples += state->dst_samples_per_frame;
|
|
||||||
}
|
|
||||||
state->sample_cnt -= nsamples;
|
|
||||||
return nbytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct mgcp_rtp_end *source_for_dest(struct mgcp_endpoint *endp,
|
|
||||||
struct mgcp_rtp_end *dst_end)
|
|
||||||
{
|
|
||||||
if (&endp->bts_end == dst_end)
|
|
||||||
return &endp->net_end;
|
|
||||||
else if (&endp->net_end == dst_end)
|
|
||||||
return &endp->bts_end;
|
|
||||||
OSMO_ASSERT(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* With some modems we get offered multiple codecs
|
|
||||||
* and we have selected one of them. It might not
|
|
||||||
* be the right one and we need to detect this with
|
|
||||||
* the first audio packets. One difficulty is that
|
|
||||||
* we patch the rtp payload type in place, so we
|
|
||||||
* need to discuss this.
|
|
||||||
*/
|
|
||||||
struct mgcp_process_rtp_state *check_transcode_state(
|
|
||||||
struct mgcp_endpoint *endp,
|
|
||||||
struct mgcp_rtp_end *dst_end,
|
|
||||||
struct rtp_hdr *rtp_hdr)
|
|
||||||
{
|
|
||||||
struct mgcp_rtp_end *src_end;
|
|
||||||
|
|
||||||
/* Only deal with messages from net to bts */
|
|
||||||
if (&endp->bts_end != dst_end)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
src_end = source_for_dest(endp, dst_end);
|
|
||||||
|
|
||||||
/* Already patched */
|
|
||||||
if (rtp_hdr->payload_type == dst_end->codec.payload_type)
|
|
||||||
goto done;
|
|
||||||
/* The payload we expect */
|
|
||||||
if (rtp_hdr->payload_type == src_end->codec.payload_type)
|
|
||||||
goto done;
|
|
||||||
/* The matching alternate payload type? Then switch */
|
|
||||||
if (rtp_hdr->payload_type == src_end->alt_codec.payload_type) {
|
|
||||||
struct mgcp_config *cfg = endp->cfg;
|
|
||||||
struct mgcp_rtp_codec tmp_codec = src_end->alt_codec;
|
|
||||||
src_end->alt_codec = src_end->codec;
|
|
||||||
src_end->codec = tmp_codec;
|
|
||||||
cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end);
|
|
||||||
cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end);
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
return dst_end->rtp_process_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
|
|
||||||
struct mgcp_rtp_end *dst_end,
|
|
||||||
char *data, int *len, int buf_size)
|
|
||||||
{
|
|
||||||
struct mgcp_process_rtp_state *state;
|
|
||||||
const size_t rtp_hdr_size = sizeof(struct rtp_hdr);
|
|
||||||
struct rtp_hdr *rtp_hdr = (struct rtp_hdr *) data;
|
|
||||||
char *payload_data = (char *) &rtp_hdr->data[0];
|
|
||||||
int payload_len = *len - rtp_hdr_size;
|
|
||||||
uint8_t *src = (uint8_t *)payload_data;
|
|
||||||
uint8_t *dst = (uint8_t *)payload_data;
|
|
||||||
size_t nbytes = payload_len;
|
|
||||||
size_t nsamples;
|
|
||||||
size_t max_samples;
|
|
||||||
uint32_t ts_no;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
state = check_transcode_state(endp, dst_end, rtp_hdr);
|
|
||||||
if (!state)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (state->src_fmt == state->dst_fmt) {
|
|
||||||
if (!state->dst_packet_duration)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* TODO: repackage without transcoding */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the remaining samples do not fit into a fixed ptime,
|
|
||||||
* a) discard them, if the next packet is much later
|
|
||||||
* b) add silence and * send it, if the current packet is not
|
|
||||||
* yet too late
|
|
||||||
* c) append the sample data, if the timestamp matches exactly
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* TODO: check payload type (-> G.711 comfort noise) */
|
|
||||||
|
|
||||||
if (payload_len > 0) {
|
|
||||||
ts_no = ntohl(rtp_hdr->timestamp);
|
|
||||||
if (!state->is_running) {
|
|
||||||
state->next_seq = ntohs(rtp_hdr->sequence);
|
|
||||||
state->next_time = ts_no;
|
|
||||||
state->is_running = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (state->sample_cnt > 0) {
|
|
||||||
int32_t delta = ts_no - state->next_time;
|
|
||||||
/* TODO: check sequence? reordering? packet loss? */
|
|
||||||
|
|
||||||
if (delta > state->sample_cnt) {
|
|
||||||
/* There is a time gap between the last packet
|
|
||||||
* and the current one. Just discard the
|
|
||||||
* partial data that is left in the buffer.
|
|
||||||
* TODO: This can be improved by adding silence
|
|
||||||
* instead if the delta is small enough.
|
|
||||||
*/
|
|
||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
|
||||||
"0x%x dropping sample buffer due delta=%d sample_cnt=%zu\n",
|
|
||||||
ENDPOINT_NUMBER(endp), delta, state->sample_cnt);
|
|
||||||
state->sample_cnt = 0;
|
|
||||||
state->next_time = ts_no;
|
|
||||||
} else if (delta < 0) {
|
|
||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
|
||||||
"RTP time jumps backwards, delta = %d, "
|
|
||||||
"discarding buffered samples\n",
|
|
||||||
delta);
|
|
||||||
state->sample_cnt = 0;
|
|
||||||
state->sample_offs = 0;
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure the samples start without offset */
|
|
||||||
if (state->sample_offs && state->sample_cnt)
|
|
||||||
memmove(&state->samples[0],
|
|
||||||
&state->samples[state->sample_offs],
|
|
||||||
state->sample_cnt *
|
|
||||||
sizeof(state->samples[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
state->sample_offs = 0;
|
|
||||||
|
|
||||||
/* Append decoded audio to samples */
|
|
||||||
decode_audio(state, &src, &nbytes);
|
|
||||||
|
|
||||||
if (nbytes > 0)
|
|
||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
|
||||||
"Skipped audio frame in RTP packet: %zu octets\n",
|
|
||||||
nbytes);
|
|
||||||
} else
|
|
||||||
ts_no = state->next_time;
|
|
||||||
|
|
||||||
if (state->sample_cnt < state->dst_packet_duration)
|
|
||||||
return -EAGAIN;
|
|
||||||
|
|
||||||
max_samples =
|
|
||||||
state->dst_packet_duration ?
|
|
||||||
state->dst_packet_duration : state->sample_cnt;
|
|
||||||
|
|
||||||
nsamples = state->sample_cnt;
|
|
||||||
|
|
||||||
rc = encode_audio(state, dst, buf_size, max_samples);
|
|
||||||
/*
|
|
||||||
* There were no samples to encode?
|
|
||||||
* TODO: how does this work for comfort noise?
|
|
||||||
*/
|
|
||||||
if (rc == 0)
|
|
||||||
return -ENOMSG;
|
|
||||||
/* Any other error during the encoding */
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
nsamples -= state->sample_cnt;
|
|
||||||
|
|
||||||
*len = rtp_hdr_size + rc;
|
|
||||||
rtp_hdr->sequence = htons(state->next_seq);
|
|
||||||
rtp_hdr->timestamp = htonl(ts_no);
|
|
||||||
|
|
||||||
state->next_seq += 1;
|
|
||||||
state->next_time = ts_no + nsamples;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* XXX: At this point we should always have consumed
|
|
||||||
* samples. So doing OSMO_ASSERT(nsamples > 0) and returning
|
|
||||||
* rtp_hdr_size should be fine.
|
|
||||||
*/
|
|
||||||
return nsamples ? rtp_hdr_size : 0;
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -20,7 +20,7 @@ AM_LDFLAGS = \
|
|||||||
|
|
||||||
# This is not at all related to the release version, but a range of supported
|
# 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!
|
# API versions. Read TODO_RELEASE in the source tree's root!
|
||||||
MGCP_CLIENT_LIBVERSION=3:0:0
|
MGCP_CLIENT_LIBVERSION=5:0:0
|
||||||
|
|
||||||
lib_LTLIBRARIES = \
|
lib_LTLIBRARIES = \
|
||||||
libosmo-mgcp-client.la \
|
libosmo-mgcp-client.la \
|
||||||
|
|||||||
@@ -36,6 +36,150 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Codec descripton for dynamic payload types (SDP) */
|
||||||
|
static const struct value_string codec_table[] = {
|
||||||
|
{ CODEC_PCMU_8000_1, "PCMU/8000/1" },
|
||||||
|
{ CODEC_GSM_8000_1, "GSM/8000/1" },
|
||||||
|
{ CODEC_PCMA_8000_1, "PCMA/8000/1" },
|
||||||
|
{ CODEC_G729_8000_1, "G729/8000/1" },
|
||||||
|
{ CODEC_GSMEFR_8000_1, "GSM-EFR/8000/1" },
|
||||||
|
{ CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
|
||||||
|
{ CODEC_AMR_8000_1, "AMR/8000/1" },
|
||||||
|
{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
|
||||||
|
{ 0, NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Get encoding name from a full codec string e,g.
|
||||||
|
* ("CODEC/8000/2" => returns "CODEC") */
|
||||||
|
static char *extract_codec_name(const char *str)
|
||||||
|
{
|
||||||
|
static char buf[64];
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if (!str)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* FIXME osmo_strlcpy */
|
||||||
|
osmo_strlcpy(buf, str, sizeof(buf));
|
||||||
|
|
||||||
|
for (i = 0; i < strlen(buf); i++) {
|
||||||
|
if (buf[i] == '/')
|
||||||
|
buf[i] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Map a string to a codec.
|
||||||
|
* \ptmap[in] str input string (e.g "GSM/8000/1", "GSM/8000" or "GSM")
|
||||||
|
* \returns codec that corresponds to the given string representation. */
|
||||||
|
enum mgcp_codecs map_str_to_codec(const char *str)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
char *codec_name;
|
||||||
|
char str_buf[64];
|
||||||
|
|
||||||
|
osmo_strlcpy(str_buf, extract_codec_name(str), sizeof(str_buf));
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(codec_table); i++) {
|
||||||
|
codec_name = extract_codec_name(codec_table[i].str);
|
||||||
|
if (!codec_name)
|
||||||
|
continue;
|
||||||
|
if (strcmp(codec_name, str_buf) == 0)
|
||||||
|
return codec_table[i].value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the ptmap for illegal mappings */
|
||||||
|
static int check_ptmap(struct ptmap *ptmap)
|
||||||
|
{
|
||||||
|
/* Check if there are mappings that leave the IANA assigned dynamic
|
||||||
|
* payload type range. Under normal conditions such mappings should
|
||||||
|
* not occur */
|
||||||
|
|
||||||
|
/* Its ok to have a 1:1 mapping in the statically defined
|
||||||
|
* range, this won't hurt */
|
||||||
|
if (ptmap->codec == ptmap->pt)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ptmap->codec < 96 || ptmap->codec > 127)
|
||||||
|
goto error;
|
||||||
|
if (ptmap->pt < 96 || ptmap->pt > 127)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"ptmap contains illegal mapping: codec=%u maps to pt=%u\n",
|
||||||
|
ptmap->codec, ptmap->pt);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Map a codec to a payload type.
|
||||||
|
* \ptmap[in] payload pointer to payload type map with specified payload types.
|
||||||
|
* \ptmap[in] ptmap_len length of the payload type map.
|
||||||
|
* \ptmap[in] codec the codec for which the payload type should be looked up.
|
||||||
|
* \returns assigned payload type */
|
||||||
|
unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||||
|
enum mgcp_codecs codec)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
/*! Note: If the payload type map is empty or the codec is not found
|
||||||
|
* in the map, then a 1:1 mapping is performed. If the codec falls
|
||||||
|
* into the statically defined range or if the mapping table isself
|
||||||
|
* tries to map to the statically defined range, then the mapping
|
||||||
|
* is also ignored and a 1:1 mapping is performed instead. */
|
||||||
|
|
||||||
|
/* we may return the codec directly since enum mgcp_codecs directly
|
||||||
|
* corresponds to the statićally assigned payload types */
|
||||||
|
if (codec < 96 || codec > 127)
|
||||||
|
return codec;
|
||||||
|
|
||||||
|
for (i = 0; i < ptmap_len; i++) {
|
||||||
|
/* Skip illegal map entries */
|
||||||
|
if (check_ptmap(ptmap) == 0 && ptmap->codec == codec)
|
||||||
|
return ptmap->pt;
|
||||||
|
ptmap++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If nothing is found, do not perform any mapping */
|
||||||
|
return codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Map a payload type to a codec.
|
||||||
|
* \ptmap[in] payload pointer to payload type map with specified payload types.
|
||||||
|
* \ptmap[in] ptmap_len length of the payload type map.
|
||||||
|
* \ptmap[in] payload type for which the codec should be looked up.
|
||||||
|
* \returns codec that corresponds to the specified payload type */
|
||||||
|
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||||
|
unsigned int pt)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
/*! Note: If the payload type map is empty or the payload type is not
|
||||||
|
* found in the map, then a 1:1 mapping is performed. If the payload
|
||||||
|
* type falls into the statically defined range or if the mapping
|
||||||
|
* table isself tries to map to the statically defined range, then
|
||||||
|
* the mapping is also ignored and a 1:1 mapping is performed
|
||||||
|
* instead. */
|
||||||
|
|
||||||
|
/* See also note in map_codec_to_pt() */
|
||||||
|
if (pt < 96 || pt > 127)
|
||||||
|
return pt;
|
||||||
|
|
||||||
|
for (i = 0; i < ptmap_len; i++) {
|
||||||
|
if (check_ptmap(ptmap) == 0 && ptmap->pt == pt)
|
||||||
|
return ptmap->codec;
|
||||||
|
ptmap++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If nothing is found, do not perform any mapping */
|
||||||
|
return pt;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Initalize MGCP client configuration struct with default values.
|
/*! Initalize MGCP client configuration struct with default values.
|
||||||
* \param[out] conf Client configuration.*/
|
* \param[out] conf Client configuration.*/
|
||||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf)
|
void mgcp_client_conf_init(struct mgcp_client_conf *conf)
|
||||||
@@ -46,75 +190,9 @@ void mgcp_client_conf_init(struct mgcp_client_conf *conf)
|
|||||||
.local_port = -1,
|
.local_port = -1,
|
||||||
.remote_addr = NULL,
|
.remote_addr = NULL,
|
||||||
.remote_port = -1,
|
.remote_port = -1,
|
||||||
.first_endpoint = 0,
|
|
||||||
.last_endpoint = 0,
|
|
||||||
.bts_base = 0,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test if a given endpoint id is currently in use */
|
|
||||||
static bool endpoint_in_use(uint16_t id, struct mgcp_client *client)
|
|
||||||
{
|
|
||||||
struct mgcp_inuse_endpoint *endpoint;
|
|
||||||
llist_for_each_entry(endpoint, &client->inuse_endpoints, entry) {
|
|
||||||
if (endpoint->id == id)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Pick next free endpoint ID.
|
|
||||||
* \param[in,out] client MGCP client descriptor.
|
|
||||||
* \returns 0 on success, -EINVAL on error. */
|
|
||||||
int mgcp_client_next_endpoint(struct mgcp_client *client)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
uint16_t first_endpoint = client->actual.first_endpoint;
|
|
||||||
uint16_t last_endpoint = client->actual.last_endpoint;
|
|
||||||
struct mgcp_inuse_endpoint *endpoint;
|
|
||||||
|
|
||||||
/* Use the maximum permitted range if the VTY
|
|
||||||
* configuration does not specify a range */
|
|
||||||
if (client->actual.last_endpoint == 0) {
|
|
||||||
first_endpoint = 1;
|
|
||||||
last_endpoint = 65534;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Test the permitted endpoint range for an endpoint
|
|
||||||
* number that is not in use. When a suitable endpoint
|
|
||||||
* number can be found, seize it by adding it to the
|
|
||||||
* inuse list. */
|
|
||||||
for (i=first_endpoint;i<last_endpoint;i++)
|
|
||||||
{
|
|
||||||
if (endpoint_in_use(i,client) == false) {
|
|
||||||
endpoint = talloc_zero(client, struct mgcp_inuse_endpoint);
|
|
||||||
endpoint->id = i;
|
|
||||||
llist_add_tail(&endpoint->entry, &client->inuse_endpoints);
|
|
||||||
return endpoint->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* All endpoints are busy! */
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Release a seized endpoint ID to make it available again for other calls.
|
|
||||||
* \param[in] id Endpoint ID
|
|
||||||
* \param[in,out] client MGCP client descriptor. */
|
|
||||||
/* Release a seized endpoint id to make it available again for other calls */
|
|
||||||
void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client)
|
|
||||||
{
|
|
||||||
struct mgcp_inuse_endpoint *endpoint;
|
|
||||||
struct mgcp_inuse_endpoint *endpoint_tmp;
|
|
||||||
llist_for_each_entry_safe(endpoint, endpoint_tmp, &client->inuse_endpoints, entry) {
|
|
||||||
if (endpoint->id == id) {
|
|
||||||
llist_del(&endpoint->entry);
|
|
||||||
talloc_free(endpoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mgcp_client_handle_response(struct mgcp_client *mgcp,
|
static void mgcp_client_handle_response(struct mgcp_client *mgcp,
|
||||||
struct mgcp_response_pending *pending,
|
struct mgcp_response_pending *pending,
|
||||||
struct mgcp_response *response)
|
struct mgcp_response *response)
|
||||||
@@ -127,7 +205,7 @@ static void mgcp_client_handle_response(struct mgcp_client *mgcp,
|
|||||||
if (pending->response_cb)
|
if (pending->response_cb)
|
||||||
pending->response_cb(response, pending->priv);
|
pending->response_cb(response, pending->priv);
|
||||||
else
|
else
|
||||||
LOGP(DLMGCP, LOGL_INFO, "MGCP response ignored (NULL cb)\n");
|
LOGP(DLMGCP, LOGL_DEBUG, "MGCP response ignored (NULL cb)\n");
|
||||||
talloc_free(pending);
|
talloc_free(pending);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,22 +256,114 @@ static bool mgcp_line_is_valid(const char *line)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse a line like "m=audio 16002 RTP/AVP 98" */
|
/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
|
||||||
static int mgcp_parse_audio_port(struct mgcp_response *r, const char *line)
|
static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
|
||||||
{
|
{
|
||||||
if (sscanf(line, "m=audio %hu",
|
char *pt_str;
|
||||||
&r->audio_port) != 1)
|
unsigned int pt;
|
||||||
goto response_parse_failure;
|
unsigned int count = 0;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
/* Extract port information */
|
||||||
|
if (sscanf(line, "m=audio %hu", &r->audio_port) != 1)
|
||||||
|
goto response_parse_failure_port;
|
||||||
if (r->audio_port == 0)
|
if (r->audio_port == 0)
|
||||||
goto response_parse_failure;
|
goto response_parse_failure_port;
|
||||||
|
|
||||||
|
/* Extract payload types */
|
||||||
|
line = strstr(line, "RTP/AVP ");
|
||||||
|
if (!line)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
pt_str = strtok(line, " ");
|
||||||
|
while (1) {
|
||||||
|
/* Do not allow excessive payload types */
|
||||||
|
if (count > ARRAY_SIZE(r->codecs))
|
||||||
|
goto response_parse_failure_pt;
|
||||||
|
|
||||||
|
pt_str = strtok(NULL, " ");
|
||||||
|
if (!pt_str)
|
||||||
|
break;
|
||||||
|
pt = atoi(pt_str);
|
||||||
|
|
||||||
|
/* Do not allow duplicate payload types */
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
if (r->codecs[i] == pt)
|
||||||
|
goto response_parse_failure_pt;
|
||||||
|
|
||||||
|
/* Note: The payload type we store may not necessarly match
|
||||||
|
* the codec types we have defined in enum mgcp_codecs. To
|
||||||
|
* ensure that the end result only contains codec types which
|
||||||
|
* match enum mgcp_codecs, we will go through afterwards and
|
||||||
|
* remap the affected entries with the inrofmation we learn
|
||||||
|
* from rtpmap */
|
||||||
|
r->codecs[count] = pt;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->codecs_len = count;
|
||||||
|
|
||||||
|
exit:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
response_parse_failure:
|
response_parse_failure_port:
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"Failed to parse MGCP response header (audio port)\n");
|
"Failed to parse SDP parameter port (%s)\n", line);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
response_parse_failure_pt:
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"Failed to parse SDP parameter payload types (%s)\n", line);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
|
||||||
|
static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *line)
|
||||||
|
{
|
||||||
|
unsigned int pt;
|
||||||
|
char codec_resp[64];
|
||||||
|
unsigned int codec;
|
||||||
|
|
||||||
|
|
||||||
|
if (strstr(line, "ptime")) {
|
||||||
|
if (sscanf(line, "a=ptime:%u", &r->ptime) != 1)
|
||||||
|
goto response_parse_failure_ptime;
|
||||||
|
} else if (strstr(line, "rtpmap")) {
|
||||||
|
if (sscanf(line, "a=rtpmap:%d %63s", &pt, codec_resp) == 2) {
|
||||||
|
/* The MGW may assign an own payload type in the
|
||||||
|
* response if the choosen codec falls into the IANA
|
||||||
|
* assigned dynamic payload type range (96-127).
|
||||||
|
* Normally the MGW should obey the 3gpp payload type
|
||||||
|
* assignments, which are fixed, so we likely wont see
|
||||||
|
* anything unexpected here. In order to be sure that
|
||||||
|
* we will now check the codec string and if the result
|
||||||
|
* does not match to what is IANA / 3gpp assigned, we
|
||||||
|
* will create an entry in the ptmap table so we can
|
||||||
|
* lookup later what has been assigned. */
|
||||||
|
codec = map_str_to_codec(codec_resp);
|
||||||
|
if (codec != pt) {
|
||||||
|
if (r->ptmap_len < ARRAY_SIZE(r->ptmap)) {
|
||||||
|
r->ptmap[r->ptmap_len].pt = pt;
|
||||||
|
r->ptmap[r->ptmap_len].codec = codec;
|
||||||
|
r->ptmap_len++;
|
||||||
|
} else
|
||||||
|
goto response_parse_failure_rtpmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else
|
||||||
|
goto response_parse_failure_rtpmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
response_parse_failure_ptime:
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"Failed to parse SDP parameter, invalid ptime (%s)\n", line);
|
||||||
|
return -EINVAL;
|
||||||
|
response_parse_failure_rtpmap:
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"Failed to parse SDP parameter, invalid rtpmap (%s)\n", line);
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse a line like "c=IN IP4 10.11.12.13" */
|
/* Parse a line like "c=IN IP4 10.11.12.13" */
|
||||||
@@ -253,6 +423,7 @@ int mgcp_response_parse_params(struct mgcp_response *r)
|
|||||||
int rc;
|
int rc;
|
||||||
char *data;
|
char *data;
|
||||||
char *data_ptr;
|
char *data_ptr;
|
||||||
|
int i;
|
||||||
|
|
||||||
/* Since this functions performs a destructive parsing, we create a
|
/* Since this functions performs a destructive parsing, we create a
|
||||||
* local copy of the body data */
|
* local copy of the body data */
|
||||||
@@ -277,8 +448,13 @@ int mgcp_response_parse_params(struct mgcp_response *r)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
switch (line[0]) {
|
switch (line[0]) {
|
||||||
|
case 'a':
|
||||||
|
rc = mgcp_parse_audio_ptime_rtpmap(r, line);
|
||||||
|
if (rc)
|
||||||
|
goto exit;
|
||||||
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
rc = mgcp_parse_audio_port(r, line);
|
rc = mgcp_parse_audio_port_pt(r, line);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto exit;
|
goto exit;
|
||||||
break;
|
break;
|
||||||
@@ -293,6 +469,10 @@ int mgcp_response_parse_params(struct mgcp_response *r)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* See also note in mgcp_parse_audio_port_pt() */
|
||||||
|
for (i = 0; i < r->codecs_len; i++)
|
||||||
|
r->codecs[i] = map_pt_to_codec(r->ptmap, r->ptmap_len, r->codecs[i]);
|
||||||
|
|
||||||
rc = 0;
|
rc = 0;
|
||||||
exit:
|
exit:
|
||||||
talloc_free(data);
|
talloc_free(data);
|
||||||
@@ -304,6 +484,7 @@ static int mgcp_parse_head_param(char *result, unsigned int result_len,
|
|||||||
char label, const char *line)
|
char label, const char *line)
|
||||||
{
|
{
|
||||||
char label_string[4];
|
char label_string[4];
|
||||||
|
size_t rc;
|
||||||
|
|
||||||
/* Detect empty parameters */
|
/* Detect empty parameters */
|
||||||
if (strlen(line) < 4)
|
if (strlen(line) < 4)
|
||||||
@@ -316,7 +497,14 @@ static int mgcp_parse_head_param(char *result, unsigned int result_len,
|
|||||||
|
|
||||||
/* Copy payload part of the string to destinations (the label string
|
/* Copy payload part of the string to destinations (the label string
|
||||||
* is always 3 chars long) */
|
* is always 3 chars long) */
|
||||||
osmo_strlcpy(result, line + 3, result_len);
|
rc = osmo_strlcpy(result, line + 3, result_len);
|
||||||
|
if (rc >= result_len) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"Failed to parse MGCP response (parameter label: %c):"
|
||||||
|
" the received conn ID is too long: %zu, maximum is %u characters\n",
|
||||||
|
label, rc, result_len - 1);
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
response_parse_failure:
|
response_parse_failure:
|
||||||
@@ -463,11 +651,13 @@ static int mgcp_do_read(struct osmo_fd *fd)
|
|||||||
|
|
||||||
ret = read(fd->fd, msg->data, 4096 - 128);
|
ret = read(fd->fd, msg->data, 4096 - 128);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno));
|
LOGP(DLMGCP, LOGL_ERROR, "Failed to read: %s: %d='%s'\n", osmo_sock_get_name2(fd->fd),
|
||||||
|
errno, strerror(errno));
|
||||||
|
|
||||||
msgb_free(msg);
|
msgb_free(msg);
|
||||||
return -1;
|
return -1;
|
||||||
} else if (ret > 4096 - 128) {
|
} else if (ret > 4096 - 128) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Too much data: %d\n", ret);
|
LOGP(DLMGCP, LOGL_ERROR, "Too much data: %s: %d\n", osmo_sock_get_name2(fd->fd), ret);
|
||||||
msgb_free(msg);
|
msgb_free(msg);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -481,26 +671,15 @@ static int mgcp_do_read(struct osmo_fd *fd)
|
|||||||
static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
|
static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
static char strbuf[4096];
|
|
||||||
unsigned int l = msg->len < sizeof(strbuf) ? msg->len : sizeof(strbuf);
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
osmo_strlcpy(strbuf, (const char*)msg->data, l);
|
LOGP(DLMGCP, LOGL_DEBUG, "Tx MGCP: %s: len=%u '%s'...\n",
|
||||||
for (i = 0; i < sizeof(strbuf); i++) {
|
osmo_sock_get_name2(fd->fd), msg->len, osmo_escape_str((const char*)msg->data, OSMO_MIN(42, msg->len)));
|
||||||
if (strbuf[i] == '\n' || strbuf[i] == '\r') {
|
|
||||||
strbuf[i] = '\0';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DEBUGP(DLMGCP, "Tx MGCP msg to MGCP GW: '%s'\n", strbuf);
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len);
|
|
||||||
|
|
||||||
ret = write(fd->fd, msg->data, msg->len);
|
ret = write(fd->fd, msg->data, msg->len);
|
||||||
if (ret != msg->len)
|
if (ret != msg->len)
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to forward message to MGCP"
|
LOGP(DLMGCP, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
|
||||||
" GW: %s\n", strerror(errno));
|
osmo_sock_get_name2(fd->fd), errno, strerror(errno),
|
||||||
|
msg->len, osmo_escape_str((const char*)msg->data, OSMO_MIN(42, msg->len)));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,13 +705,55 @@ struct mgcp_client *mgcp_client_init(void *ctx,
|
|||||||
mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
|
mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
|
||||||
MGCP_CLIENT_REMOTE_PORT_DEFAULT;
|
MGCP_CLIENT_REMOTE_PORT_DEFAULT;
|
||||||
|
|
||||||
mgcp->actual.first_endpoint = conf->first_endpoint > 0 ? (uint16_t)conf->first_endpoint : 0;
|
if (osmo_strlcpy(mgcp->actual.endpoint_domain_name, conf->endpoint_domain_name,
|
||||||
mgcp->actual.last_endpoint = conf->last_endpoint > 0 ? (uint16_t)conf->last_endpoint : 0;
|
sizeof(mgcp->actual.endpoint_domain_name))
|
||||||
mgcp->actual.bts_base = conf->bts_base > 0 ? (uint16_t)conf->bts_base : 4000;
|
>= sizeof(mgcp->actual.endpoint_domain_name)) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR, "MGCP client: endpoint domain name is too long, max length is %zu: '%s'\n",
|
||||||
|
sizeof(mgcp->actual.endpoint_domain_name) - 1, conf->endpoint_domain_name);
|
||||||
|
talloc_free(mgcp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
LOGP(DLMGCP, LOGL_NOTICE, "MGCP client: using endpoint domain '@%s'\n", mgcp_client_endpoint_domain(mgcp));
|
||||||
|
|
||||||
return mgcp;
|
return mgcp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int init_socket(struct mgcp_client *mgcp)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct osmo_wqueue *wq;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
wq = &mgcp->wq;
|
||||||
|
|
||||||
|
for (i = 0; i < 100; i++) {
|
||||||
|
|
||||||
|
/* Initalize socket with the currently configured port
|
||||||
|
* number */
|
||||||
|
rc = osmo_sock_init2_ofd(&wq->bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
|
||||||
|
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
|
||||||
|
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
||||||
|
if (rc > 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* If there is a different port than the default port
|
||||||
|
* configured then we assume that the user has choosen
|
||||||
|
* that port conciously and we will not try to resolve
|
||||||
|
* this by silently choosing a different port. */
|
||||||
|
if (mgcp->actual.local_port != MGCP_CLIENT_LOCAL_PORT_DEFAULT && i == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Choose a new port number to try next */
|
||||||
|
LOGP(DLMGCP, LOGL_NOTICE,
|
||||||
|
"MGCPGW failed to bind to %s:%u, retrying with port %u\n",
|
||||||
|
mgcp->actual.local_addr, mgcp->actual.local_port, mgcp->actual.local_port + 1);
|
||||||
|
mgcp->actual.local_port++;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGP(DLMGCP, LOGL_FATAL, "MGCPGW failed to find a port to bind on %i times.\n", i);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Initalize client connection (opens socket only, no request is sent yet)
|
/*! Initalize client connection (opens socket only, no request is sent yet)
|
||||||
* \param[in,out] mgcp MGCP client descriptor.
|
* \param[in,out] mgcp MGCP client descriptor.
|
||||||
* \returns 0 on success, -EINVAL on error. */
|
* \returns 0 on success, -EINVAL on error. */
|
||||||
@@ -549,10 +770,7 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
|
|||||||
|
|
||||||
wq = &mgcp->wq;
|
wq = &mgcp->wq;
|
||||||
|
|
||||||
rc = osmo_sock_init2_ofd(&wq->bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
rc = init_socket(mgcp);
|
||||||
mgcp->actual.local_addr, mgcp->actual.local_port,
|
|
||||||
mgcp->actual.remote_addr, mgcp->actual.remote_port,
|
|
||||||
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
LOGP(DLMGCP, LOGL_FATAL,
|
LOGP(DLMGCP, LOGL_FATAL,
|
||||||
"Failed to initialize socket %s:%u -> %s:%u for MGCP GW: %s\n",
|
"Failed to initialize socket %s:%u -> %s:%u for MGCP GW: %s\n",
|
||||||
@@ -570,9 +788,7 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
|
|||||||
wq->read_cb = mgcp_do_read;
|
wq->read_cb = mgcp_do_read;
|
||||||
wq->write_cb = mgcp_do_write;
|
wq->write_cb = mgcp_do_write;
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s:%u -> %s:%u\n",
|
LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s\n", osmo_sock_get_name2(wq->bfd.fd));
|
||||||
mgcp->actual.local_addr, mgcp->actual.local_port,
|
|
||||||
mgcp->actual.remote_addr, mgcp->actual.remote_port);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
error_close_fd:
|
error_close_fd:
|
||||||
@@ -605,6 +821,32 @@ uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
|
|||||||
return mgcp->remote_addr;
|
return mgcp->remote_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* To compose endpoint names, usually for CRCX, use this as domain name.
|
||||||
|
* For example, snprintf("rtpbridge\*@%s", mgcp_client_endpoint_domain(mgcp)). */
|
||||||
|
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp)
|
||||||
|
{
|
||||||
|
return mgcp->actual.endpoint_domain_name[0] ? mgcp->actual.endpoint_domain_name : "mgw";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
|
||||||
|
{
|
||||||
|
static char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
#define RTPBRIDGE_WILDCARD_FMT "rtpbridge/*@%s"
|
||||||
|
rc = snprintf(endpoint, sizeof(endpoint), RTPBRIDGE_WILDCARD_FMT, mgcp_client_endpoint_domain(mgcp));
|
||||||
|
if (rc > sizeof(endpoint) - 1) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '" RTPBRIDGE_WILDCARD_FMT "'\n",
|
||||||
|
sizeof(endpoint) - 1, mgcp_client_endpoint_domain(mgcp));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (rc < 1) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP endpoint name\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
struct mgcp_response_pending * mgcp_client_pending_add(
|
struct mgcp_response_pending * mgcp_client_pending_add(
|
||||||
struct mgcp_client *mgcp,
|
struct mgcp_client *mgcp,
|
||||||
mgcp_trans_id_t trans_id,
|
mgcp_trans_id_t trans_id,
|
||||||
@@ -662,7 +904,7 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
|||||||
msgb_free(msg);
|
msgb_free(msg);
|
||||||
goto mgcp_tx_error;
|
goto mgcp_tx_error;
|
||||||
} else
|
} else
|
||||||
LOGP(DLMGCP, LOGL_INFO, "Queued %u bytes for MGCP GW\n",
|
LOGP(DLMGCP, LOGL_DEBUG, "Queued %u bytes for MGCP GW\n",
|
||||||
msgb_l2len(msg));
|
msgb_l2len(msg));
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -686,10 +928,10 @@ int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id)
|
|||||||
struct mgcp_response_pending *pending = mgcp_client_response_pending_get(mgcp, trans_id);
|
struct mgcp_response_pending *pending = mgcp_client_response_pending_get(mgcp, trans_id);
|
||||||
if (!pending) {
|
if (!pending) {
|
||||||
/*! Note: it is not harmful to cancel a transaction twice. */
|
/*! Note: it is not harmful to cancel a transaction twice. */
|
||||||
LOGP(DLMGCP, LOGL_INFO, "Cannot cancel, no such transaction: %u\n", trans_id);
|
LOGP(DLMGCP, LOGL_ERROR, "Cannot cancel, no such transaction: %u\n", trans_id);
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
LOGP(DLMGCP, LOGL_INFO, "Canceled transaction %u\n", trans_id);
|
LOGP(DLMGCP, LOGL_DEBUG, "Canceled transaction %u\n", trans_id);
|
||||||
talloc_free(pending);
|
talloc_free(pending);
|
||||||
return 0;
|
return 0;
|
||||||
/*! We don't really need to clean up the wqueue: In all sane cases, the msgb has already been sent
|
/*! We don't really need to clean up the wqueue: In all sane cases, the msgb has already been sent
|
||||||
@@ -701,54 +943,6 @@ int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id)
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id,
|
|
||||||
const char *buf, int len)
|
|
||||||
{
|
|
||||||
struct msgb *msg;
|
|
||||||
|
|
||||||
if (len > (4096 - 128)) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot send to MGCP GW:"
|
|
||||||
" message too large: %d\n", len);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
|
|
||||||
OSMO_ASSERT(msg);
|
|
||||||
|
|
||||||
char *dst = (char*)msgb_put(msg, len);
|
|
||||||
memcpy(dst, buf, len);
|
|
||||||
msg->l2h = msg->data;
|
|
||||||
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct msgb *mgcp_msg_from_str(mgcp_trans_id_t trans_id,
|
|
||||||
const char *fmt, ...)
|
|
||||||
{
|
|
||||||
static char compose[4096 - 128];
|
|
||||||
va_list ap;
|
|
||||||
int len;
|
|
||||||
OSMO_ASSERT(fmt);
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
len = vsnprintf(compose, sizeof(compose), fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
if (len >= sizeof(compose)) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Message too large: trans_id=%u len=%d\n",
|
|
||||||
trans_id, len);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (len < 1) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Failed to compose message: trans_id=%u len=%d\n",
|
|
||||||
trans_id, len);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return mgcp_msg_from_buf(trans_id, compose, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
|
static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
|
||||||
{
|
{
|
||||||
/* avoid zero trans_id to distinguish from unset trans_id */
|
/* avoid zero trans_id to distinguish from unset trans_id */
|
||||||
@@ -757,52 +951,6 @@ static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
|
|||||||
return mgcp->next_trans_id ++;
|
return mgcp->next_trans_id ++;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
|
|
||||||
uint16_t rtp_endpoint, unsigned int call_id,
|
|
||||||
enum mgcp_connection_mode mode)
|
|
||||||
{
|
|
||||||
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
|
|
||||||
return mgcp_msg_from_str(trans_id,
|
|
||||||
"CRCX %u %x@mgw MGCP 1.0\r\n"
|
|
||||||
"C: %x\r\n"
|
|
||||||
"L: p:20, a:AMR, nt:IN\r\n"
|
|
||||||
"M: %s\r\n"
|
|
||||||
,
|
|
||||||
trans_id,
|
|
||||||
rtp_endpoint,
|
|
||||||
call_id,
|
|
||||||
mgcp_client_cmode_name(mode));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
|
|
||||||
uint16_t rtp_endpoint, const char *rtp_conn_addr,
|
|
||||||
uint16_t rtp_port, enum mgcp_connection_mode mode)
|
|
||||||
|
|
||||||
{
|
|
||||||
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
|
|
||||||
return mgcp_msg_from_str(trans_id,
|
|
||||||
"MDCX %u %x@mgw MGCP 1.0\r\n"
|
|
||||||
"M: %s\r\n"
|
|
||||||
"\r\n"
|
|
||||||
"c=IN IP4 %s\r\n"
|
|
||||||
"m=audio %u RTP/AVP 255\r\n"
|
|
||||||
,
|
|
||||||
trans_id,
|
|
||||||
rtp_endpoint,
|
|
||||||
mgcp_client_cmode_name(mode),
|
|
||||||
rtp_conn_addr,
|
|
||||||
rtp_port);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
|
|
||||||
unsigned int call_id)
|
|
||||||
{
|
|
||||||
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
|
|
||||||
return mgcp_msg_from_str(trans_id,
|
|
||||||
"DLCX %u %x@mgw MGCP 1.0\r\n"
|
|
||||||
"C: %x\r\n", trans_id, rtp_endpoint, call_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MGCP_CRCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
|
#define MGCP_CRCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
|
||||||
MGCP_MSG_PRESENCE_CALL_ID | \
|
MGCP_MSG_PRESENCE_CALL_ID | \
|
||||||
MGCP_MSG_PRESENCE_CONN_MODE)
|
MGCP_MSG_PRESENCE_CONN_MODE)
|
||||||
@@ -813,6 +961,119 @@ struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
|
|||||||
#define MGCP_AUEP_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
|
#define MGCP_AUEP_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
|
||||||
#define MGCP_RSIP_MANDATORY 0 /* none */
|
#define MGCP_RSIP_MANDATORY 0 /* none */
|
||||||
|
|
||||||
|
/* Helper function for mgcp_msg_gen(): Add LCO information to MGCP message */
|
||||||
|
static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
int rc = 0;
|
||||||
|
const char *codec;
|
||||||
|
unsigned int pt;
|
||||||
|
|
||||||
|
rc += msgb_printf(msg, "L:");
|
||||||
|
|
||||||
|
if (mgcp_msg->ptime)
|
||||||
|
rc += msgb_printf(msg, " p:%u,", mgcp_msg->ptime);
|
||||||
|
|
||||||
|
if (mgcp_msg->codecs_len) {
|
||||||
|
rc += msgb_printf(msg, " a:");
|
||||||
|
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||||
|
pt = mgcp_msg->codecs[i];
|
||||||
|
codec = get_value_string_or_null(codec_table, pt);
|
||||||
|
|
||||||
|
/* Note: Use codec descriptors from enum mgcp_codecs
|
||||||
|
* in mgcp_client only! */
|
||||||
|
OSMO_ASSERT(codec);
|
||||||
|
rc += msgb_printf(msg, "%s", extract_codec_name(codec));
|
||||||
|
if (i < mgcp_msg->codecs_len - 1)
|
||||||
|
rc += msgb_printf(msg, ";");
|
||||||
|
}
|
||||||
|
rc += msgb_printf(msg, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
rc += msgb_printf(msg, " nt:IN\r\n");
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper function for mgcp_msg_gen(): Add SDP information to MGCP message */
|
||||||
|
static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_client *mgcp)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
int rc = 0;
|
||||||
|
char local_ip[INET_ADDRSTRLEN];
|
||||||
|
const char *codec;
|
||||||
|
unsigned int pt;
|
||||||
|
|
||||||
|
/* Add separator to mark the beginning of the SDP block */
|
||||||
|
rc += msgb_printf(msg, "\r\n");
|
||||||
|
|
||||||
|
/* Add SDP protocol version */
|
||||||
|
rc += msgb_printf(msg, "v=0\r\n");
|
||||||
|
|
||||||
|
/* Determine local IP-Address */
|
||||||
|
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"Could not determine local IP-Address!\n");
|
||||||
|
msgb_free(msg);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add owner/creator (SDP) */
|
||||||
|
rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
|
||||||
|
mgcp_msg->call_id, local_ip);
|
||||||
|
|
||||||
|
/* Add session name (none) */
|
||||||
|
rc += msgb_printf(msg, "s=-\r\n");
|
||||||
|
|
||||||
|
/* Add RTP address and port */
|
||||||
|
if (mgcp_msg->audio_port == 0) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"Invalid port number, can not generate MGCP message\n");
|
||||||
|
msgb_free(msg);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
if (strlen(mgcp_msg->audio_ip) <= 0) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"Empty ip address, can not generate MGCP message\n");
|
||||||
|
msgb_free(msg);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
|
||||||
|
|
||||||
|
/* Add time description, active time (SDP) */
|
||||||
|
rc += msgb_printf(msg, "t=0 0\r\n");
|
||||||
|
|
||||||
|
rc += msgb_printf(msg, "m=audio %u RTP/AVP", mgcp_msg->audio_port);
|
||||||
|
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||||
|
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
||||||
|
rc += msgb_printf(msg, " %u", pt);
|
||||||
|
|
||||||
|
}
|
||||||
|
rc += msgb_printf(msg, "\r\n");
|
||||||
|
|
||||||
|
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||||
|
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
||||||
|
|
||||||
|
/* Note: Only dynamic payload type from the range 96-127
|
||||||
|
* require to be explained further via rtpmap. All others
|
||||||
|
* are implcitly definedby the number in m=audio */
|
||||||
|
if (pt >= 96 && pt <= 127) {
|
||||||
|
codec = get_value_string_or_null(codec_table, mgcp_msg->codecs[i]);
|
||||||
|
|
||||||
|
/* Note: Use codec descriptors from enum mgcp_codecs
|
||||||
|
* in mgcp_client only! */
|
||||||
|
OSMO_ASSERT(codec);
|
||||||
|
|
||||||
|
rc += msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mgcp_msg->ptime)
|
||||||
|
rc += msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Generate an MGCP message
|
/*! Generate an MGCP message
|
||||||
* \param[in] mgcp MGCP client descriptor.
|
* \param[in] mgcp MGCP client descriptor.
|
||||||
* \param[in] mgcp_msg Message description
|
* \param[in] mgcp_msg Message description
|
||||||
@@ -823,7 +1084,8 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
|||||||
uint32_t mandatory_mask;
|
uint32_t mandatory_mask;
|
||||||
struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
|
struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
char local_ip[INET_ADDRSTRLEN];
|
int rc_sdp;
|
||||||
|
bool use_sdp = false;
|
||||||
|
|
||||||
msg->l2h = msg->data;
|
msg->l2h = msg->data;
|
||||||
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
|
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
|
||||||
@@ -902,9 +1164,17 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
|||||||
rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id);
|
rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add local connection options */
|
/* Using SDP makes sense when a valid IP/Port combination is specifiec,
|
||||||
if (mgcp_msg->verb == MGCP_VERB_CRCX)
|
* if we do not know this information yet, we fall back to LCO */
|
||||||
rc += msgb_printf(msg, "L: p:20, a:AMR, nt:IN\r\n");
|
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
|
||||||
|
&& mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT)
|
||||||
|
use_sdp = true;
|
||||||
|
|
||||||
|
/* Add local connection options (LCO) */
|
||||||
|
if (!use_sdp
|
||||||
|
&& (mgcp_msg->verb == MGCP_VERB_CRCX
|
||||||
|
|| mgcp_msg->verb == MGCP_VERB_MDCX))
|
||||||
|
rc += add_lco(msg, mgcp_msg);
|
||||||
|
|
||||||
/* Add mode */
|
/* Add mode */
|
||||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE)
|
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE)
|
||||||
@@ -912,52 +1182,22 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
|||||||
msgb_printf(msg, "M: %s\r\n",
|
msgb_printf(msg, "M: %s\r\n",
|
||||||
mgcp_client_cmode_name(mgcp_msg->conn_mode));
|
mgcp_client_cmode_name(mgcp_msg->conn_mode));
|
||||||
|
|
||||||
/* Add SDP body */
|
/* Add X-Osmo-IGN */
|
||||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
|
if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_X_OSMO_IGN)
|
||||||
&& mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) {
|
&& (mgcp_msg->x_osmo_ign != 0))
|
||||||
|
|
||||||
/* Add separator to mark the beginning of the SDP block */
|
|
||||||
rc += msgb_printf(msg, "\r\n");
|
|
||||||
|
|
||||||
/* Add SDP protocol version */
|
|
||||||
rc += msgb_printf(msg, "v=0\r\n");
|
|
||||||
|
|
||||||
/* Determine local IP-Address */
|
|
||||||
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Could not determine local IP-Address!\n");
|
|
||||||
msgb_free(msg);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add owner/creator (SDP) */
|
|
||||||
rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
|
|
||||||
mgcp_msg->call_id, local_ip);
|
|
||||||
|
|
||||||
/* Add session name (none) */
|
|
||||||
rc += msgb_printf(msg, "s=-\r\n");
|
|
||||||
|
|
||||||
/* Add RTP address and port */
|
|
||||||
if (mgcp_msg->audio_port == 0) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Invalid port number, can not generate MGCP message\n");
|
|
||||||
msgb_free(msg);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (strlen(mgcp_msg->audio_ip) <= 0) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Empty ip address, can not generate MGCP message\n");
|
|
||||||
msgb_free(msg);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
|
|
||||||
|
|
||||||
/* Add time description, active time (SDP) */
|
|
||||||
rc += msgb_printf(msg, "t=0 0\r\n");
|
|
||||||
|
|
||||||
rc +=
|
rc +=
|
||||||
msgb_printf(msg, "m=audio %u RTP/AVP 255\r\n",
|
msgb_printf(msg, MGCP_X_OSMO_IGN_HEADER "%s\r\n",
|
||||||
mgcp_msg->audio_port);
|
mgcp_msg->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID ? " C": "");
|
||||||
|
|
||||||
|
/* Add session description protocol (SDP) */
|
||||||
|
if (use_sdp
|
||||||
|
&& (mgcp_msg->verb == MGCP_VERB_CRCX
|
||||||
|
|| mgcp_msg->verb == MGCP_VERB_MDCX)) {
|
||||||
|
rc_sdp = add_sdp(msg, mgcp_msg, mgcp);
|
||||||
|
if (rc_sdp == -2)
|
||||||
|
return NULL;
|
||||||
|
else
|
||||||
|
rc += rc_sdp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ struct mgcp_ctx {
|
|||||||
bool mgw_trans_pending;
|
bool mgw_trans_pending;
|
||||||
|
|
||||||
/* Connection ID which has been assigned by he MGW */
|
/* Connection ID which has been assigned by he MGW */
|
||||||
char conn_id[MGCP_CONN_ID_LENGTH];
|
char conn_id[MGCP_CONN_ID_MAXLEN];
|
||||||
|
|
||||||
/* Local RTP connection info, the MGW will send outgoing traffic to the
|
/* Local RTP connection info, the MGW will send outgoing traffic to the
|
||||||
* ip/port specified here. The Address does not have to be choosen right
|
* ip/port specified here. The Address does not have to be choosen right
|
||||||
@@ -104,37 +104,34 @@ static const struct value_string fsm_mgcp_client_evt_names[] = {
|
|||||||
{0, NULL}
|
{0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct msgb *make_crcx_msg_bind(struct mgcp_ctx *mgcp_ctx)
|
static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
|
||||||
{
|
{
|
||||||
struct mgcp_msg mgcp_msg;
|
*mgcp_msg = (struct mgcp_msg) {
|
||||||
|
|
||||||
mgcp_msg = (struct mgcp_msg) {
|
|
||||||
.verb = MGCP_VERB_CRCX,
|
.verb = MGCP_VERB_CRCX,
|
||||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
|
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
|
||||||
.call_id = mgcp_ctx->conn_peer_local.call_id,
|
| MGCP_MSG_PRESENCE_CONN_MODE),
|
||||||
.conn_mode = MGCP_CONN_LOOPBACK,
|
.call_id = info->call_id,
|
||||||
|
.conn_mode = MGCP_CONN_RECV_ONLY,
|
||||||
|
.ptime = info->ptime,
|
||||||
|
.codecs_len = info->codecs_len,
|
||||||
|
.ptmap_len = info->ptmap_len
|
||||||
};
|
};
|
||||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
|
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));
|
||||||
|
|
||||||
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
if (info->x_osmo_ign) {
|
||||||
|
mgcp_msg->x_osmo_ign = info->x_osmo_ign;
|
||||||
|
mgcp_msg->presence |= MGCP_MSG_PRESENCE_X_OSMO_IGN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct msgb *make_crcx_msg_bind_connect(struct mgcp_ctx *mgcp_ctx)
|
static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
|
||||||
{
|
{
|
||||||
struct mgcp_msg mgcp_msg;
|
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT;
|
||||||
|
mgcp_msg->audio_ip = info->addr;
|
||||||
mgcp_msg = (struct mgcp_msg) {
|
mgcp_msg->audio_port = info->port;
|
||||||
.verb = MGCP_VERB_CRCX,.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
|
||||||
MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
|
|
||||||
MGCP_MSG_PRESENCE_AUDIO_PORT),
|
|
||||||
.call_id = mgcp_ctx->conn_peer_local.call_id,
|
|
||||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
|
||||||
.audio_ip = mgcp_ctx->conn_peer_local.addr,
|
|
||||||
.audio_port = mgcp_ctx->conn_peer_local.port,
|
|
||||||
};
|
|
||||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
|
|
||||||
|
|
||||||
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||||
@@ -150,8 +147,13 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
|||||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||||
.audio_ip = mgcp_ctx->conn_peer_local.addr,
|
.audio_ip = mgcp_ctx->conn_peer_local.addr,
|
||||||
.audio_port = mgcp_ctx->conn_peer_local.port,
|
.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
|
||||||
};
|
};
|
||||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
|
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));
|
||||||
|
|
||||||
/* Note: We take the endpoint and the call_id from the remote
|
/* Note: We take the endpoint and the call_id from the remote
|
||||||
* connection info, because we can be confident that the
|
* connection info, because we can be confident that the
|
||||||
@@ -181,6 +183,7 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|||||||
{
|
{
|
||||||
struct mgcp_ctx *mgcp_ctx = data;
|
struct mgcp_ctx *mgcp_ctx = data;
|
||||||
struct mgcp_client *mgcp;
|
struct mgcp_client *mgcp;
|
||||||
|
struct mgcp_msg mgcp_msg;
|
||||||
struct msgb *msg;
|
struct msgb *msg;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@@ -193,10 +196,10 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|||||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: creating connection on MGW endpoint:%s...\n",
|
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: creating connection on MGW endpoint:%s...\n",
|
||||||
mgcp_ctx->conn_peer_local.endpoint);
|
mgcp_ctx->conn_peer_local.endpoint);
|
||||||
|
|
||||||
|
make_crcx_msg(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||||
if (mgcp_ctx->conn_peer_local.port)
|
if (mgcp_ctx->conn_peer_local.port)
|
||||||
msg = make_crcx_msg_bind_connect(mgcp_ctx);
|
add_audio(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||||
else
|
msg = mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||||
msg = make_crcx_msg_bind(mgcp_ctx);
|
|
||||||
OSMO_ASSERT(msg);
|
OSMO_ASSERT(msg);
|
||||||
|
|
||||||
mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
|
mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
|
||||||
@@ -215,6 +218,14 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return the CI that the MGW allocated during CRCX response. This is purely informational for logging
|
||||||
|
* and identity tracking; the mgcp_conn_*() functions take care of using the right CI internally. */
|
||||||
|
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi)
|
||||||
|
{
|
||||||
|
struct mgcp_ctx *mgcp_ctx = fi->priv;
|
||||||
|
return mgcp_ctx->conn_id;
|
||||||
|
}
|
||||||
|
|
||||||
static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv)
|
static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv)
|
||||||
{
|
{
|
||||||
struct osmo_fsm_inst *fi = priv;
|
struct osmo_fsm_inst *fi = priv;
|
||||||
@@ -476,7 +487,7 @@ static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca
|
|||||||
* mgcp_conn_delete() to instruct the FSM to perform a graceful exit */
|
* mgcp_conn_delete() to instruct the FSM to perform a graceful exit */
|
||||||
if (strlen(mgcp_ctx->conn_id)) {
|
if (strlen(mgcp_ctx->conn_id)) {
|
||||||
LOGPFSML(fi, LOGL_ERROR,
|
LOGPFSML(fi, LOGL_ERROR,
|
||||||
"MGW/DLCX: aprupt FSM termination with connections still present, sending unconditional DLCX...\n");
|
"MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n");
|
||||||
msg = make_dlcx_msg(mgcp_ctx);
|
msg = make_dlcx_msg(mgcp_ctx);
|
||||||
OSMO_ASSERT(msg);
|
OSMO_ASSERT(msg);
|
||||||
mgcp_client_tx(mgcp, msg, NULL, NULL);
|
mgcp_client_tx(mgcp, msg, NULL, NULL);
|
||||||
@@ -548,7 +559,7 @@ static struct osmo_fsm fsm_mgcp_client = {
|
|||||||
|
|
||||||
/*! allocate FSM, and create a new connection on the MGW.
|
/*! allocate FSM, and create a new connection on the MGW.
|
||||||
* \param[in] mgcp MGCP client descriptor.
|
* \param[in] mgcp MGCP client descriptor.
|
||||||
* \param[in] mgcpparent_fi Parent FSM instance.
|
* \param[in] parent_fi Parent FSM instance.
|
||||||
* \param[in] parent_term_evt Event to be sent to parent when terminating.
|
* \param[in] parent_term_evt Event to be sent to parent when terminating.
|
||||||
* \param[in] parent_evt Event to be sent to parent when operation is done.
|
* \param[in] parent_evt Event to be sent to parent when operation is done.
|
||||||
* \param[in] conn_peer Connection parameters (ip, port...).
|
* \param[in] conn_peer Connection parameters (ip, port...).
|
||||||
@@ -565,7 +576,7 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm
|
|||||||
OSMO_ASSERT(mgcp);
|
OSMO_ASSERT(mgcp);
|
||||||
OSMO_ASSERT(conn_peer);
|
OSMO_ASSERT(conn_peer);
|
||||||
|
|
||||||
/* Check if IP/Port informstaion in conn info makes sense */
|
/* Check if IP/Port information in conn info makes sense */
|
||||||
if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0)
|
if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -622,17 +633,24 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
|
|||||||
OSMO_ASSERT(fi->state != ST_DLCX_RESP);
|
OSMO_ASSERT(fi->state != ST_DLCX_RESP);
|
||||||
|
|
||||||
/* Check if IP/Port parameters make sense */
|
/* Check if IP/Port parameters make sense */
|
||||||
if (conn_peer->port == 0)
|
if (conn_peer->port == 0) {
|
||||||
|
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, port == 0\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (inet_aton(conn_peer->addr, &ip_test) == 0)
|
}
|
||||||
|
if (inet_aton(conn_peer->addr, &ip_test) == 0) {
|
||||||
|
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address == 0.0.0.0\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/*! The user may supply an endpoint identifier in conn_peer. The
|
/*! The user may supply an endpoint identifier in conn_peer. The
|
||||||
* identifier is then checked. This check is optional. Later steps do
|
* identifier is then checked. This check is optional. Later steps do
|
||||||
* not depend on the endpoint identifier supplied here because it is
|
* not depend on the endpoint identifier supplied here because it is
|
||||||
* already implicitly known from the CRCX phase. */
|
* already implicitly known from the CRCX phase. */
|
||||||
if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint))
|
if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint)) {
|
||||||
|
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, endpoint mismatches: requested %s, should be %s\n",
|
||||||
|
conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Note: The call-id is implicitly known from the previous CRCX and
|
/*! Note: The call-id is implicitly known from the previous CRCX and
|
||||||
* will not be checked even when it is set in conn_peer. */
|
* will not be checked even when it is set in conn_peer. */
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||||
|
|
||||||
#define MGW_STR "Configure MGCP connection to Media Gateway\n"
|
#define MGW_STR MGCP_CLIENT_MGW_STR
|
||||||
|
|
||||||
void *global_mgcp_client_ctx = NULL;
|
void *global_mgcp_client_ctx = NULL;
|
||||||
struct mgcp_client_conf *global_mgcp_client_conf = NULL;
|
struct mgcp_client_conf *global_mgcp_client_conf = NULL;
|
||||||
@@ -43,8 +43,9 @@ DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
|
|||||||
if (!global_mgcp_client_conf)
|
if (!global_mgcp_client_conf)
|
||||||
return CMD_ERR_NOTHING_TODO;
|
return CMD_ERR_NOTHING_TODO;
|
||||||
OSMO_ASSERT(global_mgcp_client_ctx);
|
OSMO_ASSERT(global_mgcp_client_ctx);
|
||||||
global_mgcp_client_conf->local_addr =
|
osmo_talloc_replace_string(global_mgcp_client_ctx,
|
||||||
talloc_strdup(global_mgcp_client_ctx, argv[0]);
|
(char**)&global_mgcp_client_conf->local_addr,
|
||||||
|
argv[0]);
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
|
ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
|
||||||
@@ -75,8 +76,9 @@ DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
|
|||||||
if (!global_mgcp_client_conf)
|
if (!global_mgcp_client_conf)
|
||||||
return CMD_ERR_NOTHING_TODO;
|
return CMD_ERR_NOTHING_TODO;
|
||||||
OSMO_ASSERT(global_mgcp_client_ctx);
|
OSMO_ASSERT(global_mgcp_client_ctx);
|
||||||
global_mgcp_client_conf->remote_addr =
|
osmo_talloc_replace_string(global_mgcp_client_ctx,
|
||||||
talloc_strdup(global_mgcp_client_ctx, argv[0]);
|
(char**)&global_mgcp_client_conf->remote_addr,
|
||||||
|
argv[0]);
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
|
ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
|
||||||
@@ -99,23 +101,14 @@ ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
|
|||||||
MGW_STR "remote bind to connect to MGCP gateway with\n"
|
MGW_STR "remote bind to connect to MGCP gateway with\n"
|
||||||
"remote bind port\n")
|
"remote bind port\n")
|
||||||
|
|
||||||
DEFUN(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
|
DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
|
||||||
"mgw endpoint-range <1-65534> <1-65534>",
|
"mgw endpoint-range <1-65534> <1-65534>",
|
||||||
MGW_STR "usable range of endpoint identifiers\n"
|
MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
|
||||||
"set first usable endpoint identifier\n"
|
"-\n" "-\n")
|
||||||
"set last usable endpoint identifier\n")
|
|
||||||
{
|
{
|
||||||
uint16_t first_endpoint = atoi(argv[0]);
|
vty_out(vty, "Please do not use legacy config 'mgw endpoint-range'"
|
||||||
uint16_t last_endpoint = atoi(argv[1]);
|
" (the range can no longer be defined by the MGCP client)%s",
|
||||||
|
VTY_NEWLINE);
|
||||||
if (last_endpoint < first_endpoint) {
|
|
||||||
vty_out(vty, "last endpoint must be greater than first endpoint!%s",
|
|
||||||
VTY_NEWLINE);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
global_mgcp_client_conf->first_endpoint = first_endpoint;
|
|
||||||
global_mgcp_client_conf->last_endpoint = last_endpoint;
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
||||||
@@ -126,14 +119,15 @@ ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
|||||||
|
|
||||||
#define BTS_START_STR "First UDP port allocated for the BTS side\n"
|
#define BTS_START_STR "First UDP port allocated for the BTS side\n"
|
||||||
#define UDP_PORT_STR "UDP Port number\n"
|
#define UDP_PORT_STR "UDP Port number\n"
|
||||||
DEFUN(cfg_mgw_rtp_bts_base_port,
|
DEFUN_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
||||||
cfg_mgw_rtp_bts_base_port_cmd,
|
cfg_mgw_rtp_bts_base_port_cmd,
|
||||||
"mgw bts-base <0-65534>",
|
"mgw bts-base <0-65534>",
|
||||||
MGW_STR
|
MGW_STR
|
||||||
BTS_START_STR
|
"DEPRECATED: there is no explicit BTS side in current osmo-mgw\n" "-\n")
|
||||||
UDP_PORT_STR)
|
|
||||||
{
|
{
|
||||||
global_mgcp_client_conf->bts_base = atoi(argv[0]);
|
vty_out(vty, "Please do not use legacy config 'mgw bts-base'"
|
||||||
|
" (there is no explicit BTS side in an MGW anymore)%s",
|
||||||
|
VTY_NEWLINE);
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
||||||
@@ -143,13 +137,26 @@ ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
|||||||
BTS_START_STR
|
BTS_START_STR
|
||||||
UDP_PORT_STR)
|
UDP_PORT_STR)
|
||||||
|
|
||||||
|
DEFUN(cfg_mgw_endpoint_domain_name,
|
||||||
|
cfg_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")
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
int mgcp_client_config_write(struct vty *vty, const char *indent)
|
int mgcp_client_config_write(struct vty *vty, const char *indent)
|
||||||
{
|
{
|
||||||
const char *addr;
|
const char *addr;
|
||||||
int port;
|
int port;
|
||||||
uint16_t first_endpoint;
|
|
||||||
uint16_t last_endpoint;
|
|
||||||
uint16_t bts_base;
|
|
||||||
|
|
||||||
addr = global_mgcp_client_conf->local_addr;
|
addr = global_mgcp_client_conf->local_addr;
|
||||||
if (addr)
|
if (addr)
|
||||||
@@ -169,18 +176,9 @@ int mgcp_client_config_write(struct vty *vty, const char *indent)
|
|||||||
vty_out(vty, "%smgw remote-port %u%s", indent,
|
vty_out(vty, "%smgw remote-port %u%s", indent,
|
||||||
(uint16_t)port, VTY_NEWLINE);
|
(uint16_t)port, VTY_NEWLINE);
|
||||||
|
|
||||||
first_endpoint = global_mgcp_client_conf->first_endpoint;
|
if (global_mgcp_client_conf->endpoint_domain_name[0])
|
||||||
last_endpoint = global_mgcp_client_conf->last_endpoint;
|
vty_out(vty, "%smgw endpoint-domain %s%s", indent,
|
||||||
if (last_endpoint != 0) {
|
global_mgcp_client_conf->endpoint_domain_name, VTY_NEWLINE);
|
||||||
vty_out(vty, "%smgw endpoint-range %u %u%s", indent,
|
|
||||||
first_endpoint, last_endpoint, VTY_NEWLINE);
|
|
||||||
}
|
|
||||||
|
|
||||||
bts_base = global_mgcp_client_conf->bts_base;
|
|
||||||
if (bts_base) {
|
|
||||||
vty_out(vty, "%smgw bts-base %u%s", indent,
|
|
||||||
bts_base, VTY_NEWLINE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -196,6 +194,7 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
|
|||||||
install_element(node, &cfg_mgw_remote_port_cmd);
|
install_element(node, &cfg_mgw_remote_port_cmd);
|
||||||
install_element(node, &cfg_mgw_endpoint_range_cmd);
|
install_element(node, &cfg_mgw_endpoint_range_cmd);
|
||||||
install_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
|
install_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
|
||||||
|
install_element(node, &cfg_mgw_endpoint_domain_name_cmd);
|
||||||
|
|
||||||
/* deprecated 'mgcpgw' commands */
|
/* deprecated 'mgcpgw' commands */
|
||||||
install_element(node, &cfg_mgcpgw_local_ip_cmd);
|
install_element(node, &cfg_mgcpgw_local_ip_cmd);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ libosmo_mgcp_a_SOURCES = \
|
|||||||
mgcp_vty.c \
|
mgcp_vty.c \
|
||||||
mgcp_osmux.c \
|
mgcp_osmux.c \
|
||||||
mgcp_sdp.c \
|
mgcp_sdp.c \
|
||||||
|
mgcp_codec.c \
|
||||||
mgcp_msg.c \
|
mgcp_msg.c \
|
||||||
mgcp_conn.c \
|
mgcp_conn.c \
|
||||||
mgcp_stat.c \
|
mgcp_stat.c \
|
||||||
|
|||||||
410
src/libosmo-mgcp/mgcp_codec.c
Normal file
410
src/libosmo-mgcp/mgcp_codec.c
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
/*
|
||||||
|
* (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||||
|
* (C) 2009-2014 by On-Waves
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU 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/mgcp/mgcp_internal.h>
|
||||||
|
#include <osmocom/mgcp/mgcp_endp.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
/* Helper function to dump codec information of a specified codec to a printable
|
||||||
|
* string, used by dump_codec_summary() */
|
||||||
|
static char *dump_codec(struct mgcp_rtp_codec *codec)
|
||||||
|
{
|
||||||
|
static char str[256];
|
||||||
|
char *pt_str;
|
||||||
|
|
||||||
|
if (codec->payload_type > 76)
|
||||||
|
pt_str = "DYNAMIC";
|
||||||
|
else if (codec->payload_type > 72)
|
||||||
|
pt_str = "RESERVED <!>";
|
||||||
|
else if (codec->payload_type != PTYPE_UNDEFINED)
|
||||||
|
pt_str = codec->subtype_name;
|
||||||
|
else
|
||||||
|
pt_str = "INVALID <!>";
|
||||||
|
|
||||||
|
snprintf(str, sizeof(str), "(pt:%i=%s, audio:%s subt=%s, rate=%u, ch=%i, t=%u/%u)", codec->payload_type, pt_str,
|
||||||
|
codec->audio_name, codec->subtype_name, codec->rate, codec->channels, codec->frame_duration_num,
|
||||||
|
codec->frame_duration_den);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Dump a summary of all negotiated codecs to debug log
|
||||||
|
* \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;
|
||||||
|
|
||||||
|
rtp = &conn->end;
|
||||||
|
endp = conn->conn->endp;
|
||||||
|
|
||||||
|
if (rtp->codecs_assigned == 0) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x conn:%s no codecs available\n", ENDPOINT_NUMBER(endp),
|
||||||
|
mgcp_conn_dump(conn->conn));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store parsed codec information */
|
||||||
|
for (i = 0; i < rtp->codecs_assigned; i++) {
|
||||||
|
codec = &rtp->codecs[i];
|
||||||
|
|
||||||
|
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x conn:%s codecs[%u]:%s", ENDPOINT_NUMBER(endp),
|
||||||
|
mgcp_conn_dump(conn->conn), i, dump_codec(codec));
|
||||||
|
|
||||||
|
if (codec == rtp->codec)
|
||||||
|
LOGPC(DLMGCP, LOGL_DEBUG, " [selected]");
|
||||||
|
|
||||||
|
LOGPC(DLMGCP, LOGL_DEBUG, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initalize or reset codec information with default data. */
|
||||||
|
void codec_init(struct mgcp_rtp_codec *codec)
|
||||||
|
{
|
||||||
|
if (codec->subtype_name)
|
||||||
|
talloc_free(codec->subtype_name);
|
||||||
|
if (codec->audio_name)
|
||||||
|
talloc_free(codec->audio_name);
|
||||||
|
memset(codec, 0, sizeof(*codec));
|
||||||
|
codec->payload_type = -1;
|
||||||
|
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
||||||
|
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
||||||
|
codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
||||||
|
codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Initalize or reset codec information with default data.
|
||||||
|
* \param[out] conn related rtp-connection. */
|
||||||
|
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
|
||||||
|
{
|
||||||
|
memset(conn->end.codecs, 0, sizeof(conn->end.codecs));
|
||||||
|
conn->end.codecs_assigned = 0;
|
||||||
|
conn->end.codec = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set members of struct mgcp_rtp_codec, extrapolate in missing information */
|
||||||
|
static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
|
||||||
|
int payload_type, const char *audio_name, unsigned int pt_offset)
|
||||||
|
{
|
||||||
|
int rate;
|
||||||
|
int channels;
|
||||||
|
char audio_codec[64];
|
||||||
|
|
||||||
|
/* Initalize the codec struct with some default data to begin with */
|
||||||
|
codec_init(codec);
|
||||||
|
|
||||||
|
if (payload_type != PTYPE_UNDEFINED) {
|
||||||
|
/* Make sure we do not get any reserved or undefined type numbers */
|
||||||
|
/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
|
||||||
|
if (payload_type == 1 || payload_type == 2 || payload_type == 19)
|
||||||
|
goto error;
|
||||||
|
if (payload_type >= 72 && payload_type <= 76)
|
||||||
|
goto error;
|
||||||
|
if (payload_type >= 127)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
codec->payload_type = payload_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When no audio name is given, we are forced to use the payload
|
||||||
|
* type to generate the audio name. This is only possible for
|
||||||
|
* non dynamic payload types, which are statically defined */
|
||||||
|
if (!audio_name) {
|
||||||
|
switch (payload_type) {
|
||||||
|
case 0:
|
||||||
|
audio_name = talloc_strdup(ctx, "PCMU/8000/1");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
audio_name = talloc_strdup(ctx, "GSM/8000/1");
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
audio_name = talloc_strdup(ctx, "PCMA/8000/1");
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
audio_name = talloc_strdup(ctx, "G729/8000/1");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* The given payload type is not known to us, or it
|
||||||
|
* it is a dynamic payload type for which we do not
|
||||||
|
* know the audio name. We must give up here */
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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(audio_name) > sizeof(audio_codec))
|
||||||
|
goto error;
|
||||||
|
channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
||||||
|
rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
||||||
|
if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* Note: We only accept configurations with one audio channel! */
|
||||||
|
if (channels != 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(audio_codec, "G729")) {
|
||||||
|
codec->frame_duration_num = 10;
|
||||||
|
codec->frame_duration_den = 1000;
|
||||||
|
} else {
|
||||||
|
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
||||||
|
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Derive the payload type if it is unknown */
|
||||||
|
if (codec->payload_type == PTYPE_UNDEFINED) {
|
||||||
|
|
||||||
|
/* For the known codecs from the static range we restore
|
||||||
|
* the IANA or 3GPP assigned payload type number */
|
||||||
|
if (codec->rate == 8000 && codec->channels == 1) {
|
||||||
|
/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
|
||||||
|
if (!strcmp(codec->subtype_name, "GSM"))
|
||||||
|
codec->payload_type = 3;
|
||||||
|
else if (!strcmp(codec->subtype_name, "PCMA"))
|
||||||
|
codec->payload_type = 8;
|
||||||
|
else if (!strcmp(codec->subtype_name, "PCMU"))
|
||||||
|
codec->payload_type = 0;
|
||||||
|
else if (!strcmp(codec->subtype_name, "G729"))
|
||||||
|
codec->payload_type = 18;
|
||||||
|
|
||||||
|
/* See also: 3GPP TS 48.103, chapter 5.4.2.2 RTP Payload
|
||||||
|
* Note: These are not fixed payload types as the IANA
|
||||||
|
* defined once, they still remain dymanic payload
|
||||||
|
* types, but with a payload type number preference. */
|
||||||
|
else if (!strcmp(codec->subtype_name, "GSM-EFR"))
|
||||||
|
codec->payload_type = 110;
|
||||||
|
else if (!strcmp(codec->subtype_name, "GSM-HR-08"))
|
||||||
|
codec->payload_type = 111;
|
||||||
|
else if (!strcmp(codec->subtype_name, "AMR"))
|
||||||
|
codec->payload_type = 112;
|
||||||
|
else if (!strcmp(codec->subtype_name, "AMR-WB"))
|
||||||
|
codec->payload_type = 113;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we could not determine a payload type we assume that
|
||||||
|
* we are dealing with a codec from the dynamic range. We
|
||||||
|
* choose a fixed identifier from 96-109. (Note: normally,
|
||||||
|
* the dynamic payload type rante is from 96-127, but from
|
||||||
|
* 110 onwards 3gpp defines prefered codec types, which are
|
||||||
|
* also fixed, see above) */
|
||||||
|
if (codec->payload_type < 0) {
|
||||||
|
codec->payload_type = 96 + pt_offset;
|
||||||
|
if (codec->payload_type > 109)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
/* Make sure we leave a clean codec entry on error. */
|
||||||
|
codec_init(codec);
|
||||||
|
memset(codec, 0, sizeof(*codec));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Add codec configuration depending on payload type and/or codec name. This
|
||||||
|
* function uses the input parameters to extrapolate the full codec information.
|
||||||
|
* \param[out] codec configuration (caller provided memory).
|
||||||
|
* \param[out] conn related rtp-connection.
|
||||||
|
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
|
||||||
|
* \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
|
||||||
|
* \returns 0 on success, -EINVAL on failure. */
|
||||||
|
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* The amount of codecs we can store is limited, make sure we do not
|
||||||
|
* overrun this limit. */
|
||||||
|
if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
rc = codec_set(conn->conn, &conn->end.codecs[conn->end.codecs_assigned], payload_type, audio_name,
|
||||||
|
conn->end.codecs_assigned);
|
||||||
|
if (rc != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
conn->end.codecs_assigned++;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
char codec_name[64];
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* We now extract the codec_name (letters before the /, e.g. "GSM"
|
||||||
|
* from the audio name that is stored in the trunk configuration.
|
||||||
|
* We do not compare to the full audio_name because we expect that
|
||||||
|
* "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the
|
||||||
|
* audio name of the codec is set to "GSM" */
|
||||||
|
if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Finally we check if the subtype_name we have generated from the
|
||||||
|
* audio_name in the trunc struct patches the codec_name of the
|
||||||
|
* given codec */
|
||||||
|
if (strcasecmp(codec_name, codec->subtype_name) == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* FIXME: It is questinable that the method to pick a compatible
|
||||||
|
* codec can work properly. Since this useses tcfg->audio_name, as
|
||||||
|
* a reference, which is set to "AMR/8000" permanently.
|
||||||
|
* tcfg->audio_name must be updated by the first connection that
|
||||||
|
* has been made on an endpoint, so that the second connection
|
||||||
|
* can make a meaningful decision here */
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! 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->tcfg->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compare two codecs, all parameters must match up, except for the payload type
|
||||||
|
* number. */
|
||||||
|
static bool codecs_cmp(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
|
||||||
|
{
|
||||||
|
if (codec_a->rate != codec_b->rate)
|
||||||
|
return false;
|
||||||
|
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;
|
||||||
|
if (strcmp(codec_a->audio_name, codec_b->audio_name))
|
||||||
|
return false;
|
||||||
|
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! 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;
|
||||||
|
|
||||||
|
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 (payload_type == rtp_src->codecs[i].payload_type) {
|
||||||
|
codec_src = &rtp_src->codecs[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!codec_src)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* 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_cmp(codec_src, &rtp_dst->codecs[i])) {
|
||||||
|
codec_dst = &rtp_dst->codecs[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!codec_dst)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return codec_dst->payload_type;
|
||||||
|
}
|
||||||
@@ -25,18 +25,31 @@
|
|||||||
#include <osmocom/mgcp/mgcp_internal.h>
|
#include <osmocom/mgcp/mgcp_internal.h>
|
||||||
#include <osmocom/mgcp/mgcp_common.h>
|
#include <osmocom/mgcp/mgcp_common.h>
|
||||||
#include <osmocom/mgcp/mgcp_endp.h>
|
#include <osmocom/mgcp/mgcp_endp.h>
|
||||||
|
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||||
|
#include <osmocom/mgcp/mgcp_codec.h>
|
||||||
#include <osmocom/gsm/gsm_utils.h>
|
#include <osmocom/gsm/gsm_utils.h>
|
||||||
|
#include <osmocom/core/rate_ctr.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
|
||||||
|
.group_name_prefix = "conn_rtp",
|
||||||
|
.group_description = "rtp connection statistics",
|
||||||
|
.class_id = 1,
|
||||||
|
.num_ctr = ARRAY_SIZE(mgcp_conn_rate_ctr_desc),
|
||||||
|
.ctr_desc = mgcp_conn_rate_ctr_desc
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Allocate a new connection identifier. According to RFC3435, they must
|
/* Allocate a new connection identifier. According to RFC3435, they must
|
||||||
* be unique only within the scope of the endpoint. (Caller must provide
|
* be unique only within the scope of the endpoint. (Caller must provide
|
||||||
* memory for id) */
|
* memory for id) */
|
||||||
static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
|
static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
|
||||||
{
|
{
|
||||||
|
#define MGCP_CONN_ID_GEN_LEN 8
|
||||||
int i;
|
int i;
|
||||||
int k;
|
int k;
|
||||||
int rc;
|
int rc;
|
||||||
uint8_t id_bin[16];
|
uint8_t id_bin[MGCP_CONN_ID_GEN_LEN / 2];
|
||||||
char *id_hex;
|
char *id_hex;
|
||||||
|
|
||||||
/* Generate a connection id that is unique for the current endpoint.
|
/* Generate a connection id that is unique for the current endpoint.
|
||||||
@@ -56,7 +69,7 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
|
|||||||
/* ensure that the generated conn_id is unique
|
/* ensure that the generated conn_id is unique
|
||||||
* for this endpoint */
|
* for this endpoint */
|
||||||
if (!mgcp_conn_get_rtp(endp, id_hex)) {
|
if (!mgcp_conn_get_rtp(endp, id_hex)) {
|
||||||
osmo_strlcpy(id, id_hex, MGCP_CONN_ID_LENGTH);
|
osmo_strlcpy(id, id_hex, MGCP_CONN_ID_MAXLEN);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,26 +80,14 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset codec state and free memory */
|
|
||||||
static void mgcp_rtp_codec_init(struct mgcp_rtp_codec *codec)
|
|
||||||
{
|
|
||||||
codec->payload_type = -1;
|
|
||||||
codec->subtype_name = NULL;
|
|
||||||
codec->audio_name = NULL;
|
|
||||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
|
||||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
|
||||||
codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
|
||||||
codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
|
||||||
|
|
||||||
/* see also mgcp_sdp.c, mgcp_set_audio_info() */
|
|
||||||
talloc_free(codec->subtype_name);
|
|
||||||
talloc_free(codec->audio_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize rtp connection struct with default values */
|
/* Initialize rtp connection struct with default values */
|
||||||
static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
|
static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
|
||||||
{
|
{
|
||||||
struct mgcp_rtp_end *end = &conn_rtp->end;
|
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 unsigned int rate_ctr_index = 0;
|
||||||
|
|
||||||
conn_rtp->type = MGCP_RTP_DEFAULT;
|
conn_rtp->type = MGCP_RTP_DEFAULT;
|
||||||
conn_rtp->osmux.allocated_cid = -1;
|
conn_rtp->osmux.allocated_cid = -1;
|
||||||
@@ -96,7 +97,6 @@ static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn
|
|||||||
|
|
||||||
end->rtp.fd = -1;
|
end->rtp.fd = -1;
|
||||||
end->rtcp.fd = -1;
|
end->rtcp.fd = -1;
|
||||||
memset(&end->stats, 0, sizeof(end->stats));
|
|
||||||
end->rtp_port = end->rtcp_port = 0;
|
end->rtp_port = end->rtcp_port = 0;
|
||||||
talloc_free(end->fmtp_extra);
|
talloc_free(end->fmtp_extra);
|
||||||
end->fmtp_extra = NULL;
|
end->fmtp_extra = NULL;
|
||||||
@@ -105,9 +105,15 @@ static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn
|
|||||||
end->frames_per_packet = 0; /* unknown */
|
end->frames_per_packet = 0; /* unknown */
|
||||||
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
|
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
|
||||||
end->output_enabled = 0;
|
end->output_enabled = 0;
|
||||||
|
end->maximum_packet_time = -1;
|
||||||
|
|
||||||
mgcp_rtp_codec_init(&end->codec);
|
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index);
|
||||||
mgcp_rtp_codec_init(&end->alt_codec);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cleanup rtp connection struct */
|
/* Cleanup rtp connection struct */
|
||||||
@@ -116,6 +122,7 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
|
|||||||
osmux_disable_conn(conn_rtp);
|
osmux_disable_conn(conn_rtp);
|
||||||
osmux_release_cid(conn_rtp);
|
osmux_release_cid(conn_rtp);
|
||||||
mgcp_free_rtp_port(&conn_rtp->end);
|
mgcp_free_rtp_port(&conn_rtp->end);
|
||||||
|
rate_ctr_group_free(conn_rtp->rate_ctr_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! allocate a new connection list entry.
|
/*! allocate a new connection list entry.
|
||||||
@@ -172,9 +179,26 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
|||||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id)
|
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id)
|
||||||
{
|
{
|
||||||
struct mgcp_conn *conn;
|
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) {
|
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||||
if (strncmp(conn->id, id, sizeof(conn->id)) == 0)
|
/* 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 conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,6 +224,27 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
aggregate_rtp_conn_stats(struct mgcp_trunk_config *trunk, struct mgcp_conn_rtp *conn_rtp)
|
||||||
|
{
|
||||||
|
struct rate_ctr_group *all_stats = trunk->all_rtp_conn_stats;
|
||||||
|
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (all_stats == NULL || conn_stats == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Compared to per-connection RTP statistics, aggregated RTP statistics
|
||||||
|
* contain one additional rate couter item (RTP_NUM_CONNECTIONS).
|
||||||
|
* All other counters in both counter groups correspond to each other. */
|
||||||
|
OSMO_ASSERT(conn_stats->desc->num_ctr + 1 == all_stats->desc->num_ctr);
|
||||||
|
|
||||||
|
for (i = 0; i < conn_stats->desc->num_ctr; i++)
|
||||||
|
rate_ctr_add(&all_stats->ctr[i], conn_stats->ctr[i].current);
|
||||||
|
|
||||||
|
rate_ctr_inc(&all_stats->ctr[RTP_NUM_CONNECTIONS]);
|
||||||
|
}
|
||||||
|
|
||||||
/*! free a connection by its ID.
|
/*! free a connection by its ID.
|
||||||
* \param[in] endp associated endpoint
|
* \param[in] endp associated endpoint
|
||||||
* \param[in] id identification number of the connection */
|
* \param[in] id identification number of the connection */
|
||||||
@@ -219,6 +264,7 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
|
|||||||
|
|
||||||
switch (conn->type) {
|
switch (conn->type) {
|
||||||
case MGCP_CONN_TYPE_RTP:
|
case MGCP_CONN_TYPE_RTP:
|
||||||
|
aggregate_rtp_conn_stats(endp->tcfg, &conn->u.rtp);
|
||||||
mgcp_rtp_conn_cleanup(&conn->u.rtp);
|
mgcp_rtp_conn_cleanup(&conn->u.rtp);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -219,8 +219,14 @@ static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
|
|||||||
if (!domain_to_check)
|
if (!domain_to_check)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (strcmp(domain_to_check+1, cfg->domain) != 0)
|
/* 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 '%s', expecting '%s'\n", mgcp, cfg->domain);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -240,7 +246,7 @@ static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
|
|||||||
|
|
||||||
/* Check if the domainname in the request is correct */
|
/* Check if the domainname in the request is correct */
|
||||||
if (check_domain_name(cfg, mgcp)) {
|
if (check_domain_name(cfg, mgcp)) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s'\n", mgcp);
|
*cause = -500;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,6 +326,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
|||||||
if (!pdata->endp) {
|
if (!pdata->endp) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"Unable to find Endpoint `%s'\n", elem);
|
"Unable to find Endpoint `%s'\n", elem);
|
||||||
|
OSMO_ASSERT(cause < 0);
|
||||||
return cause;
|
return cause;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -401,6 +408,11 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
|
|||||||
|
|
||||||
if (!endp)
|
if (!endp)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
/* Accept any CallID for "X-Osmo-IGN: C" */
|
||||||
|
if (endp->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (!callid)
|
if (!callid)
|
||||||
return -1;
|
return -1;
|
||||||
if (!endp->callid)
|
if (!endp->callid)
|
||||||
@@ -408,7 +420,7 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
|
|||||||
|
|
||||||
if (strcmp(endp->callid, callid) != 0) {
|
if (strcmp(endp->callid, callid) != 0) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"endpoint:0x%x CallIDs does not match '%s' != '%s'\n",
|
"endpoint:0x%x CallIDs mismatch: '%s' != '%s'\n",
|
||||||
ENDPOINT_NUMBER(endp), endp->callid, callid);
|
ENDPOINT_NUMBER(endp), endp->callid, callid);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -419,15 +431,19 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
|
|||||||
/*! Check if the specified connection id seems plausible.
|
/*! Check if the specified connection id seems plausible.
|
||||||
* \param[in] endp pointer to endpoint
|
* \param[in] endp pointer to endpoint
|
||||||
* \param{in] connection id to verify
|
* \param{in] connection id to verify
|
||||||
* \returns 1 when connection id seems plausible, 0 on error */
|
* \returns 0 when connection id is valid and exists, an RFC3435 error code on error.
|
||||||
|
*/
|
||||||
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
|
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
|
||||||
{
|
{
|
||||||
|
/* For invalid conn_ids, return 510 "The transaction could not be executed, because some
|
||||||
|
* unspecified protocol error was detected." */
|
||||||
|
|
||||||
/* Check for null identifiers */
|
/* Check for null identifiers */
|
||||||
if (!conn_id) {
|
if (!conn_id) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"endpoint:0x%x invalid ConnectionIdentifier (missing)\n",
|
"endpoint:0x%x invalid ConnectionIdentifier (missing)\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
return -1;
|
return 510;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for empty connection identifiers */
|
/* Check for empty connection identifiers */
|
||||||
@@ -435,15 +451,15 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
|
|||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"endpoint:0x%x invalid ConnectionIdentifier (empty)\n",
|
"endpoint:0x%x invalid ConnectionIdentifier (empty)\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
return -1;
|
return 510;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for over long connection identifiers */
|
/* Check for over long connection identifiers */
|
||||||
if (strlen(conn_id) > MGCP_CONN_ID_LENGTH) {
|
if (strlen(conn_id) > (MGCP_CONN_ID_MAXLEN-1)) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"endpoint:0x%x invalid ConnectionIdentifier (too long) 0x%s\n",
|
"endpoint:0x%x invalid ConnectionIdentifier (too long: %zu > %d) 0x%s\n",
|
||||||
ENDPOINT_NUMBER(endp), conn_id);
|
ENDPOINT_NUMBER(endp), strlen(conn_id), MGCP_CONN_ID_MAXLEN-1, conn_id);
|
||||||
return -1;
|
return 510;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if connection exists */
|
/* Check if connection exists */
|
||||||
@@ -454,7 +470,9 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
|
|||||||
"endpoint:0x%x no connection found under ConnectionIdentifier 0x%s\n",
|
"endpoint:0x%x no connection found under ConnectionIdentifier 0x%s\n",
|
||||||
ENDPOINT_NUMBER(endp), conn_id);
|
ENDPOINT_NUMBER(endp), conn_id);
|
||||||
|
|
||||||
return -1;
|
/* When the conn_id was not found, return error code 515 "The transaction refers to an incorrect
|
||||||
|
* connection-id (may have been already deleted)." */
|
||||||
|
return 515;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Extract individual lines from MCGP message.
|
/*! Extract individual lines from MCGP message.
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include <osmocom/core/msgb.h>
|
#include <osmocom/core/msgb.h>
|
||||||
#include <osmocom/core/select.h>
|
#include <osmocom/core/select.h>
|
||||||
#include <osmocom/core/socket.h>
|
#include <osmocom/core/socket.h>
|
||||||
|
#include <osmocom/core/byteswap.h>
|
||||||
#include <osmocom/netif/rtp.h>
|
#include <osmocom/netif/rtp.h>
|
||||||
#include <osmocom/mgcp/mgcp.h>
|
#include <osmocom/mgcp/mgcp.h>
|
||||||
#include <osmocom/mgcp/mgcp_common.h>
|
#include <osmocom/mgcp/mgcp_common.h>
|
||||||
@@ -40,8 +41,10 @@
|
|||||||
#include <osmocom/mgcp/osmux.h>
|
#include <osmocom/mgcp/osmux.h>
|
||||||
#include <osmocom/mgcp/mgcp_conn.h>
|
#include <osmocom/mgcp/mgcp_conn.h>
|
||||||
#include <osmocom/mgcp/mgcp_endp.h>
|
#include <osmocom/mgcp/mgcp_endp.h>
|
||||||
|
#include <osmocom/mgcp/mgcp_codec.h>
|
||||||
#include <osmocom/mgcp/debug.h>
|
#include <osmocom/mgcp/debug.h>
|
||||||
|
|
||||||
|
|
||||||
#define RTP_SEQ_MOD (1 << 16)
|
#define RTP_SEQ_MOD (1 << 16)
|
||||||
#define RTP_MAX_DROPOUT 3000
|
#define RTP_MAX_DROPOUT 3000
|
||||||
#define RTP_MAX_MISORDER 100
|
#define RTP_MAX_MISORDER 100
|
||||||
@@ -222,7 +225,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
|
|||||||
|
|
||||||
if (seq == sstate->last_seq) {
|
if (seq == sstate->last_seq) {
|
||||||
if (timestamp != sstate->last_timestamp) {
|
if (timestamp != sstate->last_timestamp) {
|
||||||
sstate->err_ts_counter += 1;
|
rate_ctr_inc(sstate->err_ts_ctr);
|
||||||
LOGP(DRTP, LOGL_ERROR,
|
LOGP(DRTP, LOGL_ERROR,
|
||||||
"The %s timestamp delta is != 0 but the sequence "
|
"The %s timestamp delta is != 0 but the sequence "
|
||||||
"number %d is the same, "
|
"number %d is the same, "
|
||||||
@@ -272,7 +275,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
|
|||||||
ts_alignment_error(sstate, state->packet_duration, timestamp);
|
ts_alignment_error(sstate, state->packet_duration, timestamp);
|
||||||
|
|
||||||
if (timestamp_error) {
|
if (timestamp_error) {
|
||||||
sstate->err_ts_counter += 1;
|
rate_ctr_inc(sstate->err_ts_ctr);
|
||||||
LOGP(DRTP, LOGL_NOTICE,
|
LOGP(DRTP, LOGL_NOTICE,
|
||||||
"The %s timestamp has an alignment error of %d "
|
"The %s timestamp has an alignment error of %d "
|
||||||
"on 0x%x SSRC: %u "
|
"on 0x%x SSRC: %u "
|
||||||
@@ -310,7 +313,7 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
|
|||||||
ENDPOINT_NUMBER(endp), tsdelta,
|
ENDPOINT_NUMBER(endp), tsdelta,
|
||||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||||
} else {
|
} else {
|
||||||
tsdelta = rtp_end->codec.rate * 20 / 1000;
|
tsdelta = rtp_end->codec->rate * 20 / 1000;
|
||||||
LOGP(DRTP, LOGL_NOTICE,
|
LOGP(DRTP, LOGL_NOTICE,
|
||||||
"Fixed packet duration and last timestamp delta "
|
"Fixed packet duration and last timestamp delta "
|
||||||
"are not available on 0x%x, "
|
"are not available on 0x%x, "
|
||||||
@@ -399,12 +402,12 @@ int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
|
|||||||
|
|
||||||
/*! dummy callback to disable transcoding (see also cfg->setup_rtp_processing_cb).
|
/*! dummy callback to disable transcoding (see also cfg->setup_rtp_processing_cb).
|
||||||
* \param[in] associated endpoint
|
* \param[in] associated endpoint
|
||||||
* \param[in] destination RTP end
|
* \param[in] destination RTP connnection
|
||||||
* \param[in] source RTP end
|
* \param[in] source RTP connection
|
||||||
* \returns ignores input parameters, return always 0 */
|
* \returns ignores input parameters, return always 0 */
|
||||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||||
struct mgcp_rtp_end *dst_end,
|
struct mgcp_conn_rtp *conn_dst,
|
||||||
struct mgcp_rtp_end *src_end)
|
struct mgcp_conn_rtp *conn_src)
|
||||||
{
|
{
|
||||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x transcoding disabled\n",
|
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x transcoding disabled\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
@@ -421,8 +424,8 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
|||||||
"endpoint:0x%x conn:%s using format defaults\n",
|
"endpoint:0x%x conn:%s using format defaults\n",
|
||||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
|
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
|
||||||
|
|
||||||
*payload_type = conn->end.codec.payload_type;
|
*payload_type = conn->end.codec->payload_type;
|
||||||
*audio_name = conn->end.codec.audio_name;
|
*audio_name = conn->end.codec->audio_name;
|
||||||
*fmtp_extra = conn->end.fmtp_extra;
|
*fmtp_extra = conn->end.fmtp_extra;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,6 +477,28 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
|
|||||||
state->stats.max_seq = seq;
|
state->stats.max_seq = seq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* There may be different payload type numbers negotiated for two connections.
|
||||||
|
* Patch the payload type of an RTP packet so that it uses the payload type
|
||||||
|
* that is valid for the destination connection (conn_dst) */
|
||||||
|
static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
|
||||||
|
struct mgcp_conn_rtp *conn_dst, char *data, int len)
|
||||||
|
{
|
||||||
|
struct rtp_hdr *rtp_hdr;
|
||||||
|
uint8_t pt_in;
|
||||||
|
int pt_out;
|
||||||
|
|
||||||
|
OSMO_ASSERT(len >= sizeof(struct rtp_hdr));
|
||||||
|
rtp_hdr = (struct rtp_hdr *)data;
|
||||||
|
|
||||||
|
pt_in = rtp_hdr->payload_type;
|
||||||
|
pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
|
||||||
|
if (pt_out < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
rtp_hdr->payload_type = (uint8_t) pt_out;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* The RFC 3550 Appendix A assumes there are multiple sources but
|
/* The RFC 3550 Appendix A assumes there are multiple sources but
|
||||||
* some of the supported endpoints (e.g. the nanoBTS) can only handle
|
* some of the supported endpoints (e.g. the nanoBTS) can only handle
|
||||||
* one source and this code will patch RTP header to appear as if there
|
* one source and this code will patch RTP header to appear as if there
|
||||||
@@ -490,7 +515,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
|||||||
uint16_t seq;
|
uint16_t seq;
|
||||||
uint32_t timestamp, ssrc;
|
uint32_t timestamp, ssrc;
|
||||||
struct rtp_hdr *rtp_hdr;
|
struct rtp_hdr *rtp_hdr;
|
||||||
int payload = rtp_end->codec.payload_type;
|
int payload = rtp_end->codec->payload_type;
|
||||||
|
|
||||||
if (len < sizeof(*rtp_hdr))
|
if (len < sizeof(*rtp_hdr))
|
||||||
return;
|
return;
|
||||||
@@ -498,7 +523,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
|||||||
rtp_hdr = (struct rtp_hdr *)data;
|
rtp_hdr = (struct rtp_hdr *)data;
|
||||||
seq = ntohs(rtp_hdr->sequence);
|
seq = ntohs(rtp_hdr->sequence);
|
||||||
timestamp = ntohl(rtp_hdr->timestamp);
|
timestamp = ntohl(rtp_hdr->timestamp);
|
||||||
arrival_time = get_current_ts(rtp_end->codec.rate);
|
arrival_time = get_current_ts(rtp_end->codec->rate);
|
||||||
ssrc = ntohl(rtp_hdr->ssrc);
|
ssrc = ntohl(rtp_hdr->ssrc);
|
||||||
transit = arrival_time - timestamp;
|
transit = arrival_time - timestamp;
|
||||||
|
|
||||||
@@ -511,7 +536,9 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
|||||||
state->in_stream.last_tsdelta = 0;
|
state->in_stream.last_tsdelta = 0;
|
||||||
state->packet_duration =
|
state->packet_duration =
|
||||||
mgcp_rtp_packet_duration(endp, rtp_end);
|
mgcp_rtp_packet_duration(endp, rtp_end);
|
||||||
state->out_stream = state->in_stream;
|
state->out_stream.last_seq = seq - 1;
|
||||||
|
state->out_stream.ssrc = state->patch.orig_ssrc = ssrc;
|
||||||
|
state->out_stream.last_tsdelta = 0;
|
||||||
state->out_stream.last_timestamp = timestamp;
|
state->out_stream.last_timestamp = timestamp;
|
||||||
state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */
|
state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */
|
||||||
LOGP(DRTP, LOGL_INFO,
|
LOGP(DRTP, LOGL_INFO,
|
||||||
@@ -522,7 +549,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
|||||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||||
if (state->packet_duration == 0) {
|
if (state->packet_duration == 0) {
|
||||||
state->packet_duration =
|
state->packet_duration =
|
||||||
rtp_end->codec.rate * 20 / 1000;
|
rtp_end->codec->rate * 20 / 1000;
|
||||||
LOGP(DRTP, LOGL_NOTICE,
|
LOGP(DRTP, LOGL_NOTICE,
|
||||||
"endpoint:0x%x fixed packet duration is not available, "
|
"endpoint:0x%x fixed packet duration is not available, "
|
||||||
"using fixed 20ms instead: %d from %s:%d\n",
|
"using fixed 20ms instead: %d from %s:%d\n",
|
||||||
@@ -663,6 +690,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
|||||||
struct mgcp_rtp_end *rtp_end;
|
struct mgcp_rtp_end *rtp_end;
|
||||||
struct mgcp_rtp_state *rtp_state;
|
struct mgcp_rtp_state *rtp_state;
|
||||||
char *dest_name;
|
char *dest_name;
|
||||||
|
int rc;
|
||||||
|
|
||||||
OSMO_ASSERT(conn_src);
|
OSMO_ASSERT(conn_src);
|
||||||
OSMO_ASSERT(conn_dst);
|
OSMO_ASSERT(conn_dst);
|
||||||
@@ -678,12 +706,26 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
LOGP(DRTP, LOGL_DEBUG,
|
LOGP(DRTP, LOGL_DEBUG,
|
||||||
"endpoint:0x%x loop:%d, mode:%d ",
|
"endpoint:0x%x loop:%d, mode:%d%s\n",
|
||||||
ENDPOINT_NUMBER(endp), tcfg->audio_loop, conn_src->conn->mode);
|
ENDPOINT_NUMBER(endp), tcfg->audio_loop, conn_src->conn->mode,
|
||||||
if (conn_src->conn->mode == MGCP_CONN_LOOPBACK)
|
conn_src->conn->mode == MGCP_CONN_LOOPBACK ? " (loopback)" : "");
|
||||||
LOGPC(DRTP, LOGL_DEBUG, "(loopback)\n");
|
|
||||||
else
|
/* FIXME: It is legal that the payload type on the egress connection is
|
||||||
LOGPC(DRTP, LOGL_DEBUG, "\n");
|
* different from the payload type that has been negotiated on the
|
||||||
|
* ingress connection. Essentially the codecs are the same so we can
|
||||||
|
* match them and patch the payload type. However, if we can not find
|
||||||
|
* the codec pendant (everything ist equal except the PT), we are of
|
||||||
|
* course unable to patch the payload type. A situation like this
|
||||||
|
* should not occur if transcoding is consequently avoided. Until
|
||||||
|
* we have transcoding support in osmo-mgw we can not resolve this. */
|
||||||
|
if (is_rtp) {
|
||||||
|
rc = mgcp_patch_pt(conn_src, conn_dst, buf, len);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DRTP, LOGL_ERROR,
|
||||||
|
"endpoint:0x%x can not patch PT because no suitable egress codec was found.\n",
|
||||||
|
ENDPOINT_NUMBER(endp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Note: In case of loopback configuration, both, the source and the
|
/* Note: In case of loopback configuration, both, the source and the
|
||||||
* destination will point to the same connection. */
|
* destination will point to the same connection. */
|
||||||
@@ -692,7 +734,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
|||||||
dest_name = conn_dst->conn->name;
|
dest_name = conn_dst->conn->name;
|
||||||
|
|
||||||
if (!rtp_end->output_enabled) {
|
if (!rtp_end->output_enabled) {
|
||||||
rtp_end->stats.dropped_packets += 1;
|
rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_DROPPED_PACKETS_CTR]);
|
||||||
LOGP(DRTP, LOGL_DEBUG,
|
LOGP(DRTP, LOGL_DEBUG,
|
||||||
"endpoint:0x%x output disabled, drop to %s %s "
|
"endpoint:0x%x output disabled, drop to %s %s "
|
||||||
"rtp_port:%u rtcp_port:%u\n",
|
"rtp_port:%u rtcp_port:%u\n",
|
||||||
@@ -733,11 +775,18 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
|||||||
* 'e400', or it will reject the RAB assignment. It seems to not harm other femto
|
* 'e400', or it will reject the RAB assignment. It seems to not harm other femto
|
||||||
* cells (as long as we patch only the first RTP payload in each stream).
|
* cells (as long as we patch only the first RTP payload in each stream).
|
||||||
*/
|
*/
|
||||||
if (!rtp_state->patched_first_rtp_payload) {
|
if (!rtp_state->patched_first_rtp_payload
|
||||||
|
&& conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
|
||||||
uint8_t *data = (uint8_t *) & buf[12];
|
uint8_t *data = (uint8_t *) & buf[12];
|
||||||
data[0] = 0xe4;
|
if (data[0] == 0xe0) {
|
||||||
data[1] = 0x00;
|
data[0] = 0xe4;
|
||||||
rtp_state->patched_first_rtp_payload = true;
|
data[1] = 0x00;
|
||||||
|
rtp_state->patched_first_rtp_payload = true;
|
||||||
|
LOGP(DRTP, LOGL_DEBUG,
|
||||||
|
"endpoint:0x%x Patching over first two bytes"
|
||||||
|
" to fake an IuUP Initialization Ack\n",
|
||||||
|
ENDPOINT_NUMBER(endp));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
len = mgcp_udp_send(rtp_end->rtp.fd,
|
len = mgcp_udp_send(rtp_end->rtp.fd,
|
||||||
@@ -747,8 +796,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
|||||||
if (len <= 0)
|
if (len <= 0)
|
||||||
return len;
|
return len;
|
||||||
|
|
||||||
conn_dst->end.stats.packets_tx += 1;
|
rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
|
||||||
conn_dst->end.stats.octets_tx += len;
|
rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
|
||||||
|
|
||||||
nbytes += len;
|
nbytes += len;
|
||||||
buflen = cont;
|
buflen = cont;
|
||||||
@@ -767,8 +816,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
|||||||
&rtp_end->addr,
|
&rtp_end->addr,
|
||||||
rtp_end->rtcp_port, buf, len);
|
rtp_end->rtcp_port, buf, len);
|
||||||
|
|
||||||
conn_dst->end.stats.packets_tx += 1;
|
rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
|
||||||
conn_dst->end.stats.octets_tx += len;
|
rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
@@ -824,10 +873,37 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn,
|
|||||||
struct mgcp_endpoint *endp;
|
struct mgcp_endpoint *endp;
|
||||||
endp = conn->conn->endp;
|
endp = conn->conn->endp;
|
||||||
|
|
||||||
|
if (conn->end.addr.s_addr == 0) {
|
||||||
|
switch (conn->conn->mode) {
|
||||||
|
case MGCP_CONN_LOOPBACK:
|
||||||
|
/* HACK: for IuUP, we want to reply with an IuUP Initialization ACK upon the first RTP
|
||||||
|
* message received. We currently hackishly accomplish that by putting the endpoint in
|
||||||
|
* loopback mode and patching over the looped back RTP message to make it look like an
|
||||||
|
* ack. We don't know the femto cell's IP address and port until the RAB Assignment
|
||||||
|
* Response is received, but the nano3G expects an IuUP Initialization Ack before it even
|
||||||
|
* sends the RAB Assignment Response. Hence, if the remote address is 0.0.0.0 and the
|
||||||
|
* MGCP port is in loopback mode, allow looping back the packet to any source. */
|
||||||
|
LOGP(DRTP, LOGL_ERROR,
|
||||||
|
"endpoint:0x%x In loopback mode and remote address not set:"
|
||||||
|
" allowing data from address: %s\n",
|
||||||
|
ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr));
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* Receiving early media before the endpoint is configured. Instead of logging
|
||||||
|
* this as an error that occurs on every call, keep it more low profile to not
|
||||||
|
* confuse humans with expected errors. */
|
||||||
|
LOGP(DRTP, LOGL_INFO,
|
||||||
|
"endpoint:0x%x I:%s Rx RTP from %s, but remote address not set:"
|
||||||
|
" dropping early media\n",
|
||||||
|
ENDPOINT_NUMBER(endp), conn->conn->id, inet_ntoa(addr->sin_addr));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Note: Check if the inbound RTP data comes from the same host to
|
/* Note: Check if the inbound RTP data comes from the same host to
|
||||||
* which we send our outgoing RTP traffic. */
|
* which we send our outgoing RTP traffic. */
|
||||||
if (memcmp(&addr->sin_addr, &conn->end.addr, sizeof(addr->sin_addr))
|
if (conn->end.addr.s_addr != addr->sin_addr.s_addr) {
|
||||||
!= 0) {
|
|
||||||
LOGP(DRTP, LOGL_ERROR,
|
LOGP(DRTP, LOGL_ERROR,
|
||||||
"endpoint:0x%x data from wrong address: %s, ",
|
"endpoint:0x%x data from wrong address: %s, ",
|
||||||
ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr));
|
ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr));
|
||||||
@@ -865,23 +941,79 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn)
|
|||||||
struct mgcp_endpoint *endp;
|
struct mgcp_endpoint *endp;
|
||||||
endp = conn->conn->endp;
|
endp = conn->conn->endp;
|
||||||
|
|
||||||
|
/* Note: it is legal to create a connection but never setting a port
|
||||||
|
* and IP-address for outgoing data. */
|
||||||
|
if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0 && conn->end.rtp_port == 0) {
|
||||||
|
LOGP(DRTP, LOGL_DEBUG,
|
||||||
|
"endpoint:0x%x destination IP-address and rtp port is (not yet) known (%s:%u)\n",
|
||||||
|
ENDPOINT_NUMBER(endp), inet_ntoa(conn->end.addr), conn->end.rtp_port);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0) {
|
if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0) {
|
||||||
LOGP(DRTP, LOGL_ERROR,
|
LOGP(DRTP, LOGL_ERROR,
|
||||||
"endpoint:0x%x destination IP-address is invalid\n",
|
"endpoint:0x%x destination IP-address is invalid (%s:%u)\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp), inet_ntoa(conn->end.addr), conn->end.rtp_port);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->end.rtp_port == 0) {
|
if (conn->end.rtp_port == 0) {
|
||||||
LOGP(DRTP, LOGL_ERROR,
|
LOGP(DRTP, LOGL_ERROR,
|
||||||
"endpoint:0x%x destination rtp port is invalid\n",
|
"endpoint:0x%x destination rtp port is invalid (%s:%u)\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp), inet_ntoa(conn->end.addr), conn->end.rtp_port);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Do some basic checks to make sure that the RTCP packets we are going to
|
||||||
|
* process are not complete garbage */
|
||||||
|
static int check_rtcp(char *buf, unsigned int buf_size)
|
||||||
|
{
|
||||||
|
struct rtcp_hdr *hdr;
|
||||||
|
unsigned int len;
|
||||||
|
uint8_t type;
|
||||||
|
|
||||||
|
/* RTPC packets that are just a header without data do not make
|
||||||
|
* any sense. */
|
||||||
|
if (buf_size < sizeof(struct rtcp_hdr))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Make sure that the length of the received packet does not exceed
|
||||||
|
* the available buffer size */
|
||||||
|
hdr = (struct rtcp_hdr *)buf;
|
||||||
|
len = (osmo_ntohs(hdr->length) + 1) * 4;
|
||||||
|
if (len > buf_size)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Make sure we accept only packets that have a proper packet type set
|
||||||
|
* See also: http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
|
||||||
|
type = hdr->type;
|
||||||
|
if ((type < 192 || type > 195) && (type < 200 || type > 213))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do some basic checks to make sure that the RTP packets we are going to
|
||||||
|
* process are not complete garbage */
|
||||||
|
static int check_rtp(char *buf, unsigned int buf_size)
|
||||||
|
{
|
||||||
|
/* RTP packets that are just a header without data do not make
|
||||||
|
* any sense. */
|
||||||
|
if (buf_size < sizeof(struct rtp_hdr))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* FIXME: Add more checks, the reason why we do not check more than
|
||||||
|
* the length is because we currently handle IUUP packets as RTP
|
||||||
|
* packets, so they must pass this check, if we weould be more
|
||||||
|
* strict here, we would possibly break 3G. (see also FIXME note
|
||||||
|
* below */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Receive RTP data from a specified source connection and dispatch it to a
|
/* Receive RTP data from a specified source connection and dispatch it to a
|
||||||
* destination connection. */
|
* destination connection. */
|
||||||
static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
|
static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
|
||||||
@@ -902,8 +1034,29 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
|
|||||||
rc = receive_from(endp, fd->fd, addr, buf, buf_size);
|
rc = receive_from(endp, fd->fd, addr, buf, buf_size);
|
||||||
if (rc <= 0)
|
if (rc <= 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
/* FIXME: The way how we detect the protocol looks odd. We should look
|
||||||
|
* into the packet header. Also we should introduce a packet type
|
||||||
|
* MGCP_PROTO_IUUP because currently we handle IUUP packets like RTP
|
||||||
|
* packets which is problematic. */
|
||||||
*proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
|
*proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
|
||||||
|
|
||||||
|
if (*proto == MGCP_PROTO_RTP) {
|
||||||
|
if (check_rtp(buf, rc) < 0) {
|
||||||
|
LOGP(DRTP, LOGL_ERROR,
|
||||||
|
"endpoint:0x%x invalid RTP packet received -- packet tossed\n",
|
||||||
|
ENDPOINT_NUMBER(endp));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else if (*proto == MGCP_PROTO_RTCP) {
|
||||||
|
if (check_rtcp(buf, rc) < 0) {
|
||||||
|
LOGP(DRTP, LOGL_ERROR,
|
||||||
|
"endpoint:0x%x invalid RTCP packet received -- packet tossed\n",
|
||||||
|
ENDPOINT_NUMBER(endp));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x ", ENDPOINT_NUMBER(endp));
|
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x ", ENDPOINT_NUMBER(endp));
|
||||||
LOGPC(DRTP, LOGL_DEBUG, "receiving from %s %s %d\n",
|
LOGPC(DRTP, LOGL_DEBUG, "receiving from %s %s %d\n",
|
||||||
conn->conn->name, inet_ntoa(addr->sin_addr),
|
conn->conn->name, inet_ntoa(addr->sin_addr),
|
||||||
@@ -928,8 +1081,8 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Increment RX statistics */
|
/* Increment RX statistics */
|
||||||
conn->end.stats.packets_rx += 1;
|
rate_ctr_inc(&conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
|
||||||
conn->end.stats.octets_rx += rc;
|
rate_ctr_add(&conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], rc);
|
||||||
|
|
||||||
/* Forward a copy of the RTP data to a debug ip/port */
|
/* Forward a copy of the RTP data to a debug ip/port */
|
||||||
forward_data(fd->fd, &conn->tap_in, buf, rc);
|
forward_data(fd->fd, &conn->tap_in, buf, rc);
|
||||||
@@ -1095,7 +1248,6 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
|||||||
|
|
||||||
/* Check if the connection is in loopback mode, if yes, just send the
|
/* Check if the connection is in loopback mode, if yes, just send the
|
||||||
* incoming data back to the origin */
|
* incoming data back to the origin */
|
||||||
|
|
||||||
if (conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
|
if (conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
|
||||||
/* When we are in loopback mode, we loop back all incoming
|
/* When we are in loopback mode, we loop back all incoming
|
||||||
* packets back to their origin. We will use the originating
|
* packets back to their origin. We will use the originating
|
||||||
|
|||||||
@@ -256,8 +256,8 @@ static void scheduled_tx_net_cb(struct msgb *msg, void *data)
|
|||||||
.sin_port = conn_net->end.rtp_port,
|
.sin_port = conn_net->end.rtp_port,
|
||||||
};
|
};
|
||||||
|
|
||||||
conn_bts->end.stats.octets_tx += msg->len;
|
rate_ctr_inc(&conn_bts->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
|
||||||
conn_bts->end.stats.packets_tx++;
|
rate_ctr_add(&conn_bts->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);
|
||||||
|
|
||||||
/* Send RTP data to NET */
|
/* Send RTP data to NET */
|
||||||
/* FIXME: Get rid of conn_bts and conn_net! */
|
/* FIXME: Get rid of conn_bts and conn_net! */
|
||||||
@@ -283,8 +283,8 @@ static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
|
|||||||
.sin_port = conn_bts->end.rtp_port,
|
.sin_port = conn_bts->end.rtp_port,
|
||||||
};
|
};
|
||||||
|
|
||||||
conn_net->end.stats.octets_tx += msg->len;
|
rate_ctr_inc(&conn_net->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
|
||||||
conn_net->end.stats.packets_tx++;
|
rate_ctr_add(&conn_net->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);
|
||||||
|
|
||||||
/* Send RTP data to BTS */
|
/* Send RTP data to BTS */
|
||||||
/* FIXME: Get rid of conn_bts and conn_net! */
|
/* FIXME: Get rid of conn_bts and conn_net! */
|
||||||
@@ -316,22 +316,69 @@ static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr)
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Updates endp osmux state and returns 0 if it can process messages, -1 otherwise */
|
||||||
|
static int endp_osmux_state_check(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||||
|
bool sending)
|
||||||
|
{
|
||||||
|
switch(conn->osmux.state) {
|
||||||
|
case OSMUX_STATE_ACTIVATING:
|
||||||
|
if (osmux_enable_conn(endp, conn, &conn->end.addr, htons(endp->cfg->osmux_port)) < 0) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"Could not enable osmux for conn:%s\n",
|
||||||
|
mgcp_conn_dump(conn->conn));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"Osmux CID %u for %s:%u is now enabled\n",
|
||||||
|
conn->osmux.cid, inet_ntoa(conn->end.addr),
|
||||||
|
endp->cfg->osmux_port);
|
||||||
|
return 0;
|
||||||
|
case OSMUX_STATE_ENABLED:
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"Osmux %s in conn %s without full negotiation, state %d\n",
|
||||||
|
sending ? "sent" : "received",
|
||||||
|
mgcp_conn_dump(conn->conn), conn->osmux.state);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int osmux_legacy_dummy_parse_cid(struct sockaddr_in *addr, struct msgb *msg,
|
||||||
|
uint8_t *osmux_cid)
|
||||||
|
{
|
||||||
|
if (msg->len < 1 + sizeof(osmux_cid)) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"Discarding truncated Osmux dummy load\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* extract the osmux CID from the dummy message */
|
||||||
|
memcpy(osmux_cid, &msg->data[1], sizeof(*osmux_cid));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#define osmux_chunk_length(msg, rem) (rem - msg->len);
|
#define osmux_chunk_length(msg, rem) (rem - msg->len);
|
||||||
|
|
||||||
int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
||||||
{
|
{
|
||||||
struct msgb *msg;
|
struct msgb *msg;
|
||||||
struct osmux_hdr *osmuxh;
|
struct osmux_hdr *osmuxh;
|
||||||
struct llist_head list;
|
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
struct mgcp_config *cfg = ofd->data;
|
struct mgcp_config *cfg = ofd->data;
|
||||||
uint32_t rem;
|
uint32_t rem;
|
||||||
struct mgcp_conn_rtp *conn_net = NULL;
|
struct mgcp_conn_rtp *conn_bts = NULL;
|
||||||
|
|
||||||
msg = osmux_recv(ofd, &addr);
|
msg = osmux_recv(ofd, &addr);
|
||||||
if (!msg)
|
if (!msg)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if (!cfg->osmux) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"bsc-nat wants to use Osmux but bsc did not request it\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* not any further processing dummy messages */
|
/* not any further processing dummy messages */
|
||||||
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
||||||
goto out;
|
goto out;
|
||||||
@@ -345,9 +392,9 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
|||||||
&addr.sin_addr, MGCP_DEST_NET);
|
&addr.sin_addr, MGCP_DEST_NET);
|
||||||
|
|
||||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||||
if (!conn_net)
|
if (!conn_bts)
|
||||||
goto out;
|
continue;
|
||||||
|
|
||||||
if (!endp) {
|
if (!endp) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
@@ -355,12 +402,12 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
|||||||
osmuxh->circuit_id);
|
osmuxh->circuit_id);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
if (endp_osmux_state_check(endp, conn_bts, false) == 0) {
|
||||||
conn_net->osmux.stats.chunks++;
|
conn_bts->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||||
|
conn_bts->osmux.stats.chunks++;
|
||||||
|
osmux_xfrm_output_sched(&conn_bts->osmux.out, osmuxh);
|
||||||
|
}
|
||||||
rem = msg->len;
|
rem = msg->len;
|
||||||
|
|
||||||
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
|
|
||||||
osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
|
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
msgb_free(msg);
|
msgb_free(msg);
|
||||||
@@ -369,54 +416,29 @@ out:
|
|||||||
|
|
||||||
/* This is called from the bsc-nat */
|
/* This is called from the bsc-nat */
|
||||||
static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
|
static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
|
||||||
struct msgb *msg)
|
struct msgb *msg, int endp_type)
|
||||||
{
|
{
|
||||||
struct mgcp_endpoint *endp;
|
struct mgcp_endpoint *endp;
|
||||||
uint8_t osmux_cid;
|
uint8_t osmux_cid;
|
||||||
struct mgcp_conn_rtp *conn_net = NULL;
|
struct mgcp_conn_rtp *conn = NULL;
|
||||||
|
|
||||||
if (msg->len < 1 + sizeof(osmux_cid)) {
|
if (osmux_legacy_dummy_parse_cid(addr, msg, &osmux_cid) < 0)
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Discarding truncated Osmux dummy load\n");
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n",
|
endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, endp_type);
|
||||||
inet_ntoa(addr->sin_addr));
|
|
||||||
|
|
||||||
if (!cfg->osmux) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"bsc wants to use Osmux but bsc-nat did not request it\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* extract the osmux CID from the dummy message */
|
|
||||||
memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid));
|
|
||||||
|
|
||||||
endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS);
|
|
||||||
if (!endp) {
|
if (!endp) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"Cannot find endpoint for Osmux CID %d\n", osmux_cid);
|
"Cannot find endpoint for Osmux CID %d\n", osmux_cid);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||||
if (!conn_net)
|
conn = mgcp_conn_get_rtp(endp, endp_type == MGCP_DEST_BTS ? CONN_ID_NET : CONN_ID_BTS);
|
||||||
|
if (!conn)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (conn_net->osmux.state == OSMUX_STATE_ENABLED)
|
endp_osmux_state_check(endp, conn, false);
|
||||||
goto out;
|
/* Only needed to punch hole in firewall, it can be dropped */
|
||||||
|
|
||||||
if (osmux_enable_conn(endp, conn_net, &addr->sin_addr, addr->sin_port) < 0 ) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Could not enable osmux in endpoint %d\n",
|
|
||||||
ENDPOINT_NUMBER(endp));
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n",
|
|
||||||
ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr),
|
|
||||||
ntohs(addr->sin_port));
|
|
||||||
out:
|
out:
|
||||||
msgb_free(msg);
|
msgb_free(msg);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -426,7 +448,6 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
|||||||
{
|
{
|
||||||
struct msgb *msg;
|
struct msgb *msg;
|
||||||
struct osmux_hdr *osmuxh;
|
struct osmux_hdr *osmuxh;
|
||||||
struct llist_head list;
|
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
struct mgcp_config *cfg = ofd->data;
|
struct mgcp_config *cfg = ofd->data;
|
||||||
uint32_t rem;
|
uint32_t rem;
|
||||||
@@ -436,9 +457,15 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
|||||||
if (!msg)
|
if (!msg)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if (!cfg->osmux) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"bsc wants to use Osmux but bsc-nat did not request it\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* not any further processing dummy messages */
|
/* not any further processing dummy messages */
|
||||||
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
||||||
return osmux_handle_dummy(cfg, &addr, msg);
|
return osmux_handle_dummy(cfg, &addr, msg, MGCP_DEST_BTS);
|
||||||
|
|
||||||
rem = msg->len;
|
rem = msg->len;
|
||||||
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
|
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
|
||||||
@@ -451,7 +478,7 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
|||||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||||
if (!conn_net)
|
if (!conn_net)
|
||||||
goto out;
|
continue;
|
||||||
|
|
||||||
if (!endp) {
|
if (!endp) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
@@ -459,12 +486,12 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
|||||||
osmuxh->circuit_id);
|
osmuxh->circuit_id);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
if (endp_osmux_state_check(endp, conn_net, false) == 0) {
|
||||||
conn_net->osmux.stats.chunks++;
|
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||||
|
conn_net->osmux.stats.chunks++;
|
||||||
|
osmux_xfrm_output_sched(&conn_net->osmux.out, osmuxh);
|
||||||
|
}
|
||||||
rem = msg->len;
|
rem = msg->len;
|
||||||
|
|
||||||
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
|
|
||||||
osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
|
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
msgb_free(msg);
|
msgb_free(msg);
|
||||||
@@ -519,25 +546,21 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
|||||||
* used to reconstruct the RTP flow from osmux. The RTP SSRC is
|
* used to reconstruct the RTP flow from osmux. The RTP SSRC is
|
||||||
* allocated based on the circuit ID (conn_net->osmux.cid), which is unique
|
* allocated based on the circuit ID (conn_net->osmux.cid), which is unique
|
||||||
* in the local scope to the BSC/BSC-NAT. We use it to divide the RTP
|
* in the local scope to the BSC/BSC-NAT. We use it to divide the RTP
|
||||||
* SSRC space (2^32) by the 256 possible circuit IDs, then randomly
|
* SSRC space (2^32) by the OSMUX_CID_MAX + 1 possible circuit IDs, then randomly
|
||||||
* select one value from that window. Thus, we have no chance to have
|
* select one value from that window. Thus, we have no chance to have
|
||||||
* overlapping RTP SSRC traveling to the BTSes behind the BSC,
|
* overlapping RTP SSRC traveling to the BTSes behind the BSC,
|
||||||
* similarly, for flows traveling to the MSC.
|
* similarly, for flows traveling to the MSC.
|
||||||
*/
|
*/
|
||||||
static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256;
|
static const uint32_t rtp_ssrc_winlen = UINT32_MAX / (OSMUX_CID_MAX + 1);
|
||||||
uint16_t osmux_dummy = endp->cfg->osmux_dummy;
|
uint16_t osmux_dummy = endp->cfg->osmux_dummy;
|
||||||
|
|
||||||
/* Check if osmux is enabled for the specified connection */
|
/* Check if osmux is enabled for the specified connection */
|
||||||
if (conn->osmux.state == OSMUX_STATE_DISABLED) {
|
if (conn->osmux.state != OSMUX_STATE_ACTIVATING) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "OSMUX not enabled for conn:%s\n",
|
LOGP(DLMGCP, LOGL_ERROR, "conn:%s didn't negotiate Osmux, state %d\n",
|
||||||
mgcp_conn_dump(conn->conn));
|
mgcp_conn_dump(conn->conn), conn->osmux.state);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
osmux_xfrm_output_init(&conn->osmux.out,
|
|
||||||
(conn->osmux.cid * rtp_ssrc_winlen) +
|
|
||||||
(random() % rtp_ssrc_winlen));
|
|
||||||
|
|
||||||
conn->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
|
conn->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
|
||||||
if (!conn->osmux.in) {
|
if (!conn->osmux.in) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle for conn:%s\n",
|
LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle for conn:%s\n",
|
||||||
@@ -550,12 +573,20 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osmux_xfrm_output_init(&conn->osmux.out,
|
||||||
|
(conn->osmux.cid * rtp_ssrc_winlen) +
|
||||||
|
(random() % rtp_ssrc_winlen));
|
||||||
|
|
||||||
switch (endp->cfg->role) {
|
switch (endp->cfg->role) {
|
||||||
case MGCP_BSC_NAT:
|
case MGCP_BSC_NAT:
|
||||||
conn->type = MGCP_OSMUX_BSC_NAT;
|
conn->type = MGCP_OSMUX_BSC_NAT;
|
||||||
|
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
|
||||||
|
scheduled_tx_net_cb, endp);
|
||||||
break;
|
break;
|
||||||
case MGCP_BSC:
|
case MGCP_BSC:
|
||||||
conn->type = MGCP_OSMUX_BSC;
|
conn->type = MGCP_OSMUX_BSC;
|
||||||
|
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
|
||||||
|
scheduled_tx_bts_cb, endp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -576,6 +607,11 @@ void osmux_disable_conn(struct mgcp_conn_rtp *conn)
|
|||||||
|
|
||||||
LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
|
LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
|
||||||
conn->conn->id, conn->osmux.cid);
|
conn->conn->id, conn->osmux.cid);
|
||||||
|
|
||||||
|
/* We are closing, we don't need pending RTP packets to be transmitted */
|
||||||
|
osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL);
|
||||||
|
osmux_xfrm_output_flush(&conn->osmux.out);
|
||||||
|
|
||||||
osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
|
osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
|
||||||
conn->osmux.state = OSMUX_STATE_DISABLED;
|
conn->osmux.state = OSMUX_STATE_DISABLED;
|
||||||
conn->osmux.cid = -1;
|
conn->osmux.cid = -1;
|
||||||
@@ -630,18 +666,9 @@ int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
|||||||
if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0)
|
if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (conn->osmux.state == OSMUX_STATE_ACTIVATING) {
|
if (endp_osmux_state_check(endp, conn, true) < 0)
|
||||||
if (osmux_enable_conn(endp, conn, &conn->end.addr,
|
return 0;
|
||||||
htons(endp->cfg->osmux_port)) < 0) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Could not activate osmux for conn:%s\n",
|
|
||||||
mgcp_conn_dump(conn->conn));
|
|
||||||
}
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Osmux CID %u for %s:%u is now enabled\n",
|
|
||||||
conn->osmux.cid, inet_ntoa(conn->end.addr),
|
|
||||||
endp->cfg->osmux_port);
|
|
||||||
}
|
|
||||||
LOGP(DLMGCP, LOGL_DEBUG,
|
LOGP(DLMGCP, LOGL_DEBUG,
|
||||||
"sending OSMUX dummy load to %s CID %u\n",
|
"sending OSMUX dummy load to %s CID %u\n",
|
||||||
inet_ntoa(conn->end.addr), conn->osmux.cid);
|
inet_ntoa(conn->end.addr), conn->osmux.cid);
|
||||||
@@ -650,8 +677,8 @@ int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
|||||||
htons(endp->cfg->osmux_port), buf, sizeof(buf));
|
htons(endp->cfg->osmux_port), buf, sizeof(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! bsc-nat allocates/releases the OSMUX cids (Circuit IDs). */
|
/* bsc-nat allocates/releases the Osmux circuit ID. +7 to round up to 8 bit boundary. */
|
||||||
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8];
|
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1 + 7) / 8];
|
||||||
|
|
||||||
/*! count the number of taken OSMUX cids.
|
/*! count the number of taken OSMUX cids.
|
||||||
* \returns number of OSMUX cids in use */
|
* \returns number of OSMUX cids in use */
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include <osmocom/core/msgb.h>
|
#include <osmocom/core/msgb.h>
|
||||||
#include <osmocom/core/talloc.h>
|
#include <osmocom/core/talloc.h>
|
||||||
#include <osmocom/core/select.h>
|
#include <osmocom/core/select.h>
|
||||||
|
#include <osmocom/core/stats.h>
|
||||||
|
|
||||||
#include <osmocom/mgcp/mgcp.h>
|
#include <osmocom/mgcp/mgcp.h>
|
||||||
#include <osmocom/mgcp/mgcp_common.h>
|
#include <osmocom/mgcp/mgcp_common.h>
|
||||||
@@ -40,6 +41,8 @@
|
|||||||
#include <osmocom/mgcp/mgcp_msg.h>
|
#include <osmocom/mgcp/mgcp_msg.h>
|
||||||
#include <osmocom/mgcp/mgcp_endp.h>
|
#include <osmocom/mgcp/mgcp_endp.h>
|
||||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||||
|
#include <osmocom/mgcp/mgcp_codec.h>
|
||||||
|
#include <osmocom/mgcp/mgcp_conn.h>
|
||||||
|
|
||||||
struct mgcp_request {
|
struct mgcp_request {
|
||||||
char *name;
|
char *name;
|
||||||
@@ -50,6 +53,84 @@ struct mgcp_request {
|
|||||||
#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
|
#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
|
||||||
{ .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
|
{ .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
|
||||||
|
|
||||||
|
static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
|
||||||
|
[MGCP_CRCX_SUCCESS] = {"crcx:success", "CRCX command processed successfully."},
|
||||||
|
[MGCP_CRCX_FAIL_BAD_ACTION] = {"crcx:bad_action", "bad action in CRCX command."},
|
||||||
|
[MGCP_CRCX_FAIL_UNHANDLED_PARAM] = {"crcx:unhandled_param", "unhandled parameter in CRCX command."},
|
||||||
|
[MGCP_CRCX_FAIL_MISSING_CALLID] = {"crcx:missing_callid", "missing CallId in CRCX command."},
|
||||||
|
[MGCP_CRCX_FAIL_INVALID_MODE] = {"crcx:invalid_mode", "invalid connection mode in CRCX command."},
|
||||||
|
[MGCP_CRCX_FAIL_LIMIT_EXCEEDED] = {"crcx:limit_exceeded", "limit of concurrent connections was reached."},
|
||||||
|
[MGCP_CRCX_FAIL_UNKNOWN_CALLID] = {"crcx:unkown_callid", "unknown CallId in CRCX command."},
|
||||||
|
[MGCP_CRCX_FAIL_ALLOC_CONN] = {"crcx:alloc_conn_fail", "connection allocation failure."},
|
||||||
|
[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."},
|
||||||
|
[MGCP_CRCX_FAIL_BIND_PORT] = {"crcx:bind_port", "port bind failure."},
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
.num_ctr = ARRAY_SIZE(mgcp_crcx_ctr_desc),
|
||||||
|
.ctr_desc = mgcp_crcx_ctr_desc
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
|
||||||
|
[MGCP_MDCX_SUCCESS] = {"mdcx:success", "MDCX command processed successfully."},
|
||||||
|
[MGCP_MDCX_FAIL_WILDCARD] = {"mdcx:wildcard", "wildcard endpoint names in MDCX commands are unsupported."},
|
||||||
|
[MGCP_MDCX_FAIL_NO_CONN] = {"mdcx:no_conn", "endpoint specified in MDCX command has no active connections."},
|
||||||
|
[MGCP_MDCX_FAIL_INVALID_CALLID] = {"mdcx:callid", "invalid CallId specified in MDCX command."},
|
||||||
|
[MGCP_MDCX_FAIL_INVALID_CONNID] = {"mdcx:connid", "invalid connection ID specified in MDCX command."},
|
||||||
|
[MGCP_MDCX_FAIL_UNHANDLED_PARAM] = {"crcx:unhandled_param", "unhandled parameter in MDCX command."},
|
||||||
|
[MGCP_MDCX_FAIL_NO_CONNID] = {"mdcx:no_connid", "no connection ID specified in MDCX command."},
|
||||||
|
[MGCP_MDCX_FAIL_CONN_NOT_FOUND] = {"mdcx:conn_not_found", "connection specified in MDCX command does not exist."},
|
||||||
|
[MGCP_MDCX_FAIL_INVALID_MODE] = {"mdcx:invalid_mode", "invalid connection mode in MDCX command."},
|
||||||
|
[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_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."},
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
.num_ctr = ARRAY_SIZE(mgcp_mdcx_ctr_desc),
|
||||||
|
.ctr_desc = mgcp_mdcx_ctr_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."},
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
.num_ctr = ARRAY_SIZE(mgcp_dlcx_ctr_desc),
|
||||||
|
.ctr_desc = mgcp_dlcx_ctr_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,
|
||||||
|
.num_ctr = ARRAY_SIZE(all_rtp_conn_rate_ctr_desc),
|
||||||
|
.ctr_desc = all_rtp_conn_rate_ctr_desc
|
||||||
|
};
|
||||||
|
|
||||||
static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data);
|
static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data);
|
||||||
static struct msgb *handle_create_con(struct mgcp_parse_data *data);
|
static struct msgb *handle_create_con(struct mgcp_parse_data *data);
|
||||||
static struct msgb *handle_delete_con(struct mgcp_parse_data *data);
|
static struct msgb *handle_delete_con(struct mgcp_parse_data *data);
|
||||||
@@ -88,8 +169,7 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cfg->setup_rtp_processing_cb(endp, &conn_dst->end,
|
return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
|
||||||
&conn_src->end);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* array of function pointers for handling various
|
/* array of function pointers for handling various
|
||||||
@@ -356,13 +436,15 @@ static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
|
|||||||
|
|
||||||
/* Try to find a free port by attempting to bind on it. Also handle the
|
/* 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
|
* counter that points on the next free port. Since we have a pointer
|
||||||
* to the next free port, binding should work on the first attempt,
|
* to the next free port, binding should in work on the first attempt in
|
||||||
* nevertheless, try at least the next 200 ports before giving up */
|
* general. In case of failure the next port is tryed until the whole port
|
||||||
|
* range is tryed once. */
|
||||||
static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct mgcp_rtp_end *end;
|
struct mgcp_rtp_end *end;
|
||||||
struct mgcp_port_range *range;
|
struct mgcp_port_range *range;
|
||||||
|
unsigned int tries;
|
||||||
|
|
||||||
OSMO_ASSERT(conn);
|
OSMO_ASSERT(conn);
|
||||||
end = &conn->end;
|
end = &conn->end;
|
||||||
@@ -371,7 +453,8 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
|||||||
range = &endp->cfg->net_ports;
|
range = &endp->cfg->net_ports;
|
||||||
|
|
||||||
/* attempt to find a port */
|
/* attempt to find a port */
|
||||||
for (i = 0; i < 200; ++i) {
|
tries = (range->range_end - range->range_start) / 2;
|
||||||
|
for (i = 0; i < tries; ++i) {
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (range->last_port >= range->range_end)
|
if (range->last_port >= range->range_end)
|
||||||
@@ -387,8 +470,123 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"Allocating a RTP/RTCP port failed 200 times 0x%x.\n",
|
"Allocating a RTP/RTCP port failed %u times 0x%x.\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
tries, ENDPOINT_NUMBER(endp));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! 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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we got any result */
|
||||||
|
if (*ptr == ':')
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Check the LCO option. This function checks for multiple appearence 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)
|
||||||
|
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 (strcmp(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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,22 +598,36 @@ static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
|
|||||||
const char *options)
|
const char *options)
|
||||||
{
|
{
|
||||||
char *p_opt, *a_opt;
|
char *p_opt, *a_opt;
|
||||||
char codec[9];
|
char codec[17];
|
||||||
|
|
||||||
|
if (!options)
|
||||||
|
return 0;
|
||||||
|
if (strlen(options) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Make sure the encoding of the LCO is consistant 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;
|
||||||
|
}
|
||||||
|
|
||||||
talloc_free(lco->string);
|
talloc_free(lco->string);
|
||||||
talloc_free(lco->codec);
|
lco->string = talloc_strdup(ctx, options);
|
||||||
lco->codec = NULL;
|
|
||||||
lco->pkt_period_min = lco->pkt_period_max = 0;
|
|
||||||
lco->string = talloc_strdup(ctx, options ? options : "");
|
|
||||||
|
|
||||||
p_opt = strstr(lco->string, "p:");
|
p_opt = strstr(lco->string, "p:");
|
||||||
if (p_opt && sscanf(p_opt, "p:%d-%d",
|
if (p_opt && sscanf(p_opt, "p:%d-%d",
|
||||||
&lco->pkt_period_min, &lco->pkt_period_max) == 1)
|
&lco->pkt_period_min, &lco->pkt_period_max) == 1)
|
||||||
lco->pkt_period_max = lco->pkt_period_min;
|
lco->pkt_period_max = lco->pkt_period_min;
|
||||||
|
|
||||||
|
/* FIXME: LCO also supports the negotiation of more then one codec.
|
||||||
|
* (e.g. a:PCMU;G726-32) But this implementation only supports a single
|
||||||
|
* codec only. */
|
||||||
a_opt = strstr(lco->string, "a:");
|
a_opt = strstr(lco->string, "a:");
|
||||||
if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1)
|
if (a_opt && sscanf(a_opt, "a:%16[^,]", codec) == 1) {
|
||||||
|
talloc_free(lco->codec);
|
||||||
lco->codec = talloc_strdup(ctx, codec);
|
lco->codec = talloc_strdup(ctx, codec);
|
||||||
|
}
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_DEBUG,
|
LOGP(DLMGCP, LOGL_DEBUG,
|
||||||
"local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
|
"local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
|
||||||
@@ -456,15 +668,15 @@ uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
|
|||||||
/* Get the number of frames per channel and packet */
|
/* Get the number of frames per channel and packet */
|
||||||
if (rtp->frames_per_packet)
|
if (rtp->frames_per_packet)
|
||||||
f = rtp->frames_per_packet;
|
f = rtp->frames_per_packet;
|
||||||
else if (rtp->packet_duration_ms && rtp->codec.frame_duration_num) {
|
else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
|
||||||
int den = 1000 * rtp->codec.frame_duration_num;
|
int den = 1000 * rtp->codec->frame_duration_num;
|
||||||
f = (rtp->packet_duration_ms * rtp->codec.frame_duration_den +
|
f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
|
||||||
den / 2)
|
den / 2)
|
||||||
/ den;
|
/ den;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rtp->codec.rate * f * rtp->codec.frame_duration_num /
|
return rtp->codec->rate * f * rtp->codec->frame_duration_num /
|
||||||
rtp->codec.frame_duration_den;
|
rtp->codec->frame_duration_den;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
|
static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
|
||||||
@@ -480,13 +692,99 @@ static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
|
|||||||
return mgcp_parse_osmux_cid(line);
|
return mgcp_parse_osmux_cid(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Process codec information contained in CRCX/MDCX */
|
||||||
|
static int handle_codec_info(struct mgcp_conn_rtp *conn,
|
||||||
|
struct mgcp_parse_data *p, int have_sdp, bool crcx)
|
||||||
|
{
|
||||||
|
struct mgcp_endpoint *endp = p->endp;
|
||||||
|
int rc;
|
||||||
|
char *cmd;
|
||||||
|
|
||||||
|
if (crcx)
|
||||||
|
cmd = "CRCX";
|
||||||
|
else
|
||||||
|
cmd = "MDCX";
|
||||||
|
|
||||||
|
/* Collect codec information */
|
||||||
|
if (have_sdp) {
|
||||||
|
/* If we have SDP, we ignore the local connection options and
|
||||||
|
* use only the SDP information. */
|
||||||
|
mgcp_codec_reset_all(conn);
|
||||||
|
rc = mgcp_parse_sdp_data(endp, conn, p);
|
||||||
|
if (rc != 0) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"%s: endpoint:%x sdp not parseable\n", cmd,
|
||||||
|
ENDPOINT_NUMBER(endp));
|
||||||
|
|
||||||
|
/* See also RFC 3661: Protocol error */
|
||||||
|
return 510;
|
||||||
|
}
|
||||||
|
} else if (endp->local_options.codec) {
|
||||||
|
/* When no SDP is available, we use the codec information from
|
||||||
|
* the local connection options (if present) */
|
||||||
|
mgcp_codec_reset_all(conn);
|
||||||
|
rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec);
|
||||||
|
if (rc != 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure we always set a sane default codec */
|
||||||
|
if (conn->end.codecs_assigned == 0) {
|
||||||
|
/* When SDP and/or LCO did not supply any codec information,
|
||||||
|
* than it makes sense to pick a sane default: (payload-type 0,
|
||||||
|
* PCMU), see also: OS#2658 */
|
||||||
|
mgcp_codec_reset_all(conn);
|
||||||
|
rc = mgcp_codec_add(conn, 0, NULL);
|
||||||
|
if (rc != 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make codec decision */
|
||||||
|
if (mgcp_codec_decide(conn) != 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"%s: endpoint:0x%x codec negotiation failure\n", cmd,
|
||||||
|
ENDPOINT_NUMBER(endp));
|
||||||
|
|
||||||
|
/* See also RFC 3661: Codec negotiation failure */
|
||||||
|
return 534;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
|
||||||
|
{
|
||||||
|
char *saveptr = NULL;
|
||||||
|
|
||||||
|
if (strncmp(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 (!strcmp(token, "C"))
|
||||||
|
endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
|
||||||
|
else
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR, "endpoint 0x%x: received unknown X-Osmo-IGN item '%s'\n",
|
||||||
|
ENDPOINT_NUMBER(endp), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* CRCX command handler, processes the received command */
|
/* CRCX command handler, processes the received command */
|
||||||
static struct msgb *handle_create_con(struct mgcp_parse_data *p)
|
static struct msgb *handle_create_con(struct mgcp_parse_data *p)
|
||||||
{
|
{
|
||||||
struct mgcp_trunk_config *tcfg;
|
struct mgcp_trunk_config *tcfg = p->endp->tcfg;
|
||||||
struct mgcp_endpoint *endp = p->endp;
|
struct mgcp_endpoint *endp = p->endp;
|
||||||
|
struct rate_ctr_group *rate_ctrs = tcfg->mgcp_crcx_ctr_group;
|
||||||
int error_code = 400;
|
int error_code = 400;
|
||||||
|
|
||||||
const char *local_options = NULL;
|
const char *local_options = NULL;
|
||||||
const char *callid = NULL;
|
const char *callid = NULL;
|
||||||
const char *mode = NULL;
|
const char *mode = NULL;
|
||||||
@@ -515,18 +813,25 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
|
|||||||
/* It is illegal to send a connection identifier
|
/* It is illegal to send a connection identifier
|
||||||
* together with a CRCX, the MGW will assign the
|
* together with a CRCX, the MGW will assign the
|
||||||
* connection identifier by itself on CRCX */
|
* connection identifier by itself on CRCX */
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_BAD_ACTION]);
|
||||||
return create_err_response(NULL, 523, "CRCX", p->trans);
|
return create_err_response(NULL, 523, "CRCX", p->trans);
|
||||||
break;
|
break;
|
||||||
case 'M':
|
case 'M':
|
||||||
mode = (const char *)line + 3;
|
mode = (const char *)line + 3;
|
||||||
break;
|
break;
|
||||||
case 'X':
|
case 'X':
|
||||||
/* If osmoux is disabled, just skip setting it up */
|
if (strncmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
|
||||||
if (!p->endp->cfg->osmux)
|
/* If osmux is disabled, just skip setting it up */
|
||||||
break;
|
if (!p->endp->cfg->osmux)
|
||||||
if (strncmp("Osmux: ", line + 2, strlen("Osmux: ")) ==
|
break;
|
||||||
0)
|
|
||||||
osmux_cid = mgcp_osmux_setup(endp, line);
|
osmux_cid = mgcp_osmux_setup(endp, line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parse_x_osmo_ign(endp, line))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Ignore unknown X-headers */
|
||||||
break;
|
break;
|
||||||
case '\0':
|
case '\0':
|
||||||
have_sdp = 1;
|
have_sdp = 1;
|
||||||
@@ -535,19 +840,19 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
|
|||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
LOGP(DLMGCP, LOGL_NOTICE,
|
||||||
"CRCX: endpoint:%x unhandled option: '%c'/%d\n",
|
"CRCX: endpoint:%x unhandled option: '%c'/%d\n",
|
||||||
ENDPOINT_NUMBER(endp), *line, *line);
|
ENDPOINT_NUMBER(endp), *line, *line);
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_UNHANDLED_PARAM]);
|
||||||
return create_err_response(NULL, 539, "CRCX", p->trans);
|
return create_err_response(NULL, 539, "CRCX", p->trans);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mgcp_header_done:
|
mgcp_header_done:
|
||||||
tcfg = p->endp->tcfg;
|
|
||||||
|
|
||||||
/* Check parameters */
|
/* Check parameters */
|
||||||
if (!callid) {
|
if (!callid) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"CRCX: endpoint:%x insufficient parameters, missing callid\n",
|
"CRCX: endpoint:%x insufficient parameters, missing callid\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_MISSING_CALLID]);
|
||||||
return create_err_response(endp, 516, "CRCX", p->trans);
|
return create_err_response(endp, 516, "CRCX", p->trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,6 +860,7 @@ mgcp_header_done:
|
|||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"CRCX: endpoint:%x insufficient parameters, missing mode\n",
|
"CRCX: endpoint:%x insufficient parameters, missing mode\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_INVALID_MODE]);
|
||||||
return create_err_response(endp, 517, "CRCX", p->trans);
|
return create_err_response(endp, 517, "CRCX", p->trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,6 +877,7 @@ mgcp_header_done:
|
|||||||
} else {
|
} else {
|
||||||
/* There is no more room for a connection, leave
|
/* There is no more room for a connection, leave
|
||||||
* everything as it is and return with an error */
|
* everything as it is and return with an error */
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_LIMIT_EXCEEDED]);
|
||||||
return create_err_response(endp, 540, "CRCX", p->trans);
|
return create_err_response(endp, 540, "CRCX", p->trans);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -588,6 +895,7 @@ mgcp_header_done:
|
|||||||
else {
|
else {
|
||||||
/* This is not our call, leave everything as it is and
|
/* This is not our call, leave everything as it is and
|
||||||
* return with an error. */
|
* return with an error. */
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_UNKNOWN_CALLID]);
|
||||||
return create_err_response(endp, 400, "CRCX", p->trans);
|
return create_err_response(endp, 400, "CRCX", p->trans);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -597,23 +905,13 @@ mgcp_header_done:
|
|||||||
* connection ids) */
|
* connection ids) */
|
||||||
endp->callid = talloc_strdup(tcfg->endpoints, callid);
|
endp->callid = talloc_strdup(tcfg->endpoints, callid);
|
||||||
|
|
||||||
/* Extract audio codec information */
|
|
||||||
rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
|
|
||||||
local_options);
|
|
||||||
if (rc != 0) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"CRCX: endpoint:%x inavlid local connection options!\n",
|
|
||||||
ENDPOINT_NUMBER(endp));
|
|
||||||
error_code = rc;
|
|
||||||
goto error2;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(conn_name, sizeof(conn_name), "%s", callid);
|
snprintf(conn_name, sizeof(conn_name), "%s", callid);
|
||||||
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, conn_name);
|
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, conn_name);
|
||||||
if (!_conn) {
|
if (!_conn) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"CRCX: endpoint:0x%x unable to allocate RTP connection\n",
|
"CRCX: endpoint:0x%x unable to allocate RTP connection\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_ALLOC_CONN]);
|
||||||
goto error2;
|
goto error2;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -622,6 +920,7 @@ mgcp_header_done:
|
|||||||
|
|
||||||
if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
|
if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
|
||||||
error_code = 517;
|
error_code = 517;
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_INVALID_MODE]);
|
||||||
goto error2;
|
goto error2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -635,15 +934,33 @@ mgcp_header_done:
|
|||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"CRCX: endpoint:0x%x osmux only and no osmux offered\n",
|
"CRCX: endpoint:0x%x osmux only and no osmux offered\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_NO_OSMUX]);
|
||||||
|
goto error2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set local connection options, if present */
|
||||||
|
if (local_options) {
|
||||||
|
rc = set_local_cx_options(endp->tcfg->endpoints,
|
||||||
|
&endp->local_options, local_options);
|
||||||
|
if (rc != 0) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"CRCX: endpoint:%x inavlid local connection options!\n",
|
||||||
|
ENDPOINT_NUMBER(endp));
|
||||||
|
error_code = rc;
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS]);
|
||||||
|
goto error2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle codec information and decide for a suitable codec */
|
||||||
|
rc = handle_codec_info(conn, p, have_sdp, true);
|
||||||
|
mgcp_codec_summary(conn);
|
||||||
|
if (rc) {
|
||||||
|
error_code = rc;
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_CODEC_NEGOTIATION]);
|
||||||
goto error2;
|
goto error2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set up RTP media parameters */
|
|
||||||
if (have_sdp)
|
|
||||||
mgcp_parse_sdp_data(endp, conn, p);
|
|
||||||
else if (endp->local_options.codec)
|
|
||||||
mgcp_set_audio_info(p->cfg, &conn->end.codec,
|
|
||||||
PTYPE_UNDEFINED, endp->local_options.codec);
|
|
||||||
conn->end.fmtp_extra = talloc_strdup(tcfg->endpoints,
|
conn->end.fmtp_extra = talloc_strdup(tcfg->endpoints,
|
||||||
tcfg->audio_fmtp_extra);
|
tcfg->audio_fmtp_extra);
|
||||||
|
|
||||||
@@ -662,10 +979,12 @@ mgcp_header_done:
|
|||||||
"CRCX: endpoint:%x selected connection mode type requires an opposite end!\n",
|
"CRCX: endpoint:%x selected connection mode type requires an opposite end!\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
error_code = 527;
|
error_code = 527;
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC]);
|
||||||
goto error2;
|
goto error2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allocate_port(endp, conn) != 0) {
|
if (allocate_port(endp, conn) != 0) {
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_BIND_PORT]);
|
||||||
goto error2;
|
goto error2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,6 +992,7 @@ mgcp_header_done:
|
|||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"CRCX: endpoint:0x%x could not start RTP processing!\n",
|
"CRCX: endpoint:0x%x could not start RTP processing!\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_START_RTP]);
|
||||||
goto error2;
|
goto error2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -687,6 +1007,7 @@ mgcp_header_done:
|
|||||||
"CRCX: endpoint:0x%x CRCX rejected by policy\n",
|
"CRCX: endpoint:0x%x CRCX rejected by policy\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
mgcp_endp_release(endp);
|
mgcp_endp_release(endp);
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_REJECTED_BY_POLICY]);
|
||||||
return create_err_response(endp, 400, "CRCX", p->trans);
|
return create_err_response(endp, 400, "CRCX", p->trans);
|
||||||
break;
|
break;
|
||||||
case MGCP_POLICY_DEFER:
|
case MGCP_POLICY_DEFER:
|
||||||
@@ -714,19 +1035,26 @@ mgcp_header_done:
|
|||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
LOGP(DLMGCP, LOGL_NOTICE,
|
||||||
"CRCX: endpoint:0x%x connection successfully created\n",
|
"CRCX: endpoint:0x%x connection successfully created\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_SUCCESS]);
|
||||||
return create_response_with_sdp(endp, conn, "CRCX", p->trans, true);
|
return create_response_with_sdp(endp, conn, "CRCX", p->trans, true);
|
||||||
error2:
|
error2:
|
||||||
mgcp_endp_release(endp);
|
mgcp_endp_release(endp);
|
||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
LOGP(DLMGCP, LOGL_NOTICE,
|
||||||
"CRCX: endpoint:0x%x unable to create connection resource error\n",
|
"CRCX: endpoint:0x%x unable to create connection\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
return create_err_response(endp, error_code, "CRCX", p->trans);
|
return create_err_response(endp, error_code, "CRCX", p->trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* MDCX command handler, processes the received command */
|
/* MDCX command handler, processes the received command */
|
||||||
static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
||||||
{
|
{
|
||||||
|
struct mgcp_trunk_config *tcfg = p->endp->tcfg;
|
||||||
struct mgcp_endpoint *endp = p->endp;
|
struct mgcp_endpoint *endp = p->endp;
|
||||||
|
struct rate_ctr_group *rate_ctrs = tcfg->mgcp_mdcx_ctr_group;
|
||||||
int error_code = 500;
|
int error_code = 500;
|
||||||
int silent = 0;
|
int silent = 0;
|
||||||
int have_sdp = 0;
|
int have_sdp = 0;
|
||||||
@@ -744,6 +1072,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
|||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"MDCX: endpoint:0x%x wildcarded endpoint names not supported.\n",
|
"MDCX: endpoint:0x%x wildcarded endpoint names not supported.\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_WILDCARD]);
|
||||||
return create_err_response(endp, 507, "MDCX", p->trans);
|
return create_err_response(endp, 507, "MDCX", p->trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -751,6 +1080,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
|||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"MDCX: endpoint:0x%x endpoint is not holding a connection.\n",
|
"MDCX: endpoint:0x%x endpoint is not holding a connection.\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_NO_CONN]);
|
||||||
return create_err_response(endp, 400, "MDCX", p->trans);
|
return create_err_response(endp, 400, "MDCX", p->trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -761,14 +1091,15 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
|||||||
switch (line[0]) {
|
switch (line[0]) {
|
||||||
case 'C':
|
case 'C':
|
||||||
if (mgcp_verify_call_id(endp, line + 3) != 0) {
|
if (mgcp_verify_call_id(endp, line + 3) != 0) {
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_CALLID]);
|
||||||
error_code = 516;
|
error_code = 516;
|
||||||
goto error3;
|
goto error3;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'I':
|
case 'I':
|
||||||
conn_id = (const char *)line + 3;
|
conn_id = (const char *)line + 3;
|
||||||
if (mgcp_verify_ci(endp, conn_id) != 0) {
|
if ((error_code = mgcp_verify_ci(endp, conn_id))) {
|
||||||
error_code = 515;
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_CONNID]);
|
||||||
goto error3;
|
goto error3;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -789,6 +1120,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
|||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
LOGP(DLMGCP, LOGL_NOTICE,
|
||||||
"MDCX: endpoint:0x%x Unhandled MGCP option: '%c'/%d\n",
|
"MDCX: endpoint:0x%x Unhandled MGCP option: '%c'/%d\n",
|
||||||
ENDPOINT_NUMBER(endp), line[0], line[0]);
|
ENDPOINT_NUMBER(endp), line[0], line[0]);
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_UNHANDLED_PARAM]);
|
||||||
return create_err_response(NULL, 539, "MDCX", p->trans);
|
return create_err_response(NULL, 539, "MDCX", p->trans);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -799,38 +1131,47 @@ mgcp_header_done:
|
|||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"MDCX: endpoint:0x%x insufficient parameters, missing ci (connectionIdentifier)\n",
|
"MDCX: endpoint:0x%x insufficient parameters, missing ci (connectionIdentifier)\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_NO_CONNID]);
|
||||||
return create_err_response(endp, 515, "MDCX", p->trans);
|
return create_err_response(endp, 515, "MDCX", p->trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||||
if (!conn)
|
if (!conn) {
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_CONN_NOT_FOUND]);
|
||||||
return create_err_response(endp, 400, "MDCX", p->trans);
|
return create_err_response(endp, 400, "MDCX", p->trans);
|
||||||
|
}
|
||||||
|
|
||||||
if (mode) {
|
if (mode) {
|
||||||
if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
|
if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_MODE]);
|
||||||
error_code = 517;
|
error_code = 517;
|
||||||
goto error3;
|
goto error3;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
conn->conn->mode = conn->conn->mode_orig;
|
conn->conn->mode = conn->conn->mode_orig;
|
||||||
|
|
||||||
if (have_sdp)
|
/* Set local connection options, if present */
|
||||||
mgcp_parse_sdp_data(endp, conn, p);
|
if (local_options) {
|
||||||
|
rc = set_local_cx_options(endp->tcfg->endpoints,
|
||||||
|
&endp->local_options, local_options);
|
||||||
|
if (rc != 0) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
|
"MDCX: endpoint:%x invalid local connection options!\n",
|
||||||
|
ENDPOINT_NUMBER(endp));
|
||||||
|
error_code = rc;
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS]);
|
||||||
|
goto error3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
|
/* Handle codec information and decide for a suitable codec */
|
||||||
local_options);
|
rc = handle_codec_info(conn, p, have_sdp, false);
|
||||||
if (rc != 0) {
|
mgcp_codec_summary(conn);
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
if (rc) {
|
||||||
"MDCX: endpoint:%x inavlid local connection options!\n",
|
|
||||||
ENDPOINT_NUMBER(endp));
|
|
||||||
error_code = rc;
|
error_code = rc;
|
||||||
goto error3;
|
goto error3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!have_sdp && endp->local_options.codec)
|
|
||||||
mgcp_set_audio_info(p->cfg, &conn->end.codec,
|
|
||||||
PTYPE_UNDEFINED, endp->local_options.codec);
|
|
||||||
|
|
||||||
/* check connection mode setting */
|
/* check connection mode setting */
|
||||||
if (conn->conn->mode != MGCP_CONN_LOOPBACK
|
if (conn->conn->mode != MGCP_CONN_LOOPBACK
|
||||||
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
|
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
|
||||||
@@ -839,11 +1180,15 @@ mgcp_header_done:
|
|||||||
"MDCX: endpoint:%x selected connection mode type requires an opposite end!\n",
|
"MDCX: endpoint:%x selected connection mode type requires an opposite end!\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
error_code = 527;
|
error_code = 527;
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC]);
|
||||||
goto error3;
|
goto error3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setup_rtp_processing(endp, conn) != 0)
|
|
||||||
|
if (setup_rtp_processing(endp, conn) != 0) {
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_START_RTP]);
|
||||||
goto error3;
|
goto error3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* policy CB */
|
/* policy CB */
|
||||||
@@ -856,6 +1201,7 @@ mgcp_header_done:
|
|||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
LOGP(DLMGCP, LOGL_NOTICE,
|
||||||
"MDCX: endpoint:0x%x rejected by policy\n",
|
"MDCX: endpoint:0x%x rejected by policy\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_REJECTED_BY_POLICY]);
|
||||||
if (silent)
|
if (silent)
|
||||||
goto out_silent;
|
goto out_silent;
|
||||||
return create_err_response(endp, 400, "MDCX", p->trans);
|
return create_err_response(endp, 400, "MDCX", p->trans);
|
||||||
@@ -863,8 +1209,9 @@ mgcp_header_done:
|
|||||||
case MGCP_POLICY_DEFER:
|
case MGCP_POLICY_DEFER:
|
||||||
/* stop processing */
|
/* stop processing */
|
||||||
LOGP(DLMGCP, LOGL_DEBUG,
|
LOGP(DLMGCP, LOGL_DEBUG,
|
||||||
"MDCX: endpoint:0x%x defered by policy\n",
|
"MDCX: endpoint:0x%x deferred by policy\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_DEFERRED_BY_POLICY]);
|
||||||
return NULL;
|
return NULL;
|
||||||
break;
|
break;
|
||||||
case MGCP_POLICY_CONT:
|
case MGCP_POLICY_CONT:
|
||||||
@@ -889,6 +1236,7 @@ mgcp_header_done:
|
|||||||
&& endp->tcfg->keepalive_interval != MGCP_KEEPALIVE_NEVER)
|
&& endp->tcfg->keepalive_interval != MGCP_KEEPALIVE_NEVER)
|
||||||
send_dummy(endp, conn);
|
send_dummy(endp, conn);
|
||||||
|
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_SUCCESS]);
|
||||||
if (silent)
|
if (silent)
|
||||||
goto out_silent;
|
goto out_silent;
|
||||||
|
|
||||||
@@ -908,7 +1256,9 @@ out_silent:
|
|||||||
/* DLCX command handler, processes the received command */
|
/* DLCX command handler, processes the received command */
|
||||||
static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||||
{
|
{
|
||||||
|
struct mgcp_trunk_config *tcfg = p->endp->tcfg;
|
||||||
struct mgcp_endpoint *endp = p->endp;
|
struct mgcp_endpoint *endp = p->endp;
|
||||||
|
struct rate_ctr_group *rate_ctrs = tcfg->mgcp_dlcx_ctr_group;
|
||||||
int error_code = 400;
|
int error_code = 400;
|
||||||
int silent = 0;
|
int silent = 0;
|
||||||
char *line;
|
char *line;
|
||||||
@@ -925,6 +1275,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
|||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"DLCX: endpoint:0x%x wildcarded endpoint names not supported.\n",
|
"DLCX: endpoint:0x%x wildcarded endpoint names not supported.\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_WILDCARD]);
|
||||||
return create_err_response(endp, 507, "DLCX", p->trans);
|
return create_err_response(endp, 507, "DLCX", p->trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -932,6 +1283,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
|||||||
LOGP(DLMGCP, LOGL_ERROR,
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
"DLCX: endpoint:0x%x endpoint is not holding a connection.\n",
|
"DLCX: endpoint:0x%x endpoint is not holding a connection.\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_NO_CONN]);
|
||||||
return create_err_response(endp, 515, "DLCX", p->trans);
|
return create_err_response(endp, 515, "DLCX", p->trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -943,13 +1295,14 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
|||||||
case 'C':
|
case 'C':
|
||||||
if (mgcp_verify_call_id(endp, line + 3) != 0) {
|
if (mgcp_verify_call_id(endp, line + 3) != 0) {
|
||||||
error_code = 516;
|
error_code = 516;
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_INVALID_CALLID]);
|
||||||
goto error3;
|
goto error3;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'I':
|
case 'I':
|
||||||
conn_id = (const char *)line + 3;
|
conn_id = (const char *)line + 3;
|
||||||
if (mgcp_verify_ci(endp, conn_id) != 0) {
|
if ((error_code = mgcp_verify_ci(endp, conn_id))) {
|
||||||
error_code = 515;
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_INVALID_CONNID]);
|
||||||
goto error3;
|
goto error3;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -960,6 +1313,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
|||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
LOGP(DLMGCP, LOGL_NOTICE,
|
||||||
"DLCX: endpoint:0x%x Unhandled MGCP option: '%c'/%d\n",
|
"DLCX: endpoint:0x%x Unhandled MGCP option: '%c'/%d\n",
|
||||||
ENDPOINT_NUMBER(endp), line[0], line[0]);
|
ENDPOINT_NUMBER(endp), line[0], line[0]);
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_UNHANDLED_PARAM]);
|
||||||
return create_err_response(NULL, 539, "DLCX", p->trans);
|
return create_err_response(NULL, 539, "DLCX", p->trans);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -975,12 +1329,14 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
|||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
LOGP(DLMGCP, LOGL_NOTICE,
|
||||||
"DLCX: endpoint:0x%x rejected by policy\n",
|
"DLCX: endpoint:0x%x rejected by policy\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
ENDPOINT_NUMBER(endp));
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_REJECTED_BY_POLICY]);
|
||||||
if (silent)
|
if (silent)
|
||||||
goto out_silent;
|
goto out_silent;
|
||||||
return create_err_response(endp, 400, "DLCX", p->trans);
|
return create_err_response(endp, 400, "DLCX", p->trans);
|
||||||
break;
|
break;
|
||||||
case MGCP_POLICY_DEFER:
|
case MGCP_POLICY_DEFER:
|
||||||
/* stop processing */
|
/* stop processing */
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_DEFERRED_BY_POLICY]);
|
||||||
return NULL;
|
return NULL;
|
||||||
break;
|
break;
|
||||||
case MGCP_POLICY_CONT:
|
case MGCP_POLICY_CONT:
|
||||||
@@ -993,9 +1349,13 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
|||||||
* wildcarded DLCX and drop all connections at once. (See also
|
* wildcarded DLCX and drop all connections at once. (See also
|
||||||
* RFC3435 Section F.7) */
|
* RFC3435 Section F.7) */
|
||||||
if (!conn_id) {
|
if (!conn_id) {
|
||||||
|
int num_conns = llist_count(&endp->conns);
|
||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
LOGP(DLMGCP, LOGL_NOTICE,
|
||||||
"DLCX: endpoint:0x%x missing ci (connectionIdentifier), will remove all connections at once\n",
|
"DLCX: endpoint:0x%x missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
|
||||||
ENDPOINT_NUMBER(endp));
|
num_conns, ENDPOINT_NUMBER(endp));
|
||||||
|
|
||||||
|
if (num_conns > 0)
|
||||||
|
rate_ctr_add(&rate_ctrs->ctr[MGCP_DLCX_SUCCESS], num_conns);
|
||||||
|
|
||||||
mgcp_endp_release(endp);
|
mgcp_endp_release(endp);
|
||||||
|
|
||||||
@@ -1007,9 +1367,10 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
|||||||
|
|
||||||
/* Find the connection */
|
/* Find the connection */
|
||||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||||
if (!conn)
|
if (!conn) {
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_INVALID_CONNID]);
|
||||||
goto error3;
|
goto error3;
|
||||||
|
}
|
||||||
/* save the statistics of the current connection */
|
/* save the statistics of the current connection */
|
||||||
mgcp_format_stats(stats, sizeof(stats), conn->conn);
|
mgcp_format_stats(stats, sizeof(stats), conn->conn);
|
||||||
|
|
||||||
@@ -1034,6 +1395,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
|||||||
p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
|
p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
|
||||||
MGCP_ENDP_DLCX);
|
MGCP_ENDP_DLCX);
|
||||||
|
|
||||||
|
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_SUCCESS]);
|
||||||
if (silent)
|
if (silent)
|
||||||
goto out_silent;
|
goto out_silent;
|
||||||
return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats);
|
return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats);
|
||||||
@@ -1163,6 +1525,45 @@ void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval)
|
|||||||
tcfg->keepalive_interval, 0);
|
tcfg->keepalive_interval, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group)
|
||||||
|
{
|
||||||
|
rate_ctr_group_free(rate_ctr_group);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void alloc_mgcp_rate_counters(struct mgcp_trunk_config *trunk, void *ctx)
|
||||||
|
{
|
||||||
|
/* 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 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 (trunk->mgcp_crcx_ctr_group == NULL) {
|
||||||
|
trunk->mgcp_crcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index);
|
||||||
|
talloc_set_destructor(trunk->mgcp_crcx_ctr_group, free_rate_counter_group);
|
||||||
|
crcx_rate_ctr_index++;
|
||||||
|
}
|
||||||
|
if (trunk->mgcp_mdcx_ctr_group == NULL) {
|
||||||
|
trunk->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index);
|
||||||
|
talloc_set_destructor(trunk->mgcp_mdcx_ctr_group, free_rate_counter_group);
|
||||||
|
mdcx_rate_ctr_index++;
|
||||||
|
}
|
||||||
|
if (trunk->mgcp_dlcx_ctr_group == NULL) {
|
||||||
|
trunk->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index);
|
||||||
|
talloc_set_destructor(trunk->mgcp_dlcx_ctr_group, free_rate_counter_group);
|
||||||
|
dlcx_rate_ctr_index++;
|
||||||
|
}
|
||||||
|
if (trunk->all_rtp_conn_stats == NULL) {
|
||||||
|
trunk->all_rtp_conn_stats = rate_ctr_group_alloc(ctx, &all_rtp_conn_rate_ctr_group_desc,
|
||||||
|
all_rtp_conn_rate_ctr_index);
|
||||||
|
talloc_set_destructor(trunk->all_rtp_conn_stats, free_rate_counter_group);
|
||||||
|
all_rtp_conn_rate_ctr_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*! allocate configuration with default values.
|
/*! allocate configuration with default values.
|
||||||
* (called once at startup by main function) */
|
* (called once at startup by main function) */
|
||||||
struct mgcp_config *mgcp_config_alloc(void)
|
struct mgcp_config *mgcp_config_alloc(void)
|
||||||
@@ -1200,6 +1601,7 @@ struct mgcp_config *mgcp_config_alloc(void)
|
|||||||
cfg->trunk.audio_send_name = 1;
|
cfg->trunk.audio_send_name = 1;
|
||||||
cfg->trunk.omit_rtcp = 0;
|
cfg->trunk.omit_rtcp = 0;
|
||||||
mgcp_trunk_set_keepalive(&cfg->trunk, MGCP_KEEPALIVE_ONCE);
|
mgcp_trunk_set_keepalive(&cfg->trunk, MGCP_KEEPALIVE_ONCE);
|
||||||
|
alloc_mgcp_rate_counters(&cfg->trunk, cfg);
|
||||||
|
|
||||||
INIT_LLIST_HEAD(&cfg->trunks);
|
INIT_LLIST_HEAD(&cfg->trunks);
|
||||||
|
|
||||||
@@ -1231,7 +1633,9 @@ struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr)
|
|||||||
trunk->vty_number_endpoints = 33;
|
trunk->vty_number_endpoints = 33;
|
||||||
trunk->omit_rtcp = 0;
|
trunk->omit_rtcp = 0;
|
||||||
mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
|
mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
|
||||||
|
alloc_mgcp_rate_counters(trunk, trunk);
|
||||||
llist_add_tail(&trunk->entry, &cfg->trunks);
|
llist_add_tail(&trunk->entry, &cfg->trunks);
|
||||||
|
|
||||||
return trunk;
|
return trunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1276,6 +1680,8 @@ int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
tcfg->number_endpoints = tcfg->vty_number_endpoints;
|
tcfg->number_endpoints = tcfg->vty_number_endpoints;
|
||||||
|
alloc_mgcp_rate_counters(tcfg, tcfg->cfg);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,13 @@
|
|||||||
#include <osmocom/mgcp/mgcp_internal.h>
|
#include <osmocom/mgcp/mgcp_internal.h>
|
||||||
#include <osmocom/mgcp/mgcp_msg.h>
|
#include <osmocom/mgcp/mgcp_msg.h>
|
||||||
#include <osmocom/mgcp/mgcp_endp.h>
|
#include <osmocom/mgcp/mgcp_endp.h>
|
||||||
|
#include <osmocom/mgcp/mgcp_codec.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
/* A struct to store intermediate parsing results. The function
|
||||||
|
* mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP
|
||||||
|
* codec information. */
|
||||||
struct sdp_rtp_map {
|
struct sdp_rtp_map {
|
||||||
/* the type */
|
/* the type */
|
||||||
int payload_type;
|
int payload_type;
|
||||||
@@ -40,89 +44,8 @@ struct sdp_rtp_map {
|
|||||||
int channels;
|
int channels;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Set codec configuration depending on payload type and codec name.
|
/* Helper function to extrapolate missing codec parameters in a codec mao from
|
||||||
* \param[in] ctx talloc context.
|
* an already filled in payload_type, called from: mgcp_parse_sdp_data() */
|
||||||
* \param[out] codec configuration (caller provided memory).
|
|
||||||
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
|
|
||||||
* \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
|
|
||||||
* \returns 0 on success, -1 on failure. */
|
|
||||||
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
|
|
||||||
int payload_type, const char *audio_name)
|
|
||||||
{
|
|
||||||
int rate = codec->rate;
|
|
||||||
int channels = codec->channels;
|
|
||||||
char audio_codec[64];
|
|
||||||
|
|
||||||
talloc_free(codec->subtype_name);
|
|
||||||
codec->subtype_name = NULL;
|
|
||||||
talloc_free(codec->audio_name);
|
|
||||||
codec->audio_name = NULL;
|
|
||||||
|
|
||||||
if (payload_type != PTYPE_UNDEFINED)
|
|
||||||
codec->payload_type = payload_type;
|
|
||||||
|
|
||||||
if (!audio_name) {
|
|
||||||
switch (payload_type) {
|
|
||||||
case 0:
|
|
||||||
audio_name = "PCMU/8000/1";
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
audio_name = "GSM/8000/1";
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
audio_name = "PCMA/8000/1";
|
|
||||||
break;
|
|
||||||
case 18:
|
|
||||||
audio_name = "G729/8000/1";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* Payload type is unknown, don't change rate and
|
|
||||||
* channels. */
|
|
||||||
/* TODO: return value? */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
|
||||||
audio_codec, &rate, &channels) < 1)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
codec->rate = rate;
|
|
||||||
codec->channels = channels;
|
|
||||||
codec->subtype_name = talloc_strdup(ctx, audio_codec);
|
|
||||||
codec->audio_name = talloc_strdup(ctx, audio_name);
|
|
||||||
|
|
||||||
if (!strcmp(audio_codec, "G729")) {
|
|
||||||
codec->frame_duration_num = 10;
|
|
||||||
codec->frame_duration_den = 1000;
|
|
||||||
} else {
|
|
||||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
|
||||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payload_type < 0) {
|
|
||||||
payload_type = 96;
|
|
||||||
if (rate == 8000 && channels == 1) {
|
|
||||||
if (!strcmp(audio_codec, "GSM"))
|
|
||||||
payload_type = 3;
|
|
||||||
else if (!strcmp(audio_codec, "PCMA"))
|
|
||||||
payload_type = 8;
|
|
||||||
else if (!strcmp(audio_codec, "PCMU"))
|
|
||||||
payload_type = 0;
|
|
||||||
else if (!strcmp(audio_codec, "G729"))
|
|
||||||
payload_type = 18;
|
|
||||||
}
|
|
||||||
|
|
||||||
codec->payload_type = payload_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channels != 1)
|
|
||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
|
||||||
"Channels != 1 in SDP: '%s'\n", audio_name);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@@ -149,10 +72,16 @@ static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
|||||||
codecs[i].rate = 8000;
|
codecs[i].rate = 8000;
|
||||||
codecs[i].channels = 1;
|
codecs[i].channels = 1;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
codecs[i].codec_name = NULL;
|
||||||
|
codecs[i].rate = 0;
|
||||||
|
codecs[i].channels = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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,
|
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
||||||
int payload, const char *audio_name)
|
int payload, const char *audio_name)
|
||||||
{
|
{
|
||||||
@@ -162,8 +91,13 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
|||||||
char audio_codec[64];
|
char audio_codec[64];
|
||||||
int rate = -1;
|
int rate = -1;
|
||||||
int channels = -1;
|
int channels = -1;
|
||||||
|
|
||||||
|
/* 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)
|
if (codecs[i].payload_type != payload)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
||||||
audio_codec, &rate, &channels) < 1) {
|
audio_codec, &rate, &channels) < 1) {
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
|
LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
|
||||||
@@ -182,43 +116,72 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
|||||||
audio_name);
|
audio_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if the codec matches what is set up in the trunk config */
|
/* Extract payload types from SDP, also check for duplicates */
|
||||||
static int is_codec_compatible(const struct mgcp_endpoint *endp,
|
static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
|
||||||
const struct sdp_rtp_map *codec)
|
unsigned int codecs_len, char *sdp)
|
||||||
{
|
{
|
||||||
char *codec_str;
|
char *str;
|
||||||
char audio_codec[64];
|
char *str_ptr;
|
||||||
|
char *pt_str;
|
||||||
|
unsigned int pt;
|
||||||
|
unsigned int count = 0;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
if (!codec->codec_name)
|
str = talloc_zero_size(ctx, strlen(sdp) + 1);
|
||||||
return 0;
|
str_ptr = str;
|
||||||
|
strcpy(str_ptr, sdp);
|
||||||
|
|
||||||
/* GSM, GSM/8000 and GSM/8000/1 should all be compatible...
|
str_ptr = strstr(str_ptr, "RTP/AVP ");
|
||||||
* let's go by name first. */
|
if (!str_ptr)
|
||||||
codec_str = endp->tcfg->audio_name;
|
goto exit;
|
||||||
if (sscanf(codec_str, "%63[^/]/%*d/%*d", audio_codec) < 1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return strcasecmp(audio_codec, codec->codec_name) == 0;
|
pt_str = strtok(str_ptr, " ");
|
||||||
|
if (!pt_str)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* Do not allow excessive payload types */
|
||||||
|
if (count > codecs_len)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
pt_str = strtok(NULL, " ");
|
||||||
|
if (!pt_str)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pt = atoi(pt_str);
|
||||||
|
|
||||||
|
/* Do not allow duplicate payload types */
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
if (codecs[i].payload_type == pt)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
codecs[count].payload_type = pt;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
talloc_free(str);
|
||||||
|
return count;
|
||||||
|
error:
|
||||||
|
talloc_free(str);
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Analyze SDP input string.
|
/*! Analyze SDP input string.
|
||||||
* \param[in] endp trunk endpoint.
|
* \param[in] endp trunk endpoint.
|
||||||
* \param[out] conn associated rtp connection.
|
* \param[out] conn associated rtp connection.
|
||||||
* \param[out] caller provided memory to store the parsing results.
|
* \param[out] caller provided memory to store the parsing results.
|
||||||
* \returns 0 on success, -1 on failure.
|
|
||||||
*
|
*
|
||||||
* Note: In conn (conn->end) the function returns the packet duration,
|
* Note: In conn (conn->end) the function returns the packet duration,
|
||||||
* the rtp port and the rtcp port */
|
* rtp port, rtcp port and the codec information.
|
||||||
|
* \returns 0 on success, -1 on failure. */
|
||||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||||
struct mgcp_conn_rtp *conn,
|
struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
|
||||||
struct mgcp_parse_data *p)
|
|
||||||
{
|
{
|
||||||
struct sdp_rtp_map codecs[10];
|
struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
|
||||||
int codecs_used = 0;
|
unsigned int codecs_used = 0;
|
||||||
char *line;
|
char *line;
|
||||||
int maxptime = -1;
|
unsigned int i;
|
||||||
int i;
|
|
||||||
int codecs_assigned = 0;
|
|
||||||
void *tmp_ctx = talloc_new(NULL);
|
void *tmp_ctx = talloc_new(NULL);
|
||||||
struct mgcp_rtp_end *rtp;
|
struct mgcp_rtp_end *rtp;
|
||||||
|
|
||||||
@@ -255,30 +218,21 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
|||||||
rtp->packet_duration_ms = 0;
|
rtp->packet_duration_ms = 0;
|
||||||
else
|
else
|
||||||
rtp->packet_duration_ms = ptime;
|
rtp->packet_duration_ms = ptime;
|
||||||
} else if (sscanf(line, "a=maxptime:%d", &ptime2)
|
} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
|
||||||
== 1) {
|
rtp->maximum_packet_time = ptime2;
|
||||||
maxptime = ptime2;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
rc = sscanf(line,
|
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
|
||||||
"m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
|
if (rc == 1) {
|
||||||
&port, &codecs[0].payload_type,
|
|
||||||
&codecs[1].payload_type,
|
|
||||||
&codecs[2].payload_type,
|
|
||||||
&codecs[3].payload_type,
|
|
||||||
&codecs[4].payload_type,
|
|
||||||
&codecs[5].payload_type,
|
|
||||||
&codecs[6].payload_type,
|
|
||||||
&codecs[7].payload_type,
|
|
||||||
&codecs[8].payload_type,
|
|
||||||
&codecs[9].payload_type);
|
|
||||||
if (rc >= 2) {
|
|
||||||
rtp->rtp_port = htons(port);
|
rtp->rtp_port = htons(port);
|
||||||
rtp->rtcp_port = htons(port + 1);
|
rtp->rtcp_port = htons(port + 1);
|
||||||
codecs_used = rc - 1;
|
|
||||||
codecs_initialize(tmp_ctx, codecs, codecs_used);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rc = pt_from_sdp(conn->conn, codecs,
|
||||||
|
ARRAY_SIZE(codecs), line);
|
||||||
|
if (rc > 0)
|
||||||
|
codecs_used = rc;
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
|
|
||||||
@@ -299,43 +253,81 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
|
||||||
|
|
||||||
/* Now select the primary and alt_codec */
|
/* So far we have only set the payload type in the codec struct. Now we
|
||||||
for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
|
* fill up the remaining fields of the codec description with some default
|
||||||
struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
|
* information */
|
||||||
&rtp->codec : &rtp->alt_codec;
|
codecs_initialize(tmp_ctx, codecs, codecs_used);
|
||||||
|
|
||||||
if (endp->tcfg->no_audio_transcoding &&
|
/* Store parsed codec information */
|
||||||
!is_codec_compatible(endp, &codecs[i])) {
|
for (i = 0; i < codecs_used; i++) {
|
||||||
LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n",
|
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
|
||||||
codecs[i].codec_name);
|
if (rc < 0)
|
||||||
continue;
|
LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
|
||||||
}
|
|
||||||
|
|
||||||
mgcp_set_audio_info(p->cfg, codec,
|
|
||||||
codecs[i].payload_type, codecs[i].map_line);
|
|
||||||
codecs_assigned += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codecs_assigned > 0) {
|
|
||||||
/* TODO/XXX: Store this per codec and derive it on use */
|
|
||||||
if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
|
|
||||||
rtp->codec.frame_duration_num * 1500) {
|
|
||||||
/* more than 1 frame */
|
|
||||||
rtp->packet_duration_ms = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
|
||||||
"Got media info via SDP: port %d, payload %d (%s), "
|
|
||||||
"duration %d, addr %s\n",
|
|
||||||
ntohs(rtp->rtp_port), rtp->codec.payload_type,
|
|
||||||
rtp->codec.subtype_name ? rtp->
|
|
||||||
codec.subtype_name : "unknown", rtp->packet_duration_ms,
|
|
||||||
inet_ntoa(rtp->addr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
talloc_free(tmp_ctx);
|
talloc_free(tmp_ctx);
|
||||||
return codecs_assigned > 0;
|
|
||||||
|
LOGP(DLMGCP, LOGL_NOTICE,
|
||||||
|
"Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
|
||||||
|
ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
|
||||||
|
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",
|
||||||
|
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");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Add rtpmap string to the sdp payload, but only when the payload type falls
|
||||||
|
* into the dynamic payload type range */
|
||||||
|
static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (payload_type >= 96 && payload_type <= 127) {
|
||||||
|
if (!audio_name)
|
||||||
|
return -EINVAL;
|
||||||
|
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", payload_type, audio_name);
|
||||||
|
if (rc < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add audio string to sdp payload */
|
||||||
|
static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if (payload_types_len < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
|
||||||
|
if (rc < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
for (i = 0; i < payload_types_len; i++) {
|
||||||
|
rc = msgb_printf(sdp, " %d", payload_types[i]);
|
||||||
|
if (rc < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = msgb_printf(sdp, "\r\n");
|
||||||
|
if (rc < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Generate SDP response string.
|
/*! Generate SDP response string.
|
||||||
@@ -352,6 +344,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
|||||||
const char *audio_name;
|
const char *audio_name;
|
||||||
int payload_type;
|
int payload_type;
|
||||||
int rc;
|
int rc;
|
||||||
|
int payload_types[1];
|
||||||
|
|
||||||
OSMO_ASSERT(endp);
|
OSMO_ASSERT(endp);
|
||||||
OSMO_ASSERT(conn);
|
OSMO_ASSERT(conn);
|
||||||
@@ -375,15 +368,14 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
|||||||
goto buffer_too_small;
|
goto buffer_too_small;
|
||||||
|
|
||||||
if (payload_type >= 0) {
|
if (payload_type >= 0) {
|
||||||
rc = msgb_printf(sdp, "m=audio %d RTP/AVP %d\r\n",
|
|
||||||
conn->end.local_port, payload_type);
|
payload_types[0] = payload_type;
|
||||||
|
rc = add_audio(sdp, payload_types, 1, conn->end.local_port);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto buffer_too_small;
|
goto buffer_too_small;
|
||||||
|
|
||||||
if (audio_name && endp->tcfg->audio_send_name) {
|
if (endp->tcfg->audio_send_name) {
|
||||||
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n",
|
rc = add_rtpmap(sdp, payload_type, audio_name);
|
||||||
payload_type, audio_name);
|
|
||||||
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto buffer_too_small;
|
goto buffer_too_small;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,12 +25,14 @@
|
|||||||
#include <osmocom/mgcp/mgcp_stat.h>
|
#include <osmocom/mgcp/mgcp_stat.h>
|
||||||
#include <osmocom/mgcp/mgcp_endp.h>
|
#include <osmocom/mgcp/mgcp_endp.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
|
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
|
||||||
void calc_loss(struct mgcp_rtp_state *state,
|
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)
|
||||||
struct mgcp_rtp_end *end, uint32_t *expected,
|
|
||||||
int *loss)
|
|
||||||
{
|
{
|
||||||
|
struct mgcp_rtp_state *state = &conn->state;
|
||||||
|
struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
|
||||||
|
|
||||||
*expected = state->stats.cycles + state->stats.max_seq;
|
*expected = state->stats.cycles + state->stats.max_seq;
|
||||||
*expected = *expected - state->stats.base_seq + 1;
|
*expected = *expected - state->stats.base_seq + 1;
|
||||||
|
|
||||||
@@ -44,8 +46,8 @@ void calc_loss(struct mgcp_rtp_state *state,
|
|||||||
* Make sure the sign is correct and use the biggest
|
* Make sure the sign is correct and use the biggest
|
||||||
* positive/negative number that fits.
|
* positive/negative number that fits.
|
||||||
*/
|
*/
|
||||||
*loss = *expected - end->stats.packets_rx;
|
*loss = *expected - packets_rx->current;
|
||||||
if (*expected < end->stats.packets_rx) {
|
if (*expected < packets_rx->current) {
|
||||||
if (*loss > 0)
|
if (*loss > 0)
|
||||||
*loss = INT_MIN;
|
*loss = INT_MIN;
|
||||||
} else {
|
} else {
|
||||||
@@ -70,13 +72,18 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
|
|||||||
int ploss;
|
int ploss;
|
||||||
int nchars;
|
int nchars;
|
||||||
|
|
||||||
calc_loss(&conn->state, &conn->end, &expected, &ploss);
|
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);
|
jitter = calc_jitter(&conn->state);
|
||||||
|
|
||||||
nchars = snprintf(str, str_len,
|
nchars = snprintf(str, str_len,
|
||||||
"\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
|
"\r\nP: PS=%" PRIu64 ", OS=%" PRIu64 ", PR=%" PRIu64 ", OR=%" PRIu64 ", PL=%d, JI=%u",
|
||||||
conn->end.stats.packets_tx, conn->end.stats.octets_tx,
|
packets_tx->current, octets_tx->current,
|
||||||
conn->end.stats.packets_rx, conn->end.stats.octets_rx,
|
packets_rx->current, octets_rx->current,
|
||||||
ploss, jitter);
|
ploss, jitter);
|
||||||
if (nchars < 0 || nchars >= str_len)
|
if (nchars < 0 || nchars >= str_len)
|
||||||
goto truncate;
|
goto truncate;
|
||||||
@@ -87,9 +94,9 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
|
|||||||
if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) {
|
if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) {
|
||||||
/* Error Counter */
|
/* Error Counter */
|
||||||
nchars = snprintf(str, str_len,
|
nchars = snprintf(str, str_len,
|
||||||
"\r\nX-Osmo-CP: EC TI=%u, TO=%u",
|
"\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64,
|
||||||
conn->state.in_stream.err_ts_counter,
|
conn->state.in_stream.err_ts_ctr->current,
|
||||||
conn->state.out_stream.err_ts_counter);
|
conn->state.out_stream.err_ts_ctr->current);
|
||||||
if (nchars < 0 || nchars >= str_len)
|
if (nchars < 0 || nchars >= str_len)
|
||||||
goto truncate;
|
goto truncate;
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <osmocom/core/talloc.h>
|
#include <osmocom/core/talloc.h>
|
||||||
|
#include <osmocom/vty/misc.h>
|
||||||
#include <osmocom/mgcp/mgcp.h>
|
#include <osmocom/mgcp/mgcp.h>
|
||||||
#include <osmocom/mgcp/mgcp_common.h>
|
#include <osmocom/mgcp/mgcp_common.h>
|
||||||
#include <osmocom/mgcp/mgcp_internal.h>
|
#include <osmocom/mgcp/mgcp_internal.h>
|
||||||
@@ -30,6 +31,8 @@
|
|||||||
#include <osmocom/mgcp/mgcp_endp.h>
|
#include <osmocom/mgcp/mgcp_endp.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
#define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
|
#define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
|
||||||
#define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
|
#define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
|
||||||
@@ -154,22 +157,37 @@ static int config_write_mgcp(struct vty *vty)
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state,
|
static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
|
||||||
struct mgcp_rtp_end *end)
|
|
||||||
{
|
{
|
||||||
struct mgcp_rtp_codec *codec = &end->codec;
|
struct mgcp_rtp_state *state = &conn->state;
|
||||||
|
struct mgcp_rtp_end *end = &conn->end;
|
||||||
|
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 = &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,
|
vty_out(vty,
|
||||||
" Timestamp Errs: %d->%d%s"
|
" Packets Sent: %" PRIu64 " (%" PRIu64 " bytes total)%s"
|
||||||
" Dropped Packets: %d%s"
|
" Packets Received: %" PRIu64 " (%" PRIu64 " bytes total)%s"
|
||||||
|
" Timestamp Errs: %" PRIu64 "->%" PRIu64 "%s"
|
||||||
|
" Dropped Packets: %" PRIu64 "%s"
|
||||||
" Payload Type: %d Rate: %u Channels: %d %s"
|
" Payload Type: %d Rate: %u Channels: %d %s"
|
||||||
" Frame Duration: %u Frame Denominator: %u%s"
|
" Frame Duration: %u Frame Denominator: %u%s"
|
||||||
" FPP: %d Packet Duration: %u%s"
|
" FPP: %d Packet Duration: %u%s"
|
||||||
" FMTP-Extra: %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",
|
" Output-Enabled: %d Force-PTIME: %d%s",
|
||||||
state->in_stream.err_ts_counter,
|
tx_packets->current, tx_bytes->current, VTY_NEWLINE,
|
||||||
state->out_stream.err_ts_counter, VTY_NEWLINE,
|
rx_packets->current, rx_bytes->current, VTY_NEWLINE,
|
||||||
end->stats.dropped_packets, VTY_NEWLINE,
|
state->in_stream.err_ts_ctr->current,
|
||||||
|
state->out_stream.err_ts_ctr->current,
|
||||||
|
VTY_NEWLINE,
|
||||||
|
dropped_packets->current, VTY_NEWLINE,
|
||||||
codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
|
codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
|
||||||
codec->frame_duration_num, codec->frame_duration_den,
|
codec->frame_duration_num, codec->frame_duration_den,
|
||||||
VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms,
|
VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms,
|
||||||
@@ -178,13 +196,40 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state,
|
|||||||
end->force_output_ptime, VTY_NEWLINE);
|
end->force_output_ptime, VTY_NEWLINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
|
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, int epidx,
|
||||||
int verbose)
|
int trunk_nr, enum mgcp_trunk_type trunk_type, int show_stats)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
struct mgcp_conn *conn;
|
struct mgcp_conn *conn;
|
||||||
|
|
||||||
vty_out(vty, "%s trunk nr %d with %d endpoints:%s",
|
vty_out(vty, "%s trunk %d endpoint %s%.2x:%s",
|
||||||
|
trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", trunk_nr,
|
||||||
|
trunk_type == MGCP_TRUNK_VIRTUAL ? MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK : "",
|
||||||
|
epidx, VTY_NEWLINE);
|
||||||
|
|
||||||
|
if (llist_empty(&endp->conns)) {
|
||||||
|
vty_out(vty, " No active connections%s", VTY_NEWLINE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||||
|
vty_out(vty, " CONN: %s%s", mgcp_conn_dump(conn), VTY_NEWLINE);
|
||||||
|
|
||||||
|
if (show_stats) {
|
||||||
|
/* FIXME: Also add verbosity for other
|
||||||
|
* connection types (E1) as soon as
|
||||||
|
* the implementation is available */
|
||||||
|
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||||
|
dump_rtp_end(vty, &conn->u.rtp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg, int show_stats)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
vty_out(vty, "%s trunk %d with %d endpoints:%s",
|
||||||
cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
|
cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
|
||||||
cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE);
|
cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE);
|
||||||
|
|
||||||
@@ -195,30 +240,35 @@ static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
|
|||||||
|
|
||||||
for (i = 1; i < cfg->number_endpoints; ++i) {
|
for (i = 1; i < cfg->number_endpoints; ++i) {
|
||||||
struct mgcp_endpoint *endp = &cfg->endpoints[i];
|
struct mgcp_endpoint *endp = &cfg->endpoints[i];
|
||||||
|
dump_endpoint(vty, endp, i, cfg->trunk_nr, cfg->trunk_type, show_stats);
|
||||||
|
if (i < cfg->number_endpoints - 1)
|
||||||
|
vty_out(vty, "%s", VTY_NEWLINE);
|
||||||
|
}
|
||||||
|
|
||||||
vty_out(vty, "Endpoint 0x%.2x:%s", i, VTY_NEWLINE);
|
if (show_stats && cfg->mgcp_crcx_ctr_group) {
|
||||||
|
vty_out(vty, " %s:%s", cfg->mgcp_crcx_ctr_group->desc->group_description, VTY_NEWLINE);
|
||||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
vty_out_rate_ctr_group_fmt(vty, " %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_crcx_ctr_group);
|
||||||
vty_out(vty, " CONN: %s%s",
|
}
|
||||||
mgcp_conn_dump(conn), VTY_NEWLINE);
|
if (show_stats && cfg->mgcp_dlcx_ctr_group) {
|
||||||
|
vty_out(vty, " %s:%s", cfg->mgcp_dlcx_ctr_group->desc->group_description, VTY_NEWLINE);
|
||||||
if (verbose) {
|
vty_out_rate_ctr_group_fmt(vty, " %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_dlcx_ctr_group);
|
||||||
/* FIXME: Also add verbosity for other
|
}
|
||||||
* connection types (E1) as soon as
|
if (show_stats && cfg->mgcp_mdcx_ctr_group) {
|
||||||
* the implementation is available */
|
vty_out(vty, " %s:%s", cfg->mgcp_mdcx_ctr_group->desc->group_description, VTY_NEWLINE);
|
||||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
vty_out_rate_ctr_group_fmt(vty, " %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_mdcx_ctr_group);
|
||||||
dump_rtp_end(vty, &conn->u.rtp.state,
|
}
|
||||||
&conn->u.rtp.end);
|
if (show_stats && cfg->all_rtp_conn_stats) {
|
||||||
}
|
vty_out(vty, " %s:%s", cfg->all_rtp_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", cfg->all_rtp_conn_stats);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SHOW_MGCP_STR "Display information about the MGCP Media Gateway\n"
|
||||||
|
|
||||||
DEFUN(show_mcgp, show_mgcp_cmd,
|
DEFUN(show_mcgp, show_mgcp_cmd,
|
||||||
"show mgcp [stats]",
|
"show mgcp [stats]",
|
||||||
SHOW_STR
|
SHOW_STR
|
||||||
"Display information about the MGCP Media Gateway\n"
|
SHOW_MGCP_STR
|
||||||
"Include Statistics\n")
|
"Include Statistics\n")
|
||||||
{
|
{
|
||||||
struct mgcp_trunk_config *trunk;
|
struct mgcp_trunk_config *trunk;
|
||||||
@@ -236,6 +286,72 @@ DEFUN(show_mcgp, show_mgcp_cmd,
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dump_mgcp_endpoint(struct vty *vty, struct mgcp_trunk_config *trunk, const char *epname)
|
||||||
|
{
|
||||||
|
const size_t virt_prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
|
||||||
|
unsigned long epidx;
|
||||||
|
char *endp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, virt_prefix_len) == 0)
|
||||||
|
epname += virt_prefix_len;
|
||||||
|
errno = 0;
|
||||||
|
epidx = strtoul(epname, &endp, 16);
|
||||||
|
if (epname[0] == '\0' || *endp != '\0') {
|
||||||
|
vty_out(vty, "endpoint name '%s' is not a hex number%s", epname, VTY_NEWLINE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((errno == ERANGE && epidx == ULONG_MAX) /* parsed value out of range */
|
||||||
|
|| epidx >= trunk->number_endpoints) {
|
||||||
|
vty_out(vty, "endpoint %.2lx not configured on trunk %d%s", epidx, trunk->trunk_nr, VTY_NEWLINE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < trunk->number_endpoints; ++i) {
|
||||||
|
struct mgcp_endpoint *endp = &trunk->endpoints[i];
|
||||||
|
if (i == epidx) {
|
||||||
|
dump_endpoint(vty, endp, i, trunk->trunk_nr, trunk->trunk_type, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(show_mcgp_endpoint, show_mgcp_endpoint_cmd,
|
||||||
|
"show mgcp endpoint NAME",
|
||||||
|
SHOW_STR
|
||||||
|
SHOW_MGCP_STR
|
||||||
|
"Display information about an endpoint\n" "The name of the endpoint\n")
|
||||||
|
{
|
||||||
|
struct mgcp_trunk_config *trunk;
|
||||||
|
|
||||||
|
dump_mgcp_endpoint(vty, &g_cfg->trunk, argv[0]);
|
||||||
|
llist_for_each_entry(trunk, &g_cfg->trunks, entry)
|
||||||
|
dump_mgcp_endpoint(vty, trunk, argv[0]);
|
||||||
|
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(show_mcgp_trunk_endpoint, show_mgcp_trunk_endpoint_cmd,
|
||||||
|
"show mgcp trunk <0-64> endpoint NAME",
|
||||||
|
SHOW_STR
|
||||||
|
SHOW_MGCP_STR
|
||||||
|
"Display information about a trunk\n" "Trunk number\n"
|
||||||
|
"Display information about an endpoint\n" "The name of the endpoint\n")
|
||||||
|
{
|
||||||
|
struct mgcp_trunk_config *trunk;
|
||||||
|
int trunkidx = atoi(argv[0]);
|
||||||
|
|
||||||
|
trunk = find_trunk(g_cfg, trunkidx);
|
||||||
|
if (!trunk) {
|
||||||
|
vty_out(vty, "trunk %d not found%s", trunkidx, VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
dump_mgcp_endpoint(vty, trunk, argv[1]);
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
DEFUN(cfg_mgcp, cfg_mgcp_cmd, "mgcp", "Configure the MGCP")
|
DEFUN(cfg_mgcp, cfg_mgcp_cmd, "mgcp", "Configure the MGCP")
|
||||||
{
|
{
|
||||||
vty->node = MGCP_NODE;
|
vty->node = MGCP_NODE;
|
||||||
@@ -281,13 +397,6 @@ DEFUN(cfg_mgcp_bind_early,
|
|||||||
return CMD_WARNING;
|
return CMD_WARNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_range(struct mgcp_port_range *range, const char **argv)
|
|
||||||
{
|
|
||||||
range->range_start = atoi(argv[0]);
|
|
||||||
range->range_end = atoi(argv[1]);
|
|
||||||
range->last_port = g_cfg->net_ports.range_start;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RTP_STR "RTP configuration\n"
|
#define RTP_STR "RTP configuration\n"
|
||||||
#define UDP_PORT_STR "UDP Port number\n"
|
#define UDP_PORT_STR "UDP Port number\n"
|
||||||
#define NET_START_STR "First UDP port allocated\n"
|
#define NET_START_STR "First UDP port allocated\n"
|
||||||
@@ -296,11 +405,38 @@ static void parse_range(struct mgcp_port_range *range, const char **argv)
|
|||||||
|
|
||||||
DEFUN(cfg_mgcp_rtp_port_range,
|
DEFUN(cfg_mgcp_rtp_port_range,
|
||||||
cfg_mgcp_rtp_port_range_cmd,
|
cfg_mgcp_rtp_port_range_cmd,
|
||||||
"rtp port-range <0-65534> <0-65534>",
|
"rtp port-range <1024-65534> <1025-65535>",
|
||||||
RTP_STR "Range of ports to use for the NET side\n"
|
RTP_STR "Range of ports to use for the NET side\n"
|
||||||
RANGE_START_STR RANGE_END_STR)
|
RANGE_START_STR RANGE_END_STR)
|
||||||
{
|
{
|
||||||
parse_range(&g_cfg->net_ports, argv);
|
int start;
|
||||||
|
int end;
|
||||||
|
|
||||||
|
start = atoi(argv[0]);
|
||||||
|
end = atoi(argv[1]);
|
||||||
|
|
||||||
|
if (end < start) {
|
||||||
|
vty_out(vty, "range end port (%i) must be greater than the range start port (%i)!%s",
|
||||||
|
end, start, VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start & 1) {
|
||||||
|
vty_out(vty, "range must begin at an even port number, autocorrecting port (%i) to: %i%s",
|
||||||
|
start, start & 0xFFFE, VTY_NEWLINE);
|
||||||
|
start &= 0xFFFE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end & 1) == 0) {
|
||||||
|
vty_out(vty, "range must end at an odd port number, autocorrecting port (%i) to: %i%s",
|
||||||
|
end, end | 1, VTY_NEWLINE);
|
||||||
|
end |= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_cfg->net_ports.range_start = start;
|
||||||
|
g_cfg->net_ports.range_end = end;
|
||||||
|
g_cfg->net_ports.last_port = g_cfg->net_ports.range_start;
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
ALIAS_DEPRECATED(cfg_mgcp_rtp_port_range,
|
ALIAS_DEPRECATED(cfg_mgcp_rtp_port_range,
|
||||||
@@ -1183,7 +1319,9 @@ DEFUN(cfg_mgcp_osmux_dummy,
|
|||||||
|
|
||||||
DEFUN(cfg_mgcp_domain,
|
DEFUN(cfg_mgcp_domain,
|
||||||
cfg_mgcp_domain_cmd,
|
cfg_mgcp_domain_cmd,
|
||||||
"domain NAME", "domain\n" "qualified domain name\n")
|
"domain NAME",
|
||||||
|
"Set the domain part expected in MGCP messages' endpoint names\n"
|
||||||
|
"Qualified domain name expected in MGCP endpoint names, or '*' to accept any domain\n")
|
||||||
{
|
{
|
||||||
osmo_strlcpy(g_cfg->domain, argv[0], sizeof(g_cfg->domain));
|
osmo_strlcpy(g_cfg->domain, argv[0], sizeof(g_cfg->domain));
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
@@ -1192,6 +1330,8 @@ DEFUN(cfg_mgcp_domain,
|
|||||||
int mgcp_vty_init(void)
|
int mgcp_vty_init(void)
|
||||||
{
|
{
|
||||||
install_element_ve(&show_mgcp_cmd);
|
install_element_ve(&show_mgcp_cmd);
|
||||||
|
install_element_ve(&show_mgcp_endpoint_cmd);
|
||||||
|
install_element_ve(&show_mgcp_trunk_endpoint_cmd);
|
||||||
install_element(ENABLE_NODE, &loop_conn_cmd);
|
install_element(ENABLE_NODE, &loop_conn_cmd);
|
||||||
install_element(ENABLE_NODE, &tap_rtp_cmd);
|
install_element(ENABLE_NODE, &tap_rtp_cmd);
|
||||||
install_element(ENABLE_NODE, &free_endp_cmd);
|
install_element(ENABLE_NODE, &free_endp_cmd);
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
AM_CPPFLAGS = \
|
|
||||||
$(all_includes) \
|
|
||||||
-I$(top_srcdir)/include \
|
|
||||||
-I$(top_builddir) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
AM_CFLAGS = \
|
|
||||||
-Wall \
|
|
||||||
$(LIBOSMOCORE_CFLAGS) \
|
|
||||||
$(LIBOSMOVTY_CFLAGS) \
|
|
||||||
$(LIBOSMONETIF_CFLAGS) \
|
|
||||||
$(LIBBCG729_CFLAGS) \
|
|
||||||
$(COVERAGE_CFLAGS) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
bin_PROGRAMS = \
|
|
||||||
osmo-bsc_mgcp \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
osmo_bsc_mgcp_SOURCES = \
|
|
||||||
mgcp_main.c \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
osmo_bsc_mgcp_LDADD = \
|
|
||||||
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
|
|
||||||
$(LIBOSMOCORE_LIBS) \
|
|
||||||
$(LIBOSMOVTY_LIBS) \
|
|
||||||
$(LIBOSMONETIF_LIBS) \
|
|
||||||
$(LIBBCG729_LIBS) \
|
|
||||||
$(LIBRARY_GSM) \
|
|
||||||
$(NULL)
|
|
||||||
@@ -1,351 +0,0 @@
|
|||||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
|
||||||
/* The main method to drive it as a standalone process */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
|
|
||||||
* (C) 2009-2011 by On-Waves
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU 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 <ctype.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include <osmocom/legacy_mgcp/mgcp.h>
|
|
||||||
#include <osmocom/legacy_mgcp/mgcp_internal.h>
|
|
||||||
#include <osmocom/legacy_mgcp/vty.h>
|
|
||||||
|
|
||||||
#include <osmocom/core/application.h>
|
|
||||||
#include <osmocom/core/msgb.h>
|
|
||||||
#include <osmocom/core/talloc.h>
|
|
||||||
#include <osmocom/core/select.h>
|
|
||||||
#include <osmocom/core/stats.h>
|
|
||||||
#include <osmocom/core/rate_ctr.h>
|
|
||||||
#include <osmocom/core/logging.h>
|
|
||||||
|
|
||||||
#include <osmocom/vty/telnet_interface.h>
|
|
||||||
#include <osmocom/vty/logging.h>
|
|
||||||
#include <osmocom/vty/ports.h>
|
|
||||||
#include <osmocom/vty/command.h>
|
|
||||||
#include <osmocom/vty/stats.h>
|
|
||||||
|
|
||||||
#include "../../bscconfig.h"
|
|
||||||
|
|
||||||
#ifdef BUILD_MGCP_TRANSCODING
|
|
||||||
#include <osmocom/legacy_mgcp/mgcp_transcode.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
#include <getopt.h>
|
|
||||||
|
|
||||||
#warning "Make use of the rtp proxy code"
|
|
||||||
|
|
||||||
static struct mgcp_config *cfg;
|
|
||||||
static struct mgcp_trunk_config *reset_trunk;
|
|
||||||
static int reset_endpoints = 0;
|
|
||||||
static int daemonize = 0;
|
|
||||||
|
|
||||||
const char *openbsc_copyright =
|
|
||||||
"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
|
|
||||||
"Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n"
|
|
||||||
"Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\r\n"
|
|
||||||
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
|
|
||||||
"This is free software: you are free to change and redistribute it.\r\n"
|
|
||||||
"There is NO WARRANTY, to the extent permitted by law.\r\n";
|
|
||||||
|
|
||||||
static char *config_file = "mgcp.cfg";
|
|
||||||
|
|
||||||
/* used by msgb and mgcp */
|
|
||||||
void *tall_bsc_ctx = NULL;
|
|
||||||
|
|
||||||
static void print_help()
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_options(int argc, char **argv)
|
|
||||||
{
|
|
||||||
while (1) {
|
|
||||||
int option_index = 0, c;
|
|
||||||
static struct option long_options[] = {
|
|
||||||
{"help", 0, 0, 'h'},
|
|
||||||
{"config-file", 1, 0, 'c'},
|
|
||||||
{"daemonize", 0, 0, 'D'},
|
|
||||||
{"version", 0, 0, 'V'},
|
|
||||||
{"disable-color", 0, 0, 's'},
|
|
||||||
{0, 0, 0, 0},
|
|
||||||
};
|
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "hc:VD", long_options, &option_index);
|
|
||||||
|
|
||||||
if (c == -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
switch(c) {
|
|
||||||
case 'h':
|
|
||||||
print_help();
|
|
||||||
exit(0);
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
config_file = talloc_strdup(tall_bsc_ctx, optarg);
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
log_set_use_color(osmo_stderr_target, 0);
|
|
||||||
break;
|
|
||||||
case 'V':
|
|
||||||
print_version(1);
|
|
||||||
exit(0);
|
|
||||||
break;
|
|
||||||
case 'D':
|
|
||||||
daemonize = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* ignore */
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* simply remember this */
|
|
||||||
static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
|
|
||||||
{
|
|
||||||
reset_endpoints = 1;
|
|
||||||
reset_trunk = tcfg;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int read_call_agent(struct osmo_fd *fd, unsigned int what)
|
|
||||||
{
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
socklen_t slen = sizeof(addr);
|
|
||||||
struct msgb *msg;
|
|
||||||
struct msgb *resp;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
msg = (struct msgb *) fd->data;
|
|
||||||
|
|
||||||
/* read one less so we can use it as a \0 */
|
|
||||||
int rc = recvfrom(cfg->gw_fd.bfd.fd, msg->data, msg->data_len - 1, 0,
|
|
||||||
(struct sockaddr *) &addr, &slen);
|
|
||||||
if (rc < 0) {
|
|
||||||
perror("Gateway failed to read");
|
|
||||||
return -1;
|
|
||||||
} else if (slen > sizeof(addr)) {
|
|
||||||
fprintf(stderr, "Gateway received message from outerspace: %zu %zu\n",
|
|
||||||
(size_t) slen, sizeof(addr));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* handle message now */
|
|
||||||
msg->l2h = msgb_put(msg, rc);
|
|
||||||
resp = mgcp_handle_message(cfg, msg);
|
|
||||||
msgb_reset(msg);
|
|
||||||
|
|
||||||
if (resp) {
|
|
||||||
sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
|
|
||||||
msgb_free(resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reset_endpoints) {
|
|
||||||
LOGP(DLMGCP, LOGL_NOTICE,
|
|
||||||
"Asked to reset endpoints: %d/%d\n",
|
|
||||||
reset_trunk->trunk_nr, reset_trunk->trunk_type);
|
|
||||||
reset_endpoints = 0;
|
|
||||||
|
|
||||||
/* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */
|
|
||||||
for (i = 1; i < reset_trunk->number_endpoints; ++i)
|
|
||||||
mgcp_release_endp(&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) {
|
|
||||||
case TRUNK_NODE:
|
|
||||||
vty->node = MGCP_NODE;
|
|
||||||
vty->index = NULL;
|
|
||||||
break;
|
|
||||||
case MGCP_NODE:
|
|
||||||
default:
|
|
||||||
if (mgcp_vty_is_config_node(vty, vty->node))
|
|
||||||
vty->node = CONFIG_NODE;
|
|
||||||
else
|
|
||||||
vty->node = ENABLE_NODE;
|
|
||||||
|
|
||||||
vty->index = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return vty->node;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static struct vty_app_info vty_info = {
|
|
||||||
.name = "OpenBSC MGCP",
|
|
||||||
.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[] = {
|
|
||||||
/* DLMGCP is provided by the MGCP library */
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct log_info log_info = {
|
|
||||||
.cat = log_categories,
|
|
||||||
.num_cat = ARRAY_SIZE(log_categories),
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
int on = 1, rc;
|
|
||||||
|
|
||||||
tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
|
|
||||||
msgb_talloc_ctx_init(tall_bsc_ctx, 0);
|
|
||||||
|
|
||||||
osmo_init_ignore_signals();
|
|
||||||
osmo_init_logging2(tall_bsc_ctx, &log_info);
|
|
||||||
|
|
||||||
cfg = mgcp_config_alloc();
|
|
||||||
if (!cfg)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
#ifdef BUILD_MGCP_TRANSCODING
|
|
||||||
cfg->setup_rtp_processing_cb = &mgcp_transcoding_setup;
|
|
||||||
cfg->rtp_processing_cb = &mgcp_transcoding_process_rtp;
|
|
||||||
cfg->get_net_downlink_format_cb = &mgcp_transcoding_net_downlink_format;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
cfg->trunk.force_realloc = 1;
|
|
||||||
|
|
||||||
vty_info.copyright = openbsc_copyright;
|
|
||||||
vty_init(&vty_info);
|
|
||||||
logging_vty_add_cmds(NULL);
|
|
||||||
osmo_stats_vty_add_cmds(&log_info);
|
|
||||||
mgcp_vty_init();
|
|
||||||
|
|
||||||
handle_options(argc, argv);
|
|
||||||
|
|
||||||
rate_ctr_init(tall_bsc_ctx);
|
|
||||||
osmo_stats_init(tall_bsc_ctx);
|
|
||||||
|
|
||||||
rc = mgcp_parse_config(config_file, cfg, MGCP_BSC);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* start telnet after reading config for vty_get_bind_addr() */
|
|
||||||
rc = telnet_init_dynif(tall_bsc_ctx, NULL,
|
|
||||||
vty_get_bind_addr(), OSMO_VTY_PORT_BSC_MGCP);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* set some callbacks */
|
|
||||||
cfg->reset_cb = mgcp_rsip_cb;
|
|
||||||
|
|
||||||
/* we need to bind a socket */
|
|
||||||
if (rc == 0) {
|
|
||||||
cfg->gw_fd.bfd.when = BSC_FD_READ;
|
|
||||||
cfg->gw_fd.bfd.cb = read_call_agent;
|
|
||||||
cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
||||||
if (cfg->gw_fd.bfd.fd < 0) {
|
|
||||||
perror("Gateway failed to listen");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
|
||||||
|
|
||||||
memset(&addr, 0, sizeof(addr));
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(cfg->source_port);
|
|
||||||
inet_aton(cfg->source_addr, &addr.sin_addr);
|
|
||||||
|
|
||||||
if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
|
||||||
perror("Gateway failed to bind");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
|
|
||||||
if (!cfg->gw_fd.bfd.data) {
|
|
||||||
fprintf(stderr, "Gateway memory error.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cfg->call_agent_addr) {
|
|
||||||
addr.sin_port = htons(2727);
|
|
||||||
inet_aton(cfg->call_agent_addr, &addr.sin_addr);
|
|
||||||
if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n",
|
|
||||||
cfg->call_agent_addr, errno);
|
|
||||||
close(cfg->gw_fd.bfd.fd);
|
|
||||||
cfg->gw_fd.bfd.fd = -1;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) {
|
|
||||||
LOGP(DLMGCP, LOGL_FATAL, "Failed to register the fd\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initialisation */
|
|
||||||
srand(time(NULL));
|
|
||||||
|
|
||||||
if (daemonize) {
|
|
||||||
rc = osmo_daemonize();
|
|
||||||
if (rc < 0) {
|
|
||||||
perror("Error during daemonize");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* main loop */
|
|
||||||
while (1) {
|
|
||||||
osmo_select_main(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -104,7 +104,7 @@ static void handle_options(int argc, char **argv)
|
|||||||
{0, 0, 0, 0},
|
{0, 0, 0, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "hc:VD", long_options, &option_index);
|
c = getopt_long(argc, argv, "hc:sVD", long_options, &option_index);
|
||||||
|
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
legacy_mgcp \
|
|
||||||
mgcp_client \
|
mgcp_client \
|
||||||
mgcp \
|
mgcp \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
@@ -26,7 +25,6 @@ EXTRA_DIST = \
|
|||||||
testsuite.at \
|
testsuite.at \
|
||||||
$(srcdir)/package.m4 \
|
$(srcdir)/package.m4 \
|
||||||
$(TESTSUITE) \
|
$(TESTSUITE) \
|
||||||
vty_test_runner.py \
|
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
TESTSUITE = $(srcdir)/testsuite
|
TESTSUITE = $(srcdir)/testsuite
|
||||||
@@ -39,7 +37,6 @@ if ENABLE_EXT_TESTS
|
|||||||
python-tests: $(BUILT_SOURCES)
|
python-tests: $(BUILT_SOURCES)
|
||||||
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||||
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||||
$(PYTHON) $(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v
|
|
||||||
else
|
else
|
||||||
python-tests: $(BUILT_SOURCES)
|
python-tests: $(BUILT_SOURCES)
|
||||||
echo "Not running python-based tests (determined at configure-time)"
|
echo "Not running python-based tests (determined at configure-time)"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
enable_nat_test='@osmo_ac_build_nat@'
|
enable_nat_test='@osmo_ac_build_nat@'
|
||||||
enable_smpp_test='@osmo_ac_build_smpp@'
|
enable_smpp_test='@osmo_ac_build_smpp@'
|
||||||
enable_bsc_test='@osmo_ac_build_bsc@'
|
enable_bsc_test='@osmo_ac_build_bsc@'
|
||||||
enable_mgcp_transcoding_test='@osmo_ac_mgcp_transcoding@'
|
|
||||||
enable_sgsn_test='@found_libgtp_and_libcares@'
|
enable_sgsn_test='@found_libgtp_and_libcares@'
|
||||||
enable_oap_test='@found_libgtp_and_libcares@'
|
enable_oap_test='@found_libgtp_and_libcares@'
|
||||||
enable_gtphub_test='@found_libgtp_and_libcares@'
|
enable_gtphub_test='@found_libgtp_and_libcares@'
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
AM_CPPFLAGS = \
|
|
||||||
$(all_includes) \
|
|
||||||
-I$(top_srcdir)/include \
|
|
||||||
-I$(top_srcdir) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
AM_CFLAGS = \
|
|
||||||
-Wall \
|
|
||||||
-ggdb3 \
|
|
||||||
$(LIBOSMOCORE_CFLAGS) \
|
|
||||||
$(LIBOSMONETIF_CFLAGS) \
|
|
||||||
$(LIBBCG729_CFLAGS) \
|
|
||||||
$(COVERAGE_CFLAGS) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
AM_LDFLAGS = \
|
|
||||||
$(COVERAGE_LDFLAGS) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
EXTRA_DIST = \
|
|
||||||
mgcp_test.ok \
|
|
||||||
mgcp_transcoding_test.ok \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
noinst_PROGRAMS = \
|
|
||||||
mgcp_test \
|
|
||||||
$(NULL)
|
|
||||||
if BUILD_MGCP_TRANSCODING
|
|
||||||
noinst_PROGRAMS += \
|
|
||||||
mgcp_transcoding_test \
|
|
||||||
$(NULL)
|
|
||||||
endif
|
|
||||||
|
|
||||||
mgcp_test_SOURCES = \
|
|
||||||
mgcp_test.c \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
mgcp_test_LDADD = \
|
|
||||||
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
|
|
||||||
$(LIBOSMOCORE_LIBS) \
|
|
||||||
$(LIBOSMOVTY_LIBS) \
|
|
||||||
$(LIBRARY_DL) \
|
|
||||||
$(LIBOSMONETIF_LIBS) \
|
|
||||||
$(LIBRARY_GSM) \
|
|
||||||
-lm \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
mgcp_transcoding_test_SOURCES = \
|
|
||||||
mgcp_transcoding_test.c \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
mgcp_transcoding_test_LDADD = \
|
|
||||||
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
|
|
||||||
$(LIBOSMOCORE_LIBS) \
|
|
||||||
$(LIBOSMOVTY_LIBS) \
|
|
||||||
$(LIBBCG729_LIBS) \
|
|
||||||
$(LIBRARY_DL) \
|
|
||||||
$(LIBOSMONETIF_LIBS) \
|
|
||||||
$(LIBRARY_GSM) \
|
|
||||||
-lm \
|
|
||||||
$(NULL)
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,480 +0,0 @@
|
|||||||
line: 'one CR'
|
|
||||||
line: 'two CR'
|
|
||||||
line: ''
|
|
||||||
line: 'one CRLF'
|
|
||||||
line: 'two CRLF'
|
|
||||||
line: ''
|
|
||||||
line: 'one LF'
|
|
||||||
line: 'two LF'
|
|
||||||
line: ''
|
|
||||||
line: 'mixed (4 lines)'
|
|
||||||
line: ''
|
|
||||||
line: ''
|
|
||||||
line: ''
|
|
||||||
Testing AUEP1
|
|
||||||
Testing AUEP2
|
|
||||||
Testing MDCX1
|
|
||||||
Testing MDCX2
|
|
||||||
Testing CRCX
|
|
||||||
Dummy packets: 1
|
|
||||||
Detected packet duration: 40
|
|
||||||
Requested packetetization period: 20-20
|
|
||||||
Connection mode: 1: RECV
|
|
||||||
Testing MDCX3
|
|
||||||
Dummy packets: 1
|
|
||||||
Packet duration not set
|
|
||||||
Requested packetization period not set
|
|
||||||
Connection mode not set
|
|
||||||
Testing MDCX4
|
|
||||||
Dummy packets: 1
|
|
||||||
Detected packet duration: 40
|
|
||||||
Requested packetetization period: 20-20
|
|
||||||
Connection mode: 3: SEND RECV
|
|
||||||
Testing MDCX4_PT1
|
|
||||||
Dummy packets: 1
|
|
||||||
Detected packet duration: 40
|
|
||||||
Requested packetetization period: 20-40
|
|
||||||
Connection mode: 3: SEND RECV
|
|
||||||
Testing MDCX4_PT2
|
|
||||||
Dummy packets: 1
|
|
||||||
Detected packet duration: 40
|
|
||||||
Requested packetetization period: 20-20
|
|
||||||
Connection mode: 3: SEND RECV
|
|
||||||
Testing MDCX4_PT3
|
|
||||||
Dummy packets: 1
|
|
||||||
Detected packet duration: 40
|
|
||||||
Requested packetization period not set
|
|
||||||
Connection mode: 3: SEND RECV
|
|
||||||
Testing MDCX4_SO
|
|
||||||
Detected packet duration: 40
|
|
||||||
Requested packetetization period: 20-20
|
|
||||||
Connection mode: 2: SEND
|
|
||||||
Testing MDCX4_RO
|
|
||||||
Dummy packets: 1
|
|
||||||
Packet duration not set
|
|
||||||
Requested packetetization period: 20-20
|
|
||||||
Connection mode: 1: RECV
|
|
||||||
Testing DLCX
|
|
||||||
Detected packet duration: 20
|
|
||||||
Requested packetization period not set
|
|
||||||
Connection mode: 0: NONE
|
|
||||||
Testing CRCX_ZYN
|
|
||||||
Dummy packets: 1
|
|
||||||
Packet duration not set
|
|
||||||
Requested packetization period not set
|
|
||||||
Connection mode: 1: RECV
|
|
||||||
Testing EMPTY
|
|
||||||
Testing SHORT1
|
|
||||||
Testing SHORT2
|
|
||||||
Testing SHORT3
|
|
||||||
Testing SHORT4
|
|
||||||
Testing RQNT1
|
|
||||||
Testing RQNT2
|
|
||||||
Testing DLCX
|
|
||||||
Detected packet duration: 20
|
|
||||||
Requested packetization period not set
|
|
||||||
Connection mode: 0: NONE
|
|
||||||
Testing CRCX
|
|
||||||
Dummy packets: 1
|
|
||||||
Detected packet duration: 40
|
|
||||||
Requested packetetization period: 20-20
|
|
||||||
Connection mode: 1: RECV
|
|
||||||
Testing MDCX3
|
|
||||||
Dummy packets: 1
|
|
||||||
Packet duration not set
|
|
||||||
Requested packetization period not set
|
|
||||||
Connection mode not set
|
|
||||||
Testing DLCX
|
|
||||||
Detected packet duration: 20
|
|
||||||
Requested packetization period not set
|
|
||||||
Connection mode: 0: NONE
|
|
||||||
Testing CRCX
|
|
||||||
Re-transmitting CRCX
|
|
||||||
Testing RQNT1
|
|
||||||
Re-transmitting RQNT1
|
|
||||||
Testing RQNT2
|
|
||||||
Re-transmitting RQNT2
|
|
||||||
Testing MDCX3
|
|
||||||
Re-transmitting MDCX3
|
|
||||||
Testing DLCX
|
|
||||||
Re-transmitting DLCX
|
|
||||||
Testing packet loss calculation.
|
|
||||||
Testing stat parsing
|
|
||||||
Parsing result: 0
|
|
||||||
Parsing result: 0
|
|
||||||
Testing packet error detection, patch SSRC.
|
|
||||||
Output SSRC changed to 11223344
|
|
||||||
In TS: 0, dTS: 0, Seq: 0
|
|
||||||
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 160, dTS: 160, Seq: 1
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 2
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 3
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 10, Transit = 160
|
|
||||||
In TS: 480, dTS: 160, Seq: 4
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 9, Transit = 160
|
|
||||||
In TS: 640, dTS: 160, Seq: 5
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 8, Transit = 160
|
|
||||||
In TS: 960, dTS: 320, Seq: 6
|
|
||||||
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 18, Transit = 0
|
|
||||||
In TS: 1120, dTS: 160, Seq: 7
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 17, Transit = 0
|
|
||||||
In TS: 1280, dTS: 160, Seq: 8
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 0
|
|
||||||
In TS: 1400, dTS: 120, Seq: 9
|
|
||||||
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 17, Transit = 40
|
|
||||||
In TS: 1560, dTS: 160, Seq: 10
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 40
|
|
||||||
In TS: 1720, dTS: 160, Seq: 11
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 15, Transit = 40
|
|
||||||
In TS: 34688, dTS: 0, Seq: 12
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 34848, dTS: 160, Seq: 13
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35008, dTS: 160, Seq: 14
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35128, dTS: 120, Seq: 15
|
|
||||||
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35288, dTS: 160, Seq: 16
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35448, dTS: 160, Seq: 17
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35768, dTS: 160, Seq: 19
|
|
||||||
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 12, Transit = -32888
|
|
||||||
In TS: 35928, dTS: 160, Seq: 20
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 11, Transit = -32888
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 20, Transit = -33048
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 29, Transit = -32888
|
|
||||||
In TS: 36248, dTS: 160, Seq: 22
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 27, Transit = -32888
|
|
||||||
In TS: 36408, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 25, Transit = -32888
|
|
||||||
In TS: 36568, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 24, Transit = -32888
|
|
||||||
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: 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
|
|
||||||
In TS: 160000, dTS: 0, Seq: 1000
|
|
||||||
Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160160, dTS: 160, Seq: 1001
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160320, dTS: 160, Seq: 1002
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
Testing packet error detection.
|
|
||||||
Output SSRC changed to 11223344
|
|
||||||
In TS: 0, dTS: 0, Seq: 0
|
|
||||||
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 160, dTS: 160, Seq: 1
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 2
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 3
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 10, Transit = 160
|
|
||||||
In TS: 480, dTS: 160, Seq: 4
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 9, Transit = 160
|
|
||||||
In TS: 640, dTS: 160, Seq: 5
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 8, Transit = 160
|
|
||||||
In TS: 960, dTS: 320, Seq: 6
|
|
||||||
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 18, Transit = 0
|
|
||||||
In TS: 1120, dTS: 160, Seq: 7
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 17, Transit = 0
|
|
||||||
In TS: 1280, dTS: 160, Seq: 8
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 0
|
|
||||||
In TS: 1400, dTS: 120, Seq: 9
|
|
||||||
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 17, Transit = 40
|
|
||||||
In TS: 1560, dTS: 160, Seq: 10
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 40
|
|
||||||
In TS: 1720, dTS: 160, Seq: 11
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 15, Transit = 40
|
|
||||||
Output SSRC changed to 10203040
|
|
||||||
In TS: 34688, dTS: 0, Seq: 12
|
|
||||||
Out TS change: 32968, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 34848, dTS: 160, Seq: 13
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35008, dTS: 160, Seq: 14
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35128, dTS: 120, Seq: 15
|
|
||||||
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35288, dTS: 160, Seq: 16
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35448, dTS: 160, Seq: 17
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35768, dTS: 160, Seq: 19
|
|
||||||
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 12, Transit = -32888
|
|
||||||
In TS: 35928, dTS: 160, Seq: 20
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 11, Transit = -32888
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 20, Transit = -33048
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 29, Transit = -32888
|
|
||||||
In TS: 36248, dTS: 160, Seq: 22
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 27, Transit = -32888
|
|
||||||
In TS: 36408, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 25, Transit = -32888
|
|
||||||
In TS: 36568, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 24, Transit = -32888
|
|
||||||
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: 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
|
|
||||||
Output SSRC changed to 50607080
|
|
||||||
In TS: 160000, dTS: 0, Seq: 1000
|
|
||||||
Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160160, dTS: 160, Seq: 1001
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160320, dTS: 160, Seq: 1002
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
Testing packet error detection, patch timestamps.
|
|
||||||
Output SSRC changed to 11223344
|
|
||||||
In TS: 0, dTS: 0, Seq: 0
|
|
||||||
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 160, dTS: 160, Seq: 1
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 2
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 3
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 10, Transit = 160
|
|
||||||
In TS: 480, dTS: 160, Seq: 4
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 9, Transit = 160
|
|
||||||
In TS: 640, dTS: 160, Seq: 5
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 8, Transit = 160
|
|
||||||
In TS: 960, dTS: 320, Seq: 6
|
|
||||||
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 18, Transit = 0
|
|
||||||
In TS: 1120, dTS: 160, Seq: 7
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 17, Transit = 0
|
|
||||||
In TS: 1280, dTS: 160, Seq: 8
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 0
|
|
||||||
In TS: 1400, dTS: 120, Seq: 9
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
|
|
||||||
Stats: Jitter = 17, Transit = 40
|
|
||||||
In TS: 1560, dTS: 160, Seq: 10
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 40
|
|
||||||
In TS: 1720, dTS: 160, Seq: 11
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 15, Transit = 40
|
|
||||||
Output SSRC changed to 10203040
|
|
||||||
In TS: 34688, dTS: 0, Seq: 12
|
|
||||||
Out TS change: 32968, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 34848, dTS: 160, Seq: 13
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35008, dTS: 160, Seq: 14
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35128, dTS: 120, Seq: 15
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35288, dTS: 160, Seq: 16
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35448, dTS: 160, Seq: 17
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35768, dTS: 160, Seq: 19
|
|
||||||
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 12, Transit = -32888
|
|
||||||
In TS: 35928, dTS: 160, Seq: 20
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 11, Transit = -32888
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 20, Transit = -33048
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 29, Transit = -32888
|
|
||||||
In TS: 36248, dTS: 160, Seq: 22
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 27, Transit = -32888
|
|
||||||
In TS: 36408, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 25, Transit = -32888
|
|
||||||
In TS: 36568, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 24, Transit = -32888
|
|
||||||
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: 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
|
|
||||||
Output SSRC changed to 50607080
|
|
||||||
In TS: 160000, dTS: 0, Seq: 1000
|
|
||||||
Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160160, dTS: 160, Seq: 1001
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160320, dTS: 160, Seq: 1002
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
Testing packet error detection, patch SSRC, patch timestamps.
|
|
||||||
Output SSRC changed to 11223344
|
|
||||||
In TS: 0, dTS: 0, Seq: 0
|
|
||||||
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 160, dTS: 160, Seq: 1
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 2
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 3
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 10, Transit = 160
|
|
||||||
In TS: 480, dTS: 160, Seq: 4
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 9, Transit = 160
|
|
||||||
In TS: 640, dTS: 160, Seq: 5
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 8, Transit = 160
|
|
||||||
In TS: 960, dTS: 320, Seq: 6
|
|
||||||
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 18, Transit = 0
|
|
||||||
In TS: 1120, dTS: 160, Seq: 7
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 17, Transit = 0
|
|
||||||
In TS: 1280, dTS: 160, Seq: 8
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 0
|
|
||||||
In TS: 1400, dTS: 120, Seq: 9
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
|
|
||||||
Stats: Jitter = 17, Transit = 40
|
|
||||||
In TS: 1560, dTS: 160, Seq: 10
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 40
|
|
||||||
In TS: 1720, dTS: 160, Seq: 11
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 15, Transit = 40
|
|
||||||
In TS: 34688, dTS: 0, Seq: 12
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 34848, dTS: 160, Seq: 13
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35008, dTS: 160, Seq: 14
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35128, dTS: 120, Seq: 15
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35288, dTS: 160, Seq: 16
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35448, dTS: 160, Seq: 17
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35768, dTS: 160, Seq: 19
|
|
||||||
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 12, Transit = -32888
|
|
||||||
In TS: 35928, dTS: 160, Seq: 20
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 11, Transit = -32888
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 20, Transit = -33048
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 29, Transit = -32888
|
|
||||||
In TS: 36248, dTS: 160, Seq: 22
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 27, Transit = -32888
|
|
||||||
In TS: 36408, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 25, Transit = -32888
|
|
||||||
In TS: 36568, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 24, Transit = -32888
|
|
||||||
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: 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
|
|
||||||
In TS: 160000, dTS: 0, Seq: 1000
|
|
||||||
Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160160, dTS: 160, Seq: 1001
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160320, dTS: 160, Seq: 1002
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
Testing multiple payload types
|
|
||||||
Testing no sequence flow on initial packet
|
|
||||||
Testing no rtpmap name
|
|
||||||
Done
|
|
||||||
@@ -1,662 +0,0 @@
|
|||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <err.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <osmocom/core/talloc.h>
|
|
||||||
#include <osmocom/core/application.h>
|
|
||||||
|
|
||||||
#include <osmocom/netif/rtp.h>
|
|
||||||
|
|
||||||
#include <osmocom/legacy_mgcp/mgcp.h>
|
|
||||||
#include <osmocom/legacy_mgcp/mgcp_internal.h>
|
|
||||||
|
|
||||||
#include "bscconfig.h"
|
|
||||||
#ifndef BUILD_MGCP_TRANSCODING
|
|
||||||
#error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <osmocom/legacy_mgcp/mgcp_transcode.h>
|
|
||||||
|
|
||||||
uint8_t *audio_frame_l16[] = {
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets {
|
|
||||||
float t;
|
|
||||||
int len;
|
|
||||||
char *data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets audio_packets_l16[] = {
|
|
||||||
/* RTP: SeqNo=1, TS=160 */
|
|
||||||
{0.020000, 332,
|
|
||||||
"\x80\x0B\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets audio_packets_gsm[] = {
|
|
||||||
/* RTP: SeqNo=1, TS=160 */
|
|
||||||
{0.020000, 45,
|
|
||||||
"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
|
|
||||||
"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
|
|
||||||
"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
|
|
||||||
"\xDE"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets audio_packets_gsm_invalid_size[] = {
|
|
||||||
/* RTP: SeqNo=1, TS=160 */
|
|
||||||
{0.020000, 41,
|
|
||||||
"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
|
|
||||||
"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
|
|
||||||
"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
|
|
||||||
"\xDE"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets audio_packets_gsm_invalid_data[] = {
|
|
||||||
/* RTP: SeqNo=1, TS=160 */
|
|
||||||
{0.020000, 45,
|
|
||||||
"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
|
|
||||||
"\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE"
|
|
||||||
"\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE"
|
|
||||||
"\xEE"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets audio_packets_gsm_invalid_ptype[] = {
|
|
||||||
/* RTP: SeqNo=1, TS=160 */
|
|
||||||
{0.020000, 45,
|
|
||||||
"\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
|
|
||||||
"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
|
|
||||||
"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
|
|
||||||
"\xDE"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets audio_packets_g729[] = {
|
|
||||||
/* RTP: SeqNo=1, TS=160 */
|
|
||||||
{0.020000, 32,
|
|
||||||
"\x80\x12\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
|
|
||||||
"\xAF\xC2\x81\x40\x00\xFA\xCE\xA4\x21\x7C\xC5\xC3\x4F\xA5\x98\xF5"
|
|
||||||
"\xB2\x95\xC4\xAD"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets audio_packets_pcma[] = {
|
|
||||||
/* RTP: SeqNo=1, TS=160 */
|
|
||||||
{0.020000, 172,
|
|
||||||
"\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
},
|
|
||||||
/* RTP: SeqNo=26527, TS=232640 */
|
|
||||||
{0.020000, 92,
|
|
||||||
"\x80\x08\x67\x9f\x00\x03\x8c\xc0\x04\xaa\x67\x9f\xd5\xd5\xd5\xd5"
|
|
||||||
"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
|
|
||||||
"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
|
|
||||||
"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
|
|
||||||
"\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\xd5\xd5\x55\x55\xd5\xd5\x55\x55"
|
|
||||||
"\xd5\xd5\xd5\x55\x55\xd5\xd5\xd5\x55\x55\xd5\xd5"
|
|
||||||
},
|
|
||||||
/* RTP: SeqNo=26528, TS=232720 */
|
|
||||||
{0.020000, 92,
|
|
||||||
"\x80\x08\x67\xa0\x00\x03\x8d\x10\x04\xaa\x67\x9f\x55\xd5\xd5\x55"
|
|
||||||
"\xd5\x55\xd5\xd5\xd5\x55\xd5\x55\xd5\xd5\x55\xd5\x55\xd5\x55\xd5"
|
|
||||||
"\x55\x55\xd5\x55\xd5\xd5\x55\x55\x55\x55\x55\xd5\xd5\x55\xd5\xd5"
|
|
||||||
"\xd5\x55\xd5\xd5\xd5\x55\x54\x55\xd5\xd5\x55\xd5\xd5\xd5\xd5\x55"
|
|
||||||
"\x54\x55\xd5\x55\xd5\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\xd4"
|
|
||||||
"\xd5\x54\x55\xd5\xd4\xd5\x54\xd5\x55\xd5\xd5\xd5"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int audio_name_to_type(const char *name)
|
|
||||||
{
|
|
||||||
if (!strcasecmp(name, "gsm"))
|
|
||||||
return 3;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
else if (!strcasecmp(name, "g729"))
|
|
||||||
return 18;
|
|
||||||
#endif
|
|
||||||
else if (!strcasecmp(name, "pcma"))
|
|
||||||
return 8;
|
|
||||||
else if (!strcasecmp(name, "l16"))
|
|
||||||
return 11;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst);
|
|
||||||
|
|
||||||
static int given_configured_endpoint(int in_samples, int out_samples,
|
|
||||||
const char *srcfmt, const char *dstfmt,
|
|
||||||
void **out_ctx, struct mgcp_endpoint **out_endp)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
struct mgcp_rtp_end *dst_end;
|
|
||||||
struct mgcp_rtp_end *src_end;
|
|
||||||
struct mgcp_config *cfg;
|
|
||||||
struct mgcp_trunk_config *tcfg;
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
|
|
||||||
cfg = mgcp_config_alloc();
|
|
||||||
tcfg = talloc_zero(cfg, struct mgcp_trunk_config);
|
|
||||||
endp = talloc_zero(tcfg, struct mgcp_endpoint);
|
|
||||||
|
|
||||||
cfg->setup_rtp_processing_cb = mgcp_transcoding_setup;
|
|
||||||
cfg->rtp_processing_cb = mgcp_transcoding_process_rtp;
|
|
||||||
cfg->get_net_downlink_format_cb = mgcp_transcoding_net_downlink_format;
|
|
||||||
|
|
||||||
tcfg->endpoints = endp;
|
|
||||||
tcfg->number_endpoints = 1;
|
|
||||||
tcfg->cfg = cfg;
|
|
||||||
endp->tcfg = tcfg;
|
|
||||||
endp->cfg = cfg;
|
|
||||||
mgcp_initialize_endp(endp);
|
|
||||||
|
|
||||||
dst_end = &endp->bts_end;
|
|
||||||
dst_end->codec.payload_type = audio_name_to_type(dstfmt);
|
|
||||||
|
|
||||||
src_end = &endp->net_end;
|
|
||||||
src_end->codec.payload_type = audio_name_to_type(srcfmt);
|
|
||||||
|
|
||||||
if (out_samples) {
|
|
||||||
dst_end->codec.frame_duration_den = dst_end->codec.rate;
|
|
||||||
dst_end->codec.frame_duration_num = out_samples;
|
|
||||||
dst_end->frames_per_packet = 1;
|
|
||||||
dst_end->force_output_ptime = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = mgcp_transcoding_setup(endp, dst_end, src_end);
|
|
||||||
if (rc < 0) {
|
|
||||||
printf("setup failed: %s", strerror(-rc));
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
*out_ctx = cfg;
|
|
||||||
*out_endp = endp;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int transcode_test(const char *srcfmt, const char *dstfmt,
|
|
||||||
uint8_t *src_pkts, size_t src_pkt_size)
|
|
||||||
{
|
|
||||||
char buf[4096] = {0x80, 0};
|
|
||||||
void *ctx;
|
|
||||||
|
|
||||||
struct mgcp_rtp_end *dst_end;
|
|
||||||
struct mgcp_process_rtp_state *state;
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
int in_size;
|
|
||||||
const int in_samples = 160;
|
|
||||||
int len, cont;
|
|
||||||
|
|
||||||
printf("== Transcoding test ==\n");
|
|
||||||
printf("converting %s -> %s\n", srcfmt, dstfmt);
|
|
||||||
|
|
||||||
given_configured_endpoint(in_samples, 0, srcfmt, dstfmt, &ctx, &endp);
|
|
||||||
|
|
||||||
dst_end = &endp->bts_end;
|
|
||||||
state = dst_end->rtp_process_data;
|
|
||||||
OSMO_ASSERT(state != NULL);
|
|
||||||
|
|
||||||
in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0);
|
|
||||||
OSMO_ASSERT(sizeof(buf) >= in_size + 12);
|
|
||||||
|
|
||||||
memcpy(buf, src_pkts, src_pkt_size);
|
|
||||||
|
|
||||||
len = src_pkt_size;
|
|
||||||
|
|
||||||
cont = mgcp_transcoding_process_rtp(endp, dst_end,
|
|
||||||
buf, &len, sizeof(buf));
|
|
||||||
if (cont < 0) {
|
|
||||||
printf("Nothing encoded due: %s\n", strerror(-cont));
|
|
||||||
talloc_free(ctx);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len < 24) {
|
|
||||||
printf("encoded: %s\n", osmo_hexdump((unsigned char *)buf, len));
|
|
||||||
} else {
|
|
||||||
const char *str = osmo_hexdump((unsigned char *)buf, len);
|
|
||||||
int i = 0;
|
|
||||||
const int prefix = 4;
|
|
||||||
const int cutlen = 48;
|
|
||||||
int nchars = 0;
|
|
||||||
|
|
||||||
printf("encoded:\n");
|
|
||||||
do {
|
|
||||||
nchars = printf("%*s%-.*s", prefix, "", cutlen, str + i);
|
|
||||||
i += nchars - prefix;
|
|
||||||
printf("\n");
|
|
||||||
} while (nchars - prefix >= cutlen);
|
|
||||||
}
|
|
||||||
printf("counted: %d\n", cont);
|
|
||||||
talloc_free(ctx);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_rtp_seq_state(void)
|
|
||||||
{
|
|
||||||
char buf[4096];
|
|
||||||
int len;
|
|
||||||
int cont;
|
|
||||||
void *ctx;
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
struct mgcp_process_rtp_state *state;
|
|
||||||
struct rtp_hdr *hdr;
|
|
||||||
uint32_t ts_no;
|
|
||||||
uint16_t seq_no;
|
|
||||||
|
|
||||||
given_configured_endpoint(160, 0, "pcma", "l16", &ctx, &endp);
|
|
||||||
state = endp->bts_end.rtp_process_data;
|
|
||||||
OSMO_ASSERT(!state->is_running);
|
|
||||||
OSMO_ASSERT(state->next_seq == 0);
|
|
||||||
OSMO_ASSERT(state->next_time == 0);
|
|
||||||
|
|
||||||
/* initialize packet */
|
|
||||||
len = audio_packets_pcma[0].len;
|
|
||||||
memcpy(buf, audio_packets_pcma[0].data, len);
|
|
||||||
cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, len);
|
|
||||||
OSMO_ASSERT(cont >= 0);
|
|
||||||
OSMO_ASSERT(state->is_running);
|
|
||||||
OSMO_ASSERT(state->next_seq == 2);
|
|
||||||
OSMO_ASSERT(state->next_time == 240);
|
|
||||||
|
|
||||||
/* verify that the right timestamp was written */
|
|
||||||
OSMO_ASSERT(len == audio_packets_pcma[0].len);
|
|
||||||
hdr = (struct rtp_hdr *) &buf[0];
|
|
||||||
|
|
||||||
memcpy(&ts_no, &hdr->timestamp, sizeof(ts_no));
|
|
||||||
OSMO_ASSERT(htonl(ts_no) == 160);
|
|
||||||
memcpy(&seq_no, &hdr->sequence, sizeof(seq_no));
|
|
||||||
OSMO_ASSERT(htons(seq_no) == 1);
|
|
||||||
/* Check the right sequence number is written */
|
|
||||||
state->next_seq = 1234;
|
|
||||||
len = audio_packets_pcma[0].len;
|
|
||||||
memcpy(buf, audio_packets_pcma[0].data, len);
|
|
||||||
cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, len);
|
|
||||||
OSMO_ASSERT(cont >= 0);
|
|
||||||
OSMO_ASSERT(len == audio_packets_pcma[0].len);
|
|
||||||
hdr = (struct rtp_hdr *) &buf[0];
|
|
||||||
|
|
||||||
memcpy(&seq_no, &hdr->sequence, sizeof(seq_no));
|
|
||||||
OSMO_ASSERT(htons(seq_no) == 1234);
|
|
||||||
|
|
||||||
talloc_free(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_transcode_result(void)
|
|
||||||
{
|
|
||||||
char buf[4096];
|
|
||||||
int len, res;
|
|
||||||
void *ctx;
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
struct mgcp_process_rtp_state *state;
|
|
||||||
|
|
||||||
{
|
|
||||||
/* from GSM to PCMA and same ptime */
|
|
||||||
given_configured_endpoint(160, 0, "gsm", "pcma", &ctx, &endp);
|
|
||||||
state = endp->bts_end.rtp_process_data;
|
|
||||||
|
|
||||||
/* result */
|
|
||||||
len = audio_packets_gsm[0].len;
|
|
||||||
memcpy(buf, audio_packets_gsm[0].data, len);
|
|
||||||
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
|
|
||||||
OSMO_ASSERT(res == sizeof(struct rtp_hdr));
|
|
||||||
OSMO_ASSERT(state->sample_cnt == 0);
|
|
||||||
|
|
||||||
len = res;
|
|
||||||
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
|
|
||||||
OSMO_ASSERT(res == -ENOMSG);
|
|
||||||
|
|
||||||
talloc_free(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
/* from GSM to PCMA and same ptime */
|
|
||||||
given_configured_endpoint(160, 160, "gsm", "pcma", &ctx, &endp);
|
|
||||||
state = endp->bts_end.rtp_process_data;
|
|
||||||
|
|
||||||
/* result */
|
|
||||||
len = audio_packets_gsm[0].len;
|
|
||||||
memcpy(buf, audio_packets_gsm[0].data, len);
|
|
||||||
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
|
|
||||||
OSMO_ASSERT(res == sizeof(struct rtp_hdr));
|
|
||||||
OSMO_ASSERT(state->sample_cnt == 0);
|
|
||||||
|
|
||||||
len = res;
|
|
||||||
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
|
|
||||||
OSMO_ASSERT(res == -EAGAIN);
|
|
||||||
|
|
||||||
talloc_free(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
/* from PCMA to GSM and wrong different ptime */
|
|
||||||
given_configured_endpoint(80, 160, "pcma", "gsm", &ctx, &endp);
|
|
||||||
state = endp->bts_end.rtp_process_data;
|
|
||||||
|
|
||||||
/* Add the first sample */
|
|
||||||
len = audio_packets_pcma[1].len;
|
|
||||||
memcpy(buf, audio_packets_pcma[1].data, len);
|
|
||||||
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
|
|
||||||
OSMO_ASSERT(state->sample_cnt == 80);
|
|
||||||
OSMO_ASSERT(state->next_time == 232640);
|
|
||||||
OSMO_ASSERT(res < 0);
|
|
||||||
|
|
||||||
/* Add the second sample and it should be consumable */
|
|
||||||
len = audio_packets_pcma[2].len;
|
|
||||||
memcpy(buf, audio_packets_pcma[2].data, len);
|
|
||||||
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
|
|
||||||
OSMO_ASSERT(state->sample_cnt == 0);
|
|
||||||
OSMO_ASSERT(state->next_time == 232640 + 80 + 160);
|
|
||||||
OSMO_ASSERT(res == sizeof(struct rtp_hdr));
|
|
||||||
|
|
||||||
talloc_free(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
/* from PCMA to GSM with a big time jump */
|
|
||||||
struct rtp_hdr *hdr;
|
|
||||||
uint32_t ts;
|
|
||||||
|
|
||||||
given_configured_endpoint(80, 160, "pcma", "gsm", &ctx, &endp);
|
|
||||||
state = endp->bts_end.rtp_process_data;
|
|
||||||
|
|
||||||
/* Add the first sample */
|
|
||||||
len = audio_packets_pcma[1].len;
|
|
||||||
memcpy(buf, audio_packets_pcma[1].data, len);
|
|
||||||
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
|
|
||||||
OSMO_ASSERT(state->sample_cnt == 80);
|
|
||||||
OSMO_ASSERT(state->next_time == 232640);
|
|
||||||
OSMO_ASSERT(state->next_seq == 26527);
|
|
||||||
OSMO_ASSERT(res < 0);
|
|
||||||
|
|
||||||
/* Add a skip to the packet to force a 'resync' */
|
|
||||||
len = audio_packets_pcma[2].len;
|
|
||||||
memcpy(buf, audio_packets_pcma[2].data, len);
|
|
||||||
hdr = (struct rtp_hdr *) &buf[0];
|
|
||||||
/* jump the time and add alignment error */
|
|
||||||
ts = ntohl(hdr->timestamp) + 123 * 80 + 2;
|
|
||||||
hdr->timestamp = htonl(ts);
|
|
||||||
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
|
|
||||||
OSMO_ASSERT(res < 0);
|
|
||||||
OSMO_ASSERT(state->sample_cnt == 80);
|
|
||||||
OSMO_ASSERT(state->next_time == ts);
|
|
||||||
OSMO_ASSERT(state->next_seq == 26527);
|
|
||||||
/* TODO: this can create alignment errors */
|
|
||||||
|
|
||||||
|
|
||||||
/* Now attempt to consume 160 samples */
|
|
||||||
len = audio_packets_pcma[2].len;
|
|
||||||
memcpy(buf, audio_packets_pcma[2].data, len);
|
|
||||||
hdr = (struct rtp_hdr *) &buf[0];
|
|
||||||
ts += 80;
|
|
||||||
hdr->timestamp = htonl(ts);
|
|
||||||
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
|
|
||||||
OSMO_ASSERT(res == 12);
|
|
||||||
OSMO_ASSERT(state->sample_cnt == 0);
|
|
||||||
OSMO_ASSERT(state->next_time == ts + 160);
|
|
||||||
OSMO_ASSERT(state->next_seq == 26528);
|
|
||||||
|
|
||||||
talloc_free(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_transcode_change(void)
|
|
||||||
{
|
|
||||||
char buf[4096] = {0x80, 0};
|
|
||||||
void *ctx;
|
|
||||||
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
struct mgcp_process_rtp_state *state;
|
|
||||||
struct rtp_hdr *hdr;
|
|
||||||
|
|
||||||
int len, res;
|
|
||||||
|
|
||||||
{
|
|
||||||
/* from GSM to PCMA and same ptime */
|
|
||||||
printf("Testing Initial L16->GSM, PCMA->GSM\n");
|
|
||||||
given_configured_endpoint(160, 0, "l16", "gsm", &ctx, &endp);
|
|
||||||
endp->net_end.alt_codec = endp->net_end.codec;
|
|
||||||
endp->net_end.alt_codec.payload_type = audio_name_to_type("pcma");
|
|
||||||
state = endp->bts_end.rtp_process_data;
|
|
||||||
|
|
||||||
/* initial transcoding work */
|
|
||||||
OSMO_ASSERT(state->src_fmt == AF_L16);
|
|
||||||
OSMO_ASSERT(state->dst_fmt == AF_GSM);
|
|
||||||
OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 8);
|
|
||||||
OSMO_ASSERT(endp->net_end.codec.payload_type == 11);
|
|
||||||
|
|
||||||
/* result */
|
|
||||||
len = audio_packets_pcma[0].len;
|
|
||||||
memcpy(buf, audio_packets_pcma[0].data, len);
|
|
||||||
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
|
|
||||||
state = endp->bts_end.rtp_process_data;
|
|
||||||
OSMO_ASSERT(res == sizeof(struct rtp_hdr));
|
|
||||||
OSMO_ASSERT(state->sample_cnt == 0);
|
|
||||||
OSMO_ASSERT(state->src_fmt == AF_PCMA);
|
|
||||||
OSMO_ASSERT(state->dst_fmt == AF_GSM);
|
|
||||||
OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 11);
|
|
||||||
OSMO_ASSERT(endp->net_end.codec.payload_type == 8);
|
|
||||||
|
|
||||||
len = res;
|
|
||||||
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
|
|
||||||
OSMO_ASSERT(res == -ENOMSG);
|
|
||||||
OSMO_ASSERT(state == endp->bts_end.rtp_process_data);
|
|
||||||
|
|
||||||
|
|
||||||
/* now check that comfort noise doesn't change anything */
|
|
||||||
len = audio_packets_pcma[1].len;
|
|
||||||
memcpy(buf, audio_packets_pcma[1].data, len);
|
|
||||||
hdr = (struct rtp_hdr *) buf;
|
|
||||||
hdr->payload_type = 12;
|
|
||||||
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
|
|
||||||
OSMO_ASSERT(state == endp->bts_end.rtp_process_data);
|
|
||||||
OSMO_ASSERT(state->sample_cnt == 80);
|
|
||||||
OSMO_ASSERT(state->src_fmt == AF_PCMA);
|
|
||||||
OSMO_ASSERT(state->dst_fmt == AF_GSM);
|
|
||||||
OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 11);
|
|
||||||
OSMO_ASSERT(endp->net_end.codec.payload_type == 8);
|
|
||||||
|
|
||||||
talloc_free(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int test_repacking(int in_samples, int out_samples, int no_transcode)
|
|
||||||
{
|
|
||||||
char buf[4096] = {0x80, 0};
|
|
||||||
int cc;
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
void *ctx;
|
|
||||||
|
|
||||||
struct mgcp_process_rtp_state *state;
|
|
||||||
int in_cnt;
|
|
||||||
int out_size;
|
|
||||||
int in_size;
|
|
||||||
uint32_t ts = 0;
|
|
||||||
uint16_t seq = 0;
|
|
||||||
const char *srcfmt = "pcma";
|
|
||||||
const char *dstfmt = no_transcode ? "pcma" : "l16";
|
|
||||||
|
|
||||||
printf("== Transcoding test ==\n");
|
|
||||||
printf("converting %s -> %s\n", srcfmt, dstfmt);
|
|
||||||
|
|
||||||
given_configured_endpoint(in_samples, out_samples, srcfmt, dstfmt, &ctx, &endp);
|
|
||||||
|
|
||||||
state = endp->bts_end.rtp_process_data;
|
|
||||||
OSMO_ASSERT(state != NULL);
|
|
||||||
|
|
||||||
in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0);
|
|
||||||
OSMO_ASSERT(sizeof(buf) >= in_size + 12);
|
|
||||||
|
|
||||||
out_size = mgcp_transcoding_get_frame_size(state, -1, 1);
|
|
||||||
OSMO_ASSERT(sizeof(buf) >= out_size + 12);
|
|
||||||
|
|
||||||
buf[1] = endp->net_end.codec.payload_type;
|
|
||||||
*(uint16_t*)(buf+2) = htons(1);
|
|
||||||
*(uint32_t*)(buf+4) = htonl(0);
|
|
||||||
*(uint32_t*)(buf+8) = htonl(0xaabbccdd);
|
|
||||||
|
|
||||||
for (in_cnt = 0; in_cnt < 16; in_cnt++) {
|
|
||||||
int cont;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
/* fake PCMA data */
|
|
||||||
printf("generating %d %s input samples\n", in_samples, srcfmt);
|
|
||||||
for (cc = 0; cc < in_samples; cc++)
|
|
||||||
buf[12+cc] = cc;
|
|
||||||
|
|
||||||
*(uint16_t*)(buf+2) = htonl(seq);
|
|
||||||
*(uint32_t*)(buf+4) = htonl(ts);
|
|
||||||
|
|
||||||
seq += 1;
|
|
||||||
ts += in_samples;
|
|
||||||
|
|
||||||
cc += 12; /* include RTP header */
|
|
||||||
|
|
||||||
len = cc;
|
|
||||||
|
|
||||||
do {
|
|
||||||
cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end,
|
|
||||||
buf, &len, sizeof(buf));
|
|
||||||
if (cont == -EAGAIN) {
|
|
||||||
fprintf(stderr, "Got EAGAIN\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cont < 0) {
|
|
||||||
printf("processing failed: %s", strerror(-cont));
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
len -= 12; /* ignore RTP header */
|
|
||||||
|
|
||||||
printf("got %d %s output frames (%d octets) count=%d\n",
|
|
||||||
len / out_size, dstfmt, len, cont);
|
|
||||||
|
|
||||||
len = cont;
|
|
||||||
} while (len > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
talloc_free(ctx);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct log_info_cat log_categories[] = {
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct log_info log_info = {
|
|
||||||
.cat = log_categories,
|
|
||||||
.num_cat = ARRAY_SIZE(log_categories),
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
void *ctx = talloc_named_const(NULL, 0, "mgcp_transcoding_test");
|
|
||||||
osmo_init_logging2(ctx, &log_info);
|
|
||||||
|
|
||||||
printf("=== Transcoding Good Cases ===\n");
|
|
||||||
|
|
||||||
transcode_test("l16", "l16",
|
|
||||||
(uint8_t *)audio_packets_l16[0].data,
|
|
||||||
audio_packets_l16[0].len);
|
|
||||||
transcode_test("l16", "gsm",
|
|
||||||
(uint8_t *)audio_packets_l16[0].data,
|
|
||||||
audio_packets_l16[0].len);
|
|
||||||
transcode_test("l16", "pcma",
|
|
||||||
(uint8_t *)audio_packets_l16[0].data,
|
|
||||||
audio_packets_l16[0].len);
|
|
||||||
transcode_test("gsm", "l16",
|
|
||||||
(uint8_t *)audio_packets_gsm[0].data,
|
|
||||||
audio_packets_gsm[0].len);
|
|
||||||
transcode_test("gsm", "gsm",
|
|
||||||
(uint8_t *)audio_packets_gsm[0].data,
|
|
||||||
audio_packets_gsm[0].len);
|
|
||||||
transcode_test("gsm", "pcma",
|
|
||||||
(uint8_t *)audio_packets_gsm[0].data,
|
|
||||||
audio_packets_gsm[0].len);
|
|
||||||
transcode_test("pcma", "l16",
|
|
||||||
(uint8_t *)audio_packets_pcma[0].data,
|
|
||||||
audio_packets_pcma[0].len);
|
|
||||||
transcode_test("pcma", "gsm",
|
|
||||||
(uint8_t *)audio_packets_pcma[0].data,
|
|
||||||
audio_packets_pcma[0].len);
|
|
||||||
transcode_test("pcma", "pcma",
|
|
||||||
(uint8_t *)audio_packets_pcma[0].data,
|
|
||||||
audio_packets_pcma[0].len);
|
|
||||||
|
|
||||||
printf("=== Transcoding Bad Cases ===\n");
|
|
||||||
|
|
||||||
printf("Invalid size:\n");
|
|
||||||
rc = transcode_test("gsm", "pcma",
|
|
||||||
(uint8_t *)audio_packets_gsm_invalid_size[0].data,
|
|
||||||
audio_packets_gsm_invalid_size[0].len);
|
|
||||||
OSMO_ASSERT(rc < 0);
|
|
||||||
|
|
||||||
printf("Invalid data:\n");
|
|
||||||
rc = transcode_test("gsm", "pcma",
|
|
||||||
(uint8_t *)audio_packets_gsm_invalid_data[0].data,
|
|
||||||
audio_packets_gsm_invalid_data[0].len);
|
|
||||||
OSMO_ASSERT(rc < 0);
|
|
||||||
|
|
||||||
printf("Invalid payload type:\n");
|
|
||||||
rc = transcode_test("gsm", "pcma",
|
|
||||||
(uint8_t *)audio_packets_gsm_invalid_ptype[0].data,
|
|
||||||
audio_packets_gsm_invalid_ptype[0].len);
|
|
||||||
OSMO_ASSERT(rc == 0);
|
|
||||||
|
|
||||||
printf("=== Repacking ===\n");
|
|
||||||
|
|
||||||
test_repacking(160, 160, 0);
|
|
||||||
test_repacking(160, 160, 1);
|
|
||||||
test_repacking(160, 80, 0);
|
|
||||||
test_repacking(160, 80, 1);
|
|
||||||
test_repacking(160, 320, 0);
|
|
||||||
test_repacking(160, 320, 1);
|
|
||||||
test_repacking(160, 240, 0);
|
|
||||||
test_repacking(160, 240, 1);
|
|
||||||
test_repacking(160, 100, 0);
|
|
||||||
test_repacking(160, 100, 1);
|
|
||||||
test_rtp_seq_state();
|
|
||||||
test_transcode_result();
|
|
||||||
test_transcode_change();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,539 +0,0 @@
|
|||||||
=== Transcoding Good Cases ===
|
|
||||||
== Transcoding test ==
|
|
||||||
converting l16 -> l16
|
|
||||||
encoded:
|
|
||||||
80 0b 00 01 00 00 00 a0 11 22 33 44 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
|
|
||||||
5a 9e 40 13 00 00 bf ed a5 62 bf ed
|
|
||||||
counted: 0
|
|
||||||
== Transcoding test ==
|
|
||||||
converting l16 -> gsm
|
|
||||||
encoded:
|
|
||||||
80 0b 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9
|
|
||||||
62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc
|
|
||||||
69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de
|
|
||||||
counted: 12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting l16 -> pcma
|
|
||||||
encoded:
|
|
||||||
80 0b 00 01 00 00 00 a0 11 22 33 44 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25
|
|
||||||
counted: 12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting gsm -> l16
|
|
||||||
encoded:
|
|
||||||
80 03 00 01 00 00 00 a0 11 22 33 44 00 00 54 00
|
|
||||||
59 f0 34 20 c4 c8 b9 f8 e2 18 f1 e8 f2 28 f0 e0
|
|
||||||
46 08 4f 80 2c a0 a9 c8 80 00 c0 58 3f 80 63 c0
|
|
||||||
24 b8 fa b8 f6 88 0b a0 c8 70 a8 b0 c8 c0 3b a8
|
|
||||||
66 a0 2e 38 d1 f8 98 98 aa 18 e8 30 26 a0 37 40
|
|
||||||
37 e8 17 00 ee 50 b7 80 b1 88 de 28 18 40 45 b0
|
|
||||||
4f 48 21 d8 df 78 ae 68 ab 98 d6 b8 18 18 48 90
|
|
||||||
4e 70 27 40 e8 10 b5 b0 ac 80 d4 60 14 50 48 48
|
|
||||||
50 10 2a 00 ec 08 ba 00 af 58 d1 c0 10 60 45 c8
|
|
||||||
54 10 30 78 f1 a8 bb 18 ad 48 ce 30 0a e8 3f 30
|
|
||||||
4f 10 32 18 f6 18 bf 20 ac 30 cd 80 0b d0 43 d8
|
|
||||||
55 e0 34 a0 f5 78 bc 98 ad 98 cd c8 0a 80 40 58
|
|
||||||
51 c0 35 40 f9 60 c1 68 ac c8 cb 38 08 00 40 98
|
|
||||||
51 e0 34 d8 fa 28 c2 f0 ae 40 c7 70 02 d0 3c a8
|
|
||||||
54 78 38 a0 fc 68 c2 08 ad 50 c7 78 01 60 39 c0
|
|
||||||
51 38 3a e8 00 e8 c6 38 ab d8 c4 00 fe 08 39 18
|
|
||||||
50 30 39 50 01 d8 ca 70 b1 80 c4 c8 fc 58 36 40
|
|
||||||
51 d8 3b 08 02 80 c8 58 b0 60 c5 a8 fb d0 33 e8
|
|
||||||
4e 80 3c e0 06 10 cb 90 ae 48 c2 60 f9 58 34 08
|
|
||||||
4d a0 3a a8 06 48 cf 80 b4 60 c3 e8 f7 90 30 18
|
|
||||||
4d a0 3b 98 07 90 cf 18 b4 68 c4 88
|
|
||||||
counted: 12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting gsm -> gsm
|
|
||||||
encoded:
|
|
||||||
80 03 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9
|
|
||||||
62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc
|
|
||||||
69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de
|
|
||||||
counted: 0
|
|
||||||
== Transcoding test ==
|
|
||||||
converting gsm -> pcma
|
|
||||||
encoded:
|
|
||||||
80 03 00 01 00 00 00 a0 11 22 33 44 d5 a0 a3 bf
|
|
||||||
38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60
|
|
||||||
17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82
|
|
||||||
04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6
|
|
||||||
02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd
|
|
||||||
19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf
|
|
||||||
10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf
|
|
||||||
62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8
|
|
||||||
db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8
|
|
||||||
f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8
|
|
||||||
ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38
|
|
||||||
counted: 12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting pcma -> l16
|
|
||||||
encoded:
|
|
||||||
80 08 00 01 00 00 00 a0 11 22 33 44 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
|
|
||||||
5a 00 42 00 00 08 be 00 a6 00 be 00
|
|
||||||
counted: 12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting pcma -> gsm
|
|
||||||
encoded:
|
|
||||||
80 08 00 01 00 00 00 a0 11 22 33 44 d4 b9 f4 5d
|
|
||||||
d9 50 5a e1 a0 cd 76 ea 52 0e 87 53 ad d4 ea a2
|
|
||||||
0a 63 ca e9 60 79 e2 2a 25 d2 c0 f3 39
|
|
||||||
counted: 12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting pcma -> pcma
|
|
||||||
encoded:
|
|
||||||
80 08 00 01 00 00 00 a0 11 22 33 44 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
|
|
||||||
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25
|
|
||||||
counted: 0
|
|
||||||
=== Transcoding Bad Cases ===
|
|
||||||
Invalid size:
|
|
||||||
== Transcoding test ==
|
|
||||||
converting gsm -> pcma
|
|
||||||
Nothing encoded due: No message of desired type
|
|
||||||
Invalid data:
|
|
||||||
== Transcoding test ==
|
|
||||||
converting gsm -> pcma
|
|
||||||
Nothing encoded due: No message of desired type
|
|
||||||
Invalid payload type:
|
|
||||||
== Transcoding test ==
|
|
||||||
converting gsm -> pcma
|
|
||||||
encoded:
|
|
||||||
80 08 00 01 00 00 00 a0 11 22 33 44 d5 a0 a3 bf
|
|
||||||
38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60
|
|
||||||
17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82
|
|
||||||
04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6
|
|
||||||
02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd
|
|
||||||
19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf
|
|
||||||
10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf
|
|
||||||
62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8
|
|
||||||
db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8
|
|
||||||
f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8
|
|
||||||
ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38
|
|
||||||
counted: 12
|
|
||||||
=== Repacking ===
|
|
||||||
== Transcoding test ==
|
|
||||||
converting pcma -> l16
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 l16 output frames (320 octets) count=12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting pcma -> pcma
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 2 pcma output frames (160 octets) count=12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting pcma -> l16
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting pcma -> pcma
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting pcma -> l16
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 l16 output frames (640 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 l16 output frames (640 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 l16 output frames (640 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 l16 output frames (640 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 l16 output frames (640 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 l16 output frames (640 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 l16 output frames (640 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 l16 output frames (640 octets) count=12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting pcma -> pcma
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 pcma output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 pcma output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 pcma output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 pcma output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 pcma output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 pcma output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 pcma output frames (320 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 4 pcma output frames (320 octets) count=12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting pcma -> l16
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 l16 output frames (480 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 l16 output frames (480 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 l16 output frames (480 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 l16 output frames (480 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 l16 output frames (480 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 l16 output frames (480 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 l16 output frames (480 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 l16 output frames (480 octets) count=12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting pcma -> pcma
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 pcma output frames (240 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 pcma output frames (240 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 pcma output frames (240 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 pcma output frames (240 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 pcma output frames (240 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 pcma output frames (240 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 pcma output frames (240 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 3 pcma output frames (240 octets) count=12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting pcma -> l16
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
got 1 l16 output frames (160 octets) count=12
|
|
||||||
== Transcoding test ==
|
|
||||||
converting pcma -> pcma
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
generating 160 pcma input samples
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
got 1 pcma output frames (80 octets) count=12
|
|
||||||
Testing Initial L16->GSM, PCMA->GSM
|
|
||||||
@@ -26,6 +26,8 @@
|
|||||||
#include <osmocom/mgcp/mgcp_stat.h>
|
#include <osmocom/mgcp/mgcp_stat.h>
|
||||||
#include <osmocom/mgcp/mgcp_msg.h>
|
#include <osmocom/mgcp/mgcp_msg.h>
|
||||||
#include <osmocom/mgcp/mgcp_endp.h>
|
#include <osmocom/mgcp/mgcp_endp.h>
|
||||||
|
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||||
|
#include <osmocom/mgcp/mgcp_codec.h>
|
||||||
|
|
||||||
#include <osmocom/core/application.h>
|
#include <osmocom/core/application.h>
|
||||||
#include <osmocom/core/talloc.h>
|
#include <osmocom/core/talloc.h>
|
||||||
@@ -35,6 +37,7 @@
|
|||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
char *strline_r(char *str, char **saveptr);
|
char *strline_r(char *str, char **saveptr);
|
||||||
|
|
||||||
@@ -156,8 +159,8 @@ static void test_strline(void)
|
|||||||
"s=-\r\n" \
|
"s=-\r\n" \
|
||||||
"c=IN IP4 0.0.0.0\r\n" \
|
"c=IN IP4 0.0.0.0\r\n" \
|
||||||
"t=0 0\r\n" \
|
"t=0 0\r\n" \
|
||||||
"m=audio 16002 RTP/AVP 96\r\n" \
|
"m=audio 16002 RTP/AVP 112\r\n" \
|
||||||
"a=rtpmap:96 AMR\r\n" \
|
"a=rtpmap:112 AMR\r\n" \
|
||||||
"a=ptime:40\r\n"
|
"a=ptime:40\r\n"
|
||||||
|
|
||||||
#define MDCX4_PT1 \
|
#define MDCX4_PT1 \
|
||||||
@@ -227,6 +230,12 @@ static void test_strline(void)
|
|||||||
"I: %s\r\n" \
|
"I: %s\r\n" \
|
||||||
"L: p:20, a:AMR, nt:IN\r\n"
|
"L: p:20, a:AMR, nt:IN\r\n"
|
||||||
|
|
||||||
|
#define MDCX_TOO_LONG_CI \
|
||||||
|
"MDCX 18983222 1@mgw MGCP 1.0\r\n" \
|
||||||
|
"I: 123456789012345678901234567890123\n"
|
||||||
|
|
||||||
|
#define MDCX_TOO_LONG_CI_RET "510 18983222 FAIL\r\n"
|
||||||
|
|
||||||
#define SHORT2 "CRCX 1"
|
#define SHORT2 "CRCX 1"
|
||||||
#define SHORT2_RET "510 000000 FAIL\r\n"
|
#define SHORT2_RET "510 000000 FAIL\r\n"
|
||||||
#define SHORT3 "CRCX 1 1@mgw"
|
#define SHORT3 "CRCX 1 1@mgw"
|
||||||
@@ -307,6 +316,32 @@ static void test_strline(void)
|
|||||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||||
"a=ptime:20\r\n"
|
"a=ptime:20\r\n"
|
||||||
|
|
||||||
|
#define CRCX_X_OSMO_IGN \
|
||||||
|
"CRCX 2 1@mgw MGCP 1.0\r\n" \
|
||||||
|
"M: recvonly\r\n" \
|
||||||
|
"C: 2\r\n" \
|
||||||
|
"L: p:20\r\n" \
|
||||||
|
"X-Osmo-IGN: C foo\r\n" \
|
||||||
|
"\r\n" \
|
||||||
|
"v=0\r\n" \
|
||||||
|
"c=IN IP4 123.12.12.123\r\n" \
|
||||||
|
"m=audio 5904 RTP/AVP 97\r\n" \
|
||||||
|
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||||
|
"a=ptime:40\r\n"
|
||||||
|
|
||||||
|
#define CRCX_X_OSMO_IGN_RET \
|
||||||
|
"200 2 OK\r\n" \
|
||||||
|
"I: %s\r\n" \
|
||||||
|
"\r\n" \
|
||||||
|
"v=0\r\n" \
|
||||||
|
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||||
|
"s=-\r\n" \
|
||||||
|
"c=IN IP4 0.0.0.0\r\n" \
|
||||||
|
"t=0 0\r\n" \
|
||||||
|
"m=audio 16010 RTP/AVP 97\r\n" \
|
||||||
|
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||||
|
"a=ptime:40\r\n"
|
||||||
|
|
||||||
#define DLCX \
|
#define DLCX \
|
||||||
"DLCX 7 1@mgw MGCP 1.0\r\n" \
|
"DLCX 7 1@mgw MGCP 1.0\r\n" \
|
||||||
"I: %s\r\n" \
|
"I: %s\r\n" \
|
||||||
@@ -404,7 +439,7 @@ static void test_strline(void)
|
|||||||
"v=0\r\n" \
|
"v=0\r\n" \
|
||||||
"o=- 1439038275 1439038275 IN IP4 192.168.181.247\r\n" \
|
"o=- 1439038275 1439038275 IN IP4 192.168.181.247\r\n" \
|
||||||
"s=-\r\nc=IN IP4 192.168.181.247\r\n" \
|
"s=-\r\nc=IN IP4 192.168.181.247\r\n" \
|
||||||
"t=0 0\r\nm=audio 29084 RTP/AVP 255 0 8 3 18 4 96 97 101\r\n" \
|
"t=0 0\r\nm=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101\r\n" \
|
||||||
"a=rtpmap:0 PCMU/8000\r\n" \
|
"a=rtpmap:0 PCMU/8000\r\n" \
|
||||||
"a=rtpmap:8 PCMA/8000\r\n" \
|
"a=rtpmap:8 PCMA/8000\r\n" \
|
||||||
"a=rtpmap:3 gsm/8000\r\n" \
|
"a=rtpmap:3 gsm/8000\r\n" \
|
||||||
@@ -425,7 +460,24 @@ static void test_strline(void)
|
|||||||
"I: %s\r\n" \
|
"I: %s\r\n" \
|
||||||
"\r\n" \
|
"\r\n" \
|
||||||
"c=IN IP4 8.8.8.8\r\n" \
|
"c=IN IP4 8.8.8.8\r\n" \
|
||||||
"m=audio 16434 RTP/AVP 255\r\n"
|
"m=audio 16434 RTP/AVP 3\r\n"
|
||||||
|
|
||||||
|
#define CRCX_NO_LCO_NO_SDP \
|
||||||
|
"CRCX 2 6@mgw MGCP 1.0\r\n" \
|
||||||
|
"M: recvonly\r\n" \
|
||||||
|
"C: 2\r\n"
|
||||||
|
|
||||||
|
#define CRCX_NO_LCO_NO_SDP_RET \
|
||||||
|
"200 2 OK\r\n" \
|
||||||
|
"I: %s\r\n" \
|
||||||
|
"\r\n" \
|
||||||
|
"v=0\r\n" \
|
||||||
|
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||||
|
"s=-\r\n" \
|
||||||
|
"c=IN IP4 0.0.0.0\r\n" \
|
||||||
|
"t=0 0\r\n" \
|
||||||
|
"m=audio 16008 RTP/AVP 0\r\n" \
|
||||||
|
"a=ptime:20\r\n"
|
||||||
|
|
||||||
struct mgcp_test {
|
struct mgcp_test {
|
||||||
const char *name;
|
const char *name;
|
||||||
@@ -462,6 +514,9 @@ static const struct mgcp_test tests[] = {
|
|||||||
{"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE,.extra_fmtp =
|
{"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE,.extra_fmtp =
|
||||||
"a=fmtp:126 0/1/2"},
|
"a=fmtp:126 0/1/2"},
|
||||||
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
|
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
|
||||||
|
{"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
|
||||||
|
{"CRCX", CRCX_X_OSMO_IGN, CRCX_X_OSMO_IGN_RET, 97},
|
||||||
|
{"MDCX_TOO_LONG_CI", MDCX_TOO_LONG_CI, MDCX_TOO_LONG_CI_RET},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct mgcp_test retransmit[] = {
|
static const struct mgcp_test retransmit[] = {
|
||||||
@@ -495,7 +550,7 @@ static int last_endpoint = -1;
|
|||||||
static int mgcp_test_policy_cb(struct mgcp_trunk_config *cfg, int endpoint,
|
static int mgcp_test_policy_cb(struct mgcp_trunk_config *cfg, int endpoint,
|
||||||
int state, const char *transactio_id)
|
int state, const char *transactio_id)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Policy CB got state %d on endpoint %d\n",
|
fprintf(stderr, "Policy CB got state %d on endpoint 0x%x\n",
|
||||||
state, endpoint);
|
state, endpoint);
|
||||||
last_endpoint = endpoint;
|
last_endpoint = endpoint;
|
||||||
return MGCP_POLICY_CONT;
|
return MGCP_POLICY_CONT;
|
||||||
@@ -552,45 +607,42 @@ static void test_values(void)
|
|||||||
MGCP_CONN_RECV_SEND);
|
MGCP_CONN_RECV_SEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extract a connection ID from a response (CRCX) */
|
/* Extract a connection ID from a response and return in conn_id;
|
||||||
|
* if there is none, return -EINVAL and leave conn_id unchanged. */
|
||||||
static int get_conn_id_from_response(uint8_t *resp, char *conn_id,
|
static int get_conn_id_from_response(uint8_t *resp, char *conn_id,
|
||||||
unsigned int conn_id_len)
|
size_t conn_id_buflen)
|
||||||
{
|
{
|
||||||
char *conn_id_ptr;
|
const char *conn_id_start;
|
||||||
int i;
|
const char *conn_id_end;
|
||||||
bool got_conn_id = false;
|
int conn_id_len;
|
||||||
|
|
||||||
/* First try to get the conn_id from the I: parameter */
|
const char *header_I = "\r\nI: ";
|
||||||
conn_id_ptr = strstr((char *)resp, "I: ");
|
const char *header_o = "\r\no=- ";
|
||||||
if (conn_id_ptr) {
|
|
||||||
memset(conn_id, 0, conn_id_len);
|
|
||||||
memcpy(conn_id, conn_id_ptr + 3, 32);
|
|
||||||
got_conn_id = true;
|
|
||||||
} else {
|
|
||||||
/* Alternatively try to extract the conn_id from the o=- SDP
|
|
||||||
* parameter */
|
|
||||||
conn_id_ptr = strstr((char *)resp, "o=- ");
|
|
||||||
if(conn_id_ptr) {
|
|
||||||
memset(conn_id, 0, conn_id_len);
|
|
||||||
memcpy(conn_id, conn_id_ptr + 4, 32);
|
|
||||||
got_conn_id = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (got_conn_id) {
|
/* Try to get the conn_id from the 'I:' or 'o=-' parameter */
|
||||||
for (i = 0; i < conn_id_len; i++) {
|
if ((conn_id_start = strstr((char *)resp, header_I))) {
|
||||||
if (conn_id[i] == '\n' || conn_id[i] == '\r')
|
conn_id_start += strlen(header_I);
|
||||||
conn_id[i] = '\0';
|
conn_id_end = strstr(conn_id_start, "\r\n");
|
||||||
}
|
} else if ((conn_id_start = strstr((char *)resp, header_o))) {
|
||||||
|
conn_id_start += strlen(header_o);
|
||||||
|
conn_id_end = strchr(conn_id_start, ' ');
|
||||||
|
} else
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
/* A valid conn_id must at least contain one digit, and must
|
if (conn_id_end)
|
||||||
* not exceed a length of 32 digits */
|
conn_id_len = conn_id_end - conn_id_start;
|
||||||
OSMO_ASSERT(strlen(conn_id) <= 32);
|
else
|
||||||
OSMO_ASSERT(strlen(conn_id) > 0);
|
conn_id_len = strlen(conn_id_start);
|
||||||
|
OSMO_ASSERT(conn_id_len <= conn_id_buflen - 1);
|
||||||
|
|
||||||
return 0;
|
/* A valid conn_id must at least contain one digit, and must
|
||||||
}
|
* not exceed a length of 32 digits */
|
||||||
return -EINVAL;
|
OSMO_ASSERT(conn_id_len <= 32);
|
||||||
|
OSMO_ASSERT(conn_id_len > 0);
|
||||||
|
|
||||||
|
strncpy(conn_id, conn_id_start, conn_id_len);
|
||||||
|
conn_id[conn_id_len] = '\0';
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check response, automatically patch connection ID if needed */
|
/* Check response, automatically patch connection ID if needed */
|
||||||
@@ -690,7 +742,7 @@ static void test_messages(void)
|
|||||||
if (msg) {
|
if (msg) {
|
||||||
rc = get_conn_id_from_response(msg->data, last_conn_id,
|
rc = get_conn_id_from_response(msg->data, last_conn_id,
|
||||||
sizeof(last_conn_id));
|
sizeof(last_conn_id));
|
||||||
if (rc)
|
if (rc == 0)
|
||||||
printf("(response contains a connection id)\n");
|
printf("(response contains a connection id)\n");
|
||||||
else
|
else
|
||||||
printf("(response does not contain a connection id)\n");
|
printf("(response does not contain a connection id)\n");
|
||||||
@@ -761,17 +813,17 @@ static void test_messages(void)
|
|||||||
OSMO_ASSERT(last_endpoint != -1);
|
OSMO_ASSERT(last_endpoint != -1);
|
||||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||||
|
|
||||||
fprintf(stderr, "endpoint %d: "
|
fprintf(stderr, "endpoint 0x%x: "
|
||||||
"payload type %d (expected %d)\n",
|
"payload type %d (expected %d)\n",
|
||||||
last_endpoint,
|
last_endpoint,
|
||||||
conn->end.codec.payload_type, t->ptype);
|
conn->end.codec->payload_type, t->ptype);
|
||||||
|
|
||||||
if (t->ptype != PTYPE_IGNORE)
|
if (t->ptype != PTYPE_IGNORE)
|
||||||
OSMO_ASSERT(conn->end.codec.payload_type ==
|
OSMO_ASSERT(conn->end.codec->payload_type ==
|
||||||
t->ptype);
|
t->ptype);
|
||||||
|
|
||||||
/* Reset them again for next test */
|
/* Reset them again for next test */
|
||||||
conn->end.codec.payload_type = PTYPE_NONE;
|
conn->end.codec->payload_type = PTYPE_NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -917,23 +969,43 @@ static const struct pl_test pl_test_dat[] = {
|
|||||||
static void test_packet_loss_calc(void)
|
static void test_packet_loss_calc(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
struct mgcp_endpoint endp;
|
||||||
|
struct mgcp_trunk_config trunk;
|
||||||
|
|
||||||
printf("Testing packet loss calculation.\n");
|
printf("Testing packet loss calculation.\n");
|
||||||
|
|
||||||
|
memset(&endp, 0, sizeof(endp));
|
||||||
|
memset(&trunk, 0, sizeof(trunk));
|
||||||
|
|
||||||
|
endp.type = &ep_typeset.rtp;
|
||||||
|
trunk.vty_number_endpoints = 1;
|
||||||
|
trunk.endpoints = &endp;
|
||||||
|
endp.tcfg = &trunk;
|
||||||
|
INIT_LLIST_HEAD(&endp.conns);
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(pl_test_dat); ++i) {
|
for (i = 0; i < ARRAY_SIZE(pl_test_dat); ++i) {
|
||||||
uint32_t expected;
|
uint32_t expected;
|
||||||
int loss;
|
int loss;
|
||||||
struct mgcp_rtp_state state;
|
|
||||||
struct mgcp_rtp_end rtp;
|
|
||||||
memset(&state, 0, sizeof(state));
|
|
||||||
memset(&rtp, 0, sizeof(rtp));
|
|
||||||
|
|
||||||
state.stats.initialized = 1;
|
struct mgcp_conn_rtp *conn = NULL;
|
||||||
state.stats.base_seq = pl_test_dat[i].base_seq;
|
struct mgcp_conn *_conn = NULL;
|
||||||
state.stats.max_seq = pl_test_dat[i].max_seq;
|
struct mgcp_rtp_state *state;
|
||||||
state.stats.cycles = pl_test_dat[i].cycles;
|
struct rate_ctr *packets_rx;
|
||||||
|
|
||||||
rtp.stats.packets_rx = pl_test_dat[i].packets;
|
_conn =
|
||||||
calc_loss(&state, &rtp, &expected, &loss);
|
mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
|
||||||
|
"test-connection");
|
||||||
|
conn = mgcp_conn_get_rtp(&endp, _conn->id);
|
||||||
|
state = &conn->state;
|
||||||
|
packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
|
||||||
|
|
||||||
|
state->stats.initialized = 1;
|
||||||
|
state->stats.base_seq = pl_test_dat[i].base_seq;
|
||||||
|
state->stats.max_seq = pl_test_dat[i].max_seq;
|
||||||
|
state->stats.cycles = pl_test_dat[i].cycles;
|
||||||
|
|
||||||
|
packets_rx->current = pl_test_dat[i].packets;
|
||||||
|
calc_loss(conn, &expected, &loss);
|
||||||
|
|
||||||
if (loss != pl_test_dat[i].loss
|
if (loss != pl_test_dat[i].loss
|
||||||
|| expected != pl_test_dat[i].expected) {
|
|| expected != pl_test_dat[i].expected) {
|
||||||
@@ -942,7 +1014,10 @@ static void test_packet_loss_calc(void)
|
|||||||
i, loss, pl_test_dat[i].loss, expected,
|
i, loss, pl_test_dat[i].loss, expected,
|
||||||
pl_test_dat[i].expected);
|
pl_test_dat[i].expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mgcp_conn_free_all(&endp);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os,
|
int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os,
|
||||||
@@ -1129,10 +1204,12 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
|||||||
uint32_t last_ssrc = 0;
|
uint32_t last_ssrc = 0;
|
||||||
uint32_t last_timestamp = 0;
|
uint32_t last_timestamp = 0;
|
||||||
uint32_t last_seqno = 0;
|
uint32_t last_seqno = 0;
|
||||||
int last_in_ts_err_cnt = 0;
|
uint64_t last_in_ts_err_cnt = 0;
|
||||||
int last_out_ts_err_cnt = 0;
|
uint64_t last_out_ts_err_cnt = 0;
|
||||||
struct mgcp_conn_rtp *conn = NULL;
|
struct mgcp_conn_rtp *conn = NULL;
|
||||||
struct mgcp_conn *_conn = NULL;
|
struct mgcp_conn *_conn = NULL;
|
||||||
|
struct rate_ctr test_ctr_in;
|
||||||
|
struct rate_ctr test_ctr_out;
|
||||||
|
|
||||||
printf("Testing packet error detection%s%s.\n",
|
printf("Testing packet error detection%s%s.\n",
|
||||||
patch_ssrc ? ", patch SSRC" : "",
|
patch_ssrc ? ", patch SSRC" : "",
|
||||||
@@ -1142,6 +1219,11 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
|||||||
memset(&endp, 0, sizeof(endp));
|
memset(&endp, 0, sizeof(endp));
|
||||||
memset(&state, 0, sizeof(state));
|
memset(&state, 0, sizeof(state));
|
||||||
|
|
||||||
|
memset(&test_ctr_in, 0, sizeof(test_ctr_in));
|
||||||
|
memset(&test_ctr_out, 0, sizeof(test_ctr_out));
|
||||||
|
state.in_stream.err_ts_ctr = &test_ctr_in;
|
||||||
|
state.out_stream.err_ts_ctr = &test_ctr_out;
|
||||||
|
|
||||||
endp.type = &ep_typeset.rtp;
|
endp.type = &ep_typeset.rtp;
|
||||||
|
|
||||||
trunk.vty_number_endpoints = 1;
|
trunk.vty_number_endpoints = 1;
|
||||||
@@ -1160,7 +1242,8 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
|||||||
|
|
||||||
rtp = &conn->end;
|
rtp = &conn->end;
|
||||||
|
|
||||||
rtp->codec.payload_type = 98;
|
OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1") == 0);
|
||||||
|
rtp->codec = &rtp->codecs[0];
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
|
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
|
||||||
struct rtp_packet_info *info = test_rtp_packets1 + i;
|
struct rtp_packet_info *info = test_rtp_packets1 + i;
|
||||||
@@ -1186,18 +1269,18 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
|||||||
state.in_stream.last_tsdelta, state.in_stream.last_seq);
|
state.in_stream.last_tsdelta, state.in_stream.last_seq);
|
||||||
|
|
||||||
printf("Out TS change: %d, dTS: %d, Seq change: %d, "
|
printf("Out TS change: %d, dTS: %d, Seq change: %d, "
|
||||||
"TS Err change: in %+d, out %+d\n",
|
"TS Err change: in +%u, out +%u\n",
|
||||||
state.out_stream.last_timestamp - last_timestamp,
|
state.out_stream.last_timestamp - last_timestamp,
|
||||||
state.out_stream.last_tsdelta,
|
state.out_stream.last_tsdelta,
|
||||||
state.out_stream.last_seq - last_seqno,
|
state.out_stream.last_seq - last_seqno,
|
||||||
state.in_stream.err_ts_counter - last_in_ts_err_cnt,
|
(unsigned int) (state.in_stream.err_ts_ctr->current - last_in_ts_err_cnt),
|
||||||
state.out_stream.err_ts_counter - last_out_ts_err_cnt);
|
(unsigned int) (state.out_stream.err_ts_ctr->current - last_out_ts_err_cnt));
|
||||||
|
|
||||||
printf("Stats: Jitter = %u, Transit = %d\n",
|
printf("Stats: Jitter = %u, Transit = %d\n",
|
||||||
calc_jitter(&state), state.stats.transit);
|
calc_jitter(&state), state.stats.transit);
|
||||||
|
|
||||||
last_in_ts_err_cnt = state.in_stream.err_ts_counter;
|
last_in_ts_err_cnt = state.in_stream.err_ts_ctr->current;
|
||||||
last_out_ts_err_cnt = state.out_stream.err_ts_counter;
|
last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
|
||||||
last_timestamp = state.out_stream.last_timestamp;
|
last_timestamp = state.out_stream.last_timestamp;
|
||||||
last_seqno = state.out_stream.last_seq;
|
last_seqno = state.out_stream.last_seq;
|
||||||
}
|
}
|
||||||
@@ -1214,6 +1297,7 @@ static void test_multilple_codec(void)
|
|||||||
struct in_addr addr;
|
struct in_addr addr;
|
||||||
struct mgcp_conn_rtp *conn = NULL;
|
struct mgcp_conn_rtp *conn = NULL;
|
||||||
char conn_id[256];
|
char conn_id[256];
|
||||||
|
int i;
|
||||||
|
|
||||||
printf("Testing multiple payload types\n");
|
printf("Testing multiple payload types\n");
|
||||||
|
|
||||||
@@ -1236,8 +1320,7 @@ static void test_multilple_codec(void)
|
|||||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||||
OSMO_ASSERT(conn);
|
OSMO_ASSERT(conn);
|
||||||
OSMO_ASSERT(conn->end.codec.payload_type == 18);
|
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == 97);
|
|
||||||
|
|
||||||
/* Allocate 2@mgw with three codecs, last one ignored */
|
/* Allocate 2@mgw with three codecs, last one ignored */
|
||||||
last_endpoint = -1;
|
last_endpoint = -1;
|
||||||
@@ -1252,10 +1335,14 @@ static void test_multilple_codec(void)
|
|||||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||||
OSMO_ASSERT(conn);
|
OSMO_ASSERT(conn);
|
||||||
OSMO_ASSERT(conn->end.codec.payload_type == 18);
|
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == 97);
|
|
||||||
|
|
||||||
/* Allocate 3@mgw with no codecs, check for PT == -1 */
|
/* Allocate 3@mgw with no codecs, check for PT == 0 */
|
||||||
|
/* Note: It usually makes no sense to leave the payload type list
|
||||||
|
* out. However RFC 2327 does not clearly forbid this case and
|
||||||
|
* it makes and since we already decided in OS#2658 that a missing
|
||||||
|
* LCO should pick a sane default codec, it makes sense to expect
|
||||||
|
* the same behaviour if SDP lacks proper payload type information */
|
||||||
last_endpoint = -1;
|
last_endpoint = -1;
|
||||||
inp = create_msg(CRCX_MULT_3, NULL);
|
inp = create_msg(CRCX_MULT_3, NULL);
|
||||||
resp = mgcp_handle_message(cfg, inp);
|
resp = mgcp_handle_message(cfg, inp);
|
||||||
@@ -1268,8 +1355,7 @@ static void test_multilple_codec(void)
|
|||||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||||
OSMO_ASSERT(conn);
|
OSMO_ASSERT(conn);
|
||||||
OSMO_ASSERT(conn->end.codec.payload_type == -1);
|
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
|
|
||||||
|
|
||||||
/* Allocate 4@mgw with a single codec */
|
/* Allocate 4@mgw with a single codec */
|
||||||
last_endpoint = -1;
|
last_endpoint = -1;
|
||||||
@@ -1284,8 +1370,7 @@ static void test_multilple_codec(void)
|
|||||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||||
OSMO_ASSERT(conn);
|
OSMO_ASSERT(conn);
|
||||||
OSMO_ASSERT(conn->end.codec.payload_type == 18);
|
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
|
|
||||||
|
|
||||||
/* Allocate 5@mgw at select GSM.. */
|
/* Allocate 5@mgw at select GSM.. */
|
||||||
last_endpoint = -1;
|
last_endpoint = -1;
|
||||||
@@ -1303,8 +1388,7 @@ static void test_multilple_codec(void)
|
|||||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||||
OSMO_ASSERT(conn);
|
OSMO_ASSERT(conn);
|
||||||
OSMO_ASSERT(conn->end.codec.payload_type == 3);
|
OSMO_ASSERT(conn->end.codec->payload_type == 3);
|
||||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
|
|
||||||
|
|
||||||
inp = create_msg(MDCX_NAT_DUMMY, conn_id);
|
inp = create_msg(MDCX_NAT_DUMMY, conn_id);
|
||||||
last_endpoint = -1;
|
last_endpoint = -1;
|
||||||
@@ -1315,8 +1399,7 @@ static void test_multilple_codec(void)
|
|||||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||||
OSMO_ASSERT(conn);
|
OSMO_ASSERT(conn);
|
||||||
OSMO_ASSERT(conn->end.codec.payload_type == 3);
|
OSMO_ASSERT(conn->end.codec->payload_type == 3);
|
||||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
|
|
||||||
OSMO_ASSERT(conn->end.rtp_port == htons(16434));
|
OSMO_ASSERT(conn->end.rtp_port == htons(16434));
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
inet_aton("8.8.8.8", &addr);
|
inet_aton("8.8.8.8", &addr);
|
||||||
@@ -1346,8 +1429,10 @@ static void test_multilple_codec(void)
|
|||||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||||
OSMO_ASSERT(conn);
|
OSMO_ASSERT(conn);
|
||||||
OSMO_ASSERT(conn->end.codec.payload_type == 255);
|
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == 0);
|
|
||||||
|
for (i = 1; i < cfg->trunk.number_endpoints; i++)
|
||||||
|
mgcp_endp_release(&cfg->trunk.endpoints[i]);
|
||||||
|
|
||||||
talloc_free(cfg);
|
talloc_free(cfg);
|
||||||
}
|
}
|
||||||
@@ -1465,6 +1550,242 @@ const struct log_info log_info = {
|
|||||||
.num_cat = ARRAY_SIZE(log_categories),
|
.num_cat = ARRAY_SIZE(log_categories),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void test_get_lco_identifier(void)
|
||||||
|
{
|
||||||
|
char *test;
|
||||||
|
printf("Testing get_lco_identifier()\n");
|
||||||
|
|
||||||
|
/* Normal case at the beginning */
|
||||||
|
test = "p:10, a:PCMU";
|
||||||
|
printf("%s -> %s\n", test, get_lco_identifier(test));
|
||||||
|
|
||||||
|
test = "p:10, a:PCMU";
|
||||||
|
printf("%s -> %s\n", test, get_lco_identifier(test));
|
||||||
|
|
||||||
|
/* Begin parsing in the middle of the value part of
|
||||||
|
* the previous LCO option value */
|
||||||
|
test = "XXXX, p:10, a:PCMU";
|
||||||
|
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||||
|
|
||||||
|
test = "XXXX,p:10,a:PCMU";
|
||||||
|
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||||
|
|
||||||
|
test = "10,a:PCMU";
|
||||||
|
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||||
|
|
||||||
|
test = "10, a:PCMU";
|
||||||
|
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||||
|
|
||||||
|
test = "10,a: PCMU";
|
||||||
|
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||||
|
|
||||||
|
test = "10 ,a: PCMU";
|
||||||
|
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||||
|
|
||||||
|
/* Begin parsing right at the end of the previous LCO
|
||||||
|
* option value */
|
||||||
|
test = ", a:PCMU";
|
||||||
|
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||||
|
|
||||||
|
test = " a:PCMU";
|
||||||
|
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||||
|
|
||||||
|
/* Empty string, result should be (null) */
|
||||||
|
test = "";
|
||||||
|
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||||
|
|
||||||
|
/* Missing colons, result should be (null) */
|
||||||
|
test = "p10, aPCMU";
|
||||||
|
printf("%s -> %s\n", test, get_lco_identifier(test));
|
||||||
|
|
||||||
|
/* Colon separated from the identifier, result should be (null) */
|
||||||
|
test = "10,a :PCMU";
|
||||||
|
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_check_local_cx_options(void *ctx)
|
||||||
|
{
|
||||||
|
/* Legal cases */
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU") == 0);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p:10, IN:10") == 0);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "one:AAA, two:BB, tree:C") ==
|
||||||
|
0);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:G726-32") == 0);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "p:10-20, b:64") == 0);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "b:32-64, e:off") == 0);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU;G726-32") == 0);
|
||||||
|
|
||||||
|
/* Illegal spaces before and after colon */
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p :10") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "a :PCMU, p:10") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "p: 10, a:PCMU") == -1);
|
||||||
|
|
||||||
|
/* Check if multiple appearances of LCOs are rejected */
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, p:10") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, a:PCMU, p:10") ==
|
||||||
|
-1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, p:10") == -1);
|
||||||
|
|
||||||
|
/* Check if empty LCO are rejected */
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "p: , a:PCMU") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "p: , a: PCMU") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a: PCMU") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "p:, a:PCMU") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:") == -1);
|
||||||
|
|
||||||
|
/* Garbeled beginning and ends */
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, ":10, a:10") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "10, a:PCMU") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, ", a:PCMU") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, " a:PCMU") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU,") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, ") == -1);
|
||||||
|
|
||||||
|
/* Illegal strings */
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, " ") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, "AAA") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, ":,") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, ",:") == -1);
|
||||||
|
OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_mgcp_codec_pt_translate_pars(struct mgcp_rtp_codec *c)
|
||||||
|
{
|
||||||
|
c->rate = 8000;
|
||||||
|
c->channels = 1;
|
||||||
|
c->frame_duration_num = 23;
|
||||||
|
c->frame_duration_den = 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_mgcp_codec_pt_translate(void)
|
||||||
|
{
|
||||||
|
struct mgcp_conn_rtp conn_src;
|
||||||
|
struct mgcp_conn_rtp conn_dst;
|
||||||
|
int pt_dst;
|
||||||
|
|
||||||
|
/* Setup a realistic set of codec configurations on both
|
||||||
|
* ends. AMR and HR will use different payload types. PCMU
|
||||||
|
* must use 0 on both ends since this is not a dynamic payload
|
||||||
|
* type */
|
||||||
|
test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[0]);
|
||||||
|
test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[0]);
|
||||||
|
test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[1]);
|
||||||
|
test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[1]);
|
||||||
|
test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[2]);
|
||||||
|
test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[2]);
|
||||||
|
conn_src.end.codecs[0].payload_type = 112;
|
||||||
|
conn_dst.end.codecs[0].payload_type = 96;
|
||||||
|
conn_src.end.codecs[1].payload_type = 0;
|
||||||
|
conn_dst.end.codecs[1].payload_type = 0;
|
||||||
|
conn_src.end.codecs[2].payload_type = 111;
|
||||||
|
conn_dst.end.codecs[2].payload_type = 97;
|
||||||
|
conn_src.end.codecs[0].audio_name = "AMR/8000/1";
|
||||||
|
conn_dst.end.codecs[0].audio_name = "AMR/8000/1";
|
||||||
|
conn_src.end.codecs[1].audio_name = "PCMU/8000/1";
|
||||||
|
conn_dst.end.codecs[1].audio_name = "PCMU/8000/1";
|
||||||
|
conn_src.end.codecs[2].audio_name = "GSM-HR-08/8000/1";
|
||||||
|
conn_dst.end.codecs[2].audio_name = "GSM-HR-08/8000/1";
|
||||||
|
conn_src.end.codecs[0].subtype_name = "AMR";
|
||||||
|
conn_dst.end.codecs[0].subtype_name = "AMR";
|
||||||
|
conn_src.end.codecs[1].subtype_name = "PCMU";
|
||||||
|
conn_dst.end.codecs[1].subtype_name = "PCMU";
|
||||||
|
conn_src.end.codecs[2].subtype_name = "GSM-HR-08";
|
||||||
|
conn_dst.end.codecs[2].subtype_name = "GSM-HR-08";
|
||||||
|
conn_src.end.codecs_assigned = 3;
|
||||||
|
conn_dst.end.codecs_assigned = 3;
|
||||||
|
|
||||||
|
/* We expect the function to find the PT we must use when we send the
|
||||||
|
* packet out to the destination. All we know is the context for both
|
||||||
|
* connections and the payload type from the source packet */
|
||||||
|
pt_dst =
|
||||||
|
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||||
|
conn_src.end.codecs[0].payload_type);
|
||||||
|
OSMO_ASSERT(pt_dst == conn_dst.end.codecs[0].payload_type);
|
||||||
|
pt_dst =
|
||||||
|
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||||
|
conn_src.end.codecs[1].payload_type);
|
||||||
|
OSMO_ASSERT(pt_dst == conn_dst.end.codecs[1].payload_type);
|
||||||
|
pt_dst =
|
||||||
|
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||||
|
conn_src.end.codecs[2].payload_type);
|
||||||
|
OSMO_ASSERT(pt_dst == conn_dst.end.codecs[2].payload_type);
|
||||||
|
|
||||||
|
/* Try some constellations that must fail */
|
||||||
|
pt_dst = mgcp_codec_pt_translate(&conn_src, &conn_dst, 123);
|
||||||
|
OSMO_ASSERT(pt_dst == -EINVAL);
|
||||||
|
conn_src.end.codecs_assigned = 0;
|
||||||
|
conn_dst.end.codecs_assigned = 3;
|
||||||
|
pt_dst =
|
||||||
|
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||||
|
conn_src.end.codecs[0].payload_type);
|
||||||
|
OSMO_ASSERT(pt_dst == -EINVAL);
|
||||||
|
pt_dst =
|
||||||
|
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||||
|
conn_src.end.codecs[1].payload_type);
|
||||||
|
OSMO_ASSERT(pt_dst == -EINVAL);
|
||||||
|
pt_dst =
|
||||||
|
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||||
|
conn_src.end.codecs[2].payload_type);
|
||||||
|
OSMO_ASSERT(pt_dst == -EINVAL);
|
||||||
|
conn_src.end.codecs_assigned = 3;
|
||||||
|
conn_dst.end.codecs_assigned = 0;
|
||||||
|
pt_dst =
|
||||||
|
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||||
|
conn_src.end.codecs[0].payload_type);
|
||||||
|
OSMO_ASSERT(pt_dst == -EINVAL);
|
||||||
|
pt_dst =
|
||||||
|
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||||
|
conn_src.end.codecs[1].payload_type);
|
||||||
|
OSMO_ASSERT(pt_dst == -EINVAL);
|
||||||
|
pt_dst =
|
||||||
|
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||||
|
conn_src.end.codecs[2].payload_type);
|
||||||
|
OSMO_ASSERT(pt_dst == -EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_conn_id_matching()
|
||||||
|
{
|
||||||
|
struct mgcp_endpoint endp = {};
|
||||||
|
struct mgcp_conn *conn;
|
||||||
|
struct mgcp_conn *conn_match;
|
||||||
|
int i;
|
||||||
|
const char *conn_id_generated = "000023AB";
|
||||||
|
const char *conn_id_request[] = {
|
||||||
|
"23AB",
|
||||||
|
"0023AB",
|
||||||
|
"000023AB",
|
||||||
|
"00000023AB",
|
||||||
|
"23ab",
|
||||||
|
"0023ab",
|
||||||
|
"000023ab",
|
||||||
|
"00000023ab",
|
||||||
|
};
|
||||||
|
|
||||||
|
printf("\nTesting %s\n", __func__);
|
||||||
|
|
||||||
|
INIT_LLIST_HEAD(&endp.conns);
|
||||||
|
|
||||||
|
conn = talloc_zero(NULL, struct mgcp_conn);
|
||||||
|
OSMO_ASSERT(conn);
|
||||||
|
osmo_strlcpy(conn->id, conn_id_generated, sizeof(conn->id));
|
||||||
|
llist_add(&conn->entry, &endp.conns);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(conn_id_request); i++) {
|
||||||
|
const char *needle = conn_id_request[i];
|
||||||
|
printf("needle='%s' ", needle);
|
||||||
|
conn_match = mgcp_conn_get(&endp, needle);
|
||||||
|
OSMO_ASSERT(conn_match);
|
||||||
|
printf("found '%s'\n", conn_match->id);
|
||||||
|
OSMO_ASSERT(conn_match == conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
llist_del(&conn->entry);
|
||||||
|
talloc_free(conn);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
|
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
|
||||||
@@ -1486,6 +1807,10 @@ int main(int argc, char **argv)
|
|||||||
test_no_cycle();
|
test_no_cycle();
|
||||||
test_no_name();
|
test_no_name();
|
||||||
test_osmux_cid();
|
test_osmux_cid();
|
||||||
|
test_get_lco_identifier();
|
||||||
|
test_check_local_cx_options(ctx);
|
||||||
|
test_mgcp_codec_pt_translate();
|
||||||
|
test_conn_id_matching();
|
||||||
|
|
||||||
OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
|
OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
|
||||||
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
|
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0
|
|||||||
checking response:
|
checking response:
|
||||||
using message as statically defined for comparison
|
using message as statically defined for comparison
|
||||||
Response matches our expectations.
|
Response matches our expectations.
|
||||||
(response contains a connection id)
|
(response does not contain a connection id)
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
Testing AUEP2
|
Testing AUEP2
|
||||||
@@ -34,7 +34,7 @@ AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0
|
|||||||
checking response:
|
checking response:
|
||||||
using message as statically defined for comparison
|
using message as statically defined for comparison
|
||||||
Response matches our expectations.
|
Response matches our expectations.
|
||||||
(response contains a connection id)
|
(response does not contain a connection id)
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
Testing MDCX1
|
Testing MDCX1
|
||||||
@@ -46,7 +46,7 @@ MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0
|
|||||||
checking response:
|
checking response:
|
||||||
using message as statically defined for comparison
|
using message as statically defined for comparison
|
||||||
Response matches our expectations.
|
Response matches our expectations.
|
||||||
(response contains a connection id)
|
(response does not contain a connection id)
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
Testing MDCX2
|
Testing MDCX2
|
||||||
@@ -58,7 +58,7 @@ MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0
|
|||||||
checking response:
|
checking response:
|
||||||
using message as statically defined for comparison
|
using message as statically defined for comparison
|
||||||
Response matches our expectations.
|
Response matches our expectations.
|
||||||
(response contains a connection id)
|
(response does not contain a connection id)
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
Testing CRCX
|
Testing CRCX
|
||||||
@@ -79,7 +79,7 @@ a=ptime:40
|
|||||||
checking response:
|
checking response:
|
||||||
using message with patched conn_id for comparison
|
using message with patched conn_id for comparison
|
||||||
Response matches our expectations.
|
Response matches our expectations.
|
||||||
(response does not contain a connection id)
|
(response contains a connection id)
|
||||||
Dummy packets: 2
|
Dummy packets: 2
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
@@ -93,7 +93,7 @@ I: %s
|
|||||||
checking response:
|
checking response:
|
||||||
using message with patched conn_id for comparison
|
using message with patched conn_id for comparison
|
||||||
Response matches our expectations.
|
Response matches our expectations.
|
||||||
(response does not contain a connection id)
|
(response contains a connection id)
|
||||||
Dummy packets: 2
|
Dummy packets: 2
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
@@ -117,7 +117,7 @@ a=ptime:40
|
|||||||
---------8<---------
|
---------8<---------
|
||||||
checking response:
|
checking response:
|
||||||
using message with patched conn_id for comparison
|
using message with patched conn_id for comparison
|
||||||
Response matches our expectations.
|
Response matches our expectations.
|
||||||
(response contains a connection id)
|
(response contains a connection id)
|
||||||
Dummy packets: 2
|
Dummy packets: 2
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ a=ptime:40
|
|||||||
|
|
||||||
---------8<---------
|
---------8<---------
|
||||||
checking response:
|
checking response:
|
||||||
using message with patched conn_id for comparison
|
using message with patched conn_id for comparison
|
||||||
Response matches our expectations.
|
Response matches our expectations.
|
||||||
(response contains a connection id)
|
(response contains a connection id)
|
||||||
Dummy packets: 2
|
Dummy packets: 2
|
||||||
@@ -165,7 +165,7 @@ a=ptime:40
|
|||||||
a=ptime:40
|
a=ptime:40
|
||||||
|
|
||||||
---------8<---------
|
---------8<---------
|
||||||
checking response:
|
checking response:
|
||||||
using message with patched conn_id for comparison
|
using message with patched conn_id for comparison
|
||||||
Response matches our expectations.
|
Response matches our expectations.
|
||||||
(response contains a connection id)
|
(response contains a connection id)
|
||||||
@@ -189,7 +189,7 @@ a=ptime:40
|
|||||||
a=rtpmap:99 AMR/8000
|
a=rtpmap:99 AMR/8000
|
||||||
a=ptime:40
|
a=ptime:40
|
||||||
|
|
||||||
---------8<---------
|
---------8<---------
|
||||||
checking response:
|
checking response:
|
||||||
using message with patched conn_id for comparison
|
using message with patched conn_id for comparison
|
||||||
Response matches our expectations.
|
Response matches our expectations.
|
||||||
@@ -213,7 +213,7 @@ a=ptime:40
|
|||||||
m=audio 4441 RTP/AVP 99
|
m=audio 4441 RTP/AVP 99
|
||||||
a=rtpmap:99 AMR/8000
|
a=rtpmap:99 AMR/8000
|
||||||
a=ptime:40
|
a=ptime:40
|
||||||
|
|
||||||
---------8<---------
|
---------8<---------
|
||||||
checking response:
|
checking response:
|
||||||
using message with patched conn_id for comparison
|
using message with patched conn_id for comparison
|
||||||
@@ -228,7 +228,7 @@ L: p:20, a:AMR, nt:IN
|
|||||||
M: recvonly
|
M: recvonly
|
||||||
C: 2
|
C: 2
|
||||||
I: %s
|
I: %s
|
||||||
L: p:20, a:AMR, nt:IN
|
L: p:20, a:AMR, nt:IN
|
||||||
|
|
||||||
---------8<---------
|
---------8<---------
|
||||||
checking response:
|
checking response:
|
||||||
@@ -243,7 +243,7 @@ C: 2
|
|||||||
---------8<---------
|
---------8<---------
|
||||||
DLCX 7 1@mgw MGCP 1.0
|
DLCX 7 1@mgw MGCP 1.0
|
||||||
I: %s
|
I: %s
|
||||||
C: 2
|
C: 2
|
||||||
|
|
||||||
---------8<---------
|
---------8<---------
|
||||||
checking response:
|
checking response:
|
||||||
@@ -256,7 +256,7 @@ v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
|
|||||||
creating message from statically defined input:
|
creating message from statically defined input:
|
||||||
---------8<---------
|
---------8<---------
|
||||||
CRCX 2 1@mgw MGCP 1.0
|
CRCX 2 1@mgw MGCP 1.0
|
||||||
M: recvonly
|
M: recvonly
|
||||||
C: 2
|
C: 2
|
||||||
|
|
||||||
v=0
|
v=0
|
||||||
@@ -277,7 +277,7 @@ CRCX
|
|||||||
|
|
||||||
|
|
||||||
---------8<---------
|
---------8<---------
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
Testing SHORT1
|
Testing SHORT1
|
||||||
creating message from statically defined input:
|
creating message from statically defined input:
|
||||||
@@ -288,7 +288,7 @@ CRCX 1
|
|||||||
checking response:
|
checking response:
|
||||||
using message as statically defined for comparison
|
using message as statically defined for comparison
|
||||||
Response matches our expectations.
|
Response matches our expectations.
|
||||||
(response contains a connection id)
|
(response does not contain a connection id)
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
Testing SHORT2
|
Testing SHORT2
|
||||||
@@ -299,7 +299,7 @@ CRCX 1 1@mgw
|
|||||||
checking response:
|
checking response:
|
||||||
using message as statically defined for comparison
|
using message as statically defined for comparison
|
||||||
Response matches our expectations.
|
Response matches our expectations.
|
||||||
(response contains a connection id)
|
(response does not contain a connection id)
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
Testing SHORT3
|
Testing SHORT3
|
||||||
@@ -310,7 +310,7 @@ CRCX 1 1@mgw MGCP
|
|||||||
checking response:
|
checking response:
|
||||||
using message as statically defined for comparison
|
using message as statically defined for comparison
|
||||||
Response matches our expectations.
|
Response matches our expectations.
|
||||||
(response contains a connection id)
|
(response does not contain a connection id)
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
Testing SHORT4
|
Testing SHORT4
|
||||||
@@ -324,7 +324,7 @@ S: D/9
|
|||||||
(response does not contain a connection id)
|
(response does not contain a connection id)
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
Testing RQNT1
|
Testing RQNT1
|
||||||
creating message from statically defined input:
|
creating message from statically defined input:
|
||||||
---------8<---------
|
---------8<---------
|
||||||
RQNT 186908780 1@mgw MGCP 1.0
|
RQNT 186908780 1@mgw MGCP 1.0
|
||||||
@@ -338,7 +338,7 @@ R: D/[0-9#*](N), G/ft, fxr/t38
|
|||||||
(response does not contain a connection id)
|
(response does not contain a connection id)
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
Testing RQNT2
|
Testing RQNT2
|
||||||
creating message from statically defined input:
|
creating message from statically defined input:
|
||||||
---------8<---------
|
---------8<---------
|
||||||
RQNT 186908781 1@mgw MGCP 1.0
|
RQNT 186908781 1@mgw MGCP 1.0
|
||||||
@@ -352,7 +352,7 @@ C: 2
|
|||||||
(response does not contain a connection id)
|
(response does not contain a connection id)
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
Testing DLCX
|
Testing DLCX
|
||||||
creating message from statically defined input:
|
creating message from statically defined input:
|
||||||
---------8<---------
|
---------8<---------
|
||||||
DLCX 7 1@mgw MGCP 1.0
|
DLCX 7 1@mgw MGCP 1.0
|
||||||
@@ -373,7 +373,7 @@ a=ptime:40
|
|||||||
M: recvonly
|
M: recvonly
|
||||||
C: 2
|
C: 2
|
||||||
L: p:20
|
L: p:20
|
||||||
|
|
||||||
v=0
|
v=0
|
||||||
c=IN IP4 123.12.12.123
|
c=IN IP4 123.12.12.123
|
||||||
m=audio 5904 RTP/AVP 97
|
m=audio 5904 RTP/AVP 97
|
||||||
@@ -387,7 +387,7 @@ I: %s
|
|||||||
(response contains a connection id)
|
(response contains a connection id)
|
||||||
Dummy packets: 2
|
Dummy packets: 2
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
Testing MDCX3
|
Testing MDCX3
|
||||||
creating message from statically defined input:
|
creating message from statically defined input:
|
||||||
---------8<---------
|
---------8<---------
|
||||||
@@ -402,7 +402,58 @@ C: 2
|
|||||||
Dummy packets: 2
|
Dummy packets: 2
|
||||||
|
|
||||||
================================================
|
================================================
|
||||||
|
Testing DLCX
|
||||||
|
creating message from statically defined input:
|
||||||
|
---------8<---------
|
||||||
|
DLCX 7 1@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 CRCX
|
Testing CRCX
|
||||||
|
creating message from statically defined input:
|
||||||
|
---------8<---------
|
||||||
|
CRCX 2 6@mgw MGCP 1.0
|
||||||
|
M: recvonly
|
||||||
|
C: 2
|
||||||
|
|
||||||
|
---------8<---------
|
||||||
|
checking response:
|
||||||
|
using message with patched conn_id for comparison
|
||||||
|
Response matches our expectations.
|
||||||
|
(response contains a connection id)
|
||||||
|
Dummy packets: 2
|
||||||
|
|
||||||
|
================================================
|
||||||
|
Testing CRCX
|
||||||
|
creating message from statically defined input:
|
||||||
|
---------8<---------
|
||||||
|
CRCX 2 1@mgw MGCP 1.0
|
||||||
|
M: recvonly
|
||||||
|
C: 2
|
||||||
|
L: p:20
|
||||||
|
X-Osmo-IGN: C foo
|
||||||
|
|
||||||
|
v=0
|
||||||
|
c=IN IP4 123.12.12.123
|
||||||
|
m=audio 5904 RTP/AVP 97
|
||||||
|
a=rtpmap:97 GSM-EFR/8000
|
||||||
|
a=ptime:40
|
||||||
|
|
||||||
|
---------8<---------
|
||||||
|
checking response:
|
||||||
|
using message with patched conn_id for comparison
|
||||||
|
Response matches our expectations.
|
||||||
|
(response contains a connection id)
|
||||||
|
Dummy packets: 2
|
||||||
|
|
||||||
|
================================================
|
||||||
Testing MDCX_TOO_LONG_CI
|
Testing MDCX_TOO_LONG_CI
|
||||||
creating message from statically defined input:
|
creating message from statically defined input:
|
||||||
---------8<---------
|
---------8<---------
|
||||||
@@ -1031,7 +1082,7 @@ o=- 1439038275 1439038275 IN IP4 192.168.181.247
|
|||||||
|
|
||||||
---------8<---------
|
---------8<---------
|
||||||
creating message from statically defined input:
|
creating message from statically defined input:
|
||||||
---------8<---------
|
---------8<---------
|
||||||
CRCX 259260421 5@mgw MGCP 1.0
|
CRCX 259260421 5@mgw MGCP 1.0
|
||||||
C: 1355c6041e
|
C: 1355c6041e
|
||||||
L: p:20, a:GSM, nt:IN
|
L: p:20, a:GSM, nt:IN
|
||||||
@@ -1054,7 +1105,7 @@ C: 1355c6041e
|
|||||||
a=rtpmap:97 iLBC/8000
|
a=rtpmap:97 iLBC/8000
|
||||||
a=fmtp:97 mode=30
|
a=fmtp:97 mode=30
|
||||||
a=rtpmap:101 telephone-event/8000
|
a=rtpmap:101 telephone-event/8000
|
||||||
a=fmtp:101 0-15
|
a=fmtp:101 0-15
|
||||||
a=recvonly
|
a=recvonly
|
||||||
|
|
||||||
---------8<---------
|
---------8<---------
|
||||||
@@ -1069,7 +1120,7 @@ o=- 1439038275 1439038275 IN IP4 192.168.181.247
|
|||||||
|
|
||||||
---------8<---------
|
---------8<---------
|
||||||
creating message from statically defined input:
|
creating message from statically defined input:
|
||||||
---------8<---------
|
---------8<---------
|
||||||
CRCX 259260421 5@mgw MGCP 1.0
|
CRCX 259260421 5@mgw MGCP 1.0
|
||||||
C: 1355c6041e
|
C: 1355c6041e
|
||||||
L: p:20, a:GSM, nt:IN
|
L: p:20, a:GSM, nt:IN
|
||||||
@@ -1104,4 +1155,28 @@ a=ptime:40
|
|||||||
M: recvonly
|
M: recvonly
|
||||||
C: 2
|
C: 2
|
||||||
L: p:20
|
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:40
|
||||||
|
|
||||||
|
---------8<---------
|
||||||
|
checking response:
|
||||||
|
using message with patched conn_id for comparison
|
||||||
|
Response matches our expectations.
|
||||||
|
Testing get_lco_identifier()
|
||||||
|
p:10, a:PCMU -> p:10, a:PCMU
|
||||||
|
p:10, a:PCMU -> p:10, a:PCMU
|
||||||
|
'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'
|
||||||
|
'10,a: PCMU' -> 'a: PCMU'
|
||||||
|
'10 ,a: PCMU' -> 'a: PCMU'
|
||||||
|
', a:PCMU' -> 'a:PCMU'
|
||||||
|
' a:PCMU' -> 'a:PCMU'
|
||||||
|
'' -> '(null)'
|
||||||
|
p10, aPCMU -> (null)
|
||||||
'10,a :PCMU' -> '(null)'
|
'10,a :PCMU' -> '(null)'
|
||||||
|
|||||||
@@ -77,14 +77,14 @@ static struct mgcp_client_conf conf;
|
|||||||
struct mgcp_client *mgcp = NULL;
|
struct mgcp_client *mgcp = NULL;
|
||||||
|
|
||||||
static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
|
static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
|
||||||
int conn_id, const char *params)
|
const char *params)
|
||||||
{
|
{
|
||||||
static char compose[4096 - 128];
|
static char compose[4096 - 128];
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
len = snprintf(compose, sizeof(compose),
|
len = snprintf(compose, sizeof(compose),
|
||||||
"%d %u %s\r\nI: %d\n\n%s",
|
"%d %u %s\r\n%s",
|
||||||
code, trans_id, comment, conn_id, params);
|
code, trans_id, comment, params);
|
||||||
OSMO_ASSERT(len < sizeof(compose));
|
OSMO_ASSERT(len < sizeof(compose));
|
||||||
OSMO_ASSERT(len > 0);
|
OSMO_ASSERT(len > 0);
|
||||||
|
|
||||||
@@ -95,21 +95,27 @@ static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
|
|||||||
|
|
||||||
void test_response_cb(struct mgcp_response *response, void *priv)
|
void test_response_cb(struct mgcp_response *response, void *priv)
|
||||||
{
|
{
|
||||||
|
unsigned int i;
|
||||||
OSMO_ASSERT(priv == mgcp);
|
OSMO_ASSERT(priv == mgcp);
|
||||||
mgcp_response_parse_params(response);
|
mgcp_response_parse_params(response);
|
||||||
|
|
||||||
printf("response cb received:\n"
|
printf("response cb received:\n");
|
||||||
" head.response_code = %d\n"
|
printf(" head.response_code = %d\n", response->head.response_code);
|
||||||
" head.trans_id = %u\n"
|
printf(" head.trans_id = %u\n", response->head.trans_id);
|
||||||
" head.comment = %s\n"
|
printf(" head.conn_id = %s\n", response->head.conn_id);
|
||||||
" audio_port = %u\n"
|
printf(" head.comment = %s\n", response->head.comment);
|
||||||
" audio_ip = %s\n",
|
printf(" audio_port = %u\n", response->audio_port);
|
||||||
response->head.response_code,
|
printf(" audio_ip = %s\n", response->audio_ip);
|
||||||
response->head.trans_id,
|
printf(" ptime = %u\n", response->ptime);
|
||||||
response->head.comment,
|
printf(" codecs_len = %u\n", response->codecs_len);
|
||||||
response->audio_port,
|
for(i=0;i<response->codecs_len;i++)
|
||||||
response->audio_ip
|
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);
|
||||||
|
printf(" ptmap[%u].pt = %u\n", i, response->ptmap[i].pt);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
|
mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
|
||||||
@@ -129,31 +135,6 @@ mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
|
|||||||
return trans_id;
|
return trans_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_crcx(void)
|
|
||||||
{
|
|
||||||
struct msgb *msg;
|
|
||||||
mgcp_trans_id_t trans_id;
|
|
||||||
|
|
||||||
printf("\n===== %s =====\n", __func__);
|
|
||||||
|
|
||||||
if (mgcp)
|
|
||||||
talloc_free(mgcp);
|
|
||||||
mgcp = mgcp_client_init(ctx, &conf);
|
|
||||||
|
|
||||||
msg = mgcp_msg_crcx(mgcp, 23, 42, MGCP_CONN_LOOPBACK);
|
|
||||||
trans_id = dummy_mgcp_send(msg);
|
|
||||||
|
|
||||||
reply_to(trans_id, 200, "OK", 1,
|
|
||||||
"v=0\r\n"
|
|
||||||
"o=- 1 23 IN IP4 10.9.1.120\r\n"
|
|
||||||
"s=-\r\n"
|
|
||||||
"c=IN IP4 10.9.1.120\r\n"
|
|
||||||
"t=0 0\r\n"
|
|
||||||
"m=audio 16002 RTP/AVP 98\r\n"
|
|
||||||
"a=rtpmap:98 AMR/8000\r\n"
|
|
||||||
"a=ptime:20\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_mgcp_msg(void)
|
void test_mgcp_msg(void)
|
||||||
{
|
{
|
||||||
struct msgb *msg;
|
struct msgb *msg;
|
||||||
@@ -166,7 +147,16 @@ void test_mgcp_msg(void)
|
|||||||
.audio_port = 1234,
|
.audio_port = 1234,
|
||||||
.call_id = 47,
|
.call_id = 47,
|
||||||
.conn_id = "11",
|
.conn_id = "11",
|
||||||
.conn_mode = MGCP_CONN_RECV_SEND
|
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||||
|
.ptime = 20,
|
||||||
|
.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,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (mgcp)
|
if (mgcp)
|
||||||
@@ -183,6 +173,26 @@ void test_mgcp_msg(void)
|
|||||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||||
printf("%s\n", (char *)msg->data);
|
printf("%s\n", (char *)msg->data);
|
||||||
|
|
||||||
|
printf("Generated CRCX message (two codecs):\n");
|
||||||
|
mgcp_msg.verb = MGCP_VERB_CRCX;
|
||||||
|
mgcp_msg.presence =
|
||||||
|
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||||
|
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
|
||||||
|
mgcp_msg.codecs_len = 2;
|
||||||
|
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||||
|
mgcp_msg.codecs_len = 1;
|
||||||
|
printf("%s\n", (char *)msg->data);
|
||||||
|
|
||||||
|
printf("Generated CRCX message (three codecs, one with custom pt):\n");
|
||||||
|
mgcp_msg.verb = MGCP_VERB_CRCX;
|
||||||
|
mgcp_msg.presence =
|
||||||
|
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||||
|
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
|
||||||
|
mgcp_msg.codecs_len = 3;
|
||||||
|
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||||
|
mgcp_msg.codecs_len = 1;
|
||||||
|
printf("%s\n", (char *)msg->data);
|
||||||
|
|
||||||
printf("Generated MDCX message:\n");
|
printf("Generated MDCX message:\n");
|
||||||
mgcp_msg.verb = MGCP_VERB_MDCX;
|
mgcp_msg.verb = MGCP_VERB_MDCX;
|
||||||
mgcp_msg.presence =
|
mgcp_msg.presence =
|
||||||
@@ -192,6 +202,28 @@ void test_mgcp_msg(void)
|
|||||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||||
printf("%s\n", (char *)msg->data);
|
printf("%s\n", (char *)msg->data);
|
||||||
|
|
||||||
|
printf("Generated MDCX message (two codecs):\n");
|
||||||
|
mgcp_msg.verb = MGCP_VERB_MDCX;
|
||||||
|
mgcp_msg.presence =
|
||||||
|
(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.codecs_len = 2;
|
||||||
|
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||||
|
mgcp_msg.codecs_len = 1;
|
||||||
|
printf("%s\n", (char *)msg->data);
|
||||||
|
|
||||||
|
printf("Generated MDCX message (three codecs, one with custom pt):\n");
|
||||||
|
mgcp_msg.verb = MGCP_VERB_MDCX;
|
||||||
|
mgcp_msg.presence =
|
||||||
|
(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.codecs_len = 3;
|
||||||
|
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||||
|
mgcp_msg.codecs_len = 1;
|
||||||
|
printf("%s\n", (char *)msg->data);
|
||||||
|
|
||||||
printf("Generated DLCX message:\n");
|
printf("Generated DLCX message:\n");
|
||||||
mgcp_msg.verb = MGCP_VERB_DLCX;
|
mgcp_msg.verb = MGCP_VERB_DLCX;
|
||||||
mgcp_msg.presence =
|
mgcp_msg.presence =
|
||||||
@@ -212,6 +244,16 @@ void test_mgcp_msg(void)
|
|||||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||||
printf("%s\n", (char *)msg->data);
|
printf("%s\n", (char *)msg->data);
|
||||||
|
|
||||||
|
printf("Generate X-Osmo-IGN message:\n");
|
||||||
|
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||||
|
mgcp_msg.verb = MGCP_VERB_CRCX;
|
||||||
|
mgcp_msg.presence =
|
||||||
|
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||||
|
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE
|
||||||
|
| MGCP_MSG_PRESENCE_X_OSMO_IGN);
|
||||||
|
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||||
|
printf("%s\n", (char *)msg->data);
|
||||||
|
|
||||||
printf("Overfolow test:\n");
|
printf("Overfolow test:\n");
|
||||||
mgcp_msg.verb = MGCP_VERB_MDCX;
|
mgcp_msg.verb = MGCP_VERB_MDCX;
|
||||||
mgcp_msg.presence =
|
mgcp_msg.presence =
|
||||||
@@ -242,6 +284,9 @@ void test_mgcp_client_cancel()
|
|||||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
|
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
|
||||||
| MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE),
|
| MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE),
|
||||||
|
.ptime = 20,
|
||||||
|
.codecs[0] = CODEC_AMR_8000_1,
|
||||||
|
.codecs_len = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
printf("\n%s():\n", __func__);
|
printf("\n%s():\n", __func__);
|
||||||
@@ -265,7 +310,7 @@ void test_mgcp_client_cancel()
|
|||||||
OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == 0);
|
OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == 0);
|
||||||
|
|
||||||
fprintf(stderr, "- late response gets discarded\n");
|
fprintf(stderr, "- late response gets discarded\n");
|
||||||
OSMO_ASSERT(reply_to(trans_id, 200, "OK", 1, "v=0\r\n") == -ENOENT);
|
OSMO_ASSERT(reply_to(trans_id, 200, "OK", "I: 1\r\n\r\nv=0\r\n") == -ENOENT);
|
||||||
|
|
||||||
fprintf(stderr, "- canceling again does nothing\n");
|
fprintf(stderr, "- canceling again does nothing\n");
|
||||||
OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == -ENOENT);
|
OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == -ENOENT);
|
||||||
@@ -376,6 +421,99 @@ void test_sdp_section_start()
|
|||||||
OSMO_ASSERT(!failures);
|
OSMO_ASSERT(!failures);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_map_pt_to_codec(void)
|
||||||
|
{
|
||||||
|
/* Full form */
|
||||||
|
OSMO_ASSERT(map_str_to_codec("PCMU/8000/1") == CODEC_PCMU_8000_1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("GSM/8000/1") == CODEC_GSM_8000_1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("PCMA/8000/1") == CODEC_PCMA_8000_1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("G729/8000/1") == CODEC_G729_8000_1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("GSM-EFR/8000/1") == CODEC_GSMEFR_8000_1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("GSM-HR-08/8000/1") == CODEC_GSMHR_8000_1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("AMR/8000/1") == CODEC_AMR_8000_1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("AMR-WB/16000/1") == CODEC_AMRWB_16000_1);
|
||||||
|
|
||||||
|
/* Short form */
|
||||||
|
OSMO_ASSERT(map_str_to_codec("GSM-EFR") == CODEC_GSMEFR_8000_1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("G729") == CODEC_G729_8000_1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("GSM-HR-08") == CODEC_GSMHR_8000_1);
|
||||||
|
|
||||||
|
/* We do not care about what is after the first delimiter */
|
||||||
|
OSMO_ASSERT(map_str_to_codec("AMR-WB/123///456") == CODEC_AMRWB_16000_1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("PCMA/asdf") == CODEC_PCMA_8000_1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("GSM/qwertz") == CODEC_GSM_8000_1);
|
||||||
|
|
||||||
|
/* A trailing delimiter should not hurt */
|
||||||
|
OSMO_ASSERT(map_str_to_codec("AMR/") == CODEC_AMR_8000_1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("G729/") == CODEC_G729_8000_1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("GSM/") == CODEC_GSM_8000_1);
|
||||||
|
|
||||||
|
/* This is expected to fail */
|
||||||
|
OSMO_ASSERT(map_str_to_codec("INVALID/1234/7") == -1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec(NULL) == -1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("") == -1);
|
||||||
|
OSMO_ASSERT(map_str_to_codec("/////") == -1);
|
||||||
|
|
||||||
|
/* The buffers are 64 bytes long, check what happens with overlong
|
||||||
|
* strings as input (This schould still work.) */
|
||||||
|
OSMO_ASSERT(map_str_to_codec("AMR-WB/16000/1############################################################################################################") == CODEC_AMRWB_16000_1);
|
||||||
|
|
||||||
|
/* This should not work, as there is no delimiter after the codec
|
||||||
|
* name */
|
||||||
|
OSMO_ASSERT(map_str_to_codec("AMR-WB####################################################################################################################") == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_map_codec_to_pt_and_map_pt_to_codec(void)
|
||||||
|
{
|
||||||
|
struct ptmap ptmap[10];
|
||||||
|
unsigned int ptmap_len;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
ptmap[0].codec = CODEC_GSMEFR_8000_1;
|
||||||
|
ptmap[0].pt = 96;
|
||||||
|
ptmap[1].codec = CODEC_GSMHR_8000_1;
|
||||||
|
ptmap[1].pt = 97;
|
||||||
|
ptmap[2].codec = CODEC_AMR_8000_1;
|
||||||
|
ptmap[2].pt = 98;
|
||||||
|
ptmap[3].codec = CODEC_AMRWB_16000_1;
|
||||||
|
ptmap[3].pt = 99;
|
||||||
|
ptmap_len = 4;
|
||||||
|
|
||||||
|
/* Mappings that are covered by the table */
|
||||||
|
for (i = 0; i < ptmap_len; i++)
|
||||||
|
printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
|
||||||
|
for (i = 0; i < ptmap_len; i++)
|
||||||
|
printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* Map some codecs/payload types from the static range, result must
|
||||||
|
* always be a 1:1 mapping */
|
||||||
|
printf(" %u => %u\n", CODEC_PCMU_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMU_8000_1));
|
||||||
|
printf(" %u => %u\n", CODEC_GSM_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_GSM_8000_1));
|
||||||
|
printf(" %u => %u\n", CODEC_PCMA_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMA_8000_1));
|
||||||
|
printf(" %u => %u\n", CODEC_G729_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_G729_8000_1));
|
||||||
|
printf(" %u <= %u\n", CODEC_PCMU_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMU_8000_1));
|
||||||
|
printf(" %u <= %u\n", CODEC_GSM_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_GSM_8000_1));
|
||||||
|
printf(" %u <= %u\n", CODEC_PCMA_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMA_8000_1));
|
||||||
|
printf(" %u <= %u\n", CODEC_G729_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_G729_8000_1));
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
/* Try to do mappings from statically defined range to danymic range and vice versa. This
|
||||||
|
* is illegal and should result into a 1:1 mapping */
|
||||||
|
ptmap[3].codec = CODEC_AMRWB_16000_1;
|
||||||
|
ptmap[3].pt = 2;
|
||||||
|
ptmap[4].codec = CODEC_PCMU_8000_1;
|
||||||
|
ptmap[4].pt = 100;
|
||||||
|
ptmap_len = 5;
|
||||||
|
|
||||||
|
/* Apply all mappings again, the illegal ones we defined should result into 1:1 mappings */
|
||||||
|
for (i = 0; i < ptmap_len; i++)
|
||||||
|
printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
|
||||||
|
for (i = 0; i < ptmap_len; i++)
|
||||||
|
printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
static const struct log_info_cat log_categories[] = {
|
static const struct log_info_cat log_categories[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -399,10 +537,11 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
mgcp_client_conf_init(&conf);
|
mgcp_client_conf_init(&conf);
|
||||||
|
|
||||||
test_crcx();
|
|
||||||
test_mgcp_msg();
|
test_mgcp_msg();
|
||||||
test_mgcp_client_cancel();
|
test_mgcp_client_cancel();
|
||||||
test_sdp_section_start();
|
test_sdp_section_start();
|
||||||
|
test_map_codec_to_pt_and_map_pt_to_codec();
|
||||||
|
test_map_pt_to_codec();
|
||||||
|
|
||||||
printf("Done\n");
|
printf("Done\n");
|
||||||
fprintf(stderr, "Done\n");
|
fprintf(stderr, "Done\n");
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
DLMGCP MGCP client: using endpoint domain '@mgw'
|
||||||
DLMGCP message buffer to small, can not generate MGCP message
|
DLMGCP message buffer to small, can not generate MGCP message
|
||||||
|
|
||||||
test_mgcp_client_cancel():
|
test_mgcp_client_cancel():
|
||||||
|
DLMGCP MGCP client: using endpoint domain '@mgw'
|
||||||
- composed msg with trans_id=1
|
- composed msg with trans_id=1
|
||||||
- not in queue yet, cannot cancel yet
|
- not in queue yet, cannot cancel yet
|
||||||
DLMGCP Cannot cancel, no such transaction: 1
|
DLMGCP Cannot cancel, no such transaction: 1
|
||||||
@@ -62,4 +64,8 @@ test_sdp_section_start() test [9]:
|
|||||||
body: "some mgcp header data\r\nand header params\n\r\rm=audio 23\r\n"
|
body: "some mgcp header data\r\nand header params\n\r\rm=audio 23\r\n"
|
||||||
DLMGCP MGCP response: cannot find start of SDP parameters
|
DLMGCP MGCP response: cannot find start of SDP parameters
|
||||||
got rc=-22
|
got rc=-22
|
||||||
|
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
|
||||||
Done
|
Done
|
||||||
|
|||||||
@@ -1,44 +1,27 @@
|
|||||||
|
|
||||||
===== test_crcx =====
|
|
||||||
composed:
|
|
||||||
-----
|
|
||||||
CRCX 1 17@mgw MGCP 1.0
|
|
||||||
C: 2a
|
|
||||||
L: p:20, a:AMR, nt:IN
|
|
||||||
M: loopback
|
|
||||||
|
|
||||||
-----
|
|
||||||
composed response:
|
|
||||||
-----
|
|
||||||
200 1 OK
|
|
||||||
I: 1
|
|
||||||
|
|
||||||
v=0
|
|
||||||
o=- 1 23 IN IP4 10.9.1.120
|
|
||||||
s=-
|
|
||||||
c=IN IP4 10.9.1.120
|
|
||||||
t=0 0
|
|
||||||
m=audio 16002 RTP/AVP 98
|
|
||||||
a=rtpmap:98 AMR/8000
|
|
||||||
a=ptime:20
|
|
||||||
|
|
||||||
-----
|
|
||||||
response cb received:
|
|
||||||
head.response_code = 200
|
|
||||||
head.trans_id = 1
|
|
||||||
head.comment = OK
|
|
||||||
audio_port = 16002
|
|
||||||
audio_ip = 10.9.1.120
|
|
||||||
|
|
||||||
Generated CRCX message:
|
Generated CRCX message:
|
||||||
CRCX 1 23@mgw MGCP 1.0
|
CRCX 1 23@mgw MGCP 1.0
|
||||||
C: 2f
|
C: 2f
|
||||||
I: 11
|
I: 11
|
||||||
L: p:20, a:AMR, nt:IN
|
L: p:20, a:GSM, nt:IN
|
||||||
|
M: sendrecv
|
||||||
|
|
||||||
|
Generated CRCX message (two codecs):
|
||||||
|
CRCX 2 23@mgw MGCP 1.0
|
||||||
|
C: 2f
|
||||||
|
I: 11
|
||||||
|
L: p:20, a:GSM;AMR, nt:IN
|
||||||
|
M: sendrecv
|
||||||
|
|
||||||
|
Generated CRCX message (three codecs, one with custom pt):
|
||||||
|
CRCX 3 23@mgw MGCP 1.0
|
||||||
|
C: 2f
|
||||||
|
I: 11
|
||||||
|
L: p:20, a:GSM;AMR;GSM-EFR, nt:IN
|
||||||
M: sendrecv
|
M: sendrecv
|
||||||
|
|
||||||
Generated MDCX message:
|
Generated MDCX message:
|
||||||
MDCX 2 23@mgw MGCP 1.0
|
MDCX 4 23@mgw MGCP 1.0
|
||||||
C: 2f
|
C: 2f
|
||||||
I: 11
|
I: 11
|
||||||
M: sendrecv
|
M: sendrecv
|
||||||
@@ -48,18 +31,58 @@ o=- 2f 23 IN IP4 127.0.0.1
|
|||||||
s=-
|
s=-
|
||||||
c=IN IP4 192.168.100.23
|
c=IN IP4 192.168.100.23
|
||||||
t=0 0
|
t=0 0
|
||||||
m=audio 1234 RTP/AVP 255
|
m=audio 1234 RTP/AVP 3
|
||||||
|
a=ptime:20
|
||||||
|
|
||||||
|
Generated MDCX message (two codecs):
|
||||||
|
MDCX 5 23@mgw MGCP 1.0
|
||||||
|
C: 2f
|
||||||
|
I: 11
|
||||||
|
M: sendrecv
|
||||||
|
|
||||||
|
v=0
|
||||||
|
o=- 2f 23 IN IP4 127.0.0.1
|
||||||
|
s=-
|
||||||
|
c=IN IP4 192.168.100.23
|
||||||
|
t=0 0
|
||||||
|
m=audio 1234 RTP/AVP 3 112
|
||||||
|
a=rtpmap:112 AMR/8000/1
|
||||||
|
a=ptime:20
|
||||||
|
|
||||||
|
Generated MDCX message (three codecs, one with custom pt):
|
||||||
|
MDCX 6 23@mgw MGCP 1.0
|
||||||
|
C: 2f
|
||||||
|
I: 11
|
||||||
|
M: sendrecv
|
||||||
|
|
||||||
|
v=0
|
||||||
|
o=- 2f 23 IN IP4 127.0.0.1
|
||||||
|
s=-
|
||||||
|
c=IN IP4 192.168.100.23
|
||||||
|
t=0 0
|
||||||
|
m=audio 1234 RTP/AVP 3 112 96
|
||||||
|
a=rtpmap:112 AMR/8000/1
|
||||||
|
a=rtpmap:96 GSM-EFR/8000/1
|
||||||
|
a=ptime:20
|
||||||
|
|
||||||
Generated DLCX message:
|
Generated DLCX message:
|
||||||
DLCX 3 23@mgw MGCP 1.0
|
DLCX 7 23@mgw MGCP 1.0
|
||||||
C: 2f
|
C: 2f
|
||||||
I: 11
|
I: 11
|
||||||
|
|
||||||
Generated AUEP message:
|
Generated AUEP message:
|
||||||
AUEP 4 23@mgw MGCP 1.0
|
AUEP 8 23@mgw MGCP 1.0
|
||||||
|
|
||||||
Generated RSIP message:
|
Generated RSIP message:
|
||||||
RSIP 5 23@mgw MGCP 1.0
|
RSIP 9 23@mgw MGCP 1.0
|
||||||
|
|
||||||
|
Generate X-Osmo-IGN message:
|
||||||
|
CRCX 11 23@mgw MGCP 1.0
|
||||||
|
C: 2f
|
||||||
|
I: 11
|
||||||
|
L: p:20, a:GSM, nt:IN
|
||||||
|
M: sendrecv
|
||||||
|
X-Osmo-IGN: C
|
||||||
|
|
||||||
Overfolow test:
|
Overfolow test:
|
||||||
|
|
||||||
@@ -77,8 +100,8 @@ M: sendrecv
|
|||||||
composed response:
|
composed response:
|
||||||
-----
|
-----
|
||||||
200 1 OK
|
200 1 OK
|
||||||
I: 1
|
I: 1
|
||||||
|
|
||||||
v=0
|
v=0
|
||||||
|
|
||||||
-----
|
-----
|
||||||
@@ -102,4 +125,33 @@ test_sdp_section_start() test [7]:
|
|||||||
test_sdp_section_start() test [8]:
|
test_sdp_section_start() test [8]:
|
||||||
|
|
||||||
test_sdp_section_start() test [9]:
|
test_sdp_section_start() test [9]:
|
||||||
|
110 => 96
|
||||||
|
111 => 97
|
||||||
|
112 => 98
|
||||||
|
113 => 99
|
||||||
|
96 <= 110
|
||||||
|
97 <= 111
|
||||||
|
98 <= 112
|
||||||
|
99 <= 113
|
||||||
|
|
||||||
|
0 => 0
|
||||||
|
3 => 3
|
||||||
|
8 => 8
|
||||||
|
18 => 18
|
||||||
|
0 <= 0
|
||||||
|
3 <= 3
|
||||||
|
8 <= 8
|
||||||
|
18 <= 18
|
||||||
|
|
||||||
|
110 => 96
|
||||||
|
111 => 97
|
||||||
|
112 => 98
|
||||||
|
113 => 113
|
||||||
|
0 => 0
|
||||||
|
96 <= 110
|
||||||
|
97 <= 111
|
||||||
|
98 <= 112
|
||||||
|
2 <= 2
|
||||||
|
100 <= 100
|
||||||
|
|
||||||
Done
|
Done
|
||||||
|
|||||||
@@ -1,19 +1,6 @@
|
|||||||
AT_INIT
|
AT_INIT
|
||||||
AT_BANNER([Regression tests.])
|
AT_BANNER([Regression tests.])
|
||||||
|
|
||||||
AT_SETUP([legacy_mgcp])
|
|
||||||
AT_KEYWORDS([legacy_mgcp])
|
|
||||||
cat $abs_srcdir/legacy_mgcp/mgcp_test.ok > expout
|
|
||||||
AT_CHECK([$abs_top_builddir/tests/legacy_mgcp/mgcp_test], [], [expout], [ignore])
|
|
||||||
AT_CLEANUP
|
|
||||||
|
|
||||||
AT_SETUP([legacy_mgcp-trans])
|
|
||||||
AT_KEYWORDS([legacy_mgcp-trans])
|
|
||||||
AT_CHECK([test "$enable_mgcp_transcoding_test" == yes || exit 77])
|
|
||||||
cat $abs_srcdir/legacy_mgcp/mgcp_transcoding_test.ok > expout
|
|
||||||
AT_CHECK([$abs_top_builddir/tests/legacy_mgcp/mgcp_transcoding_test], [], [expout], [ignore])
|
|
||||||
AT_CLEANUP
|
|
||||||
|
|
||||||
AT_SETUP([mgcp_client])
|
AT_SETUP([mgcp_client])
|
||||||
AT_KEYWORDS([mgcp_client])
|
AT_KEYWORDS([mgcp_client])
|
||||||
cat $abs_srcdir/mgcp_client/mgcp_client_test.ok > expout
|
cat $abs_srcdir/mgcp_client/mgcp_client_test.ok > expout
|
||||||
|
|||||||
@@ -1,171 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
|
|
||||||
# (C) 2013 by Holger Hans Peter Freyther
|
|
||||||
# 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/>.
|
|
||||||
|
|
||||||
import os, sys
|
|
||||||
import time
|
|
||||||
import unittest
|
|
||||||
import socket
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
import osmopy.obscvty as obscvty
|
|
||||||
import osmopy.osmoutil as osmoutil
|
|
||||||
|
|
||||||
# add $top_srcdir/contrib to find ipa.py
|
|
||||||
sys.path.append(os.path.join(sys.path[0], '..', 'contrib'))
|
|
||||||
|
|
||||||
from ipa import IPA
|
|
||||||
|
|
||||||
# to be able to find $top_srcdir/doc/...
|
|
||||||
confpath = os.path.join(sys.path[0], '..')
|
|
||||||
|
|
||||||
class TestVTYBase(unittest.TestCase):
|
|
||||||
|
|
||||||
def checkForEndAndExit(self):
|
|
||||||
res = self.vty.command("list")
|
|
||||||
#print ('looking for "exit"\n')
|
|
||||||
self.assert_(res.find(' exit\r') > 0)
|
|
||||||
#print 'found "exit"\nlooking for "end"\n'
|
|
||||||
self.assert_(res.find(' end\r') > 0)
|
|
||||||
#print 'found "end"\n'
|
|
||||||
|
|
||||||
def vty_command(self):
|
|
||||||
raise Exception("Needs to be implemented by a subclass")
|
|
||||||
|
|
||||||
def vty_app(self):
|
|
||||||
raise Exception("Needs to be implemented by a subclass")
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
osmo_vty_cmd = self.vty_command()[:]
|
|
||||||
config_index = osmo_vty_cmd.index('-c')
|
|
||||||
if config_index:
|
|
||||||
cfi = config_index + 1
|
|
||||||
osmo_vty_cmd[cfi] = os.path.join(confpath, osmo_vty_cmd[cfi])
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.proc = osmoutil.popen_devnull(osmo_vty_cmd)
|
|
||||||
except OSError:
|
|
||||||
print >> sys.stderr, "Current directory: %s" % os.getcwd()
|
|
||||||
print >> sys.stderr, "Consider setting -b"
|
|
||||||
|
|
||||||
appstring = self.vty_app()[2]
|
|
||||||
appport = self.vty_app()[0]
|
|
||||||
self.vty = obscvty.VTYInteract(appstring, "127.0.0.1", appport)
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
if self.vty:
|
|
||||||
self.vty._close_socket()
|
|
||||||
self.vty = None
|
|
||||||
osmoutil.end_proc(self.proc)
|
|
||||||
|
|
||||||
class TestVTYMGCP(TestVTYBase):
|
|
||||||
def vty_command(self):
|
|
||||||
return ["./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "-c",
|
|
||||||
"doc/examples/osmo-bsc_mgcp/mgcp.cfg"]
|
|
||||||
|
|
||||||
def vty_app(self):
|
|
||||||
return (4243, "./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp")
|
|
||||||
|
|
||||||
def testForcePtime(self):
|
|
||||||
self.vty.enable()
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assert_(res.find(' rtp force-ptime 20\r') > 0)
|
|
||||||
self.assertEquals(res.find(' no rtp force-ptime\r'), -1)
|
|
||||||
|
|
||||||
self.vty.command("configure terminal")
|
|
||||||
self.vty.command("mgcp")
|
|
||||||
self.vty.command("no rtp force-ptime")
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assertEquals(res.find(' rtp force-ptime 20\r'), -1)
|
|
||||||
self.assertEquals(res.find(' no rtp force-ptime\r'), -1)
|
|
||||||
|
|
||||||
def testOmitAudio(self):
|
|
||||||
self.vty.enable()
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assert_(res.find(' sdp audio-payload send-name\r') > 0)
|
|
||||||
self.assertEquals(res.find(' no sdp audio-payload send-name\r'), -1)
|
|
||||||
|
|
||||||
self.vty.command("configure terminal")
|
|
||||||
self.vty.command("mgcp")
|
|
||||||
self.vty.command("no sdp audio-payload send-name")
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assertEquals(res.find(' rtp sdp audio-payload send-name\r'), -1)
|
|
||||||
self.assert_(res.find(' no sdp audio-payload send-name\r') > 0)
|
|
||||||
|
|
||||||
# TODO: test it for the trunk!
|
|
||||||
|
|
||||||
def testBindAddr(self):
|
|
||||||
self.vty.enable()
|
|
||||||
|
|
||||||
self.vty.command("configure terminal")
|
|
||||||
self.vty.command("mgcp")
|
|
||||||
|
|
||||||
# enable.. disable bts-bind-ip
|
|
||||||
self.vty.command("rtp bts-bind-ip 254.253.252.250")
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assert_(res.find('rtp bts-bind-ip 254.253.252.250') > 0)
|
|
||||||
self.vty.command("no rtp bts-bind-ip")
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assertEquals(res.find(' rtp bts-bind-ip'), -1)
|
|
||||||
|
|
||||||
# enable.. disable net-bind-ip
|
|
||||||
self.vty.command("rtp net-bind-ip 254.253.252.250")
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assert_(res.find('rtp net-bind-ip 254.253.252.250') > 0)
|
|
||||||
self.vty.command("no rtp net-bind-ip")
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assertEquals(res.find(' rtp net-bind-ip'), -1)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
import argparse
|
|
||||||
import sys
|
|
||||||
|
|
||||||
workdir = '.'
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("-v", "--verbose", dest="verbose",
|
|
||||||
action="store_true", help="verbose mode")
|
|
||||||
parser.add_argument("-p", "--pythonconfpath", dest="p",
|
|
||||||
help="searchpath for config")
|
|
||||||
parser.add_argument("-w", "--workdir", dest="w",
|
|
||||||
help="Working directory")
|
|
||||||
parser.add_argument("test_name", nargs="*", help="(parts of) test names to run, case-insensitive")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
verbose_level = 1
|
|
||||||
if args.verbose:
|
|
||||||
verbose_level = 2
|
|
||||||
|
|
||||||
if args.w:
|
|
||||||
workdir = args.w
|
|
||||||
|
|
||||||
if args.p:
|
|
||||||
confpath = args.p
|
|
||||||
|
|
||||||
print "confpath %s, workdir %s" % (confpath, workdir)
|
|
||||||
os.chdir(workdir)
|
|
||||||
print "Running tests for specific VTY commands"
|
|
||||||
suite = unittest.TestSuite()
|
|
||||||
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestVTYMGCP))
|
|
||||||
|
|
||||||
if args.test_name:
|
|
||||||
osmoutil.pick_tests(suite, *args.test_name)
|
|
||||||
|
|
||||||
res = unittest.TextTestRunner(verbosity=verbose_level, stream=sys.stdout).run(suite)
|
|
||||||
sys.exit(len(res.errors) + len(res.failures))
|
|
||||||
|
|
||||||
# vim: shiftwidth=4 expandtab nocin ai
|
|
||||||
Reference in New Issue
Block a user