mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 08:12:01 +00:00
Compare commits
240 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
3ed3eb8e0f | ||
|
fdc5f111aa | ||
|
02d612a926 | ||
|
944af2f641 | ||
|
f2a65b8bbb | ||
|
332308d994 | ||
|
099332824e | ||
|
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 | ||
|
d761d355f9 | ||
|
8d064dfd24 | ||
|
7181cc1f02 | ||
|
60f8e31a2f | ||
|
086c3f3c67 | ||
|
d0b470d1a9 | ||
|
77f76d0be5 | ||
|
892dec0be9 | ||
|
c430d19112 | ||
|
7b8e419d92 | ||
|
4f7613eeb1 | ||
|
01f039538e | ||
|
d2e3a52230 | ||
|
2924825bc6 | ||
|
bd86f94a09 | ||
|
1083533db4 | ||
|
0793d2f5d5 | ||
|
a8c6a9c37a | ||
|
2da99a2946 | ||
|
c3eed40f00 | ||
|
515e341ff6 | ||
|
8bda7a7766 | ||
|
df5d219f39 | ||
|
5656fbf49d | ||
|
19d640e806 | ||
|
207ab51270 | ||
|
c3cc654add | ||
|
b911b879d8 | ||
|
af07f66ca3 | ||
|
dd0c522cd4 | ||
|
37d11c80da | ||
|
1355d7e4f7 | ||
|
fdd603c4c8 | ||
|
1fc6999a77 | ||
|
220f487238 | ||
|
edc00f4ea7 | ||
|
a49e32a1ab | ||
|
dd4ede34ad | ||
|
3aa815755d | ||
|
3261dc7540 | ||
|
771b26a043 | ||
|
36a81129ec | ||
|
a390d0ba52 | ||
|
b759473d10 | ||
|
c3cfae2cf8 | ||
|
3cbfb8a53c | ||
|
275ac97036 | ||
|
abe8c897fd | ||
|
e9d645b3b3 | ||
|
7f0966c13d | ||
|
55295f7b07 | ||
|
9d25d7a2e6 | ||
|
490cbaa89e | ||
|
eb0bde09bb | ||
|
3b12e1b011 | ||
|
2138779559 | ||
|
12943ea0c1 | ||
|
6efb43a210 | ||
|
03cc48474c | ||
|
5672563f7e | ||
|
106743c66b | ||
|
8890dfa634 | ||
|
10f32dbfbf | ||
|
e35eeae8e0 | ||
|
abbb6b9088 | ||
|
3f35a3708a | ||
|
33eafe050b | ||
|
1d1b98f4a4 | ||
|
a0ac30faa5 | ||
|
49e3d5a9c9 | ||
|
3338135ead | ||
|
5dbfc78aff | ||
|
7bc5552512 | ||
|
a330b864e5 | ||
|
7df419b434 | ||
|
7cedfd753b | ||
|
23b8e29835 | ||
|
1c3287f1db | ||
|
922ae9a5bf | ||
|
189d6bf4d4 | ||
|
c8f37cb4d6 | ||
|
c0dcc3c60c | ||
|
230e4fc270 | ||
|
ead2f60f73 | ||
|
f8bfbe8b14 | ||
|
ffd75e420c | ||
|
01d24a3281 | ||
|
0be3ce66c0 | ||
|
fcd0655176 | ||
|
48454983c4 | ||
|
9a3543a8d4 | ||
|
465446b4b4 | ||
|
b597b4fc9d | ||
|
d20910c655 | ||
|
677f4ad968 | ||
|
7c20c9de5a | ||
|
839fc954d0 | ||
|
d164b05655 | ||
|
a896f9cdeb | ||
|
1b0cf6fa1c | ||
|
9bf7c53779 | ||
|
6be0fdb5e4 | ||
|
641c4d47b9 | ||
|
c1b9fa158e | ||
|
6a421f1b6f | ||
|
f1889d8640 | ||
|
2982e42d93 | ||
|
ddf1f9d7d5 | ||
|
653c4ff354 | ||
|
4efce88a62 |
13
.gitignore
vendored
13
.gitignore
vendored
@@ -22,6 +22,7 @@ src/utils/smpp_mirror
|
||||
*.pyc
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.pc
|
||||
|
||||
#configure
|
||||
aclocal.m4
|
||||
@@ -38,6 +39,7 @@ missing
|
||||
stamp-h1
|
||||
libtool
|
||||
ltmain.sh
|
||||
m4/*.m4
|
||||
|
||||
# git-version-gen magic
|
||||
.tarball-version
|
||||
@@ -81,3 +83,14 @@ gsn_restart
|
||||
src/openbsc.cfg*
|
||||
writtenconfig/
|
||||
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,14 +18,15 @@ SUBDIRS = \
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = \
|
||||
libosmo-legacy-mgcp.pc \
|
||||
libosmo-mgcp-client.pc \
|
||||
libosmo-mgcp.pc \
|
||||
$(NULL)
|
||||
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = git-version-gen osmoappdesc.py .version
|
||||
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
||||
@RELMAKE@
|
||||
|
||||
$(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
|
||||
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
|
||||
https://osmocom.org/projects/osmo-mgw
|
||||
https://osmocom.org/projects/osmo-mgw/wiki
|
||||
|
@@ -23,4 +23,5 @@
|
||||
# If any interfaces have been added since the last public release, a++;
|
||||
# If any interfaces have been removed or changed since the last public release, a=0.
|
||||
#
|
||||
#library what description / commit summary line
|
||||
#library what description / commit summary line
|
||||
libosmo-mgcp-client various Drop legacy API functions and mgcp_client_conf members
|
||||
|
119
configure.ac
119
configure.ac
@@ -39,25 +39,40 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
|
||||
AC_SUBST(LIBRARY_DL)
|
||||
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.12.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.12.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.12.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.3.0)
|
||||
|
||||
# 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])
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-sanitize],
|
||||
[Compile with address sanitizer enabled],
|
||||
)],
|
||||
[sanitize=$enableval], [sanitize="no"])
|
||||
if test x"$sanitize" = x"yes"
|
||||
then
|
||||
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(werror,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-werror],
|
||||
[Turn all compiler warnings into errors, with exceptions:
|
||||
a) deprecation (allow upstream to mark deprecation without breaking builds);
|
||||
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
|
||||
]
|
||||
)],
|
||||
[werror=$enableval], [werror="no"])
|
||||
if test x"$werror" = x"yes"
|
||||
then
|
||||
WERROR_FLAGS="-Werror"
|
||||
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
|
||||
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
|
||||
CFLAGS="$CFLAGS $WERROR_FLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
|
||||
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
|
||||
|
||||
@@ -113,30 +128,88 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
|
||||
AC_MSG_RESULT([$enable_ext_tests])
|
||||
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([CPPFLAGS="$CPPFLAGS"])
|
||||
|
||||
dnl Generate the output
|
||||
AM_CONFIG_HEADER(bscconfig.h)
|
||||
|
||||
AC_OUTPUT(
|
||||
libosmo-legacy-mgcp.pc
|
||||
libosmo-mgcp-client.pc
|
||||
libosmo-mgcp.pc
|
||||
include/Makefile
|
||||
include/osmocom/Makefile
|
||||
include/osmocom/legacy_mgcp/Makefile
|
||||
include/osmocom/mgcp_client/Makefile
|
||||
include/osmocom/mgcp/Makefile
|
||||
src/Makefile
|
||||
src/libosmo-legacy-mgcp/Makefile
|
||||
src/libosmo-mgcp-client/Makefile
|
||||
src/libosmo-mgcp/Makefile
|
||||
src/osmo-bsc_mgcp/Makefile
|
||||
src/osmo-mgw/Makefile
|
||||
tests/Makefile
|
||||
tests/atlocal
|
||||
tests/legacy_mgcp/Makefile
|
||||
tests/mgcp_client/Makefile
|
||||
tests/mgcp/Makefile
|
||||
tests/iuup/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
doc/manuals/Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
Makefile)
|
||||
|
@@ -1 +1,3 @@
|
||||
SUBDIRS = systemd
|
||||
|
||||
EXTRA_DIST = ipa.py
|
||||
|
@@ -1,5 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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
|
||||
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 LD_LIBRARY_PATH="$inst/lib"
|
||||
export PATH="$inst/bin:$PATH"
|
||||
|
||||
osmo-build-dep.sh libosmo-abis
|
||||
osmo-build-dep.sh libosmo-netif
|
||||
|
||||
# Additional configure options and depends
|
||||
CONFIG=""
|
||||
if [ "$WITH_MANUALS" = "1" ]; then
|
||||
osmo-build-dep.sh osmo-gsm-manuals
|
||||
CONFIG="--enable-manuals"
|
||||
fi
|
||||
|
||||
set +x
|
||||
echo
|
||||
echo
|
||||
@@ -37,13 +50,17 @@ set -x
|
||||
|
||||
cd "$base"
|
||||
autoreconf --install --force
|
||||
./configure $MGCP --enable-vty-tests --enable-external-tests
|
||||
./configure --enable-vty-tests --enable-external-tests --enable-werror $CONFIG
|
||||
$MAKE $PARALLEL_MAKE
|
||||
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
|
||||
|| cat-testlogs.sh
|
||||
LD_LIBRARY_PATH="$inst/lib" \
|
||||
DISTCHECK_CONFIGURE_FLAGS="$MGCP --enable-vty-tests --enable-external-tests" \
|
||||
DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests $CONFIG" \
|
||||
$MAKE distcheck \
|
||||
|| cat-testlogs.sh
|
||||
|
||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
|
||||
make -C "$base/doc/manuals" publish
|
||||
fi
|
||||
|
||||
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
|
12
contrib/systemd/osmo-mgw.service
Normal file
12
contrib/systemd/osmo-mgw.service
Normal file
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Osmocom Media Gateway (MGW)
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
|
||||
RestartSec=2
|
||||
ExecStopPost=/usr/local/bin/save_log_tail osmo-mgw
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
173
debian/changelog
vendored
173
debian/changelog
vendored
@@ -1,3 +1,176 @@
|
||||
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
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* contrib: Add osmo-mgw systemd service
|
||||
* legacy: mgcp_protocol: Don't print osmux stats if it is off
|
||||
* mgcp_stat: Don't print osmux stats if it is off
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* fix segfault: DLCX for unknown endpoint: dont try to log NULL endpoint
|
||||
* MGCP endpoints: parse as decimal, not hex
|
||||
* add --enable-sanitize config option
|
||||
* legacy_mgcp: mgcp_test: sanitize: free msgb_ctx
|
||||
* mgcp_test: test_packet_error_detection: sanitize: free all conns
|
||||
* mgcp_test: test_no_cycle: sanitize: free endp
|
||||
* mgcp_test: sanitize: free msgb_ctx
|
||||
* mgcp_client: don't configure "bts base"
|
||||
* Revert "mgcp_client: don't configure "bts base"" until osmo-msc is ready
|
||||
* mgcp_client: add transaction cleanup
|
||||
* mgcp_client_test makefile: add update_exp target
|
||||
* cosmetic: mgcp_network: typo in log
|
||||
* osmo-mgw: Add talloc context introspection via VTY
|
||||
* mgcp_client: show failure by MGCP SDP section parsing test
|
||||
* mgcp_client: cosmetic: clean up SDP params parsing
|
||||
* mgcp_client: detect SDP section-start parsing errors
|
||||
* compiler warning: ignore deprecated in mgcp_client_test.c
|
||||
* configure: add --enable-werror
|
||||
* jenkins.sh: add --enable-werror to configure flags
|
||||
* cosmetic: mgcp, legacy_mgcp: drop unused vty.h definitions
|
||||
* use osmo_init_logging2() with proper talloc ctx
|
||||
|
||||
[ Philipp Maier ]
|
||||
* osmux: fix nullpointer dereference
|
||||
* cosmetic: guard dead osmux vty code with ifdef
|
||||
* cosmetic: remove prefix "net" from rtp related vty commands
|
||||
* doc: update sample config file
|
||||
* cosmetic: use correct VTY port number constant
|
||||
* vty: simplify endpoint allocation
|
||||
* vty: do not change number_endpoints at runtime
|
||||
* MGCP: Connection Identifiers are hex strings
|
||||
* libosmo-mgcp: Connection Identifiers are allocated by MGW, not CA
|
||||
* client: use osmo_strlcpy instead of strncpy
|
||||
* cosmetic: fix sourcecode formatting
|
||||
* cosmetic: clearly mark endpoint numbers as hex
|
||||
* client: use string as connection identifier
|
||||
* conn: remove assertions
|
||||
* mgcp_test: fix wrong strcmp() parameters
|
||||
* mgcp_test: fix nullpointer dereference
|
||||
* mgcp_test: add returncode check
|
||||
* mgcp_test: fix possible double free
|
||||
* mcgp_client: mgcp_msg_gen(): add checks to verify params
|
||||
* network: use originating RTP packet address for loopback
|
||||
* client: mgcp_response_parse_params: check rtp port
|
||||
* mgcp: allow endpoints beginning from zero
|
||||
* client/common: move constant MGCP_ENDPOINT_MAXLEN
|
||||
* mgcp: make domain name configurable
|
||||
* cosmetic: protocol: remove unnecessary nul termination
|
||||
* client: do not insist on \n\n when parsing MGCP messages
|
||||
* main: display mgcp ip/port
|
||||
* client: make callid in MDCX mandatory
|
||||
* client: add missing mandatory SDP fields
|
||||
* mgcp: permit wildcarded endpoint assignment (CRCX)
|
||||
* mgcp: add prefix to virtual trunk
|
||||
* client: eliminate destructive parameter parsing
|
||||
* client: eliminate destructive head parsing
|
||||
* cosmetic: client: add doxygen comments
|
||||
* protocol: fix problem with line break and OSMUX
|
||||
* protocol: fix missing carriage return
|
||||
* client: fix sdp parameter ordering
|
||||
* protocol: check the packetization in local cx options
|
||||
* cosmetic: remove spaces from pointer symbol
|
||||
* client: Do not accept endpoint ids with wildcards in responses
|
||||
* client: do not accept endpoint ids without @ character in responses
|
||||
* client: prohibit endpoint ids without @ character
|
||||
* protocol: on wildcarded CRCX return endpoint number as hex
|
||||
* msg: fix response code on exhausted endp resources
|
||||
* cosmetic: move mgcp_release_endp() to mgcp_ep.c
|
||||
* client: use heap to store mgcp_response
|
||||
* ep: move endpoint struct and define to mgcp_ep.h
|
||||
* cosmetic: rename mgcp_release_endp to mgcp_endp_release
|
||||
* cosmetic: rename mgcp_ep.c/h to mgcp_endp.c/h
|
||||
* protocol: reject DLCX/CRCX/MDCX on unsupported parameters
|
||||
* protocol: exit cleanly when local cx options check fails
|
||||
* cosmetic: Add missing \n on log line
|
||||
* protocol: check requested connection mode
|
||||
* protocol: fix tagging of wildcarded requests
|
||||
* protocol: prohibit wildcarded requests for MDCX and DLCX
|
||||
* mgcp: fix use-after-free and add callback for endpoint cleanup
|
||||
* client: add an optional FSM interface
|
||||
* mgcp_client_fsm: Add FSM event names
|
||||
* cosmetic: mgcp_client_fsm: rename enums
|
||||
* cosmetic: rename function .._conn_reset() to .._conn_init()
|
||||
* mgcp_conn: do not touch u.rtp in mgcp_conn_alloc()
|
||||
* cosmetic: rename .._codec_reset() to .._codec_init()
|
||||
* mgcp_conn: add function mgcp_rtp_conn_cleanup()
|
||||
* stats: use libosmocore rate counter for in/out_stream.err_ts_counter
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* debian/control: correct library dependency of osmo-mgw against libosmo-mgcp1
|
||||
* debian: include systemd service osmo-mgw.service
|
||||
* Revert "stats: use libosmocore rate counter for in/out_stream.err_ts_counter"
|
||||
|
||||
[ Harald Welte ]
|
||||
* cosmetic: fix whitespaces; we use tabs for indentation
|
||||
* Fix possible buffer overflow in mgcp_conn_dump()
|
||||
* osmo-mgw: Update copyright statement
|
||||
* osmo-mgw: Config file is osmo-mgw.cfg, and not mgcp.cfg
|
||||
* osmo-mgw: Fix copyright notice
|
||||
* strct mgcp_rtp_state: Group + document struct members related to patching
|
||||
* mgcp_rtp_state: grup 'stats' members into sub-structure
|
||||
* mgcp_rtp_end: Group statistics members into 'stats' sub-struct
|
||||
* libosmo-mgcp: Cosmetic spelling fixes in comments
|
||||
* mgcp_msg: We must parse endpoint numbers as hex, not decimal!
|
||||
* mgcp_internal.h: document more struct members with comments
|
||||
* centralize handling of common errors like "endpoint not found"
|
||||
* Return proper MGCP Error codes, as per spec
|
||||
* osmo-mgw: Use libosmocore socket abstraction
|
||||
* osmo-bsc_mgcp: Add LIBOSMONETIF_{CFLAGS,LIBS}
|
||||
* libosmo-mgcp-client is GPLv2+, not AGPLv3+
|
||||
* Turn libosmo-mgcp into local, non-installed library
|
||||
|
||||
[ Stefan Sperling ]
|
||||
* enable osmo_fsm vty commands in libosmo-mgcp-client vty
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 03 May 2018 17:40:35 +0200
|
||||
|
||||
osmo-mgw (1.2.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
|
42
debian/control
vendored
42
debian/control
vendored
@@ -16,25 +16,10 @@ Homepage: https://osmocom.org/projects/osmo-mgw
|
||||
Package: osmo-mgw
|
||||
Architecture: any
|
||||
Multi-Arch: foreign
|
||||
Depends: libosmo-mgcp0, ${misc:Depends}, ${shlibs:Depends}
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
||||
|
||||
Package: libosmo-mgcp1
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Pre-Depends: ${misc:Pre-Depends}
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: libosmo-mgcp: Osmocom's Media Gateway server library
|
||||
|
||||
Package: libosmo-mgcp-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-mgcp1 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: libosmo-mgcp: Osmocom's Media Gateway server library
|
||||
|
||||
Package: libosmo-mgcp-client2
|
||||
Package: libosmo-mgcp-client3
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
@@ -46,26 +31,5 @@ Package: libosmo-mgcp-client-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-mgcp-client2 (= ${binary:Version}), ${misc:Depends}
|
||||
Depends: libosmo-mgcp-client3 (= ${binary:Version}), ${misc:Depends}
|
||||
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.
|
||||
|
29
debian/copyright
vendored
29
debian/copyright
vendored
@@ -21,11 +21,12 @@ License: AGPL-3.0+
|
||||
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/>.
|
||||
|
||||
Files: src/libosmo-legacy-mgcp/g711common.h
|
||||
Copyright: 2000 Abramo Bagnara <abramo@alsa-project.org>
|
||||
Files: src/libosmo-mgcp-client/* include/osmocom/mgcp_client/*
|
||||
Copyright: 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
|
||||
Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
|
||||
2009 by Harald Welte <laforge@gnumonks.org>
|
||||
2009-2011 by Holger Hans Peter Freyther
|
||||
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
|
||||
@@ -37,13 +38,7 @@ License: GPL-2.0+
|
||||
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'.
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Files: tests/vty_test_runner.py
|
||||
Copyright: 2013 Holger Hans Peter Freyther
|
||||
@@ -68,15 +63,3 @@ License: GPL-3.0+
|
||||
Files: osmoappdesc.py
|
||||
Copyright: 2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
|
||||
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.*
|
4
debian/libosmo-mgcp-dev.install
vendored
4
debian/libosmo-mgcp-dev.install
vendored
@@ -1,4 +0,0 @@
|
||||
usr/include/osmocom/mgcp
|
||||
usr/lib/*/libosmo-mgcp.so
|
||||
usr/lib/*/libosmo-mgcp.a
|
||||
usr/lib/*/pkgconfig/libosmo-mgcp.pc
|
1
debian/libosmo-mgcp1.install
vendored
1
debian/libosmo-mgcp1.install
vendored
@@ -1 +0,0 @@
|
||||
usr/lib/*/libosmo-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/share/doc/osmo-mgw/examples/osmo-mgw/osmo-mgw.cfg
|
||||
|
5
debian/rules
vendored
5
debian/rules
vendored
@@ -29,8 +29,7 @@ CFLAGS += -g
|
||||
override_dh_auto_test:
|
||||
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
|
||||
|
||||
override_dh_autoreconf:
|
||||
echo $(VERSION) > .tarball-version
|
||||
dh_autoreconf
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system
|
||||
|
||||
# See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg
|
||||
|
@@ -1,3 +1,4 @@
|
||||
SUBDIRS = \
|
||||
examples \
|
||||
manuals \
|
||||
$(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),,'
|
||||
|
||||
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
|
@@ -2,12 +2,17 @@
|
||||
! 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 force-ptime 20
|
||||
sdp audio payload number 98
|
||||
sdp audio payload name AMR/8000
|
||||
number endpoints 31
|
||||
no rtcp-omit
|
||||
bind ip 127.0.0.1
|
||||
rtp port-range 4002 16000
|
||||
rtp bind-ip 10.9.1.122
|
||||
rtp ip-probing
|
||||
rtp ip-tos 184
|
||||
bind port 2427
|
||||
sdp audio payload number 98
|
||||
sdp audio payload name GSM
|
||||
number endpoints 31
|
||||
loop 0
|
||||
force-realloc 1
|
||||
rtcp-omit
|
||||
rtp-patch ssrc
|
||||
rtp-patch timestamp
|
||||
|
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"
|
||||
then
|
||||
: # use $v
|
||||
elif test -d ./.git \
|
||||
&& v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|
||||
elif
|
||||
v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|
||||
|| git describe --abbrev=4 HEAD 2>/dev/null` \
|
||||
&& case $v in
|
||||
[0-9]*) ;;
|
||||
|
@@ -3,10 +3,8 @@ SUBDIRS = \
|
||||
$(NULL)
|
||||
|
||||
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_fsm.h \
|
||||
osmocom/mgcp_client/mgcp_common.h \
|
||||
osmocom/mgcp/mgcp.h \
|
||||
osmocom/mgcp/mgcp_common.h \
|
||||
|
@@ -1,5 +1,4 @@
|
||||
SUBDIRS = \
|
||||
legacy_mgcp \
|
||||
mgcp_client \
|
||||
mgcp \
|
||||
$(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,31 +0,0 @@
|
||||
#ifndef OPENBSC_VTY_H
|
||||
#define OPENBSC_VTY_H
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/buffer.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
struct gsm_network;
|
||||
struct vty;
|
||||
|
||||
void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *);
|
||||
|
||||
struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base);
|
||||
|
||||
extern struct cmd_element cfg_description_cmd;
|
||||
extern struct cmd_element cfg_no_description_cmd;
|
||||
|
||||
enum mgcp_vty_node {
|
||||
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
TRUNK_NODE,
|
||||
};
|
||||
|
||||
struct log_info;
|
||||
int bsc_vty_init(struct gsm_network *network);
|
||||
int bsc_vty_init_extra(void);
|
||||
|
||||
void msc_vty_init(struct gsm_network *msc_network);
|
||||
|
||||
struct gsm_network *gsmnet_from_vty(struct vty *vty);
|
||||
|
||||
#endif
|
@@ -3,7 +3,8 @@ noinst_HEADERS = \
|
||||
mgcp_msg.h \
|
||||
mgcp_conn.h \
|
||||
mgcp_stat.h \
|
||||
mgcp_ep.h \
|
||||
mgcp_endp.h \
|
||||
mgcp_sdp.h \
|
||||
mgcp_codec.h \
|
||||
debug.h \
|
||||
$(NULL)
|
||||
|
@@ -29,6 +29,7 @@
|
||||
/* Debug Areas of the code */
|
||||
enum {
|
||||
DRTP,
|
||||
DIUUP,
|
||||
Debug_LastEntry,
|
||||
};
|
||||
|
||||
|
41
include/osmocom/mgcp/iuup_cn_node.h
Normal file
41
include/osmocom/mgcp/iuup_cn_node.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* IuUP CN node, minimal implementation */
|
||||
|
||||
/* _____IuUP_CN_____
|
||||
* | |
|
||||
* UE <--> RNC --PDU-> osmo_iuup_cn_rx_pdu() -+-> ---+-> rx_payload()
|
||||
* | | |
|
||||
* | <-PDU-- tx_msg() <-------------+-- <-+--- osmo_iuup_cn_tx_payload()
|
||||
* | |
|
||||
* -----------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct osmo_iuup_cn;
|
||||
struct msgb;
|
||||
|
||||
typedef int (*osmo_iuup_data_cb_t)(struct msgb *msg, void *node_priv);
|
||||
|
||||
struct osmo_iuup_cn_cfg {
|
||||
void *node_priv;
|
||||
|
||||
/* When the IuUP peer sent a voice packet, the clean RTP without the IuUP header is fed to this
|
||||
* callback. */
|
||||
osmo_iuup_data_cb_t rx_payload;
|
||||
|
||||
/* IuUP handler requests that a PDU shall be sent to the IuUP peer (e.g. the RNC).
|
||||
* It is guaranteed that the msgb->dst pointer is preserved or copied from the msgb that
|
||||
* originated the request. */
|
||||
osmo_iuup_data_cb_t tx_msg;
|
||||
};
|
||||
|
||||
bool osmo_iuup_cn_is_iuup_init(struct msgb *msg);
|
||||
|
||||
struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
|
||||
const char *name_fmt, ...);
|
||||
void osmo_iuup_cn_free(struct osmo_iuup_cn *cn);
|
||||
|
||||
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *payload);
|
||||
|
||||
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu);
|
117
include/osmocom/mgcp/iuup_protocol.h
Normal file
117
include/osmocom/mgcp/iuup_protocol.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* IuUP protocol handling, minimal implementation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/endian.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#define OSMO_IUUP_HEADROOM 32
|
||||
|
||||
enum osmo_iuup_pdu_type {
|
||||
OSMO_IUUP_PDU_DATA_WITH_CRC = 0,
|
||||
OSMO_IUUP_PDU_CONTROL_PROCEDURE = 14,
|
||||
};
|
||||
|
||||
enum osmo_iuup_acknack {
|
||||
OSMO_IUUP_ACKNACK_PROCEDURE = 0,
|
||||
OSMO_IUUP_ACKNACK_ACK = 1,
|
||||
OSMO_IUUP_ACKNACK_NACK = 2,
|
||||
};
|
||||
|
||||
enum osmo_iuup_procedure {
|
||||
OSMO_IUUP_PROC_INITIALIZATION = 0,
|
||||
OSMO_IUUP_PROC_RATE_CONTROL = 1,
|
||||
OSMO_IUUP_PROC_TIME_ALIGNMENT = 2,
|
||||
OSMO_IUUP_PROC_ERROR_EVENT = 3,
|
||||
};
|
||||
|
||||
enum osmo_iuup_frame_good {
|
||||
OSMO_IUUP_FRAME_GOOD = 0,
|
||||
OSMO_IUUP_FRAME_BAD = 1,
|
||||
OSMO_IUUP_FRAME_BAD_DUE_TO_RADIO = 2,
|
||||
};
|
||||
|
||||
struct osmo_iuup_hdr_ctrl {
|
||||
#if OSMO_IS_BIG_ENDIAN
|
||||
uint8_t pdu_type:4,
|
||||
ack_nack:2,
|
||||
frame_nr:2;
|
||||
uint8_t mode_version:4,
|
||||
procedure:4;
|
||||
uint8_t header_crc:6,
|
||||
payload_crc_hi:2;
|
||||
uint8_t payload_crc_lo;
|
||||
uint8_t payload[0];
|
||||
#elif OSMO_IS_LITTLE_ENDIAN
|
||||
uint8_t frame_nr:2,
|
||||
ack_nack:2,
|
||||
pdu_type:4;
|
||||
uint8_t procedure:4,
|
||||
mode_version:4;
|
||||
uint8_t payload_crc_hi:2,
|
||||
header_crc:6;
|
||||
uint8_t payload_crc_lo;
|
||||
uint8_t payload[0];
|
||||
#endif
|
||||
} __attribute__((packed));
|
||||
|
||||
union osmo_iuup_hdr_ctrl_payload {
|
||||
struct {
|
||||
#if OSMO_IS_BIG_ENDIAN
|
||||
uint8_t spare:3,
|
||||
iptis_present:1,
|
||||
subflows:3,
|
||||
chain:1;
|
||||
#elif OSMO_IS_LITTLE_ENDIAN
|
||||
uint8_t spare:3,
|
||||
iptis_present:1,
|
||||
subflows:3,
|
||||
chain:1;
|
||||
#endif
|
||||
} initialization;
|
||||
|
||||
struct {
|
||||
#if OSMO_IS_BIG_ENDIAN
|
||||
uint8_t error_distance:2,
|
||||
error_cause:6;
|
||||
#elif OSMO_IS_LITTLE_ENDIAN
|
||||
uint8_t error_cause:6,
|
||||
error_distance:2;
|
||||
#endif
|
||||
} error_event;
|
||||
};
|
||||
|
||||
extern const struct value_string osmo_iuup_error_cause_names[];
|
||||
static inline const char *osmo_iuup_error_cause_name(uint8_t val)
|
||||
{ return get_value_string(osmo_iuup_error_cause_names, val); }
|
||||
|
||||
struct osmo_iuup_hdr_data {
|
||||
#if OSMO_IS_BIG_ENDIAN
|
||||
uint8_t pdu_type:4,
|
||||
frame_nr:4;
|
||||
uint8_t frame_good:2,
|
||||
rfci:6;
|
||||
uint8_t header_crc:6,
|
||||
payload_crc_hi:2;
|
||||
uint8_t payload_crc_lo;
|
||||
#elif OSMO_IS_LITTLE_ENDIAN
|
||||
uint8_t frame_nr:4,
|
||||
pdu_type:4;
|
||||
uint8_t rfci:6,
|
||||
frame_good:2;
|
||||
uint8_t payload_crc_hi:2,
|
||||
header_crc:6;
|
||||
uint8_t payload_crc_lo;
|
||||
#endif
|
||||
uint8_t payload[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
int osmo_iuup_classify(bool log_errors,
|
||||
const char *log_label,
|
||||
struct msgb *pdu,
|
||||
struct osmo_iuup_hdr_ctrl **is_ctrl,
|
||||
struct osmo_iuup_hdr_data **is_data);
|
||||
bool osmo_iuup_is_init(struct msgb *pdu);
|
||||
void osmo_iuup_make_init_ack(struct msgb *ack);
|
||||
void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len);
|
@@ -74,11 +74,13 @@ typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
|
||||
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);
|
||||
|
||||
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,
|
||||
int *payload_type,
|
||||
const char**subtype_name,
|
||||
@@ -105,17 +107,66 @@ struct mgcp_port_range {
|
||||
};
|
||||
|
||||
/* There are up to three modes in which the keep-alive dummy packet can be
|
||||
* sent. The beviour is controlled viw the keepalive_interval member of the
|
||||
* sent. The behaviour is controlled via the keepalive_interval member of the
|
||||
* trunk config. If that member is set to 0 (MGCP_KEEPALIVE_NEVER) no dummy-
|
||||
* packet is sent at all and the timer that sends regular dummy packets
|
||||
* is no longer scheduled. If the keepalive_interval is set to -1, only
|
||||
* one dummy packet is sent when an CRCX or an MDCX is performed. No timer
|
||||
* is scheduled. For all vales greater 0, the a timer is scheduled and the
|
||||
* is scheduled. For all vales greater 0, the timer is scheduled and the
|
||||
* value is used as interval. See also mgcp_keepalive_timer_cb(),
|
||||
* handle_modify_con(), and handle_create_con() */
|
||||
#define MGCP_KEEPALIVE_ONCE (-1)
|
||||
#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 llist_head entry;
|
||||
|
||||
@@ -151,7 +202,17 @@ struct mgcp_trunk_config {
|
||||
int rtp_accept_all;
|
||||
|
||||
unsigned int number_endpoints;
|
||||
int vty_number_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 {
|
||||
@@ -211,6 +272,8 @@ struct mgcp_config {
|
||||
* message.
|
||||
*/
|
||||
uint16_t osmux_dummy;
|
||||
/* domain name of the media gateway */
|
||||
char domain[255+1];
|
||||
};
|
||||
|
||||
/* config management */
|
||||
@@ -219,7 +282,6 @@ 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_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
|
||||
|
||||
/*
|
||||
|
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,
|
||||
};
|
||||
|
||||
#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. */
|
||||
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
||||
{
|
||||
@@ -68,4 +76,25 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Maximum length of the comment field */
|
||||
#define MGCP_COMMENT_MAXLEN 256
|
||||
|
||||
/* Maximum allowed String length of Connection Identifiers as per spec
|
||||
* (see also RFC3435 2.1.3.2 Names of Connections), plus one for '\0'. */
|
||||
#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.
|
||||
/ (see also RFC3435 section 3.2.1.3) */
|
||||
#define MGCP_ENDPOINT_MAXLEN (255*2+1+1)
|
||||
|
||||
/* A prefix to denote the virtual trunk (RTP on both ends) */
|
||||
#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
|
||||
|
@@ -25,15 +25,55 @@
|
||||
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/rate_ctr.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,
|
||||
uint32_t id, enum mgcp_conn_type type,
|
||||
char *name);
|
||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, uint32_t id);
|
||||
enum mgcp_conn_type type, char *name);
|
||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
|
||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
|
||||
uint32_t id);
|
||||
void mgcp_conn_free(struct mgcp_endpoint *endp, uint32_t id);
|
||||
const char *id);
|
||||
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id);
|
||||
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
|
||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
|
||||
char *mgcp_conn_dump(struct mgcp_conn *conn);
|
||||
|
115
include/osmocom/mgcp/mgcp_endp.h
Normal file
115
include/osmocom/mgcp/mgcp_endp.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/* Endpoint types */
|
||||
|
||||
/*
|
||||
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 <osmocom/core/msgb.h>
|
||||
|
||||
struct sockaddr_in;
|
||||
struct mgcp_conn;
|
||||
struct mgcp_conn_rtp;
|
||||
struct mgcp_endpoint;
|
||||
|
||||
struct osmo_rtp_msg_ctx {
|
||||
int proto;
|
||||
struct mgcp_conn_rtp *conn_src;
|
||||
struct sockaddr_in *from_addr;
|
||||
};
|
||||
|
||||
#define OSMO_RTP_MSG_CTX(MSGB) (*(struct osmo_rtp_msg_ctx**)&((MSGB)->dst))
|
||||
|
||||
#define LOG_ENDP(endp, level, fmt, args...) \
|
||||
LOGP(DRTP, level, "%x@ " fmt, ENDPOINT_NUMBER(endp), ## args)
|
||||
|
||||
/* Callback type for RTP dispatcher functions (e.g mgcp_dispatch_rtp_bridge_cb, see below).
|
||||
* The fields OSMO_RTP_MSG_PROTO, OSMO_RTP_MSG_CONN_SRC, OSMO_RTP_MSG_FROM_ADDR should be set
|
||||
* appropriately on the msg. */
|
||||
typedef int (*mgcp_dispatch_rtp_cb) (struct msgb *msg);
|
||||
|
||||
/* Callback type for endpoint specific cleanup actions. This function
|
||||
* is automatically executed when a connection is freed (see mgcp_conn_free()
|
||||
* in mgcp_conn.c). Depending on the type of the endpoint there may be endpoint
|
||||
* specific things to take care of once a connection has been removed. */
|
||||
typedef void (*mgcp_cleanup_cp) (struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn *conn);
|
||||
|
||||
/*! MGCP endpoint properties */
|
||||
struct mgcp_endpoint_type {
|
||||
/*! maximum number of connections */
|
||||
int max_conns;
|
||||
|
||||
/*! callback that defines how to dispatch incoming RTP data */
|
||||
mgcp_dispatch_rtp_cb dispatch_rtp_cb;
|
||||
|
||||
/*! callback that implements endpoint specific cleanup actions */
|
||||
mgcp_cleanup_cp cleanup_cb;
|
||||
};
|
||||
|
||||
/*! MGCP endpoint typeset */
|
||||
struct mgcp_endpoint_typeset {
|
||||
struct mgcp_endpoint_type rtp;
|
||||
};
|
||||
|
||||
/*! static MGCP endpoint typeset (pre-initalized, read-only) */
|
||||
extern const struct mgcp_endpoint_typeset ep_typeset;
|
||||
|
||||
/*! MGCP endpoint model */
|
||||
struct mgcp_endpoint {
|
||||
|
||||
/*! Call identifier string (as supplied by the call agant) */
|
||||
char *callid;
|
||||
|
||||
/*! Local connection options (see mgcp_internal.h) */
|
||||
struct mgcp_lco local_options;
|
||||
|
||||
/*! List of struct mgcp_conn, of the connections active on this endpoint */
|
||||
struct llist_head conns;
|
||||
|
||||
/*! Backpointer to the MGW configuration */
|
||||
struct mgcp_config *cfg;
|
||||
|
||||
/*! Backpointer to the Trunk specific configuration */
|
||||
struct mgcp_trunk_config *tcfg;
|
||||
|
||||
/*! Endpoint properties (see above) */
|
||||
const struct mgcp_endpoint_type *type;
|
||||
|
||||
/*! Last MGCP transmission (in case re-transmission is required) */
|
||||
char *last_trans;
|
||||
|
||||
/*! Last MGCP response (in case re-transmission is required) */
|
||||
char *last_response;
|
||||
|
||||
/*! Memorize if this endpoint was choosen by the MGW (wildcarded, true)
|
||||
* or if the user has choosen the particular endpoint explicitly. */
|
||||
bool wildcarded_req;
|
||||
|
||||
/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */
|
||||
uint32_t x_osmo_ign;
|
||||
};
|
||||
|
||||
/*! Extract endpoint number for a given endpoint */
|
||||
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
|
||||
|
||||
void mgcp_endp_release(struct mgcp_endpoint *endp);
|
||||
|
@@ -1,50 +0,0 @@
|
||||
/* Endpoint types */
|
||||
|
||||
/*
|
||||
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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
|
||||
|
||||
struct sockaddr_in;
|
||||
struct mgcp_conn;
|
||||
|
||||
/* Callback type for RTP dispatcher functions
|
||||
(e.g mgcp_dispatch_rtp_bridge_cb, see below) */
|
||||
typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in * addr,
|
||||
char *buf, unsigned int buf_size,
|
||||
struct mgcp_conn * conn);
|
||||
|
||||
/*! MGCP endpoint properties */
|
||||
struct mgcp_endpoint_type {
|
||||
/*!< maximum number of connections */
|
||||
int max_conns;
|
||||
|
||||
/*!< callback that defines how to dispatch incoming RTP data */
|
||||
mgcp_dispatch_rtp_cb dispatch_rtp_cb;
|
||||
};
|
||||
|
||||
/*! MGCP endpoint typeset */
|
||||
struct mgcp_endpoint_typeset {
|
||||
struct mgcp_endpoint_type rtp;
|
||||
};
|
||||
|
||||
/*! static MGCP endpoint typeset (pre-initalized, read-only) */
|
||||
extern const struct mgcp_endpoint_typeset ep_typeset;
|
@@ -27,11 +27,22 @@
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/counter.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
|
||||
#define CI_UNUSED 0
|
||||
|
||||
#define CONN_ID_BTS 0
|
||||
#define CONN_ID_NET 1
|
||||
/* FIXME: This this is only needed to compile the currently
|
||||
* broken OSMUX support. Remove when fixed */
|
||||
#define CONN_ID_BTS "0"
|
||||
#define CONN_ID_NET "1"
|
||||
|
||||
#define LOG_CONN(conn, level, fmt, args...) \
|
||||
LOGP(DRTP, level, "(%d@ I:%s) " fmt, \
|
||||
ENDPOINT_NUMBER((conn)->endp), (conn)->id, ## args)
|
||||
|
||||
#define LOG_CONN_RTP(conn_rtp, level, fmt, args...) \
|
||||
LOG_CONN(conn_rtp->conn, level, fmt, ## args)
|
||||
|
||||
enum mgcp_trunk_type {
|
||||
MGCP_TRUNK_VIRTUAL,
|
||||
@@ -42,33 +53,43 @@ struct mgcp_rtp_stream_state {
|
||||
uint32_t ssrc;
|
||||
uint16_t last_seq;
|
||||
uint32_t last_timestamp;
|
||||
uint32_t err_ts_counter;
|
||||
struct rate_ctr *err_ts_ctr;
|
||||
int32_t last_tsdelta;
|
||||
uint32_t last_arrival_time;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_state {
|
||||
/* has this state structure been initialized? */
|
||||
int initialized;
|
||||
int patch_ssrc;
|
||||
|
||||
uint32_t orig_ssrc;
|
||||
struct {
|
||||
/* are we patching the SSRC value? */
|
||||
int patch_ssrc;
|
||||
/* original SSRC (to which we shall patch any different SSRC) */
|
||||
uint32_t orig_ssrc;
|
||||
/* offset to apply on the sequence number */
|
||||
int seq_offset;
|
||||
/* offset to apply on the timestamp number */
|
||||
int32_t timestamp_offset;
|
||||
} patch;
|
||||
|
||||
int seq_offset;
|
||||
|
||||
int32_t timestamp_offset;
|
||||
/* duration of a packet (FIXME: in which unit?) */
|
||||
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;
|
||||
struct {
|
||||
int initialized;
|
||||
uint16_t base_seq;
|
||||
uint16_t max_seq;
|
||||
uint32_t ssrc;
|
||||
uint32_t jitter;
|
||||
int32_t transit;
|
||||
int cycles;
|
||||
} stats;
|
||||
|
||||
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
|
||||
};
|
||||
|
||||
@@ -83,43 +104,50 @@ struct mgcp_rtp_codec {
|
||||
char *subtype_name;
|
||||
};
|
||||
|
||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
|
||||
struct mgcp_rtp_end {
|
||||
/* statistics */
|
||||
unsigned int packets_rx;
|
||||
unsigned int octets_rx;
|
||||
unsigned int packets_tx;
|
||||
unsigned int octets_tx;
|
||||
unsigned int dropped_packets;
|
||||
/* local IP address of the RTP socket */
|
||||
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 */
|
||||
/* currently selected audio codec */
|
||||
struct mgcp_rtp_codec *codec;
|
||||
|
||||
/* array with assigned audio codecs to choose from (SDP) */
|
||||
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
|
||||
|
||||
/* number of assigned audio codecs (SDP) */
|
||||
unsigned int codecs_assigned;
|
||||
|
||||
/* per endpoint data */
|
||||
int frames_per_packet;
|
||||
uint32_t packet_duration_ms;
|
||||
int maximum_packet_time; /* -1: not set */
|
||||
char *fmtp_extra;
|
||||
/* are we transmitting packets (1) or dropping (0) outbound packets */
|
||||
int output_enabled;
|
||||
/* FIXME: This parameter can be set + printed, but is nowhere used! */
|
||||
int force_output_ptime;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
|
||||
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
|
||||
int force_aligned_timing;
|
||||
void *rtp_process_data;
|
||||
|
||||
/* Each end has a separate socket for RTP and RTCP */
|
||||
struct osmo_fd rtp;
|
||||
struct osmo_fd rtcp;
|
||||
|
||||
/* local UDP port number of the RTP socket; RTCP is +1 */
|
||||
int local_port;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_tap {
|
||||
/* is this tap active (1) or not (0) */
|
||||
int enabled;
|
||||
/* IP/port to which we're forwarding the tapped data */
|
||||
struct sockaddr_in forward;
|
||||
};
|
||||
|
||||
@@ -155,7 +183,7 @@ struct mgcp_conn_rtp {
|
||||
/* Sequence bits */
|
||||
struct mgcp_rtp_state state;
|
||||
|
||||
/* taps for the rtp connection */
|
||||
/* taps for the rtp connection; one per direction */
|
||||
struct mgcp_rtp_tap tap_in;
|
||||
struct mgcp_rtp_tap tap_out;
|
||||
|
||||
@@ -177,6 +205,10 @@ struct mgcp_conn_rtp {
|
||||
uint32_t octets;
|
||||
} stats;
|
||||
} osmux;
|
||||
|
||||
struct rate_ctr_group *rate_ctr_group;
|
||||
|
||||
struct osmo_iuup_cn *iuup;
|
||||
};
|
||||
|
||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
||||
@@ -187,33 +219,33 @@ enum mgcp_conn_type {
|
||||
|
||||
/*! MGCP connection (untyped) */
|
||||
struct mgcp_conn {
|
||||
/*!< list head */
|
||||
/*! list head */
|
||||
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;
|
||||
|
||||
/*!< type of the connection (union) */
|
||||
/*! type of the connection (union) */
|
||||
enum mgcp_conn_type type;
|
||||
|
||||
/*!< mode of the connection */
|
||||
/*! mode of the connection */
|
||||
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;
|
||||
|
||||
/*!< connection id to identify the conntion */
|
||||
uint32_t id;
|
||||
/*! connection id to identify the connection */
|
||||
char id[MGCP_CONN_ID_MAXLEN];
|
||||
|
||||
/*!< human readable name (vty, logging) */
|
||||
/*! human readable name (vty, logging) */
|
||||
char name[256];
|
||||
|
||||
/*!< union with connection description */
|
||||
/*! union with connection description */
|
||||
union {
|
||||
struct mgcp_conn_rtp rtp;
|
||||
} u;
|
||||
|
||||
/*!< pointer to optional private data */
|
||||
/*! pointer to optional private data */
|
||||
void *priv;
|
||||
};
|
||||
|
||||
@@ -221,25 +253,9 @@ struct mgcp_conn {
|
||||
|
||||
struct mgcp_endpoint_type;
|
||||
|
||||
struct mgcp_endpoint {
|
||||
char *callid;
|
||||
struct mgcp_lco local_options;
|
||||
|
||||
struct llist_head conns;
|
||||
|
||||
/* backpointer */
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_trunk_config *tcfg;
|
||||
|
||||
const struct mgcp_endpoint_type *type;
|
||||
|
||||
/* fields for re-transmission */
|
||||
char *last_trans;
|
||||
char *last_response;
|
||||
};
|
||||
|
||||
|
||||
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
|
||||
|
||||
|
||||
/**
|
||||
* Internal structure while parsing a request
|
||||
@@ -249,15 +265,14 @@ struct mgcp_parse_data {
|
||||
struct mgcp_endpoint *endp;
|
||||
char *trans;
|
||||
char *save;
|
||||
int found;
|
||||
};
|
||||
|
||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
char *buf, int rc, struct mgcp_conn_rtp *conn_src,
|
||||
struct msgb *msg, struct mgcp_conn_rtp *conn_src,
|
||||
struct mgcp_conn_rtp *conn_dst);
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
|
||||
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
unsigned int buf_size, struct mgcp_conn *conn);
|
||||
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
|
||||
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
|
||||
@@ -271,6 +286,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_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,
|
||||
struct mgcp_rtp_end *rtp);
|
||||
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
|
||||
@@ -281,8 +298,8 @@ int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_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);
|
||||
struct mgcp_conn_rtp *conn_dst,
|
||||
struct mgcp_conn_rtp *conn_src);
|
||||
|
||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
int *payload_type,
|
||||
@@ -319,3 +336,8 @@ enum {
|
||||
#define PTYPE_UNDEFINED (-1)
|
||||
|
||||
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
|
||||
|
||||
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *rtp_end,
|
||||
struct sockaddr_in *addr, struct msgb *msg);
|
||||
|
@@ -43,7 +43,7 @@ int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line);
|
||||
|
||||
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid);
|
||||
|
||||
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *ci);
|
||||
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id);
|
||||
|
||||
char *mgcp_strline(char *str, char **saveptr);
|
||||
|
||||
@@ -54,5 +54,3 @@ char *mgcp_strline(char *str, char **saveptr);
|
||||
#define for_each_non_empty_line(line, save)\
|
||||
for (line = strtok_r(NULL, "\r\n", &save); line;\
|
||||
line = strtok_r(NULL, "\r\n", &save))
|
||||
|
||||
int mgcp_parse_ci(uint32_t *conn_id, const char *ci);
|
||||
|
@@ -21,15 +21,11 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
|
||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn,
|
||||
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,
|
||||
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
|
||||
const char *addr);
|
||||
|
@@ -30,8 +30,7 @@
|
||||
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn);
|
||||
|
||||
/* Exposed for test purposes only, do not use actively */
|
||||
void calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *,
|
||||
uint32_t *expected, int *loss);
|
||||
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss);
|
||||
|
||||
/* Exposed for test purposes only, do not use actively */
|
||||
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);
|
||||
|
||||
enum osmux_state {
|
||||
OSMUX_STATE_DISABLED = 0,
|
||||
OSMUX_STATE_NEGOTIATING,
|
||||
OSMUX_STATE_ACTIVATING,
|
||||
OSMUX_STATE_ENABLED,
|
||||
OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */
|
||||
OSMUX_STATE_NEGOTIATING, /* Osmux was locally requested in MGCP CRCX */
|
||||
OSMUX_STATE_ACTIVATING, /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref osmux_enable_endpoint. */
|
||||
OSMUX_STATE_ENABLED, /* Osmux was initialized by \ref osmux_enable_endpoint and can process frames */
|
||||
};
|
||||
|
||||
enum osmux_usage {
|
||||
@@ -35,4 +35,3 @@ enum osmux_usage {
|
||||
OSMUX_USAGE_ON = 1,
|
||||
OSMUX_USAGE_ONLY = 2,
|
||||
};
|
||||
|
||||
|
@@ -1,31 +1,8 @@
|
||||
#ifndef OPENBSC_VTY_H
|
||||
#define OPENBSC_VTY_H
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/buffer.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
struct gsm_network;
|
||||
struct vty;
|
||||
|
||||
void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *);
|
||||
|
||||
struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base);
|
||||
|
||||
extern struct cmd_element cfg_description_cmd;
|
||||
extern struct cmd_element cfg_no_description_cmd;
|
||||
|
||||
enum mgcp_vty_node {
|
||||
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
TRUNK_NODE,
|
||||
};
|
||||
|
||||
struct log_info;
|
||||
int bsc_vty_init(struct gsm_network *network);
|
||||
int bsc_vty_init_extra(void);
|
||||
|
||||
void msc_vty_init(struct gsm_network *msc_network);
|
||||
|
||||
struct gsm_network *gsmnet_from_vty(struct vty *vty);
|
||||
|
||||
#endif
|
||||
|
@@ -5,11 +5,14 @@
|
||||
|
||||
#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_PORT_DEFAULT 0
|
||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727
|
||||
#define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
|
||||
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
|
||||
|
||||
#define MGCP_CLIENT_MGW_STR "Configure MGCP connection to Media Gateway\n"
|
||||
|
||||
struct msgb;
|
||||
struct vty;
|
||||
struct mgcp_client;
|
||||
@@ -19,17 +22,47 @@ struct mgcp_client_conf {
|
||||
int local_port;
|
||||
const char *remote_addr;
|
||||
int remote_port;
|
||||
uint16_t first_endpoint;
|
||||
uint16_t last_endpoint;
|
||||
uint16_t bts_base;
|
||||
|
||||
/* By default, we are always addressing the MGW with e.g. 'rtpbridge/123@mgw'.
|
||||
* If this is nonempty, the contained name will be used instead of 'mgw'. */
|
||||
char endpoint_domain_name[64];
|
||||
};
|
||||
|
||||
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 {
|
||||
int response_code;
|
||||
mgcp_trans_id_t trans_id;
|
||||
const char *comment;
|
||||
int response_code;
|
||||
mgcp_trans_id_t trans_id;
|
||||
char comment[MGCP_COMMENT_MAXLEN];
|
||||
char conn_id[MGCP_CONN_ID_MAXLEN];
|
||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
};
|
||||
|
||||
struct mgcp_response {
|
||||
@@ -37,6 +70,11 @@ struct mgcp_response {
|
||||
struct mgcp_response_head head;
|
||||
uint16_t audio_port;
|
||||
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 {
|
||||
@@ -53,9 +91,7 @@ enum mgcp_verb {
|
||||
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
|
||||
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
|
||||
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
|
||||
|
||||
/* See also RFC3435 section 3.2.1.3 */
|
||||
#define MGCP_ENDPOINT_MAXLEN (255*2+1+1)
|
||||
#define MGCP_MSG_PRESENCE_X_OSMO_IGN 0x8000
|
||||
|
||||
struct mgcp_msg {
|
||||
enum mgcp_verb verb;
|
||||
@@ -63,10 +99,16 @@ struct mgcp_msg {
|
||||
uint32_t presence;
|
||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
unsigned int call_id;
|
||||
uint32_t conn_id;
|
||||
uint16_t audio_port;
|
||||
char *audio_ip;
|
||||
char *conn_id;
|
||||
uint16_t audio_port;
|
||||
char *audio_ip;
|
||||
enum mgcp_connection_mode conn_mode;
|
||||
unsigned int ptime;
|
||||
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_len;
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
uint32_t x_osmo_ign;
|
||||
};
|
||||
|
||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
|
||||
@@ -82,8 +124,8 @@ const char *mgcp_client_remote_addr_str(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);
|
||||
|
||||
int mgcp_client_next_endpoint(struct mgcp_client *client);
|
||||
void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client);
|
||||
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp);
|
||||
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp);
|
||||
|
||||
/* Invoked when an MGCP response is received or sending failed. When the
|
||||
* response is passed as NULL, this indicates failure during transmission. */
|
||||
@@ -92,27 +134,21 @@ int mgcp_response_parse_params(struct mgcp_response *r);
|
||||
|
||||
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
mgcp_response_cb_t response_cb, void *priv);
|
||||
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
|
||||
|
||||
enum mgcp_connection_mode;
|
||||
|
||||
struct msgb *mgcp_msg_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);
|
||||
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
|
||||
|
||||
extern const struct value_string mgcp_client_connection_mode_strs[];
|
||||
static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
|
||||
{
|
||||
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);
|
||||
|
57
include/osmocom/mgcp_client/mgcp_client_fsm.h
Normal file
57
include/osmocom/mgcp_client/mgcp_client_fsm.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_common.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
|
||||
/*! This struct organizes the connection infromation one connection side
|
||||
* (either remote or local). It is used to pass parameters (local) to the FSM
|
||||
* and get responses (remote) from the FSM as pointer attached to the FSM
|
||||
* event.
|
||||
*
|
||||
* When modifiying a connection, the endpoint and call_id members may be left
|
||||
* unpopulated. The call_id field is ignored in this case. If an endpoint
|
||||
* identifier is supplied it is checked against the internal state to make
|
||||
* sure it is correct. */
|
||||
struct mgcp_conn_peer {
|
||||
/*! RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
|
||||
char addr[INET_ADDRSTRLEN];
|
||||
|
||||
/*! RTP connection IP-Port (optional) */
|
||||
uint16_t port;
|
||||
|
||||
/*! RTP endpoint */
|
||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
|
||||
/*! CALL ID (unique per connection) */
|
||||
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,
|
||||
uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
|
||||
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
|
||||
void mgcp_conn_delete(struct osmo_fsm_inst *fi);
|
||||
|
||||
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
|
@@ -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}/
|
@@ -1,10 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: Osmocom Media Gateway Control Protocol library
|
||||
Description: C Utility Library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -losmo-mgcp
|
||||
Cflags: -I${includedir}/
|
@@ -17,11 +17,9 @@
|
||||
|
||||
app_configs = {
|
||||
"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"),
|
||||
(4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "osmo-bsc_mgcp"),
|
||||
]
|
||||
|
||||
vty_command = ["./src/osmo-mgw/osmo-mgw", "-c",
|
||||
|
@@ -21,13 +21,11 @@ AM_LDFLAGS = \
|
||||
|
||||
# Libraries
|
||||
SUBDIRS = \
|
||||
libosmo-legacy-mgcp \
|
||||
libosmo-mgcp-client \
|
||||
libosmo-mgcp \
|
||||
$(NULL)
|
||||
|
||||
# Programs
|
||||
SUBDIRS += \
|
||||
osmo-bsc_mgcp \
|
||||
osmo-mgw \
|
||||
$(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:0: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
|
||||
# API versions. Read TODO_RELEASE in the source tree's root!
|
||||
MGCP_CLIENT_LIBVERSION=2:0:0
|
||||
MGCP_CLIENT_LIBVERSION=4:0:1
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp-client.la \
|
||||
@@ -29,6 +29,7 @@ lib_LTLIBRARIES = \
|
||||
libosmo_mgcp_client_la_SOURCES = \
|
||||
mgcp_client.c \
|
||||
mgcp_client_vty.c \
|
||||
mgcp_client_fsm.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)
|
||||
|
File diff suppressed because it is too large
Load Diff
687
src/libosmo-mgcp-client/mgcp_client_fsm.c
Normal file
687
src/libosmo-mgcp-client/mgcp_client_fsm.c
Normal file
@@ -0,0 +1,687 @@
|
||||
/* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_fsm.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/byteswap.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
/* Context information, this is attached to the priv pointer of the FSM and
|
||||
* is also handed back when dispatcheing events to the parent FSM. This is
|
||||
* purly intened and not meant to be accessible for the API user */
|
||||
struct mgcp_ctx {
|
||||
/* MGCP client instance that is used to interact with the MGW */
|
||||
struct mgcp_client *mgcp;
|
||||
|
||||
/* The ID of the last pending transaction. This is used internally
|
||||
* to cancel the transaction in case of an error */
|
||||
mgcp_trans_id_t mgw_pending_trans;
|
||||
|
||||
/* Flag to mark that there is a pending transaction */
|
||||
bool mgw_trans_pending;
|
||||
|
||||
/* Connection ID which has been assigned by he MGW */
|
||||
char conn_id[MGCP_CONN_ID_MAXLEN];
|
||||
|
||||
/* 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
|
||||
* on the creation of a connection. It can always be modified later by
|
||||
* the user. */
|
||||
struct mgcp_conn_peer conn_peer_local;
|
||||
|
||||
/* Remote RTP connection info, the ip/port specified here is the address
|
||||
* where the MGW expects the RTP data to be sent. This address is
|
||||
* defined by soly by the MGW and can not be influenced by the user. */
|
||||
struct mgcp_conn_peer conn_peer_remote;
|
||||
|
||||
/* The terminate flag is a way to handle cornercase sitations that
|
||||
* might occur when the user runs into an error situation and sends
|
||||
* a DLCX command while the FSM is waiting for a response. In this
|
||||
* case the DLCX command is not executed immediately. Instead the
|
||||
* terminate flag is set. When the response to from the previous
|
||||
* operation is received, we know that there is a DLCX event is
|
||||
* pending. The FSM then generates the EV_DLCX by itsself before
|
||||
* it enters ST_READY to cause the immediate execution of the
|
||||
* DLCX procedure. (If normal operations are executed too fast,
|
||||
* the API functions will return an error. In general, the user
|
||||
* should synchronize using the callback events) */
|
||||
bool terminate;
|
||||
|
||||
/* Event that is sent when the current operation is completed (except
|
||||
* for DLCX, there the specified parent_term_evt is sent instead) */
|
||||
uint32_t parent_evt;
|
||||
};
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
#define MGCP_MGW_TIMEOUT 4 /* in seconds */
|
||||
#define MGCP_MGW_TIMEOUT_TIMER_NR 1
|
||||
|
||||
enum fsm_mgcp_client_states {
|
||||
ST_CRCX,
|
||||
ST_CRCX_RESP,
|
||||
ST_READY,
|
||||
ST_MDCX_RESP,
|
||||
ST_DLCX_RESP,
|
||||
};
|
||||
|
||||
enum fsm_mgcp_client_evt {
|
||||
EV_CRCX,
|
||||
EV_CRCX_RESP,
|
||||
EV_MDCX,
|
||||
EV_MDCX_RESP,
|
||||
EV_DLCX,
|
||||
EV_DLCX_RESP,
|
||||
};
|
||||
|
||||
static const struct value_string fsm_mgcp_client_evt_names[] = {
|
||||
OSMO_VALUE_STRING(EV_CRCX),
|
||||
OSMO_VALUE_STRING(EV_CRCX_RESP),
|
||||
OSMO_VALUE_STRING(EV_MDCX),
|
||||
OSMO_VALUE_STRING(EV_MDCX_RESP),
|
||||
OSMO_VALUE_STRING(EV_DLCX),
|
||||
OSMO_VALUE_STRING(EV_DLCX_RESP),
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
|
||||
{
|
||||
*mgcp_msg = (struct mgcp_msg) {
|
||||
.verb = MGCP_VERB_CRCX,
|
||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
|
||||
| MGCP_MSG_PRESENCE_CONN_MODE),
|
||||
.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, 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));
|
||||
|
||||
if (info->x_osmo_ign) {
|
||||
mgcp_msg->x_osmo_ign = info->x_osmo_ign;
|
||||
mgcp_msg->presence |= MGCP_MSG_PRESENCE_X_OSMO_IGN;
|
||||
}
|
||||
}
|
||||
|
||||
static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
|
||||
{
|
||||
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT;
|
||||
mgcp_msg->audio_ip = info->addr;
|
||||
mgcp_msg->audio_port = info->port;
|
||||
mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
|
||||
}
|
||||
|
||||
static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
{
|
||||
struct mgcp_msg mgcp_msg;
|
||||
|
||||
mgcp_msg = (struct mgcp_msg) {
|
||||
.verb = MGCP_VERB_MDCX,
|
||||
.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),
|
||||
.call_id = mgcp_ctx->conn_peer_remote.call_id,
|
||||
.conn_id = mgcp_ctx->conn_id,
|
||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||
.audio_ip = mgcp_ctx->conn_peer_local.addr,
|
||||
.audio_port = mgcp_ctx->conn_peer_local.port,
|
||||
.ptime = mgcp_ctx->conn_peer_local.ptime,
|
||||
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len,
|
||||
.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len
|
||||
};
|
||||
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
|
||||
* connection info, because we can be confident that the
|
||||
* information there is valid. For the local info, we explicitly
|
||||
* allow endpoint and call_id to be optional */
|
||||
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
}
|
||||
|
||||
struct msgb *make_dlcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
{
|
||||
struct mgcp_msg mgcp_msg;
|
||||
|
||||
mgcp_msg = (struct mgcp_msg) {
|
||||
.verb = MGCP_VERB_DLCX,
|
||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID),
|
||||
.call_id = mgcp_ctx->conn_peer_remote.call_id,
|
||||
.conn_id = mgcp_ctx->conn_id,
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
|
||||
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
}
|
||||
|
||||
static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv);
|
||||
|
||||
static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = data;
|
||||
struct mgcp_client *mgcp;
|
||||
struct mgcp_msg mgcp_msg;
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
mgcp = mgcp_ctx->mgcp;
|
||||
OSMO_ASSERT(mgcp);
|
||||
|
||||
switch (event) {
|
||||
case EV_CRCX:
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: creating connection on MGW endpoint:%s...\n",
|
||||
mgcp_ctx->conn_peer_local.endpoint);
|
||||
|
||||
make_crcx_msg(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
if (mgcp_ctx->conn_peer_local.port)
|
||||
add_audio(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
msg = mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
OSMO_ASSERT(msg);
|
||||
|
||||
mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
|
||||
mgcp_ctx->mgw_trans_pending = true;
|
||||
rc = mgcp_client_tx(mgcp, msg, mgw_crcx_resp_cb, fi);
|
||||
if (rc < 0) {
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
osmo_fsm_inst_state_chg(fi, ST_CRCX_RESP, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = priv;
|
||||
struct mgcp_ctx *mgcp_ctx;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(fi);
|
||||
mgcp_ctx = fi->priv;
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
mgcp_ctx->mgw_trans_pending = false;
|
||||
|
||||
if (r->head.response_code != 200) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"MGW/CRCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
osmo_strlcpy(mgcp_ctx->conn_id, r->head.conn_id, sizeof(mgcp_ctx->conn_id));
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with CI: %s\n", mgcp_ctx->conn_id);
|
||||
|
||||
rc = mgcp_response_parse_params(r);
|
||||
if (rc) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: Cannot parse CRCX response\n");
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
|
||||
|
||||
osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr));
|
||||
mgcp_ctx->conn_peer_remote.port = r->audio_port;
|
||||
|
||||
if (strlen(r->head.endpoint) > 0) {
|
||||
/* If we get an endpoint identifier back from the MGW, take it */
|
||||
osmo_strlcpy(mgcp_ctx->conn_peer_remote.endpoint, r->head.endpoint,
|
||||
sizeof(mgcp_ctx->conn_peer_remote.endpoint));
|
||||
} else if (strstr(mgcp_ctx->conn_peer_local.endpoint, "*") == NULL) {
|
||||
/* If we do not get an endpoint identifier back and the
|
||||
* identifier we used to create the connection is not a
|
||||
* wildcarded one, we take the local endpoint identifier
|
||||
* instead */
|
||||
osmo_strlcpy(mgcp_ctx->conn_peer_remote.endpoint, mgcp_ctx->conn_peer_local.endpoint,
|
||||
sizeof(mgcp_ctx->conn_peer_local.endpoint));
|
||||
} else {
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: CRCX yielded not suitable endpoint identifier\n");
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
mgcp_ctx->conn_peer_remote.call_id = mgcp_ctx->conn_peer_local.call_id;
|
||||
|
||||
osmo_fsm_inst_dispatch(fi, EV_CRCX_RESP, mgcp_ctx);
|
||||
}
|
||||
|
||||
static void fsm_crcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = data;
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
switch (event) {
|
||||
case EV_CRCX_RESP:
|
||||
osmo_fsm_inst_state_chg(fi, ST_READY, 0, 0);
|
||||
if (mgcp_ctx->terminate) {
|
||||
/* Trigger immediate DLCX if DLCX was requested while the FSM was
|
||||
* busy with the previous operation */
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: FSM was busy while DLCX was requested, executing now...\n");
|
||||
osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx);
|
||||
} else
|
||||
osmo_fsm_inst_dispatch(fi->proc.parent, mgcp_ctx->parent_evt, &mgcp_ctx->conn_peer_remote);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv);
|
||||
static void mgw_dlcx_resp_cb(struct mgcp_response *r, void *priv);
|
||||
|
||||
static void fsm_ready_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = data;
|
||||
struct msgb *msg;
|
||||
struct mgcp_client *mgcp;
|
||||
uint32_t new_state;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
mgcp = mgcp_ctx->mgcp;
|
||||
OSMO_ASSERT(mgcp);
|
||||
|
||||
switch (event) {
|
||||
case EV_MDCX:
|
||||
msg = make_mdcx_msg(mgcp_ctx);
|
||||
OSMO_ASSERT(msg);
|
||||
rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_resp_cb, fi);
|
||||
new_state = ST_MDCX_RESP;
|
||||
break;
|
||||
case EV_DLCX:
|
||||
msg = make_dlcx_msg(mgcp_ctx);
|
||||
OSMO_ASSERT(msg);
|
||||
rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_resp_cb, fi);
|
||||
new_state = ST_DLCX_RESP;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
|
||||
mgcp_ctx->mgw_trans_pending = true;
|
||||
|
||||
if (rc < 0) {
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
osmo_fsm_inst_state_chg(fi, new_state, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
|
||||
}
|
||||
|
||||
static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = priv;
|
||||
struct mgcp_ctx *mgcp_ctx;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(fi);
|
||||
mgcp_ctx = fi->priv;
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
mgcp_ctx->mgw_trans_pending = false;
|
||||
|
||||
if (r->head.response_code != 200) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: response yields error: %d %s\n", r->head.response_code,
|
||||
r->head.comment);
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = mgcp_response_parse_params(r);
|
||||
if (rc) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: Cannot parse MDCX response\n");
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/MDCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
|
||||
|
||||
osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr));
|
||||
mgcp_ctx->conn_peer_remote.port = r->audio_port;
|
||||
|
||||
osmo_fsm_inst_dispatch(fi, EV_MDCX_RESP, mgcp_ctx);
|
||||
}
|
||||
|
||||
static void fsm_mdcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = data;
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
switch (event) {
|
||||
case EV_MDCX_RESP:
|
||||
osmo_fsm_inst_state_chg(fi, ST_READY, 0, 0);
|
||||
if (mgcp_ctx->terminate) {
|
||||
/* Trigger immediate DLCX if DLCX was requested while the FSM was
|
||||
* busy with the previous operation */
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: FSM was busy while DLCX was requested, executing now...\n");
|
||||
osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx);
|
||||
} else
|
||||
osmo_fsm_inst_dispatch(fi->proc.parent, mgcp_ctx->parent_evt, &mgcp_ctx->conn_peer_remote);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mgw_dlcx_resp_cb(struct mgcp_response *r, void *priv)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = priv;
|
||||
struct mgcp_ctx *mgcp_ctx;
|
||||
|
||||
OSMO_ASSERT(fi);
|
||||
mgcp_ctx = fi->priv;
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
mgcp_ctx->mgw_trans_pending = false;
|
||||
|
||||
if (r->head.response_code != 250) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"MGW/DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
osmo_fsm_inst_dispatch(fi, EV_DLCX_RESP, mgcp_ctx);
|
||||
}
|
||||
|
||||
static void fsm_dlcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = data;
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
switch (event) {
|
||||
case EV_DLCX_RESP:
|
||||
/* Rub out the connection identifier, since the connection
|
||||
* is no longer present and we will use the connection id
|
||||
* to know in error cases if the connection is still present
|
||||
* or not */
|
||||
memset(mgcp_ctx->conn_id, 0, sizeof(mgcp_ctx->conn_id));
|
||||
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int fsm_timeout_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = fi->priv;
|
||||
struct mgcp_client *mgcp;
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
mgcp = mgcp_ctx->mgcp;
|
||||
OSMO_ASSERT(mgcp);
|
||||
|
||||
if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) {
|
||||
/* Note: We were unable to communicate with the MGW,
|
||||
* unfortunately there is no meaningful action we can take
|
||||
* now other than giving up. */
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
} else {
|
||||
/* Note: Ther must not be any unsolicited timers
|
||||
* in this FSM. If so, we have serious problem. */
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = fi->priv;
|
||||
struct mgcp_client *mgcp;
|
||||
struct msgb *msg;
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
mgcp = mgcp_ctx->mgcp;
|
||||
OSMO_ASSERT(mgcp);
|
||||
|
||||
/* If there is still a transaction pending, cancel it now. */
|
||||
if (mgcp_ctx->mgw_trans_pending)
|
||||
mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
|
||||
|
||||
/* Should the FSM be terminated while there are still open connections
|
||||
* on the MGW, we send an unconditional DLCX to terminate the
|
||||
* connection. This is not the normal case. The user should always use
|
||||
* mgcp_conn_delete() to instruct the FSM to perform a graceful exit */
|
||||
if (strlen(mgcp_ctx->conn_id)) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n");
|
||||
msg = make_dlcx_msg(mgcp_ctx);
|
||||
OSMO_ASSERT(msg);
|
||||
mgcp_client_tx(mgcp, msg, NULL, NULL);
|
||||
}
|
||||
|
||||
talloc_free(mgcp_ctx);
|
||||
}
|
||||
|
||||
static struct osmo_fsm_state fsm_mgcp_client_states[] = {
|
||||
|
||||
/* Initial CRCX state. This state is immediately entered and executed
|
||||
* when the FSM is started. The rationale is that we first have to
|
||||
* create a connectin before we can execute other operations on that
|
||||
* connection. */
|
||||
[ST_CRCX] = {
|
||||
.in_event_mask = S(EV_CRCX),
|
||||
.out_state_mask = S(ST_CRCX_RESP),
|
||||
.name = OSMO_STRINGIFY(ST_CRCX),
|
||||
.action = fsm_crcx_cb,
|
||||
},
|
||||
|
||||
/* Wait for the response to a CRCX operation, check and process the
|
||||
* results, change to ST_READY afterwards. */
|
||||
[ST_CRCX_RESP] = {
|
||||
.in_event_mask = S(EV_CRCX_RESP),
|
||||
.out_state_mask = S(ST_READY),
|
||||
.name = OSMO_STRINGIFY(ST_CRCX_RESP),
|
||||
.action = fsm_crcx_resp_cb,
|
||||
},
|
||||
|
||||
/* In this idle state we wait for further operations (e.g. MDCX) that
|
||||
* can be executed by the user using the API. There is no timeout in
|
||||
* this state. The connection lives on until the user decides to
|
||||
* terminate it (DLCX). */
|
||||
[ST_READY] = {
|
||||
.in_event_mask = S(EV_MDCX) | S(EV_DLCX),
|
||||
.out_state_mask = S(ST_MDCX_RESP) | S(ST_DLCX_RESP),
|
||||
.name = OSMO_STRINGIFY(ST_READY),
|
||||
.action = fsm_ready_cb,
|
||||
},
|
||||
|
||||
/* Wait for the response of a MDCX operation, check and process the
|
||||
* results, change to ST_READY afterwards. */
|
||||
[ST_MDCX_RESP] = {
|
||||
.in_event_mask = S(EV_MDCX_RESP),
|
||||
.out_state_mask = S(ST_READY),
|
||||
.name = OSMO_STRINGIFY(ST_MDCX_RESP),
|
||||
.action = fsm_mdcx_resp_cb,
|
||||
},
|
||||
|
||||
/* Wait for the response of a DLCX operation and terminate the FSM
|
||||
* normally. */
|
||||
[ST_DLCX_RESP] = {
|
||||
.in_event_mask = S(EV_DLCX_RESP),
|
||||
.out_state_mask = 0,
|
||||
.name = OSMO_STRINGIFY(ST_DLCX_RESP),
|
||||
.action = fsm_dlcx_resp_cb,
|
||||
},
|
||||
};
|
||||
|
||||
static struct osmo_fsm fsm_mgcp_client = {
|
||||
.name = "MGCP_CONN",
|
||||
.states = fsm_mgcp_client_states,
|
||||
.num_states = ARRAY_SIZE(fsm_mgcp_client_states),
|
||||
.timer_cb = fsm_timeout_cb,
|
||||
.cleanup = fsm_cleanup_cb,
|
||||
.event_names = fsm_mgcp_client_evt_names,
|
||||
};
|
||||
|
||||
/*! allocate FSM, and create a new connection on the MGW.
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \param[in] parent_fi Parent FSM instance.
|
||||
* \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] conn_peer Connection parameters (ip, port...).
|
||||
* \returns newly-allocated, initialized and registered FSM instance, NULL on error. */
|
||||
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)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx;
|
||||
static bool fsm_registered = false;
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct in_addr ip_test;
|
||||
|
||||
OSMO_ASSERT(parent_fi);
|
||||
OSMO_ASSERT(mgcp);
|
||||
OSMO_ASSERT(conn_peer);
|
||||
|
||||
/* Check if IP/Port information in conn info makes sense */
|
||||
if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0)
|
||||
return NULL;
|
||||
|
||||
/* Register the fsm description (if not already done) */
|
||||
if (fsm_registered == false) {
|
||||
osmo_fsm_register(&fsm_mgcp_client);
|
||||
fsm_registered = true;
|
||||
}
|
||||
|
||||
/* Allocate and configure a new fsm instance */
|
||||
fi = osmo_fsm_inst_alloc_child(&fsm_mgcp_client, parent_fi, parent_term_evt);
|
||||
OSMO_ASSERT(fi);
|
||||
mgcp_ctx = talloc_zero(fi, struct mgcp_ctx);
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
mgcp_ctx->mgcp = mgcp;
|
||||
mgcp_ctx->parent_evt = parent_evt;
|
||||
|
||||
memcpy(&mgcp_ctx->conn_peer_local, conn_peer, sizeof(mgcp_ctx->conn_peer_local));
|
||||
fi->priv = mgcp_ctx;
|
||||
|
||||
/* start state machine */
|
||||
OSMO_ASSERT(fi->state == ST_CRCX);
|
||||
osmo_fsm_inst_dispatch(fi, EV_CRCX, mgcp_ctx);
|
||||
|
||||
return fi;
|
||||
}
|
||||
|
||||
/*! modify an existing connection on the MGW.
|
||||
* \param[in] fi FSM instance.
|
||||
* \param[in] parent_evt Event to be sent to parent when operation is done.
|
||||
* \param[in] conn_peer New connection information (ip, port...).
|
||||
* \returns 0 on success, -EINVAL on error. */
|
||||
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
|
||||
{
|
||||
OSMO_ASSERT(fi);
|
||||
struct mgcp_ctx *mgcp_ctx = fi->priv;
|
||||
struct in_addr ip_test;
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
OSMO_ASSERT(conn_peer);
|
||||
|
||||
/* The user must not issue an MDCX before the CRCX has completed,
|
||||
* if this happens, it means that the parent FSM has overhead the
|
||||
* parent_evt (mandatory!) and executed the MDCX without even
|
||||
* waiting for the results. Another reason could be that the
|
||||
* parent FSM got messed up */
|
||||
OSMO_ASSERT(fi->state != ST_CRCX_RESP);
|
||||
|
||||
/* If the user tries to issue an MDCX while an DLCX operation is
|
||||
* pending, there must be a serious problem with the paren FSM.
|
||||
* Eeither the parent_term_evt (mandatory!) has been overheard,
|
||||
* or the parant FSM got messed so badly that it still assumes
|
||||
* a live connection although it as killed it. */
|
||||
OSMO_ASSERT(fi->state != ST_DLCX_RESP);
|
||||
|
||||
/* Check if IP/Port parameters make sense */
|
||||
if (conn_peer->port == 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, port == 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (inet_aton(conn_peer->addr, &ip_test) == 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address == 0.0.0.0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! The user may supply an endpoint identifier in conn_peer. The
|
||||
* identifier is then checked. This check is optional. Later steps do
|
||||
* not depend on the endpoint identifier supplied here because it is
|
||||
* already implicitly known from the CRCX phase. */
|
||||
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;
|
||||
}
|
||||
|
||||
/*! Note: The call-id is implicitly known from the previous CRCX and
|
||||
* will not be checked even when it is set in conn_peer. */
|
||||
|
||||
mgcp_ctx->parent_evt = parent_evt;
|
||||
memcpy(&mgcp_ctx->conn_peer_local, conn_peer, sizeof(mgcp_ctx->conn_peer_local));
|
||||
osmo_fsm_inst_dispatch(fi, EV_MDCX, mgcp_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! delete existing connection on the MGW, destroy FSM afterwards.
|
||||
* \param[in] fi FSM instance. */
|
||||
void mgcp_conn_delete(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
OSMO_ASSERT(fi);
|
||||
struct mgcp_ctx *mgcp_ctx = fi->priv;
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
/* Unlink FSM from parent */
|
||||
osmo_fsm_inst_unlink_parent(fi, NULL);
|
||||
|
||||
/* An error situation where the parent FSM must be killed immediately
|
||||
* may lead into a situation where the DLCX can not be executed right
|
||||
* at that moment because the FSM is still busy with another operation.
|
||||
* In those cases we postpone the DLCX so that the FSM and the
|
||||
* connections on the MGW get cleaned up gracefully. */
|
||||
if (fi->state != ST_READY) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW: operation still pending, DLCX will be postponed.\n");
|
||||
mgcp_ctx->terminate = true;
|
||||
return;
|
||||
}
|
||||
osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx);
|
||||
}
|
@@ -6,16 +6,16 @@
|
||||
* 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
|
||||
* 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 Affero General Public License for more details.
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero 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/>.
|
||||
*
|
||||
*/
|
||||
@@ -25,11 +25,12 @@
|
||||
#include <talloc.h>
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/core/utils.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;
|
||||
struct mgcp_client_conf *global_mgcp_client_conf = NULL;
|
||||
@@ -42,8 +43,9 @@ DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
|
||||
if (!global_mgcp_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
OSMO_ASSERT(global_mgcp_client_ctx);
|
||||
global_mgcp_client_conf->local_addr =
|
||||
talloc_strdup(global_mgcp_client_ctx, argv[0]);
|
||||
osmo_talloc_replace_string(global_mgcp_client_ctx,
|
||||
(char**)&global_mgcp_client_conf->local_addr,
|
||||
argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
|
||||
@@ -74,8 +76,9 @@ DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
|
||||
if (!global_mgcp_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
OSMO_ASSERT(global_mgcp_client_ctx);
|
||||
global_mgcp_client_conf->remote_addr =
|
||||
talloc_strdup(global_mgcp_client_ctx, argv[0]);
|
||||
osmo_talloc_replace_string(global_mgcp_client_ctx,
|
||||
(char**)&global_mgcp_client_conf->remote_addr,
|
||||
argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
|
||||
@@ -98,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"
|
||||
"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_STR "usable range of endpoint identifiers\n"
|
||||
"set first usable endpoint identifier\n"
|
||||
"set last usable endpoint identifier\n")
|
||||
MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
|
||||
"-\n" "-\n")
|
||||
{
|
||||
uint16_t first_endpoint = atoi(argv[0]);
|
||||
uint16_t last_endpoint = atoi(argv[1]);
|
||||
|
||||
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;
|
||||
vty_out(vty, "Please do not use legacy config 'mgw endpoint-range'"
|
||||
" (the range can no longer be defined by the MGCP client)%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
||||
@@ -125,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 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,
|
||||
"mgw bts-base <0-65534>",
|
||||
MGW_STR
|
||||
BTS_START_STR
|
||||
UDP_PORT_STR)
|
||||
"DEPRECATED: there is no explicit BTS side in current osmo-mgw\n" "-\n")
|
||||
{
|
||||
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;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
||||
@@ -142,13 +137,21 @@ ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
||||
BTS_START_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")
|
||||
{
|
||||
osmo_strlcpy(global_mgcp_client_conf->endpoint_domain_name, argv[0],
|
||||
sizeof(global_mgcp_client_conf->endpoint_domain_name));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int mgcp_client_config_write(struct vty *vty, const char *indent)
|
||||
{
|
||||
const char *addr;
|
||||
int port;
|
||||
uint16_t first_endpoint;
|
||||
uint16_t last_endpoint;
|
||||
uint16_t bts_base;
|
||||
|
||||
addr = global_mgcp_client_conf->local_addr;
|
||||
if (addr)
|
||||
@@ -168,18 +171,9 @@ int mgcp_client_config_write(struct vty *vty, const char *indent)
|
||||
vty_out(vty, "%smgw remote-port %u%s", indent,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
|
||||
first_endpoint = global_mgcp_client_conf->first_endpoint;
|
||||
last_endpoint = global_mgcp_client_conf->last_endpoint;
|
||||
if (last_endpoint != 0) {
|
||||
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);
|
||||
}
|
||||
if (global_mgcp_client_conf->endpoint_domain_name[0])
|
||||
vty_out(vty, "%smgw endpoint-domain %s%s", indent,
|
||||
global_mgcp_client_conf->endpoint_domain_name, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -195,6 +189,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_endpoint_range_cmd);
|
||||
install_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
|
||||
install_element(node, &cfg_mgw_endpoint_domain_name_cmd);
|
||||
|
||||
/* deprecated 'mgcpgw' commands */
|
||||
install_element(node, &cfg_mgcpgw_local_ip_cmd);
|
||||
@@ -203,4 +198,6 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
|
||||
install_element(node, &cfg_mgcpgw_remote_port_cmd);
|
||||
install_element(node, &cfg_mgcpgw_endpoint_range_cmd);
|
||||
install_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
|
||||
|
||||
osmo_fsm_vty_add_cmds();
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ AM_CPPFLAGS = \
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
@@ -14,33 +15,31 @@ AM_CFLAGS = \
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
# This is not at all related to the release version, but a range of supported
|
||||
# API versions. Read TODO_RELEASE in the source tree's root!
|
||||
MGCP_LIBVERSION=1:0:0
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp.la \
|
||||
noinst_LIBRARIES = \
|
||||
libosmo-mgcp.a \
|
||||
$(NULL)
|
||||
|
||||
noinst_HEADERS = \
|
||||
g711common.h \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_la_SOURCES = \
|
||||
libosmo_mgcp_a_SOURCES = \
|
||||
mgcp_protocol.c \
|
||||
mgcp_network.c \
|
||||
mgcp_vty.c \
|
||||
mgcp_osmux.c \
|
||||
mgcp_sdp.c \
|
||||
mgcp_codec.c \
|
||||
mgcp_msg.c \
|
||||
mgcp_conn.c \
|
||||
mgcp_stat.c \
|
||||
mgcp_ep.c \
|
||||
mgcp_endp.c \
|
||||
iuup_protocol.c \
|
||||
iuup_cn_node.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_LIBVERSION)
|
||||
|
216
src/libosmo-mgcp/iuup_cn_node.c
Normal file
216
src/libosmo-mgcp/iuup_cn_node.c
Normal file
@@ -0,0 +1,216 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* IuUP Core Network side protocol handling, minimal implementation */
|
||||
|
||||
/*
|
||||
* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Neels Hofmeyr <neels@hofmeyr.de>
|
||||
*
|
||||
* 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 <talloc.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include <osmocom/netif/rtp.h>
|
||||
|
||||
#include <osmocom/mgcp/iuup_cn_node.h>
|
||||
#include <osmocom/mgcp/iuup_protocol.h>
|
||||
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
|
||||
#define LOG_IUUP_CN(cn, level, fmt, args...) \
|
||||
LOGP(DIUUP, level, "(%s) " fmt, (cn)->name, ## args)
|
||||
|
||||
struct osmo_iuup_cn {
|
||||
struct osmo_iuup_cn_cfg cfg;
|
||||
char *name;
|
||||
uint8_t next_frame_nr;
|
||||
int rtp_payload_type;
|
||||
};
|
||||
|
||||
struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
|
||||
const char *name_fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
struct osmo_iuup_cn *cn = talloc_zero(ctx, struct osmo_iuup_cn);
|
||||
OSMO_ASSERT(cn);
|
||||
|
||||
cn->cfg = *cfg;
|
||||
|
||||
if (!name_fmt)
|
||||
name_fmt = "-";
|
||||
|
||||
va_start(ap, name_fmt);
|
||||
cn->name = talloc_vasprintf(cn, name_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
LOGP(DIUUP, LOGL_INFO, "(%s) Initializing IuUP node\n", cn->name);
|
||||
|
||||
if (!osmo_identifier_valid(cn->name)) {
|
||||
LOGP(DIUUP, LOGL_ERROR, "Attempting to set illegal id for IuUP CN instance: %s\n",
|
||||
osmo_quote_str(cn->name, -1));
|
||||
talloc_free(cn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cn;
|
||||
}
|
||||
|
||||
void osmo_iuup_cn_free(struct osmo_iuup_cn *cn)
|
||||
{
|
||||
talloc_free(cn);
|
||||
}
|
||||
|
||||
static int rx_data(struct osmo_iuup_cn *cn, struct msgb *pdu,
|
||||
struct osmo_iuup_hdr_data *hdr)
|
||||
{
|
||||
/* Remove the IuUP bit from the middle of the buffer by writing the RTP header forward. */
|
||||
/* And append AMR 12.2k header "0xf03c". - AD HOC fix */
|
||||
unsigned int pre_hdr_len = ((uint8_t*)hdr) - pdu->data;
|
||||
memmove(pdu->data + sizeof(*hdr) - 2, pdu->data, pre_hdr_len);
|
||||
((uint8_t*)hdr)[2] = 0xf0;
|
||||
((uint8_t*)hdr)[3] = 0x3c;
|
||||
msgb_pull(pdu, sizeof(*hdr) - 2);
|
||||
|
||||
LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP stripping IuUP header from RTP data\n", cn->name);
|
||||
cn->cfg.rx_payload(pdu, cn->cfg.node_priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tx_init_ack(struct osmo_iuup_cn *cn, struct msgb *src_pdu)
|
||||
{
|
||||
/* Send Initialization Ack PDU back to the sender */
|
||||
int rc;
|
||||
struct msgb *ack = msgb_alloc(4096, "IuUP Initialization Ack");
|
||||
OSMO_ASSERT(ack);
|
||||
ack->dst = src_pdu->dst;
|
||||
|
||||
/* Just copy the RTP header that was sent... TODO: tweak some RTP values?? */
|
||||
memcpy(msgb_put(ack, sizeof(struct rtp_hdr)), src_pdu->data, sizeof(struct rtp_hdr));
|
||||
|
||||
osmo_iuup_make_init_ack(ack);
|
||||
|
||||
LOGP(DIUUP, LOGL_DEBUG, "(%s) Sending Initialization ACK %p\n", cn->name, cn->cfg.node_priv);
|
||||
rc = cn->cfg.tx_msg(ack, cn->cfg.node_priv);
|
||||
msgb_free(ack);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int rx_control(struct osmo_iuup_cn *cn, struct msgb *pdu,
|
||||
struct osmo_iuup_hdr_ctrl *hdr)
|
||||
{
|
||||
switch (hdr->procedure) {
|
||||
case OSMO_IUUP_PROC_INITIALIZATION:
|
||||
switch (hdr->ack_nack) {
|
||||
case OSMO_IUUP_ACKNACK_PROCEDURE:
|
||||
LOGP(DIUUP, LOGL_INFO, "(%s) Rx IuUP Initialization, sending ACK\n", cn->name);
|
||||
cn->rtp_payload_type = ((struct rtp_hdr*)pdu->data)->payload_type;
|
||||
return tx_init_ack(cn, pdu);
|
||||
|
||||
default:
|
||||
LOGP(DIUUP, LOGL_DEBUG, "(%s) Rx IuUP Initialization, unhandled ack_nack = %d\n",
|
||||
cn->name, hdr->ack_nack);
|
||||
break;
|
||||
}
|
||||
/* Continue to log "unexpected procedure" below. */
|
||||
break;
|
||||
|
||||
case OSMO_IUUP_PROC_ERROR_EVENT:
|
||||
{
|
||||
union osmo_iuup_hdr_ctrl_payload *p = (void*)hdr->payload;
|
||||
LOGP(DIUUP, LOGL_ERROR,
|
||||
"(%s) Rx IuUP Error Event: distance=%u, cause=%u=\"%s\"\n",
|
||||
cn->name, p->error_event.error_distance, p->error_event.error_cause,
|
||||
osmo_iuup_error_cause_name(p->error_event.error_cause));
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
LOG_IUUP_CN(cn, LOGL_ERROR,
|
||||
"Rx control PDU with unexpected procedure: 0x%x acknack=0x%x\n",
|
||||
hdr->procedure, hdr->ack_nack);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Feed a received PDU to the IuUP CN node. This function takes ownership of the msgb, it must not be
|
||||
* freed by the caller. */
|
||||
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu)
|
||||
{
|
||||
struct osmo_iuup_hdr_ctrl *is_ctrl;
|
||||
struct osmo_iuup_hdr_data *is_data;
|
||||
int rc;
|
||||
|
||||
rc = osmo_iuup_classify(true, cn->name, pdu, &is_ctrl, &is_data);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (is_ctrl)
|
||||
return rx_control(cn, pdu, is_ctrl);
|
||||
if (is_data)
|
||||
return rx_data(cn, pdu, is_data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static uint8_t next_frame_nr(struct osmo_iuup_cn *cn)
|
||||
{
|
||||
uint8_t frame_nr = cn->next_frame_nr;
|
||||
cn->next_frame_nr = (frame_nr + 1) & 0x0f;
|
||||
return frame_nr;
|
||||
}
|
||||
|
||||
/* Send this RTP packet to the IuUP peer: add IuUP header and call the tx_msg() to transmit the resulting
|
||||
* message to the IuUP peer.
|
||||
* Returns 0 on success, negative on error. */
|
||||
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu)
|
||||
{
|
||||
struct rtp_hdr *rtp_was, *rtp;
|
||||
struct osmo_iuup_hdr_data *iuup_hdr;
|
||||
|
||||
/* Splice an IuUP header in between RTP header and payload data */
|
||||
rtp_was = (void*)pdu->data;
|
||||
|
||||
/* copy the RTP header part backwards by the size needed for the IuUP header */
|
||||
/* also strips 2 bytes from the front of RTP payload - AMR header - AD HOC fix */
|
||||
rtp = (void*)msgb_push(pdu, sizeof(*iuup_hdr) - 2);
|
||||
memmove(rtp, rtp_was, sizeof(*rtp));
|
||||
|
||||
/* Send the same payload type to the peer (erm...) */
|
||||
rtp->payload_type = cn->rtp_payload_type;
|
||||
|
||||
iuup_hdr = (void*)rtp->data;
|
||||
|
||||
*iuup_hdr = (struct osmo_iuup_hdr_data){
|
||||
.pdu_type = OSMO_IUUP_PDU_DATA_WITH_CRC,
|
||||
.frame_nr = next_frame_nr(cn),
|
||||
.frame_good = OSMO_IUUP_FRAME_GOOD,
|
||||
};
|
||||
|
||||
osmo_iuup_set_checksums((uint8_t*)iuup_hdr, pdu->tail - (uint8_t*)iuup_hdr);
|
||||
LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP inserting IuUP header in RTP data (frame nr %u)\n",
|
||||
cn->name, iuup_hdr->frame_nr);
|
||||
|
||||
return cn->cfg.tx_msg(pdu, cn->cfg.node_priv);
|
||||
}
|
286
src/libosmo-mgcp/iuup_protocol.c
Normal file
286
src/libosmo-mgcp/iuup_protocol.c
Normal file
@@ -0,0 +1,286 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* IuUP Core Network side protocol, minimal implementation */
|
||||
|
||||
/*
|
||||
* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Neels Hofmeyr <neels@hofmeyr.de>
|
||||
*
|
||||
* 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/mgcp/iuup_protocol.h>
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
#include <osmocom/netif/rtp.h>
|
||||
|
||||
/* Calculating two bytes of CRC is ok to do by a loop */
|
||||
static uint8_t header_crc6(const uint8_t *hdr)
|
||||
{
|
||||
int bit;
|
||||
/* Polynomial: D^6 + D^5 + D^3 + D^2 + D^1 + 1
|
||||
* that's 1101111 or 0x6f;
|
||||
* align its lowest bit with a uint16_t's highest bit: */
|
||||
uint32_t polynomial = 0x6f << 15; // 00110111 10000000 00000000
|
||||
uint32_t remainder = ( ((uint32_t)hdr[0]) << 8 | hdr[1] ) << 6;
|
||||
|
||||
for (bit = 15; bit >= 0; bit--)
|
||||
{
|
||||
if (remainder & (0x40 << bit))
|
||||
remainder ^= polynomial;
|
||||
polynomial >>= 1;
|
||||
}
|
||||
|
||||
return remainder;
|
||||
}
|
||||
|
||||
/*
|
||||
* Charles Michael Heard's CRC-10 code, from
|
||||
*
|
||||
* http://web.archive.org/web/20061005231950/http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/crc10.html
|
||||
*
|
||||
* with the CRC table initialized with values computed by
|
||||
* his "gen_byte_crc10_table()" routine, rather than by calling that
|
||||
* routine at run time, and with various data type cleanups.
|
||||
*/
|
||||
static const uint16_t byte_crc10_table[256] = {
|
||||
0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
|
||||
0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
|
||||
0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
|
||||
0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
|
||||
0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
|
||||
0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
|
||||
0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
|
||||
0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
|
||||
0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
|
||||
0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
|
||||
0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
|
||||
0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
|
||||
0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
|
||||
0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
|
||||
0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
|
||||
0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
|
||||
0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
|
||||
0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
|
||||
0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
|
||||
0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
|
||||
0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
|
||||
0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
|
||||
0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
|
||||
0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
|
||||
0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
|
||||
0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
|
||||
0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
|
||||
0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
|
||||
0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
|
||||
0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
|
||||
0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
|
||||
0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1
|
||||
};
|
||||
|
||||
static uint16_t crc10(uint16_t crc10_accum, const uint8_t *payload, unsigned int payload_len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < payload_len; i++) {
|
||||
crc10_accum = ((crc10_accum << 8) & 0x300)
|
||||
^ byte_crc10_table[(crc10_accum >> 2) & 0xff]
|
||||
^ payload[i];
|
||||
}
|
||||
return crc10_accum;
|
||||
}
|
||||
|
||||
/* When a payload of a multiple of bytes has run through, we need to still feed 10 bits of zeros into the
|
||||
* CRC10 to get the payload's checksum result that we can send to a peer. That can't be done with above
|
||||
* table, because it acts as if full 16 bits are fed. This stops after 10 bits. */
|
||||
static uint16_t crc10_remainder(uint16_t crc10_accum)
|
||||
{
|
||||
int bit;
|
||||
/* Polynomial: D^10 + D^9 + D^5 + D^4 + D^1 + 1
|
||||
* that's 11000110011 or 0x633;
|
||||
* align its lowest bit with a 10bit value's highest bit: */
|
||||
uint32_t polynomial = 0x633 << 9; // 1100 01100110 00000000
|
||||
uint32_t remainder = ((uint32_t)crc10_accum) << 10;
|
||||
|
||||
/* Run on 10 bits */
|
||||
for (bit = 9; bit >= 0; bit--)
|
||||
{
|
||||
if (remainder & ((1 << 10) << bit))
|
||||
remainder ^= polynomial;
|
||||
polynomial >>= 1;
|
||||
}
|
||||
|
||||
return remainder & 0x3ff;
|
||||
}
|
||||
|
||||
static uint16_t payload_crc10(const uint8_t *payload, unsigned int payload_len)
|
||||
{
|
||||
uint16_t crc10_accum = crc10(0, payload, payload_len);
|
||||
return crc10_remainder(crc10_accum);
|
||||
}
|
||||
|
||||
/* Given an IuUP PDU data block, write the correct header and payload CRC checksums at the right places.
|
||||
*/
|
||||
void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len)
|
||||
{
|
||||
/* For both data and ctrl, the checksums and payload are at the same offset */
|
||||
struct osmo_iuup_hdr_data *hdr = (void*)iuup_header_and_payload;
|
||||
uint16_t crc;
|
||||
unsigned int payload_len;
|
||||
|
||||
hdr->header_crc = header_crc6(iuup_header_and_payload);
|
||||
|
||||
payload_len = iuup_header_and_payload + header_and_payload_len - hdr->payload;
|
||||
crc = payload_crc10(hdr->payload, payload_len);
|
||||
hdr->payload_crc_hi = (crc >> 8) & 0x3;
|
||||
hdr->payload_crc_lo = crc & 0xff;
|
||||
|
||||
}
|
||||
|
||||
/* Validate minimum message sizes, IuUP PDU type, header- and payload checksums. If it is a Control
|
||||
* Procedure PDU, return the header position in is_ctrl, if it is a Data PDU, return the header position
|
||||
* in is_data. If log_errors is true, log on DIUUP with the given log label for context. Return NULL in
|
||||
* both is_ctrl and is_data, and return a negative error code if the PDU could not be identified as a
|
||||
* valid RTP PDU containing an IuUP part. */
|
||||
int osmo_iuup_classify(bool log_errors,
|
||||
const char *log_label,
|
||||
struct msgb *pdu,
|
||||
struct osmo_iuup_hdr_ctrl **is_ctrl,
|
||||
struct osmo_iuup_hdr_data **is_data)
|
||||
{
|
||||
struct rtp_hdr *rtp = (void*)pdu->data;
|
||||
struct osmo_iuup_hdr_ctrl *hdr = (void*)rtp->data;
|
||||
unsigned int payload_len;
|
||||
uint16_t crc_calculated;
|
||||
uint16_t crc_from_peer;
|
||||
|
||||
#define ERR(fmt, args...) do { \
|
||||
if (log_errors) \
|
||||
LOGP(DIUUP, LOGL_ERROR, "(%s) " fmt, log_label? : "-", ## args); \
|
||||
return -EINVAL; \
|
||||
} while (0)
|
||||
|
||||
if (is_ctrl)
|
||||
*is_ctrl = NULL;
|
||||
if (is_data)
|
||||
*is_data = NULL;
|
||||
|
||||
/* We need at least a header of 4 bytes. The osmo_iuup_hdr_ctrl already includes a byte of
|
||||
* payload, so use osmo_iuup_hdr_data to check the minimum here. */
|
||||
if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_data)))
|
||||
ERR("IuUP PDU too short: %u\n", pdu->len);
|
||||
|
||||
/* Let's not validate checksums if the header type isn't sane */
|
||||
switch (hdr->pdu_type) {
|
||||
case OSMO_IUUP_PDU_DATA_WITH_CRC:
|
||||
/* If the caller isn't interested in data PDUs, cut short here. */
|
||||
if (!is_data)
|
||||
return 0;
|
||||
break;
|
||||
case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
|
||||
/* If the caller isn't interested in control PDUs, cut short here. */
|
||||
if (!is_ctrl)
|
||||
return 0;
|
||||
if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_ctrl)))
|
||||
ERR("IuUP control PDU too short: %u\n", pdu->len);
|
||||
break;
|
||||
default:
|
||||
ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
|
||||
}
|
||||
|
||||
/* For both data and ctrl, the checksums and payload are at the same offset */
|
||||
|
||||
crc_calculated = header_crc6((uint8_t*)hdr);
|
||||
if (crc_calculated != hdr->header_crc)
|
||||
ERR("IuUP PDU with invalid header CRC (peer sent 0x%x, calculated 0x%x)\n",
|
||||
hdr->header_crc, crc_calculated);
|
||||
|
||||
payload_len = pdu->tail - hdr->payload;
|
||||
crc_calculated = payload_crc10(hdr->payload, payload_len);
|
||||
crc_from_peer = (((uint16_t)hdr->payload_crc_hi) << 8) | hdr->payload_crc_lo;
|
||||
if (crc_from_peer != crc_calculated)
|
||||
ERR("IuUP PDU with invalid payload CRC (peer sent 0x%x, calculated 0x%x)\n",
|
||||
crc_from_peer, crc_calculated);
|
||||
|
||||
switch (hdr->pdu_type) {
|
||||
case OSMO_IUUP_PDU_DATA_WITH_CRC:
|
||||
if (is_data)
|
||||
*is_data = (void*)hdr;
|
||||
return 0;
|
||||
case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
|
||||
if (is_ctrl)
|
||||
*is_ctrl = hdr;
|
||||
return 0;
|
||||
default:
|
||||
ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
|
||||
}
|
||||
#undef ERR
|
||||
}
|
||||
|
||||
/* Return true if this RTP packet contains an IuUP Initialization header (detect IuUP peer). */
|
||||
bool osmo_iuup_is_init(struct msgb *pdu)
|
||||
{
|
||||
struct osmo_iuup_hdr_ctrl *is_ctrl;
|
||||
osmo_iuup_classify(false, NULL, pdu, &is_ctrl, NULL);
|
||||
return is_ctrl
|
||||
&& is_ctrl->procedure == OSMO_IUUP_PROC_INITIALIZATION
|
||||
&& is_ctrl->ack_nack == OSMO_IUUP_ACKNACK_PROCEDURE;
|
||||
}
|
||||
|
||||
/* Append an IuUP Initialization ACK message */
|
||||
void osmo_iuup_make_init_ack(struct msgb *ack)
|
||||
{
|
||||
/* Send Initialization Ack PDU back to the sender */
|
||||
struct osmo_iuup_hdr_ctrl *hdr;
|
||||
OSMO_ASSERT(ack);
|
||||
|
||||
hdr = (void*)msgb_put(ack, sizeof(*hdr));
|
||||
|
||||
*hdr = (struct osmo_iuup_hdr_ctrl){
|
||||
.pdu_type = OSMO_IUUP_PDU_CONTROL_PROCEDURE,
|
||||
.ack_nack = OSMO_IUUP_ACKNACK_ACK,
|
||||
.procedure = OSMO_IUUP_PROC_INITIALIZATION,
|
||||
};
|
||||
|
||||
osmo_iuup_set_checksums((uint8_t*)hdr, sizeof(*hdr));
|
||||
}
|
||||
|
||||
const struct value_string osmo_iuup_error_cause_names[] = {
|
||||
{ 0, "CRC error of frame header" },
|
||||
{ 1, "CRC error of frame payload" },
|
||||
{ 2, "Unexpected frame number" },
|
||||
{ 3, "Frame loss" },
|
||||
{ 4, "PDU type unknown" },
|
||||
{ 5, "Unknown procedure" },
|
||||
{ 6, "Unknown reserved value" },
|
||||
{ 7, "Unknown field" },
|
||||
{ 8, "Frame too short" },
|
||||
{ 9, "Missing fields" },
|
||||
{ 16, "Unexpected PDU type" },
|
||||
{ 17, "spare" },
|
||||
{ 18, "Unexpected procedure" },
|
||||
{ 19, "Unexpected RFCI" },
|
||||
{ 20, "Unexpected value" },
|
||||
{ 42, "Initialisation failure" },
|
||||
{ 43, "Initialisation failure (network error, timer expiry)" },
|
||||
{ 44, "Initialisation failure (Iu UP function error, repeated NACK)" },
|
||||
{ 45, "Rate control failure" },
|
||||
{ 46, "Error event failure" },
|
||||
{ 47, "Time Alignment not supported" },
|
||||
{ 48, "Requested Time Alignment not possible" },
|
||||
{ 49, "Iu UP Mode version not supported" },
|
||||
{}
|
||||
};
|
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;
|
||||
}
|
@@ -24,40 +24,79 @@
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_ep.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/core/rate_ctr.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/* Reset codec state and free memory */
|
||||
static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec)
|
||||
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
|
||||
* be unique only within the scope of the endpoint. (Caller must provide
|
||||
* memory for id) */
|
||||
static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
|
||||
{
|
||||
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;
|
||||
#define MGCP_CONN_ID_GEN_LEN 8
|
||||
int i;
|
||||
int k;
|
||||
int rc;
|
||||
uint8_t id_bin[MGCP_CONN_ID_GEN_LEN / 2];
|
||||
char *id_hex;
|
||||
|
||||
/* see also mgcp_sdp.c, mgcp_set_audio_info() */
|
||||
talloc_free(codec->subtype_name);
|
||||
talloc_free(codec->audio_name);
|
||||
/* Generate a connection id that is unique for the current endpoint.
|
||||
* Technically a counter would be sufficient, but in order to
|
||||
* be able to find a specific connection in large logfiles and to
|
||||
* prevent unintentional connections we assign the connection
|
||||
* identifiers randomly from a reasonable large number space */
|
||||
for (i = 0; i < 32; i++) {
|
||||
rc = osmo_get_rand_id(id_bin, sizeof(id_bin));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
id_hex = osmo_hexdump_nospc(id_bin, sizeof(id_bin));
|
||||
for (k = 0; k < strlen(id_hex); k++)
|
||||
id_hex[k] = toupper(id_hex[k]);
|
||||
|
||||
/* ensure that the generated conn_id is unique
|
||||
* for this endpoint */
|
||||
if (!mgcp_conn_get_rtp(endp, id_hex)) {
|
||||
osmo_strlcpy(id, id_hex, MGCP_CONN_ID_MAXLEN);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x, unable to generate a unique connectionIdentifier\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Reset states, free memory, set defaults and reset codec state */
|
||||
static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn)
|
||||
/* Initialize rtp connection struct with default values */
|
||||
static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
|
||||
{
|
||||
struct mgcp_rtp_end *end = &conn->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->type = MGCP_RTP_DEFAULT;
|
||||
conn->osmux.allocated_cid = -1;
|
||||
conn_rtp->type = MGCP_RTP_DEFAULT;
|
||||
conn_rtp->osmux.allocated_cid = -1;
|
||||
|
||||
/* backpointer to the generic part of the connection */
|
||||
conn->u.rtp.conn = conn;
|
||||
|
||||
end->rtp.fd = -1;
|
||||
end->rtcp.fd = -1;
|
||||
end->local_port = 0;
|
||||
end->packets_rx = 0;
|
||||
end->octets_rx = 0;
|
||||
end->packets_tx = 0;
|
||||
end->octets_tx = 0;
|
||||
end->dropped_packets = 0;
|
||||
end->rtp_port = end->rtcp_port = 0;
|
||||
talloc_free(end->fmtp_extra);
|
||||
end->fmtp_extra = NULL;
|
||||
@@ -66,9 +105,24 @@ static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn)
|
||||
end->frames_per_packet = 0; /* unknown */
|
||||
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
|
||||
end->output_enabled = 0;
|
||||
end->maximum_packet_time = -1;
|
||||
|
||||
mgcp_rtp_codec_reset(&end->codec);
|
||||
mgcp_rtp_codec_reset(&end->alt_codec);
|
||||
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index);
|
||||
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 */
|
||||
static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
osmux_disable_conn(conn_rtp);
|
||||
osmux_release_cid(conn_rtp);
|
||||
mgcp_free_rtp_port(&conn_rtp->end);
|
||||
rate_ctr_group_free(conn_rtp->rate_ctr_group);
|
||||
}
|
||||
|
||||
/*! allocate a new connection list entry.
|
||||
@@ -78,22 +132,15 @@ static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn)
|
||||
* \param[in] type connection type (e.g. MGCP_CONN_TYPE_RTP)
|
||||
* \returns pointer to allocated connection, NULL on error */
|
||||
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
uint32_t id, enum mgcp_conn_type type,
|
||||
char *name)
|
||||
enum mgcp_conn_type type, char *name)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
|
||||
OSMO_ASSERT(strlen(name) < sizeof(conn->name));
|
||||
int rc;
|
||||
|
||||
/* Do not allow more then two connections */
|
||||
if (llist_count(&endp->conns) >= endp->type->max_conns)
|
||||
return NULL;
|
||||
|
||||
/* Prevent duplicate connection IDs */
|
||||
if (mgcp_conn_get(endp, id))
|
||||
return NULL;
|
||||
|
||||
/* Create new connection and add it to the list */
|
||||
conn = talloc_zero(ctx, struct mgcp_conn);
|
||||
if (!conn)
|
||||
@@ -102,13 +149,16 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
conn->type = type;
|
||||
conn->mode = MGCP_CONN_NONE;
|
||||
conn->mode_orig = MGCP_CONN_NONE;
|
||||
conn->id = id;
|
||||
conn->u.rtp.conn = conn;
|
||||
strcpy(conn->name, name);
|
||||
osmo_strlcpy(conn->name, name, sizeof(conn->name));
|
||||
rc = mgcp_alloc_id(endp, conn->id);
|
||||
if (rc < 0) {
|
||||
talloc_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
mgcp_rtp_conn_reset(&conn->u.rtp);
|
||||
mgcp_rtp_conn_init(&conn->u.rtp, conn);
|
||||
break;
|
||||
default:
|
||||
/* NOTE: This should never be called with an
|
||||
@@ -126,15 +176,29 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection
|
||||
* \returns pointer to allocated connection, NULL if not found */
|
||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, uint32_t id)
|
||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id)
|
||||
{
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
|
||||
|
||||
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) {
|
||||
if (conn->id == id)
|
||||
/* Ignore leading zeros in haystack */
|
||||
for (conn_id=conn->id; *conn_id == '0'; conn_id++);
|
||||
|
||||
if (strcmp(conn_id, id_upper) == 0)
|
||||
return conn;
|
||||
}
|
||||
|
||||
@@ -145,11 +209,9 @@ struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, uint32_t id)
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection
|
||||
* \returns pointer to allocated connection, NULL if not found */
|
||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp, uint32_t id)
|
||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
|
||||
const char *id)
|
||||
{
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
|
||||
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
conn = mgcp_conn_get(endp, id);
|
||||
@@ -162,25 +224,48 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp, uint32_t id)
|
||||
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.
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection */
|
||||
void mgcp_conn_free(struct mgcp_endpoint *endp, uint32_t id)
|
||||
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
|
||||
{
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
|
||||
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
conn = mgcp_conn_get(endp, id);
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
/* Run endpoint cleanup action. By this we inform the endpoint about
|
||||
* the removal of the connection and allow it to clean up its inner
|
||||
* state accordingly */
|
||||
if (endp->type->cleanup_cb)
|
||||
endp->type->cleanup_cb(endp, conn);
|
||||
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
osmux_disable_conn(&conn->u.rtp);
|
||||
osmux_release_cid(&conn->u.rtp);
|
||||
mgcp_free_rtp_port(&conn->u.rtp.end);
|
||||
aggregate_rtp_conn_stats(endp->tcfg, &conn->u.rtp);
|
||||
mgcp_rtp_conn_cleanup(&conn->u.rtp);
|
||||
break;
|
||||
default:
|
||||
/* NOTE: This should never be called with an
|
||||
@@ -197,9 +282,6 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, uint32_t id)
|
||||
* \param[in] endp associated endpoint */
|
||||
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
|
||||
{
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
|
||||
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
if (llist_empty(&endp->conns))
|
||||
@@ -216,9 +298,6 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
|
||||
* \param[in] endp associated endpoint */
|
||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
|
||||
{
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
|
||||
|
||||
struct mgcp_conn *conn;
|
||||
struct mgcp_conn *conn_tmp;
|
||||
|
||||
@@ -230,12 +309,12 @@ void mgcp_conn_free_all(struct mgcp_endpoint *endp)
|
||||
return;
|
||||
}
|
||||
|
||||
/*! dump basic connection information to human readble string.
|
||||
/*! dump basic connection information to human readable string.
|
||||
* \param[in] conn to dump
|
||||
* \returns human readble string */
|
||||
* \returns human readable string */
|
||||
char *mgcp_conn_dump(struct mgcp_conn *conn)
|
||||
{
|
||||
static char str[256];
|
||||
static char str[sizeof(conn->name)+sizeof(conn->id)+256];
|
||||
|
||||
if (!conn) {
|
||||
snprintf(str, sizeof(str), "(null connection)");
|
||||
@@ -245,7 +324,7 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
/* Dump RTP connection */
|
||||
snprintf(str, sizeof(str), "(%s/rtp, id:%u, ip:%s, "
|
||||
snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
|
||||
"rtp:%u rtcp:%u)",
|
||||
conn->name,
|
||||
conn->id,
|
||||
|
@@ -21,12 +21,36 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp/mgcp_ep.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
|
||||
/* Endpoint typeset definition */
|
||||
const struct mgcp_endpoint_typeset ep_typeset = {
|
||||
/* Specify endpoint properties for RTP endpoint */
|
||||
.rtp.max_conns = 2,
|
||||
.rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb
|
||||
.rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
|
||||
.rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb
|
||||
};
|
||||
|
||||
/*! release endpoint, all open connections are closed.
|
||||
* \param[in] endp endpoint to release */
|
||||
void mgcp_endp_release(struct mgcp_endpoint *endp)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint:0x%x\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
|
||||
/* Normally this function should only be called when
|
||||
* all connections have been removed already. In case
|
||||
* that there are still connections open (e.g. when
|
||||
* RSIP is executed), free them all at once. */
|
||||
mgcp_conn_free_all(endp);
|
||||
|
||||
/* Reset endpoint parameters and states */
|
||||
talloc_free(endp->callid);
|
||||
endp->callid = NULL;
|
||||
talloc_free(endp->local_options.string);
|
||||
endp->local_options.string = NULL;
|
||||
talloc_free(endp->local_options.codec);
|
||||
endp->local_options.codec = NULL;
|
||||
endp->wildcarded_req = false;
|
||||
}
|
@@ -28,6 +28,7 @@
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
|
||||
/*! Display an mgcp message on the log output.
|
||||
* \param[in] message mgcp message string
|
||||
@@ -82,7 +83,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
|
||||
if (!mode) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:%x missing connection mode\n",
|
||||
"endpoint:0x%x missing connection mode\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
}
|
||||
@@ -101,7 +102,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
conn->mode = MGCP_CONN_LOOPBACK;
|
||||
else {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:%x unknown connection mode: '%s'\n",
|
||||
"endpoint:0x%x unknown connection mode: '%s'\n",
|
||||
ENDPOINT_NUMBER(endp), mode);
|
||||
ret = -1;
|
||||
}
|
||||
@@ -113,16 +114,16 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint:%x conn:%s\n",
|
||||
"endpoint:0x%x conn:%s\n",
|
||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn));
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint:%x connection mode '%s' %d\n",
|
||||
"endpoint:0x%x connection mode '%s' %d\n",
|
||||
ENDPOINT_NUMBER(endp), mode, conn->mode);
|
||||
|
||||
/* Special handling für RTP connections */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:%x output_enabled %d\n",
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x output_enabled %d\n",
|
||||
ENDPOINT_NUMBER(endp), conn->u.rtp.end.output_enabled);
|
||||
}
|
||||
|
||||
@@ -142,6 +143,7 @@ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
|
||||
char *rest = NULL;
|
||||
struct mgcp_trunk_config *tcfg;
|
||||
int trunk, endp;
|
||||
struct mgcp_endpoint *endp_ptr;
|
||||
|
||||
trunk = strtoul(mgcp + 6, &rest, 10);
|
||||
if (rest == NULL || rest[0] != '/' || trunk < 1) {
|
||||
@@ -178,25 +180,118 @@ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &tcfg->endpoints[endp];
|
||||
endp_ptr = &tcfg->endpoints[endp];
|
||||
endp_ptr->wildcarded_req = false;
|
||||
return endp_ptr;
|
||||
}
|
||||
|
||||
/* Find an endpoint that is not in use. Do this by going through the endpoint
|
||||
* array, check the callid. A callid nullpointer indicates that the endpoint
|
||||
* is free */
|
||||
static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
|
||||
unsigned int number_endpoints)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < number_endpoints; i++) {
|
||||
if (endpoints[i].callid == NULL) {
|
||||
endp = &endpoints[i];
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint:0x%x found free endpoint\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
endp->wildcarded_req = true;
|
||||
return endp;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if the domain name, which is supplied with the endpoint name
|
||||
* matches the configuration. */
|
||||
static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
|
||||
{
|
||||
char *domain_to_check;
|
||||
|
||||
domain_to_check = strstr(mgcp, "@");
|
||||
if (!domain_to_check)
|
||||
return -EINVAL;
|
||||
|
||||
/* Accept any domain if configured as "*" */
|
||||
if (!strcmp(cfg->domain, "*"))
|
||||
return 0;
|
||||
|
||||
if (strcmp(domain_to_check+1, cfg->domain) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s', expecting '%s'\n", mgcp, cfg->domain);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Search the endpoint pool for the endpoint that had been selected via the
|
||||
* MGCP message (helper function for mgcp_analyze_header()) */
|
||||
static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
|
||||
const char *mgcp)
|
||||
const char *mgcp,
|
||||
int *cause)
|
||||
{
|
||||
char *endptr = NULL;
|
||||
unsigned int gw = INT_MAX;
|
||||
const char *endpoint_number_str;
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
if (strncmp(mgcp, "ds/e1", 5) == 0)
|
||||
return find_e1_endpoint(cfg, mgcp);
|
||||
*cause = 0;
|
||||
|
||||
/* Check if the domainname in the request is correct */
|
||||
if (check_domain_name(cfg, mgcp)) {
|
||||
*cause = -500;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if the E1 trunk is requested */
|
||||
if (strncmp(mgcp, "ds/e1", 5) == 0) {
|
||||
endp = find_e1_endpoint(cfg, mgcp);
|
||||
if (!endp)
|
||||
*cause = -500;
|
||||
return endp;
|
||||
}
|
||||
|
||||
/* Check if the virtual trunk is addressed (new, correct way with prefix) */
|
||||
if (strncmp
|
||||
(mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
|
||||
strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) {
|
||||
endpoint_number_str =
|
||||
mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
|
||||
if (endpoint_number_str[0] == '*') {
|
||||
endp = find_free_endpoint(cfg->trunk.endpoints,
|
||||
cfg->trunk.number_endpoints);
|
||||
if (!endp)
|
||||
*cause = -403;
|
||||
return endp;
|
||||
}
|
||||
gw = strtoul(endpoint_number_str, &endptr, 16);
|
||||
if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
|
||||
endp = &cfg->trunk.endpoints[gw];
|
||||
endp->wildcarded_req = false;
|
||||
return endp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Deprecated method without prefix */
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n",
|
||||
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp);
|
||||
gw = strtoul(mgcp, &endptr, 16);
|
||||
if (gw > 0 && gw < cfg->trunk.number_endpoints && endptr[0] == '@')
|
||||
return &cfg->trunk.endpoints[gw];
|
||||
if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
|
||||
endp = &cfg->trunk.endpoints[gw];
|
||||
endp->wildcarded_req = false;
|
||||
return endp;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
|
||||
*cause = -500;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -209,6 +304,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
{
|
||||
int i = 0;
|
||||
char *elem, *save = NULL;
|
||||
int cause;
|
||||
|
||||
/*! This function will parse the header part of the received
|
||||
* MGCP message. The parsing results are stored in pdata.
|
||||
@@ -226,25 +322,26 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
pdata->trans = elem;
|
||||
break;
|
||||
case 1:
|
||||
pdata->endp = find_endpoint(pdata->cfg, elem);
|
||||
pdata->endp = find_endpoint(pdata->cfg, elem, &cause);
|
||||
if (!pdata->endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Unable to find Endpoint `%s'\n", elem);
|
||||
return -1;
|
||||
OSMO_ASSERT(cause < 0);
|
||||
return cause;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (strcmp("MGCP", elem)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MGCP header parsing error\n");
|
||||
return -1;
|
||||
return -510;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (strcmp("1.0", elem)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
|
||||
"not supported\n", elem);
|
||||
return -1;
|
||||
return -528;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -255,7 +352,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
|
||||
pdata->trans = "000000";
|
||||
pdata->endp = NULL;
|
||||
return -1;
|
||||
return -510;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -311,6 +408,11 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
|
||||
|
||||
if (!endp)
|
||||
return -1;
|
||||
|
||||
/* Accept any CallID for "X-Osmo-IGN: C" */
|
||||
if (endp->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID)
|
||||
return 0;
|
||||
|
||||
if (!callid)
|
||||
return -1;
|
||||
if (!endp->callid)
|
||||
@@ -318,7 +420,7 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
|
||||
|
||||
if (strcmp(endp->callid, callid) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:%x CallIDs does not match '%s' != '%s'\n",
|
||||
"endpoint:0x%x CallIDs mismatch: '%s' != '%s'\n",
|
||||
ENDPOINT_NUMBER(endp), endp->callid, callid);
|
||||
return -1;
|
||||
}
|
||||
@@ -329,24 +431,48 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
|
||||
/*! Check if the specified connection id seems plausible.
|
||||
* \param[in] endp pointer to endpoint
|
||||
* \param{in] connection id to verify
|
||||
* \returns 1 when connection id seems plausible, 0 on error */
|
||||
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *ci)
|
||||
* \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)
|
||||
{
|
||||
uint32_t id;
|
||||
/* For invalid conn_ids, return 510 "The transaction could not be executed, because some
|
||||
* unspecified protocol error was detected." */
|
||||
|
||||
if (!endp)
|
||||
return -1;
|
||||
/* Check for null identifiers */
|
||||
if (!conn_id) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid ConnectionIdentifier (missing)\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return 510;
|
||||
}
|
||||
|
||||
id = strtoul(ci, NULL, 10);
|
||||
/* Check for empty connection identifiers */
|
||||
if (strlen(conn_id) == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid ConnectionIdentifier (empty)\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return 510;
|
||||
}
|
||||
|
||||
if (mgcp_conn_get(endp, id))
|
||||
/* Check for over long connection identifiers */
|
||||
if (strlen(conn_id) > (MGCP_CONN_ID_MAXLEN-1)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid ConnectionIdentifier (too long: %zu > %d) 0x%s\n",
|
||||
ENDPOINT_NUMBER(endp), strlen(conn_id), MGCP_CONN_ID_MAXLEN-1, conn_id);
|
||||
return 510;
|
||||
}
|
||||
|
||||
/* Check if connection exists */
|
||||
if (mgcp_conn_get(endp, conn_id))
|
||||
return 0;
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:%x No connection found under ConnectionIdentifier %u\n",
|
||||
ENDPOINT_NUMBER(endp), id);
|
||||
"endpoint:0x%x no connection found under ConnectionIdentifier 0x%s\n",
|
||||
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.
|
||||
@@ -386,20 +512,3 @@ char *mgcp_strline(char *str, char **saveptr)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*! Parse CI from a given string.
|
||||
* \param[out] caller provided memory to store the result
|
||||
* \param{in] string containing the connection id
|
||||
* \returns 0 on success, -1 on error */
|
||||
int mgcp_parse_ci(uint32_t *conn_id, const char *ci)
|
||||
{
|
||||
|
||||
OSMO_ASSERT(conn_id);
|
||||
|
||||
if (!ci)
|
||||
return -1;
|
||||
|
||||
*conn_id = strtoul(ci, NULL, 10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,7 @@
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
|
||||
static struct osmo_fd osmux_fd;
|
||||
|
||||
@@ -142,7 +143,7 @@ osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
|
||||
}
|
||||
|
||||
/* Lookup existing handle for a specified address, if the handle can not be
|
||||
* foud a the function will automatically allocate one */
|
||||
* found, the function will automatically allocate one */
|
||||
static struct osmux_in_handle *
|
||||
osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
|
||||
{
|
||||
@@ -207,12 +208,18 @@ endpoint_lookup(struct mgcp_config *cfg, int cid,
|
||||
case MGCP_DEST_NET:
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
this = &conn_net->end.addr;
|
||||
if (conn_net)
|
||||
this = &conn_net->end.addr;
|
||||
else
|
||||
this = NULL;
|
||||
break;
|
||||
case MGCP_DEST_BTS:
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||
this = &conn_bts->end.addr;
|
||||
if (conn_bts)
|
||||
this = &conn_bts->end.addr;
|
||||
else
|
||||
this = NULL;
|
||||
break;
|
||||
default:
|
||||
/* Should not ever happen */
|
||||
@@ -222,7 +229,8 @@ endpoint_lookup(struct mgcp_config *cfg, int cid,
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (conn_net->osmux.cid == cid && this->s_addr == from_addr->s_addr)
|
||||
if (conn_net && this && conn_net->osmux.cid == cid
|
||||
&& this->s_addr == from_addr->s_addr)
|
||||
return endp;
|
||||
}
|
||||
|
||||
@@ -248,13 +256,12 @@ static void scheduled_tx_net_cb(struct msgb *msg, void *data)
|
||||
.sin_port = conn_net->end.rtp_port,
|
||||
};
|
||||
|
||||
conn_bts->end.octets_tx += msg->len;
|
||||
conn_bts->end.packets_tx++;
|
||||
rate_ctr_inc(&conn_bts->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
|
||||
rate_ctr_add(&conn_bts->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);
|
||||
|
||||
/* Send RTP data to NET */
|
||||
/* FIXME: Get rid of conn_bts and conn_net! */
|
||||
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
|
||||
conn_bts, conn_net);
|
||||
mgcp_send(endp, 1, &addr, msg, conn_bts, conn_net);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
@@ -275,13 +282,12 @@ static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
|
||||
.sin_port = conn_bts->end.rtp_port,
|
||||
};
|
||||
|
||||
conn_net->end.octets_tx += msg->len;
|
||||
conn_net->end.packets_tx++;
|
||||
rate_ctr_inc(&conn_net->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
|
||||
rate_ctr_add(&conn_net->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);
|
||||
|
||||
/* Send RTP data to BTS */
|
||||
/* FIXME: Get rid of conn_bts and conn_net! */
|
||||
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
|
||||
conn_net, conn_bts);
|
||||
mgcp_send(endp, 1, &addr, msg, conn_net, conn_bts);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
@@ -308,22 +314,69 @@ static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr)
|
||||
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);
|
||||
|
||||
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;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
struct mgcp_conn_rtp *conn_bts = NULL;
|
||||
|
||||
msg = osmux_recv(ofd, &addr);
|
||||
if (!msg)
|
||||
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 */
|
||||
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
||||
goto out;
|
||||
@@ -337,9 +390,9 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
&addr.sin_addr, MGCP_DEST_NET);
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_net)
|
||||
goto out;
|
||||
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||
if (!conn_bts)
|
||||
continue;
|
||||
|
||||
if (!endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
@@ -347,12 +400,12 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
osmuxh->circuit_id);
|
||||
goto out;
|
||||
}
|
||||
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||
conn_net->osmux.stats.chunks++;
|
||||
if (endp_osmux_state_check(endp, conn_bts, false) == 0) {
|
||||
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;
|
||||
|
||||
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
|
||||
osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
@@ -361,54 +414,29 @@ out:
|
||||
|
||||
/* This is called from the bsc-nat */
|
||||
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;
|
||||
uint8_t osmux_cid;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
|
||||
if (msg->len < 1 + sizeof(osmux_cid)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Discarding truncated Osmux dummy load\n");
|
||||
if (osmux_legacy_dummy_parse_cid(addr, msg, &osmux_cid) < 0)
|
||||
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);
|
||||
endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, endp_type);
|
||||
if (!endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find endpoint for Osmux CID %d\n", osmux_cid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_net)
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn = mgcp_conn_get_rtp(endp, endp_type == MGCP_DEST_BTS ? CONN_ID_NET : CONN_ID_BTS);
|
||||
if (!conn)
|
||||
goto out;
|
||||
|
||||
if (conn_net->osmux.state == OSMUX_STATE_ENABLED)
|
||||
goto out;
|
||||
|
||||
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));
|
||||
endp_osmux_state_check(endp, conn, false);
|
||||
/* Only needed to punch hole in firewall, it can be dropped */
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
@@ -418,7 +446,6 @@ 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;
|
||||
@@ -428,9 +455,15 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
if (!msg)
|
||||
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 */
|
||||
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;
|
||||
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
|
||||
@@ -443,7 +476,7 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_net)
|
||||
goto out;
|
||||
continue;
|
||||
|
||||
if (!endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
@@ -451,12 +484,12 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
osmuxh->circuit_id);
|
||||
goto out;
|
||||
}
|
||||
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||
conn_net->osmux.stats.chunks++;
|
||||
if (endp_osmux_state_check(endp, conn_net, false) == 0) {
|
||||
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;
|
||||
|
||||
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
|
||||
osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
@@ -511,25 +544,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
|
||||
* 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
|
||||
* 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
|
||||
* 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;
|
||||
static const uint32_t rtp_ssrc_winlen = UINT32_MAX / (OSMUX_CID_MAX + 1);
|
||||
uint16_t osmux_dummy = endp->cfg->osmux_dummy;
|
||||
|
||||
/* Check if osmux is enabled for the specified connection */
|
||||
if (conn->osmux.state == OSMUX_STATE_DISABLED) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "OSMUX not enabled for conn:%s\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
if (conn->osmux.state != OSMUX_STATE_ACTIVATING) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "conn:%s didn't negotiate Osmux, state %d\n",
|
||||
mgcp_conn_dump(conn->conn), conn->osmux.state);
|
||||
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);
|
||||
if (!conn->osmux.in) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle for conn:%s\n",
|
||||
@@ -542,12 +571,20 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||
return -1;
|
||||
}
|
||||
|
||||
osmux_xfrm_output_init(&conn->osmux.out,
|
||||
(conn->osmux.cid * rtp_ssrc_winlen) +
|
||||
(random() % rtp_ssrc_winlen));
|
||||
|
||||
switch (endp->cfg->role) {
|
||||
case MGCP_BSC_NAT:
|
||||
conn->type = MGCP_OSMUX_BSC_NAT;
|
||||
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
|
||||
scheduled_tx_net_cb, endp);
|
||||
break;
|
||||
case MGCP_BSC:
|
||||
conn->type = MGCP_OSMUX_BSC;
|
||||
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
|
||||
scheduled_tx_bts_cb, endp);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -566,8 +603,13 @@ void osmux_disable_conn(struct mgcp_conn_rtp *conn)
|
||||
if (conn->osmux.state != OSMUX_STATE_ENABLED)
|
||||
return;
|
||||
|
||||
LOGP(DLMGCP, LOGL_INFO, "Releasing connection %u using Osmux CID %u\n",
|
||||
LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
|
||||
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);
|
||||
conn->osmux.state = OSMUX_STATE_DISABLED;
|
||||
conn->osmux.cid = -1;
|
||||
@@ -622,18 +664,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)
|
||||
return 0;
|
||||
|
||||
if (conn->osmux.state == OSMUX_STATE_ACTIVATING) {
|
||||
if (osmux_enable_conn(endp, conn, &conn->end.addr,
|
||||
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);
|
||||
}
|
||||
if (endp_osmux_state_check(endp, conn, true) < 0)
|
||||
return 0;
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"sending OSMUX dummy load to %s CID %u\n",
|
||||
inet_ntoa(conn->end.addr), conn->osmux.cid);
|
||||
@@ -642,8 +675,8 @@ int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
htons(endp->cfg->osmux_port), buf, sizeof(buf));
|
||||
}
|
||||
|
||||
/*! bsc-nat allocates/releases the OSMUX cids (Circuit IDs). */
|
||||
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8];
|
||||
/* 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 + 7) / 8];
|
||||
|
||||
/*! count the number of taken OSMUX cids.
|
||||
* \returns number of OSMUX cids in use */
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -24,9 +24,14 @@
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.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 {
|
||||
/* the type */
|
||||
int payload_type;
|
||||
@@ -39,89 +44,8 @@ struct sdp_rtp_map {
|
||||
int channels;
|
||||
};
|
||||
|
||||
/*! Set codec configuration depending on payload type and codec name.
|
||||
* \param[in] ctx talloc context.
|
||||
* \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;
|
||||
}
|
||||
|
||||
/* Helper function to extrapolate missing codec parameters in a codec mao from
|
||||
* an already filled in payload_type, called from: mgcp_parse_sdp_data() */
|
||||
static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
||||
{
|
||||
int i;
|
||||
@@ -148,10 +72,16 @@ static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
||||
codecs[i].rate = 8000;
|
||||
codecs[i].channels = 1;
|
||||
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,
|
||||
int payload, const char *audio_name)
|
||||
{
|
||||
@@ -161,8 +91,13 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
||||
char audio_codec[64];
|
||||
int rate = -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)
|
||||
continue;
|
||||
|
||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
||||
audio_codec, &rate, &channels) < 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
|
||||
@@ -181,43 +116,72 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
||||
audio_name);
|
||||
}
|
||||
|
||||
/* Check if the codec matches what is set up in the trunk config */
|
||||
static int is_codec_compatible(const struct mgcp_endpoint *endp,
|
||||
const struct sdp_rtp_map *codec)
|
||||
/* Extract payload types from SDP, also check for duplicates */
|
||||
static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
|
||||
unsigned int codecs_len, char *sdp)
|
||||
{
|
||||
char *codec_str;
|
||||
char audio_codec[64];
|
||||
char *str;
|
||||
char *str_ptr;
|
||||
char *pt_str;
|
||||
unsigned int pt;
|
||||
unsigned int count = 0;
|
||||
unsigned int i;
|
||||
|
||||
if (!codec->codec_name)
|
||||
return 0;
|
||||
str = talloc_zero_size(ctx, strlen(sdp) + 1);
|
||||
str_ptr = str;
|
||||
strcpy(str_ptr, sdp);
|
||||
|
||||
/* GSM, GSM/8000 and GSM/8000/1 should all be compatible...
|
||||
* let's go by name first. */
|
||||
codec_str = endp->tcfg->audio_name;
|
||||
if (sscanf(codec_str, "%63[^/]/%*d/%*d", audio_codec) < 1)
|
||||
return 0;
|
||||
str_ptr = strstr(str_ptr, "RTP/AVP ");
|
||||
if (!str_ptr)
|
||||
goto exit;
|
||||
|
||||
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.
|
||||
* \param[in] endp trunk endpoint.
|
||||
* \param[out] conn associated rtp connection.
|
||||
* \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,
|
||||
* 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,
|
||||
struct mgcp_conn_rtp *conn,
|
||||
struct mgcp_parse_data *p)
|
||||
struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
|
||||
{
|
||||
struct sdp_rtp_map codecs[10];
|
||||
int codecs_used = 0;
|
||||
struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_used = 0;
|
||||
char *line;
|
||||
int maxptime = -1;
|
||||
int i;
|
||||
int codecs_assigned = 0;
|
||||
unsigned int i;
|
||||
void *tmp_ctx = talloc_new(NULL);
|
||||
struct mgcp_rtp_end *rtp;
|
||||
|
||||
@@ -254,30 +218,21 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
rtp->packet_duration_ms = 0;
|
||||
else
|
||||
rtp->packet_duration_ms = ptime;
|
||||
} else if (sscanf(line, "a=maxptime:%d", &ptime2)
|
||||
== 1) {
|
||||
maxptime = ptime2;
|
||||
} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
|
||||
rtp->maximum_packet_time = ptime2;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
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) {
|
||||
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
|
||||
if (rc == 1) {
|
||||
rtp->rtp_port = htons(port);
|
||||
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;
|
||||
case 'c':
|
||||
|
||||
@@ -298,43 +253,81 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
break;
|
||||
}
|
||||
}
|
||||
OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
|
||||
|
||||
/* 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;
|
||||
/* So far we have only set the payload type in the codec struct. Now we
|
||||
* fill up the remaining fields of the codec description with some default
|
||||
* information */
|
||||
codecs_initialize(tmp_ctx, codecs, codecs_used);
|
||||
|
||||
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));
|
||||
/* Store parsed codec information */
|
||||
for (i = 0; i < codecs_used; i++) {
|
||||
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
|
||||
if (rc < 0)
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -351,6 +344,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
const char *audio_name;
|
||||
int payload_type;
|
||||
int rc;
|
||||
int payload_types[1];
|
||||
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
@@ -365,7 +359,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
|
||||
rc = msgb_printf(sdp,
|
||||
"v=0\r\n"
|
||||
"o=- %u 23 IN IP4 %s\r\n"
|
||||
"o=- %s 23 IN IP4 %s\r\n"
|
||||
"s=-\r\n"
|
||||
"c=IN IP4 %s\r\n"
|
||||
"t=0 0\r\n", conn->conn->id, addr, addr);
|
||||
@@ -374,15 +368,14 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
goto buffer_too_small;
|
||||
|
||||
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)
|
||||
goto buffer_too_small;
|
||||
|
||||
if (audio_name && endp->tcfg->audio_send_name) {
|
||||
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n",
|
||||
payload_type, audio_name);
|
||||
|
||||
if (endp->tcfg->audio_send_name) {
|
||||
rc = add_rtpmap(sdp, payload_type, audio_name);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
}
|
||||
|
@@ -23,17 +23,20 @@
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp/mgcp_stat.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
|
||||
void calc_loss(struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *end, uint32_t *expected,
|
||||
int *loss)
|
||||
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)
|
||||
{
|
||||
*expected = state->stats_cycles + state->stats_max_seq;
|
||||
*expected = *expected - state->stats_base_seq + 1;
|
||||
struct mgcp_rtp_state *state = &conn->state;
|
||||
struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
|
||||
|
||||
if (!state->stats_initialized) {
|
||||
*expected = state->stats.cycles + state->stats.max_seq;
|
||||
*expected = *expected - state->stats.base_seq + 1;
|
||||
|
||||
if (!state->stats.initialized) {
|
||||
*expected = 0;
|
||||
*loss = 0;
|
||||
return;
|
||||
@@ -43,8 +46,8 @@ void calc_loss(struct mgcp_rtp_state *state,
|
||||
* Make sure the sign is correct and use the biggest
|
||||
* positive/negative number that fits.
|
||||
*/
|
||||
*loss = *expected - end->packets_rx;
|
||||
if (*expected < end->packets_rx) {
|
||||
*loss = *expected - packets_rx->current;
|
||||
if (*expected < packets_rx->current) {
|
||||
if (*loss > 0)
|
||||
*loss = INT_MIN;
|
||||
} else {
|
||||
@@ -56,9 +59,9 @@ void calc_loss(struct mgcp_rtp_state *state,
|
||||
/* Helper function for mgcp_format_stats_rtp() to calculate jitter */
|
||||
uint32_t calc_jitter(struct mgcp_rtp_state *state)
|
||||
{
|
||||
if (!state->stats_initialized)
|
||||
if (!state->stats.initialized)
|
||||
return 0;
|
||||
return state->stats_jitter >> 4;
|
||||
return state->stats.jitter >> 4;
|
||||
}
|
||||
|
||||
/* Generate statistics for an RTP connection */
|
||||
@@ -69,13 +72,18 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
|
||||
int ploss;
|
||||
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);
|
||||
|
||||
nchars = snprintf(str, str_len,
|
||||
"\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
|
||||
conn->end.packets_tx, conn->end.octets_tx,
|
||||
conn->end.packets_rx, conn->end.octets_rx,
|
||||
"\r\nP: PS=%" PRIu64 ", OS=%" PRIu64 ", PR=%" PRIu64 ", OR=%" PRIu64 ", PL=%d, JI=%u",
|
||||
packets_tx->current, octets_tx->current,
|
||||
packets_rx->current, octets_rx->current,
|
||||
ploss, jitter);
|
||||
if (nchars < 0 || nchars >= str_len)
|
||||
goto truncate;
|
||||
@@ -83,21 +91,23 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
|
||||
str += nchars;
|
||||
str_len -= nchars;
|
||||
|
||||
/* Error Counter */
|
||||
nchars = snprintf(str, str_len,
|
||||
"\r\nX-Osmo-CP: EC TI=%u, TO=%u",
|
||||
conn->state.in_stream.err_ts_counter,
|
||||
conn->state.out_stream.err_ts_counter);
|
||||
if (nchars < 0 || nchars >= str_len)
|
||||
goto truncate;
|
||||
if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) {
|
||||
/* Error Counter */
|
||||
nchars = snprintf(str, str_len,
|
||||
"\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64,
|
||||
conn->state.in_stream.err_ts_ctr->current,
|
||||
conn->state.out_stream.err_ts_ctr->current);
|
||||
if (nchars < 0 || nchars >= str_len)
|
||||
goto truncate;
|
||||
|
||||
str += nchars;
|
||||
str_len -= nchars;
|
||||
str += nchars;
|
||||
str_len -= nchars;
|
||||
|
||||
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
|
||||
snprintf(str, str_len,
|
||||
"\r\nX-Osmux-ST: CR=%u, BR=%u",
|
||||
conn->osmux.stats.chunks, conn->osmux.stats.octets);
|
||||
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
|
||||
snprintf(str, str_len,
|
||||
"\r\nX-Osmux-ST: CR=%u, BR=%u",
|
||||
conn->osmux.stats.chunks, conn->osmux.stats.octets);
|
||||
}
|
||||
}
|
||||
|
||||
truncate:
|
||||
|
@@ -22,13 +22,17 @@
|
||||
*/
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/vty.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
|
||||
#define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
|
||||
@@ -63,15 +67,16 @@ struct cmd_node trunk_node = {
|
||||
static int config_write_mgcp(struct vty *vty)
|
||||
{
|
||||
vty_out(vty, "mgcp%s", VTY_NEWLINE);
|
||||
vty_out(vty, " domain %s%s", g_cfg->domain, VTY_NEWLINE);
|
||||
if (g_cfg->local_ip)
|
||||
vty_out(vty, " local ip %s%s", g_cfg->local_ip, VTY_NEWLINE);
|
||||
vty_out(vty, " bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE);
|
||||
vty_out(vty, " bind port %u%s", g_cfg->source_port, VTY_NEWLINE);
|
||||
vty_out(vty, " rtp net-range %u %u%s",
|
||||
vty_out(vty, " rtp port-range %u %u%s",
|
||||
g_cfg->net_ports.range_start, g_cfg->net_ports.range_end,
|
||||
VTY_NEWLINE);
|
||||
if (g_cfg->net_ports.bind_addr)
|
||||
vty_out(vty, " rtp net-bind-ip %s%s",
|
||||
vty_out(vty, " rtp bind-ip %s%s",
|
||||
g_cfg->net_ports.bind_addr, VTY_NEWLINE);
|
||||
if (g_cfg->net_ports.bind_addr_probe)
|
||||
vty_out(vty, " rtp ip-probing%s", VTY_NEWLINE);
|
||||
@@ -115,7 +120,7 @@ static int config_write_mgcp(struct vty *vty)
|
||||
g_cfg->trunk.audio_send_name ? "" : "no ", VTY_NEWLINE);
|
||||
vty_out(vty, " loop %u%s", ! !g_cfg->trunk.audio_loop, VTY_NEWLINE);
|
||||
vty_out(vty, " number endpoints %u%s",
|
||||
g_cfg->trunk.number_endpoints - 1, VTY_NEWLINE);
|
||||
g_cfg->trunk.vty_number_endpoints - 1, VTY_NEWLINE);
|
||||
vty_out(vty, " %sallow-transcoding%s",
|
||||
g_cfg->trunk.no_audio_transcoding ? "no " : "", VTY_NEWLINE);
|
||||
if (g_cfg->call_agent_addr)
|
||||
@@ -152,22 +157,37 @@ static int config_write_mgcp(struct vty *vty)
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *end)
|
||||
static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
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,
|
||||
" Timestamp Errs: %d->%d%s"
|
||||
" Dropped Packets: %d%s"
|
||||
" Packets Sent: %" PRIu64 " (%" PRIu64 " bytes total)%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"
|
||||
" Frame Duration: %u Frame Denominator: %u%s"
|
||||
" FPP: %d Packet Duration: %u%s"
|
||||
" FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"
|
||||
" Output-Enabled: %d Force-PTIME: %d%s",
|
||||
state->in_stream.err_ts_counter,
|
||||
state->out_stream.err_ts_counter, VTY_NEWLINE,
|
||||
end->dropped_packets, VTY_NEWLINE,
|
||||
tx_packets->current, tx_bytes->current, VTY_NEWLINE,
|
||||
rx_packets->current, rx_bytes->current, VTY_NEWLINE,
|
||||
state->in_stream.err_ts_ctr->current,
|
||||
state->out_stream.err_ts_ctr->current,
|
||||
VTY_NEWLINE,
|
||||
dropped_packets->current, VTY_NEWLINE,
|
||||
codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
|
||||
codec->frame_duration_num, codec->frame_duration_den,
|
||||
VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms,
|
||||
@@ -176,13 +196,40 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state,
|
||||
end->force_output_ptime, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
|
||||
int verbose)
|
||||
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, int epidx,
|
||||
int trunk_nr, enum mgcp_trunk_type trunk_type, int show_stats)
|
||||
{
|
||||
int i;
|
||||
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_nr, cfg->number_endpoints - 1, VTY_NEWLINE);
|
||||
|
||||
@@ -193,30 +240,35 @@ static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
|
||||
|
||||
for (i = 1; i < cfg->number_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);
|
||||
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
vty_out(vty, " CONN: %s%s",
|
||||
mgcp_conn_dump(conn), VTY_NEWLINE);
|
||||
|
||||
if (verbose) {
|
||||
/* 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.state,
|
||||
&conn->u.rtp.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (show_stats && cfg->mgcp_crcx_ctr_group) {
|
||||
vty_out(vty, " %s:%s", cfg->mgcp_crcx_ctr_group->desc->group_description, VTY_NEWLINE);
|
||||
vty_out_rate_ctr_group_fmt(vty, " %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_crcx_ctr_group);
|
||||
}
|
||||
if (show_stats && cfg->mgcp_dlcx_ctr_group) {
|
||||
vty_out(vty, " %s:%s", cfg->mgcp_dlcx_ctr_group->desc->group_description, VTY_NEWLINE);
|
||||
vty_out_rate_ctr_group_fmt(vty, " %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_dlcx_ctr_group);
|
||||
}
|
||||
if (show_stats && cfg->mgcp_mdcx_ctr_group) {
|
||||
vty_out(vty, " %s:%s", cfg->mgcp_mdcx_ctr_group->desc->group_description, VTY_NEWLINE);
|
||||
vty_out_rate_ctr_group_fmt(vty, " %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_mdcx_ctr_group);
|
||||
}
|
||||
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,
|
||||
"show mgcp [stats]",
|
||||
SHOW_STR
|
||||
"Display information about the MGCP Media Gateway\n"
|
||||
SHOW_MGCP_STR
|
||||
"Include Statistics\n")
|
||||
{
|
||||
struct mgcp_trunk_config *trunk;
|
||||
@@ -234,6 +286,72 @@ DEFUN(show_mcgp, show_mgcp_cmd,
|
||||
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")
|
||||
{
|
||||
vty->node = MGCP_NODE;
|
||||
@@ -279,41 +397,70 @@ DEFUN(cfg_mgcp_bind_early,
|
||||
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 UDP_PORT_STR "UDP Port number\n"
|
||||
#define NET_START_STR "First UDP port allocated\n"
|
||||
#define RANGE_START_STR "Start of the range of ports\n"
|
||||
#define RANGE_END_STR "End of the range of ports\n"
|
||||
|
||||
DEFUN(cfg_mgcp_rtp_net_range,
|
||||
cfg_mgcp_rtp_net_range_cmd,
|
||||
"rtp net-range <0-65534> <0-65534>",
|
||||
DEFUN(cfg_mgcp_rtp_port_range,
|
||||
cfg_mgcp_rtp_port_range_cmd,
|
||||
"rtp port-range <1024-65534> <1025-65535>",
|
||||
RTP_STR "Range of ports to use for the NET side\n"
|
||||
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;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgcp_rtp_port_range,
|
||||
cfg_mgcp_rtp_net_range_cmd,
|
||||
"rtp net-range <0-65534> <0-65534>",
|
||||
RTP_STR "Range of ports to use for the NET side\n"
|
||||
RANGE_START_STR RANGE_END_STR)
|
||||
|
||||
DEFUN(cfg_mgcp_rtp_net_bind_ip,
|
||||
cfg_mgcp_rtp_net_bind_ip_cmd,
|
||||
"rtp net-bind-ip A.B.C.D",
|
||||
DEFUN(cfg_mgcp_rtp_bind_ip,
|
||||
cfg_mgcp_rtp_bind_ip_cmd,
|
||||
"rtp bind-ip A.B.C.D",
|
||||
RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n")
|
||||
{
|
||||
osmo_talloc_replace_string(g_cfg, &g_cfg->net_ports.bind_addr, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgcp_rtp_bind_ip,
|
||||
cfg_mgcp_rtp_net_bind_ip_cmd,
|
||||
"rtp net-bind-ip A.B.C.D",
|
||||
RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n")
|
||||
|
||||
DEFUN(cfg_mgcp_rtp_no_net_bind_ip,
|
||||
cfg_mgcp_rtp_no_net_bind_ip_cmd,
|
||||
"no rtp net-bind-ip",
|
||||
DEFUN(cfg_mgcp_rtp_no_bind_ip,
|
||||
cfg_mgcp_rtp_no_bind_ip_cmd,
|
||||
"no rtp bind-ip",
|
||||
NO_STR RTP_STR "Bind endpoints facing the Network\n"
|
||||
"Address to bind to\n")
|
||||
{
|
||||
@@ -321,6 +468,11 @@ DEFUN(cfg_mgcp_rtp_no_net_bind_ip,
|
||||
g_cfg->net_ports.bind_addr = NULL;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgcp_rtp_no_bind_ip,
|
||||
cfg_mgcp_rtp_no_net_bind_ip_cmd,
|
||||
"no rtp net-bind-ip",
|
||||
NO_STR RTP_STR "Bind endpoints facing the Network\n"
|
||||
"Address to bind to\n")
|
||||
|
||||
DEFUN(cfg_mgcp_rtp_net_bind_ip_probing,
|
||||
cfg_mgcp_rtp_net_bind_ip_probing_cmd,
|
||||
@@ -510,7 +662,7 @@ DEFUN(cfg_mgcp_number_endp,
|
||||
"Number options\n" "Endpoints available\n" "Number endpoints\n")
|
||||
{
|
||||
/* + 1 as we start counting at one */
|
||||
g_cfg->trunk.number_endpoints = atoi(argv[0]) + 1;
|
||||
g_cfg->trunk.vty_number_endpoints = atoi(argv[0]) + 1;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -956,7 +1108,7 @@ DEFUN(tap_rtp,
|
||||
struct mgcp_trunk_config *trunk;
|
||||
struct mgcp_endpoint *endp;
|
||||
struct mgcp_conn_rtp *conn;
|
||||
uint32_t conn_id;
|
||||
const char *conn_id = NULL;
|
||||
|
||||
trunk = find_trunk(g_cfg, atoi(argv[0]));
|
||||
if (!trunk) {
|
||||
@@ -980,11 +1132,11 @@ DEFUN(tap_rtp,
|
||||
|
||||
endp = &trunk->endpoints[endp_no];
|
||||
|
||||
conn_id = strtoul(argv[2], NULL, 10);
|
||||
conn_id = argv[2];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
if (!conn) {
|
||||
vty_out(vty, "Conn ID %s/%d is invalid.%s",
|
||||
argv[2], conn_id, VTY_NEWLINE);
|
||||
vty_out(vty, "Conn ID %s is invalid.%s",
|
||||
conn_id, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
@@ -1032,7 +1184,7 @@ DEFUN(free_endp, free_endp_cmd,
|
||||
}
|
||||
|
||||
endp = &trunk->endpoints[endp_no];
|
||||
mgcp_release_endp(endp);
|
||||
mgcp_endp_release(endp);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1102,7 +1254,7 @@ DEFUN(cfg_mgcp_osmux,
|
||||
* allow to turn it on yet. */
|
||||
vty_out(vty, "OSMUX currently unavailable in this software version.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
|
||||
#if 0
|
||||
if (strcmp(argv[0], "on") == 0)
|
||||
g_cfg->osmux = OSMUX_USAGE_ON;
|
||||
else if (strcmp(argv[0], "only") == 0)
|
||||
@@ -1114,6 +1266,7 @@ DEFUN(cfg_mgcp_osmux,
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
#endif
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_osmux_ip,
|
||||
@@ -1164,9 +1317,21 @@ DEFUN(cfg_mgcp_osmux_dummy,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_domain,
|
||||
cfg_mgcp_domain_cmd,
|
||||
"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));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int mgcp_vty_init(void)
|
||||
{
|
||||
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, &tap_rtp_cmd);
|
||||
install_element(ENABLE_NODE, &free_endp_cmd);
|
||||
@@ -1181,8 +1346,11 @@ int mgcp_vty_init(void)
|
||||
install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_rtp_net_range_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_rtp_port_range_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_rtp_net_bind_ip_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_rtp_bind_ip_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_rtp_no_net_bind_ip_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_rtp_no_bind_ip_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_rtp_net_bind_ip_probing_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_rtp_no_net_bind_ip_probing_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd);
|
||||
@@ -1222,6 +1390,7 @@ int mgcp_vty_init(void)
|
||||
install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_domain_cmd);
|
||||
|
||||
install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
|
||||
install_node(&trunk_node, config_write_trunk);
|
||||
@@ -1251,18 +1420,6 @@ int mgcp_vty_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int allocate_trunk(struct mgcp_trunk_config *trunk)
|
||||
{
|
||||
if (mgcp_endpoints_allocate(trunk) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to allocate %d endpoints on trunk %d.\n",
|
||||
trunk->number_endpoints, trunk->trunk_nr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
|
||||
enum mgcp_role role)
|
||||
{
|
||||
@@ -1286,17 +1443,18 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (allocate_trunk(&g_cfg->trunk) != 0) {
|
||||
if (mgcp_endpoints_allocate(&g_cfg->trunk) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to initialize the virtual trunk.\n");
|
||||
"Failed to initialize the virtual trunk (%d endpoints)\n",
|
||||
g_cfg->trunk.number_endpoints);
|
||||
return -1;
|
||||
}
|
||||
|
||||
llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
|
||||
if (allocate_trunk(trunk) != 0) {
|
||||
if (mgcp_endpoints_allocate(trunk) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to initialize E1 trunk %d.\n",
|
||||
trunk->trunk_nr);
|
||||
"Failed to initialize trunk %d (%d endpoints)\n",
|
||||
trunk->trunk_nr, trunk->number_endpoints);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@@ -1,29 +0,0 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_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) \
|
||||
$(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_logging(&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;
|
||||
}
|
@@ -8,6 +8,8 @@ AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -20,7 +22,9 @@ osmo_mgw_SOURCES = \
|
||||
$(NULL)
|
||||
|
||||
osmo_mgw_LDADD = \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(NULL)
|
||||
|
@@ -4,6 +4,7 @@
|
||||
/*
|
||||
* (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2011 by On-Waves
|
||||
* (C) 2017 by sysmocom - s.f.m.c. GmbH, Author: Philipp Maier
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -36,6 +37,7 @@
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/vty.h>
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
@@ -44,12 +46,14 @@
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/socket.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 <osmocom/vty/misc.h>
|
||||
|
||||
#include "../../bscconfig.h"
|
||||
|
||||
@@ -63,16 +67,16 @@ static struct mgcp_trunk_config *reset_trunk;
|
||||
static int reset_endpoints = 0;
|
||||
static int daemonize = 0;
|
||||
|
||||
const char *openbsc_copyright =
|
||||
const char *osmomgw_copyright =
|
||||
"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
|
||||
"Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
|
||||
"Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n"
|
||||
"Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\r\n"
|
||||
"Contributions by Pablo Neira Ayuso, Jacob Erlbeck, Neels Hofmeyr\r\n"
|
||||
"Philipp Maier\r\n\r\n"
|
||||
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
|
||||
"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";
|
||||
static char *config_file = "osmo-mgw.cfg";
|
||||
|
||||
/* used by msgb and mgcp */
|
||||
void *tall_bsc_ctx = NULL;
|
||||
@@ -100,7 +104,7 @@ static void handle_options(int argc, char **argv)
|
||||
{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)
|
||||
break;
|
||||
@@ -187,7 +191,7 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
|
||||
/* Walk over all endpoints and trigger a release, this will release all
|
||||
* endpoints, possible open connections are forcefully dropped */
|
||||
for (i = 1; i < reset_trunk->number_endpoints; ++i)
|
||||
mgcp_release_endp(&reset_trunk->endpoints[i]);
|
||||
mgcp_endp_release(&reset_trunk->endpoints[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -195,33 +199,33 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
|
||||
|
||||
int mgcp_vty_is_config_node(struct vty *vty, int node)
|
||||
{
|
||||
switch (node) {
|
||||
case CONFIG_NODE:
|
||||
return 0;
|
||||
switch (node) {
|
||||
case CONFIG_NODE:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
}
|
||||
vty->index = NULL;
|
||||
}
|
||||
|
||||
return vty->node;
|
||||
return vty->node;
|
||||
}
|
||||
|
||||
|
||||
@@ -240,31 +244,40 @@ static const struct log_info_cat log_categories[] = {
|
||||
.color = "\033[1;30m",
|
||||
.enabled = 1,.loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DIUUP] = {
|
||||
.name = "DIUUP",
|
||||
.description = "IuUP within RTP stream handling",
|
||||
.color = "\033[1;31m",
|
||||
.enabled = 1,.loglevel = LOGL_NOTICE,
|
||||
},
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
.cat = log_categories,
|
||||
.num_cat = ARRAY_SIZE(log_categories),
|
||||
.cat = log_categories,
|
||||
.num_cat = ARRAY_SIZE(log_categories),
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int on = 1, rc;
|
||||
unsigned int flags;
|
||||
int rc;
|
||||
|
||||
tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
|
||||
vty_info.tall_ctx = tall_bsc_ctx;
|
||||
|
||||
msgb_talloc_ctx_init(tall_bsc_ctx, 0);
|
||||
|
||||
osmo_init_ignore_signals();
|
||||
osmo_init_logging(&log_info);
|
||||
osmo_init_logging2(tall_bsc_ctx, &log_info);
|
||||
|
||||
cfg = mgcp_config_alloc();
|
||||
if (!cfg)
|
||||
return -1;
|
||||
|
||||
vty_info.copyright = openbsc_copyright;
|
||||
vty_info.copyright = osmomgw_copyright;
|
||||
vty_init(&vty_info);
|
||||
logging_vty_add_cmds(NULL);
|
||||
osmo_talloc_vty_add_cmds();
|
||||
osmo_stats_vty_add_cmds(&log_info);
|
||||
mgcp_vty_init();
|
||||
|
||||
@@ -279,7 +292,7 @@ int main(int argc, char **argv)
|
||||
|
||||
/* 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);
|
||||
vty_get_bind_addr(), OSMO_VTY_PORT_MGW);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
@@ -287,54 +300,29 @@ int main(int argc, char **argv)
|
||||
* mgcp-command "RSIP" (Reset in Progress) is received */
|
||||
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;
|
||||
}
|
||||
/* we need to bind a socket */
|
||||
flags = OSMO_SOCK_F_BIND;
|
||||
if (cfg->call_agent_addr)
|
||||
flags |= OSMO_SOCK_F_CONNECT;
|
||||
|
||||
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");
|
||||
rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||
cfg->source_addr, cfg->source_port,
|
||||
cfg->call_agent_addr, cfg->call_agent_addr ? 2727 : 0, flags);
|
||||
if (rc < 0) {
|
||||
perror("Gateway failed to bind");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfg->gw_fd.bfd.cb = read_call_agent;
|
||||
cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
|
||||
if (!cfg->gw_fd.bfd.data) {
|
||||
fprintf(stderr, "Gateway memory error.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP, listen on %s:%u\n",
|
||||
cfg->source_addr, cfg->source_port);
|
||||
|
||||
/* initialisation */
|
||||
srand(time(NULL));
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
SUBDIRS = \
|
||||
legacy_mgcp \
|
||||
mgcp_client \
|
||||
mgcp \
|
||||
iuup \
|
||||
$(NULL)
|
||||
|
||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||
@@ -26,7 +26,6 @@ EXTRA_DIST = \
|
||||
testsuite.at \
|
||||
$(srcdir)/package.m4 \
|
||||
$(TESTSUITE) \
|
||||
vty_test_runner.py \
|
||||
$(NULL)
|
||||
|
||||
TESTSUITE = $(srcdir)/testsuite
|
||||
@@ -39,7 +38,6 @@ if ENABLE_EXT_TESTS
|
||||
python-tests: $(BUILT_SOURCES)
|
||||
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
$(PYTHON) $(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v
|
||||
else
|
||||
python-tests: $(BUILT_SOURCES)
|
||||
echo "Not running python-based tests (determined at configure-time)"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user