mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 00:02:01 +00:00
Compare commits
285 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 | ||
|
e7d27aeae1 | ||
|
e6f172dd57 | ||
|
b969455941 | ||
|
54dd4b3f72 | ||
|
dbd88e5167 | ||
|
87203f2a37 | ||
|
1cb1e38dbc | ||
|
e726d4fad2 | ||
|
c341388b30 | ||
|
7bf4ce3aed | ||
|
f4c0e37352 | ||
|
06da85ed3a | ||
|
1dc6be6a82 | ||
|
8348abb372 | ||
|
d8d7b4b188 | ||
|
31c4305225 | ||
|
c7a228aceb | ||
|
e472b4e39a | ||
|
8970c497bc | ||
|
1e0b9f872c | ||
|
8863f7ae64 | ||
|
1ae2d5ebf4 | ||
|
5852a78d2e | ||
|
59e2d518fd | ||
|
062b0f0eee | ||
|
71c619ee05 | ||
|
e3b5de2285 | ||
|
f96e46fcce | ||
|
88d4dae645 | ||
|
8042539444 | ||
|
6ba4406c47 | ||
|
1e940d615a | ||
|
fde78ad412 | ||
|
3d9b656a1c | ||
|
2152f8523e | ||
|
c5ecebb711 | ||
|
020e89bce9 | ||
|
6779354245 | ||
|
87bd9be0b0 | ||
|
f83ec56212 | ||
|
1c8d67d7f5 | ||
|
9d4f1d57f3 | ||
|
d95ab1e9ad | ||
|
a1fa2f4bc3 | ||
|
c221b7ad01 |
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,13 +18,15 @@ SUBDIRS = \
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = \
|
||||
libosmo-legacy-mgcp.pc \
|
||||
libosmo-mgcp-client.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
|
||||
|
36
TODO-RELEASE
36
TODO-RELEASE
@@ -1,9 +1,27 @@
|
||||
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
|
||||
# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
|
||||
# In short:
|
||||
# LIBVERSION=c:r:a
|
||||
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
|
||||
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
|
||||
# If any interfaces have been added since the last public release: c:r:a + 1.
|
||||
# If any interfaces have been removed or changed since the last public release: c:r:0.
|
||||
#library what description / commit summary line
|
||||
# When cleaning up this file upon a release:
|
||||
#
|
||||
# - Note that the release version number is entirely unrelated to the API
|
||||
# versions. A release version 5.2.3 may happily have an API version of 42:7:5.
|
||||
#
|
||||
# - Bump API version in src/lib*/Makefile.am files according to chapter
|
||||
# "Library interface versions" of the libtool documentation.
|
||||
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
|
||||
#
|
||||
# - Iff the 'current' API version has changed, rename debian/lib*.install
|
||||
#
|
||||
# API version bumping for the impatient:
|
||||
# LIBVERSION=c:r:a (current:revision_of_current:backwards_compat_age)
|
||||
# 5:2:4 means that
|
||||
# - this implements version 5 of the API;
|
||||
# - this is the 2nd (compatible) revision of API version 5;
|
||||
# - this is backwards compatible to all APIs since 4 versions ago,
|
||||
# i.e. callers that need API versions from 1 to 5 can use this.
|
||||
#
|
||||
# Bumping API versions recipe:
|
||||
# If the library source code has changed at all since the last update, r++;
|
||||
# If any interfaces have been added, removed, or changed since the last update, c++, r=0;
|
||||
# If any interfaces have been added since the last public release, a++;
|
||||
# If any interfaces have been removed or changed since the last public release, a=0.
|
||||
#
|
||||
#library what description / commit summary line
|
||||
libosmo-mgcp-client various Drop legacy API functions and mgcp_client_conf members
|
||||
|
122
configure.ac
122
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.9.5)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.0.1)
|
||||
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,25 +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
|
||||
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/osmo-bsc_mgcp/Makefile
|
||||
src/libosmo-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 !"
|
||||
@@ -14,19 +19,27 @@ deps="$base/deps"
|
||||
inst="$deps/install"
|
||||
export deps inst
|
||||
|
||||
mkdir "$deps" || true
|
||||
rm -rf "$inst"
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
mkdir "$deps" || true
|
||||
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
|
||||
|
||||
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,11 +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
|
209
debian/changelog
vendored
209
debian/changelog
vendored
@@ -1,3 +1,212 @@
|
||||
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 ]
|
||||
* jenkins: use osmo-clean-workspace.sh before and after build
|
||||
* vty: skip installing cmds now always installed by default
|
||||
* mgcp-client vty: use name 'mgw' instead of 'mgcpgw'
|
||||
* mgcp client: vty: tweak doc strings
|
||||
|
||||
[ Philipp Maier ]
|
||||
* sdp: refactoring sdp parser/generator
|
||||
* cosmetic: rename bts_codec to codec_str
|
||||
* cosmetic: fix coding style for mgcp_parse_sdp_data()
|
||||
* cosmetic: fix commenting style
|
||||
* cosmetic: correct whitespaces
|
||||
* client: fix stderror logging in unit-test
|
||||
* client: add unified function to generate MGCP messages
|
||||
* client: add ip address parsing to the client
|
||||
* protocol: allow wildcarded DLCX
|
||||
* mgcp: remove port/timeslot calculator functions from mgcp.h
|
||||
* network: add separate log category
|
||||
* cosmetic: make dummy packet handling more explicit
|
||||
* network: autdetect rtp bind ip-address
|
||||
* network: fix rtp packet length
|
||||
* network: remove unused return code
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* mgcp_client_vty.c: Fix VTY compatibility with 'mgcpgw bts-base'
|
||||
|
||||
-- Harald Welte <laforge@gnumonks.org> Fri, 10 Nov 2017 11:10:23 +0900
|
||||
|
||||
osmo-mgw (1.1.0) unstable; urgency=medium
|
||||
|
||||
* New upstream release
|
||||
|
||||
-- Harald Welte <lafore@gnumonks.org> Sat, 28 Oct 2017 12:48:41 +0200
|
||||
|
||||
osmo-mgw (1.0.2) unstable; urgency=low
|
||||
|
||||
* First release after major rename.
|
||||
|
21
debian/control
vendored
21
debian/control
vendored
@@ -16,25 +16,10 @@ Homepage: https://osmocom.org/projects/osmo-mgw
|
||||
Package: osmo-mgw
|
||||
Architecture: any
|
||||
Multi-Arch: foreign
|
||||
Depends: libosmo-legacy-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-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 Media Gateway server library
|
||||
|
||||
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 Media Gateway server library
|
||||
|
||||
Package: libosmo-mgcp-client1
|
||||
Package: libosmo-mgcp-client3
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
@@ -46,5 +31,5 @@ Package: libosmo-mgcp-client-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-mgcp-client1 (= ${binary:Version}), ${misc:Depends}
|
||||
Depends: libosmo-mgcp-client3 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
|
||||
|
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/*/*legacy-mgcp*.so
|
||||
usr/lib/*/*legacy-mgcp*.a
|
||||
usr/lib/*/pkgconfig/*legacy-mgcp*.pc
|
1
debian/libosmo-legacy-mgcp0.install
vendored
1
debian/libosmo-legacy-mgcp0.install
vendored
@@ -1 +0,0 @@
|
||||
usr/lib/*/*legacy-mgcp*.so.*
|
1
debian/libosmo-mgcp-client1.install
vendored
1
debian/libosmo-mgcp-client1.install
vendored
@@ -1 +0,0 @@
|
||||
usr/lib/*/*mgcp-client*.so.*
|
1
debian/libosmo-mgcp-client3.install
vendored
Normal file
1
debian/libosmo-mgcp-client3.install
vendored
Normal file
@@ -0,0 +1 @@
|
||||
usr/lib/*/libosmo-mgcp-client.so.*
|
5
debian/osmo-mgw.install
vendored
5
debian/osmo-mgw.install
vendored
@@ -1 +1,4 @@
|
||||
usr/bin
|
||||
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
|
||||
|
10
debian/rules
vendored
10
debian/rules
vendored
@@ -14,7 +14,7 @@ CFLAGS += -g
|
||||
|
||||
# main packaging script based on dh7 syntax
|
||||
%:
|
||||
dh $@ --with autoreconf
|
||||
dh $@ --with autoreconf
|
||||
|
||||
# debmake generated override targets
|
||||
# Set options for ./configure
|
||||
@@ -26,8 +26,10 @@ CFLAGS += -g
|
||||
#override_dh_install:
|
||||
# dh_install --list-missing -X.la -X.pyc -X.pyo
|
||||
|
||||
override_dh_autoreconf:
|
||||
echo $(VERSION) > .tarball-version
|
||||
dh_autoreconf
|
||||
override_dh_auto_test:
|
||||
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
|
||||
|
||||
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
|
18
doc/examples/osmo-mgw/osmo-mgw.cfg
Normal file
18
doc/examples/osmo-mgw/osmo-mgw.cfg
Normal file
@@ -0,0 +1,18 @@
|
||||
!
|
||||
! MGCP configuration example
|
||||
!
|
||||
mgcp
|
||||
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,8 +3,11 @@ 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 \
|
||||
osmocom/mgcp/mgcp_internal.h \
|
||||
osmocom/mgcp/osmux.h \
|
||||
$(NULL)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
SUBDIRS = \
|
||||
legacy_mgcp \
|
||||
mgcp_client \
|
||||
mgcp \
|
||||
$(NULL)
|
||||
|
@@ -1,4 +0,0 @@
|
||||
noinst_HEADERS = \
|
||||
mgcp_transcode.h \
|
||||
vty.h \
|
||||
$(NULL)
|
@@ -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
|
10
include/osmocom/mgcp/Makefile.am
Normal file
10
include/osmocom/mgcp/Makefile.am
Normal file
@@ -0,0 +1,10 @@
|
||||
noinst_HEADERS = \
|
||||
vty.h \
|
||||
mgcp_msg.h \
|
||||
mgcp_conn.h \
|
||||
mgcp_stat.h \
|
||||
mgcp_endp.h \
|
||||
mgcp_sdp.h \
|
||||
mgcp_codec.h \
|
||||
debug.h \
|
||||
$(NULL)
|
36
include/osmocom/mgcp/debug.h
Normal file
36
include/osmocom/mgcp/debug.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* (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 <stdio.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#define DEBUG
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
/* Debug Areas of the code */
|
||||
enum {
|
||||
DRTP,
|
||||
DIUUP,
|
||||
Debug_LastEntry,
|
||||
};
|
||||
|
||||
extern const struct log_info log_info;
|
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);
|
@@ -20,46 +20,22 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef OPENBSC_MGCP_H
|
||||
#define OPENBSC_MGCP_H
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_common.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);
|
||||
}
|
||||
|
||||
#define RTP_PORT_DEFAULT_RANGE_START 16002
|
||||
#define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64
|
||||
|
||||
/*
|
||||
* Handling of MGCP Endpoints and the MGCP Config
|
||||
@@ -98,37 +74,98 @@ 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);
|
||||
|
||||
struct mgcp_conn_rtp;
|
||||
|
||||
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 *conn_dst,
|
||||
struct mgcp_conn_rtp *conn_src);
|
||||
|
||||
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
|
||||
const char**fmtp_extra,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/* set to true to enable automatic probing
|
||||
* of the local bind IP-Address, bind_addr
|
||||
* (or its fall back) is used when automatic
|
||||
* probing fails */
|
||||
bool bind_addr_probe;
|
||||
};
|
||||
|
||||
/* There are up to three modes in which the keep-alive dummy packet can be
|
||||
* 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 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;
|
||||
@@ -160,8 +197,22 @@ struct mgcp_trunk_config {
|
||||
/* timer */
|
||||
struct osmo_timer_list keepalive_timer;
|
||||
|
||||
/* When set, incoming RTP packets are not filtered
|
||||
* when ports and ip-address do not match (debug) */
|
||||
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 {
|
||||
@@ -169,35 +220,12 @@ enum mgcp_role {
|
||||
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,
|
||||
};
|
||||
|
||||
extern const struct value_string mgcp_connection_mode_strs[];
|
||||
|
||||
static inline const char *mgcp_cmode_name(enum mgcp_connection_mode mode)
|
||||
{
|
||||
return get_value_string(mgcp_connection_mode_strs, mode);
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -206,12 +234,10 @@ struct mgcp_config {
|
||||
|
||||
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;
|
||||
int force_ptime;
|
||||
|
||||
mgcp_change change_cb;
|
||||
mgcp_policy policy_cb;
|
||||
@@ -226,10 +252,6 @@ struct mgcp_config {
|
||||
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 */
|
||||
@@ -250,6 +272,8 @@ struct mgcp_config {
|
||||
* message.
|
||||
*/
|
||||
uint16_t osmux_dummy;
|
||||
/* domain name of the media gateway */
|
||||
char domain[255+1];
|
||||
};
|
||||
|
||||
/* config management */
|
||||
@@ -258,12 +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_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);
|
||||
|
||||
/*
|
||||
@@ -271,29 +289,10 @@ void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
|
||||
*/
|
||||
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
|
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);
|
100
include/osmocom/mgcp/mgcp_common.h
Normal file
100
include/osmocom/mgcp/mgcp_common.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/* MGCP common implementations.
|
||||
* These are used in libosmo-mgcp as well as libosmo-mgcp-client.
|
||||
* To avoid interdependency, these are implemented in .h file only. */
|
||||
|
||||
/*
|
||||
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* (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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Two copies of this file are kept in osmocom/mgcp/ and osmocom/mgcp_client/.
|
||||
* Since both are by definition identical, use the old header exclusion ifdefs
|
||||
* instead of '#pragma once' to avoid including both of these files.
|
||||
* Though at the time of writing there are no such users, this allows including
|
||||
* both libosmo-mgcp and libosmo-mgcp-client headers in the same file. */
|
||||
#ifndef OSMO_MGCP_COMMON_H
|
||||
#define OSMO_MGCP_COMMON_H
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#define for_each_non_empty_line(line, save) \
|
||||
for (line = strtok_r(NULL, "\r\n", &save); line; \
|
||||
line = strtok_r(NULL, "\r\n", &save))
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
#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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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
|
80
include/osmocom/mgcp/mgcp_conn.h
Normal file
80
include/osmocom/mgcp/mgcp_conn.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/* Message connection list handling */
|
||||
|
||||
/*
|
||||
* (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/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,
|
||||
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,
|
||||
const char *id);
|
||||
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id);
|
||||
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
|
||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
|
||||
char *mgcp_conn_dump(struct mgcp_conn *conn);
|
||||
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);
|
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);
|
||||
|
343
include/osmocom/mgcp/mgcp_internal.h
Normal file
343
include/osmocom/mgcp/mgcp_internal.h
Normal file
@@ -0,0 +1,343 @@
|
||||
/* 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 <inttypes.h>
|
||||
#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
|
||||
|
||||
/* 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,
|
||||
MGCP_TRUNK_E1,
|
||||
};
|
||||
|
||||
struct mgcp_rtp_stream_state {
|
||||
uint32_t ssrc;
|
||||
uint16_t last_seq;
|
||||
uint32_t last_timestamp;
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
/* 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 */
|
||||
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 */
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
|
||||
struct mgcp_rtp_end {
|
||||
/* local IP address of the RTP socket */
|
||||
struct in_addr addr;
|
||||
|
||||
/* in network byte order */
|
||||
int rtp_port, rtcp_port;
|
||||
|
||||
/* currently selected audio codec */
|
||||
struct mgcp_rtp_codec *codec;
|
||||
|
||||
/* array with assigned audio codecs to choose from (SDP) */
|
||||
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
|
||||
|
||||
/* number of assigned audio codecs (SDP) */
|
||||
unsigned int codecs_assigned;
|
||||
|
||||
/* per endpoint data */
|
||||
int frames_per_packet;
|
||||
uint32_t packet_duration_ms;
|
||||
int maximum_packet_time; /* -1: not set */
|
||||
char *fmtp_extra;
|
||||
/* are we transmitting packets (1) or dropping (0) outbound packets */
|
||||
int output_enabled;
|
||||
/* FIXME: This parameter can be set + printed, but is nowhere used! */
|
||||
int force_output_ptime;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
|
||||
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
|
||||
int force_aligned_timing;
|
||||
|
||||
/* 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;
|
||||
};
|
||||
|
||||
struct mgcp_lco {
|
||||
char *string;
|
||||
char *codec;
|
||||
int pkt_period_min; /* time in ms */
|
||||
int pkt_period_max; /* time in ms */
|
||||
};
|
||||
|
||||
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
|
||||
enum mgcp_conn_rtp_type {
|
||||
MGCP_RTP_DEFAULT = 0,
|
||||
MGCP_OSMUX_BSC,
|
||||
MGCP_OSMUX_BSC_NAT,
|
||||
};
|
||||
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
struct mgcp_conn;
|
||||
|
||||
/* MGCP connection (RTP) */
|
||||
struct mgcp_conn_rtp {
|
||||
|
||||
/* Backpointer to conn struct */
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
/* Specific connection type */
|
||||
enum mgcp_conn_rtp_type type;
|
||||
|
||||
/* Port status */
|
||||
struct mgcp_rtp_end end;
|
||||
|
||||
/* Sequence bits */
|
||||
struct mgcp_rtp_state state;
|
||||
|
||||
/* taps for the rtp connection; one per direction */
|
||||
struct mgcp_rtp_tap tap_in;
|
||||
struct mgcp_rtp_tap tap_out;
|
||||
|
||||
/* Osmux states (optional) */
|
||||
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;
|
||||
|
||||
struct rate_ctr_group *rate_ctr_group;
|
||||
|
||||
struct osmo_iuup_cn *iuup;
|
||||
};
|
||||
|
||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
||||
* contains a useful connection description (currently only RTP) */
|
||||
enum mgcp_conn_type {
|
||||
MGCP_CONN_TYPE_RTP,
|
||||
};
|
||||
|
||||
/*! MGCP connection (untyped) */
|
||||
struct mgcp_conn {
|
||||
/*! list head */
|
||||
struct llist_head entry;
|
||||
|
||||
/*! Backpointer to the endpoint where the conn belongs to */
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
/*! type of the connection (union) */
|
||||
enum mgcp_conn_type type;
|
||||
|
||||
/*! mode of the connection */
|
||||
enum mgcp_connection_mode mode;
|
||||
|
||||
/*! copy of the mode to restore the original setting (VTY) */
|
||||
enum mgcp_connection_mode mode_orig;
|
||||
|
||||
/*! connection id to identify the connection */
|
||||
char id[MGCP_CONN_ID_MAXLEN];
|
||||
|
||||
/*! human readable name (vty, logging) */
|
||||
char name[256];
|
||||
|
||||
/*! union with connection description */
|
||||
union {
|
||||
struct mgcp_conn_rtp rtp;
|
||||
} u;
|
||||
|
||||
/*! pointer to optional private data */
|
||||
void *priv;
|
||||
};
|
||||
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
|
||||
struct mgcp_endpoint_type;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Internal structure while parsing a request
|
||||
*/
|
||||
struct mgcp_parse_data {
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_endpoint *endp;
|
||||
char *trans;
|
||||
char *save;
|
||||
};
|
||||
|
||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
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(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);
|
||||
|
||||
/* 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);
|
||||
|
||||
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,
|
||||
struct mgcp_rtp_end *rtp);
|
||||
|
||||
/* payload processing default functions */
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size);
|
||||
|
||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst,
|
||||
struct mgcp_conn_rtp *conn_src);
|
||||
|
||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
int *payload_type,
|
||||
const char**audio_name,
|
||||
const char**fmtp_extra,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
|
||||
/* 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)
|
||||
|
||||
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);
|
56
include/osmocom/mgcp/mgcp_msg.h
Normal file
56
include/osmocom/mgcp/mgcp_msg.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* Message parser/generator utilities */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* (C) 2017 by sysmocom s.f.m.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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct mgcp_conn;
|
||||
struct mgcp_parse_data;
|
||||
struct mgcp_endpoint;
|
||||
|
||||
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble);
|
||||
|
||||
int mgcp_parse_conn_mode(const char *msg, struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn *conn);
|
||||
|
||||
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data);
|
||||
|
||||
int mgcp_parse_osmux_cid(const char *line);
|
||||
|
||||
int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line);
|
||||
|
||||
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid);
|
||||
|
||||
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id);
|
||||
|
||||
char *mgcp_strline(char *str, char **saveptr);
|
||||
|
||||
#define for_each_line(line, save)\
|
||||
for (line = mgcp_strline(NULL, &save); line;\
|
||||
line = mgcp_strline(NULL, &save))
|
||||
|
||||
#define for_each_non_empty_line(line, save)\
|
||||
for (line = strtok_r(NULL, "\r\n", &save); line;\
|
||||
line = strtok_r(NULL, "\r\n", &save))
|
31
include/osmocom/mgcp/mgcp_sdp.h
Normal file
31
include/osmocom/mgcp/mgcp_sdp.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* SDP generation and 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn,
|
||||
struct mgcp_parse_data *p);
|
||||
|
||||
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
|
||||
const char *addr);
|
36
include/osmocom/mgcp/mgcp_stat.h
Normal file
36
include/osmocom/mgcp/mgcp_stat.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* The statistics generator */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* (C) 2017 by sysmocom s.f.m.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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
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_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 *);
|
37
include/osmocom/mgcp/osmux.h
Normal file
37
include/osmocom/mgcp/osmux.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <osmocom/netif/osmux.h>
|
||||
struct mgcp_conn_rtp;
|
||||
|
||||
#define OSMUX_PORT 1984
|
||||
|
||||
enum {
|
||||
OSMUX_ROLE_BSC = 0,
|
||||
OSMUX_ROLE_BSC_NAT,
|
||||
};
|
||||
|
||||
int osmux_init(int role, struct mgcp_config *cfg);
|
||||
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||
struct in_addr *addr, uint16_t port);
|
||||
void osmux_disable_conn(struct mgcp_conn_rtp *conn);
|
||||
void osmux_allocate_cid(struct mgcp_conn_rtp *conn);
|
||||
void osmux_release_cid(struct mgcp_conn_rtp *conn);
|
||||
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
|
||||
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
|
||||
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 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 {
|
||||
OSMUX_USAGE_OFF = 0,
|
||||
OSMUX_USAGE_ON = 1,
|
||||
OSMUX_USAGE_ONLY = 2,
|
||||
};
|
8
include/osmocom/mgcp/vty.h
Normal file
8
include/osmocom/mgcp/vty.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
enum mgcp_vty_node {
|
||||
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
TRUNK_NODE,
|
||||
};
|
@@ -1,3 +1,13 @@
|
||||
BUILT_SOURCES = \
|
||||
mgcp_common.h \
|
||||
$(NULL)
|
||||
|
||||
noinst_HEADERS = \
|
||||
mgcp_client_internal.h \
|
||||
$(NULL)
|
||||
|
||||
mgcp_common.h: $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h
|
||||
echo -e "/*\n\n DO NOT EDIT THIS FILE!\n THIS IS OVERWRITTEN DURING BUILD\n This is an automatic copy of <osmocom/mgcp/mgcp_common.h>\n\n */" > mgcp_common.h
|
||||
cat $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h >> mgcp_common.h
|
||||
|
||||
CLEANFILES = mgcp_common.h
|
||||
|
@@ -1,12 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_common.h>
|
||||
|
||||
/* See also: RFC 3435, chapter 3.5 Transmission over UDP */
|
||||
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
|
||||
#define MGCP_CLIENT_LOCAL_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;
|
||||
@@ -16,23 +22,93 @@ 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 {
|
||||
char *body;
|
||||
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 {
|
||||
MGCP_VERB_CRCX,
|
||||
MGCP_VERB_MDCX,
|
||||
MGCP_VERB_DLCX,
|
||||
MGCP_VERB_AUEP,
|
||||
MGCP_VERB_RSIP,
|
||||
};
|
||||
|
||||
#define MGCP_MSG_PRESENCE_ENDPOINT 0x0001
|
||||
#define MGCP_MSG_PRESENCE_CALL_ID 0x0002
|
||||
#define MGCP_MSG_PRESENCE_CONN_ID 0x0004
|
||||
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
|
||||
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
|
||||
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
|
||||
#define MGCP_MSG_PRESENCE_X_OSMO_IGN 0x8000
|
||||
|
||||
struct mgcp_msg {
|
||||
enum mgcp_verb verb;
|
||||
/* See MGCP_MSG_PRESENCE_* constants */
|
||||
uint32_t presence;
|
||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
unsigned int call_id;
|
||||
char *conn_id;
|
||||
uint16_t audio_port;
|
||||
char *audio_ip;
|
||||
enum mgcp_connection_mode conn_mode;
|
||||
unsigned int ptime;
|
||||
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_len;
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
uint32_t x_osmo_ign;
|
||||
};
|
||||
|
||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
|
||||
@@ -48,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. */
|
||||
@@ -58,16 +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);
|
||||
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);
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
|
||||
unsigned int call_id);
|
||||
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,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/write_queue.h>
|
||||
|
||||
#define MSGB_CB_MGCP_TRANS_ID 0
|
||||
|
||||
struct mgcp_client {
|
||||
|
@@ -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}/
|
@@ -16,13 +16,13 @@
|
||||
|
||||
|
||||
app_configs = {
|
||||
"mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"],
|
||||
"osmo-mgw": ["doc/examples/osmo-mgw/osmo-mgw.cfg"],
|
||||
}
|
||||
|
||||
apps = [(4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp"),
|
||||
apps = [(4243, "src/osmo-mgw/osmo-mgw", "OsmoMGW", "osmo-mgw"),
|
||||
]
|
||||
|
||||
vty_command = ["./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "-c",
|
||||
"doc/examples/osmo-bsc_mgcp/mgcp.cfg"]
|
||||
vty_command = ["./src/osmo-mgw/osmo-mgw", "-c",
|
||||
"doc/examples/osmo-mgw/osmo-mgw.cfg"]
|
||||
|
||||
vty_app = apps[0]
|
||||
|
@@ -21,11 +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) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(LIBBCG729_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
# This is _NOT_ the library release version, it's an API version.
|
||||
# Please read Chapter 6 "Library interface versions" of the libtool
|
||||
# documentation before making any modification
|
||||
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,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;
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir)/include \
|
||||
-I$(top_builddir) \
|
||||
$(NULL)
|
||||
|
||||
@@ -8,21 +9,18 @@ AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
# This is _NOT_ the library release version, it's an API version.
|
||||
# Please read Chapter 6 "Library interface versions" of the libtool
|
||||
# documentation before making any modification
|
||||
MGCP_CLIENT_LIBVERSION=1:0:1
|
||||
# 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=4:0:1
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp-client.la \
|
||||
@@ -31,7 +29,7 @@ lib_LTLIBRARIES = \
|
||||
libosmo_mgcp_client_la_SOURCES = \
|
||||
mgcp_client.c \
|
||||
mgcp_client_vty.c \
|
||||
../libosmo-legacy-mgcp/mgcp_common.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);
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
/* MGCPGW client interface to quagga VTY */
|
||||
/* MGCP client interface to quagga VTY */
|
||||
/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
|
||||
* Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
|
||||
* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
@@ -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,32 +25,37 @@
|
||||
#include <talloc.h>
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/legacy_mgcp/vty.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
|
||||
#define MGCPGW_STR "MGCP gateway configuration for RTP streams\n"
|
||||
#define MGW_STR MGCP_CLIENT_MGW_STR
|
||||
|
||||
void *global_mgcp_client_ctx = NULL;
|
||||
struct mgcp_client_conf *global_mgcp_client_conf = NULL;
|
||||
|
||||
DEFUN(cfg_mgcpgw_local_ip, cfg_mgcpgw_local_ip_cmd,
|
||||
"mgcpgw local-ip A.B.C.D",
|
||||
MGCPGW_STR "local bind to connect to MGCP gateway with\n"
|
||||
DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
|
||||
"mgw local-ip A.B.C.D",
|
||||
MGW_STR "local bind to connect to MGW from\n"
|
||||
"local bind IP address\n")
|
||||
{
|
||||
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,
|
||||
"mgcpgw local-ip A.B.C.D",
|
||||
MGW_STR "local bind to connect to MGCP gateway with\n"
|
||||
"local bind IP address\n")
|
||||
|
||||
DEFUN(cfg_mgcpgw_local_port, cfg_mgcpgw_local_port_cmd,
|
||||
"mgcpgw local-port <0-65535>",
|
||||
MGCPGW_STR "local bind to connect to MGCP gateway with\n"
|
||||
DEFUN(cfg_mgw_local_port, cfg_mgw_local_port_cmd,
|
||||
"mgw local-port <0-65535>",
|
||||
MGW_STR "local port to connect to MGW from\n"
|
||||
"local bind port\n")
|
||||
{
|
||||
if (!global_mgcp_client_conf)
|
||||
@@ -58,61 +63,88 @@ DEFUN(cfg_mgcpgw_local_port, cfg_mgcpgw_local_port_cmd,
|
||||
global_mgcp_client_conf->local_port = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd,
|
||||
"mgcpgw local-port <0-65535>",
|
||||
MGW_STR "local bind to connect to MGCP gateway with\n"
|
||||
"local bind port\n")
|
||||
|
||||
DEFUN(cfg_mgcpgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
|
||||
"mgcpgw remote-ip A.B.C.D",
|
||||
MGCPGW_STR "remote bind to connect to MGCP gateway with\n"
|
||||
"remote bind IP address\n")
|
||||
DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
|
||||
"mgw remote-ip A.B.C.D",
|
||||
MGW_STR "remote IP address to reach the MGW at\n"
|
||||
"remote IP address\n")
|
||||
{
|
||||
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,
|
||||
"mgcpgw remote-ip A.B.C.D",
|
||||
MGW_STR "remote bind to connect to MGCP gateway with\n"
|
||||
"remote bind IP address\n")
|
||||
|
||||
DEFUN(cfg_mgcpgw_remote_port, cfg_mgcpgw_remote_port_cmd,
|
||||
"mgcpgw remote-port <0-65535>",
|
||||
MGCPGW_STR "remote bind to connect to MGCP gateway with\n"
|
||||
"remote bind port\n")
|
||||
DEFUN(cfg_mgw_remote_port, cfg_mgw_remote_port_cmd,
|
||||
"mgw remote-port <0-65535>",
|
||||
MGW_STR "remote port to reach the MGW at\n"
|
||||
"remote port\n")
|
||||
{
|
||||
if (!global_mgcp_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
global_mgcp_client_conf->remote_port = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
|
||||
"mgcpgw remote-port <0-65535>",
|
||||
MGW_STR "remote bind to connect to MGCP gateway with\n"
|
||||
"remote bind port\n")
|
||||
|
||||
DEFUN(cfg_mgcpgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
||||
"mgcpgw endpoint-range <1-65534> <1-65534>",
|
||||
MGCPGW_STR "usable range of endpoint identifiers\n"
|
||||
"set first useable endpoint identifier\n"
|
||||
"set the last useable endpoint identifier\n")
|
||||
DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
|
||||
"mgw endpoint-range <1-65534> <1-65534>",
|
||||
MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
|
||||
"-\n" "-\n")
|
||||
{
|
||||
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,
|
||||
"mgcpgw endpoint-range <1-65534> <1-65534>",
|
||||
MGW_STR "usable range of endpoint identifiers\n"
|
||||
"set first useable endpoint identifier\n"
|
||||
"set the last useable endpoint identifier\n")
|
||||
|
||||
#define BTS_START_STR "First UDP port allocated for the BTS side\n"
|
||||
#define UDP_PORT_STR "UDP Port number\n"
|
||||
DEFUN(cfg_mgcp_rtp_bts_base_port,
|
||||
cfg_mgcp_rtp_bts_base_port_cmd,
|
||||
DEFUN_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
||||
cfg_mgw_rtp_bts_base_port_cmd,
|
||||
"mgw bts-base <0-65534>",
|
||||
MGW_STR
|
||||
"DEPRECATED: there is no explicit BTS side in current osmo-mgw\n" "-\n")
|
||||
{
|
||||
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,
|
||||
cfg_mgcpgw_rtp_bts_base_port_cmd,
|
||||
"mgcpgw bts-base <0-65534>",
|
||||
MGCPGW_STR
|
||||
MGW_STR
|
||||
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")
|
||||
{
|
||||
global_mgcp_client_conf->bts_base = atoi(argv[0]);
|
||||
osmo_strlcpy(global_mgcp_client_conf->endpoint_domain_name, argv[0],
|
||||
sizeof(global_mgcp_client_conf->endpoint_domain_name));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -120,40 +152,28 @@ 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)
|
||||
vty_out(vty, "%smgcpgw local-ip %s%s", indent, addr,
|
||||
vty_out(vty, "%smgw local-ip %s%s", indent, addr,
|
||||
VTY_NEWLINE);
|
||||
port = global_mgcp_client_conf->local_port;
|
||||
if (port >= 0)
|
||||
vty_out(vty, "%smgcpgw local-port %u%s", indent,
|
||||
vty_out(vty, "%smgw local-port %u%s", indent,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
|
||||
addr = global_mgcp_client_conf->remote_addr;
|
||||
if (addr)
|
||||
vty_out(vty, "%smgcpgw remote-ip %s%s", indent, addr,
|
||||
vty_out(vty, "%smgw remote-ip %s%s", indent, addr,
|
||||
VTY_NEWLINE);
|
||||
port = global_mgcp_client_conf->remote_port;
|
||||
if (port >= 0)
|
||||
vty_out(vty, "%smgcpgw remote-port %u%s", 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, "%smgcpgw 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, "%smgcpgw 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;
|
||||
}
|
||||
@@ -163,10 +183,21 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
|
||||
global_mgcp_client_ctx = talloc_ctx;
|
||||
global_mgcp_client_conf = conf;
|
||||
|
||||
install_element(node, &cfg_mgw_local_ip_cmd);
|
||||
install_element(node, &cfg_mgw_local_port_cmd);
|
||||
install_element(node, &cfg_mgw_remote_ip_cmd);
|
||||
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);
|
||||
install_element(node, &cfg_mgcpgw_local_port_cmd);
|
||||
install_element(node, &cfg_mgcpgw_remote_ip_cmd);
|
||||
install_element(node, &cfg_mgcpgw_remote_port_cmd);
|
||||
install_element(node, &cfg_mgcpgw_endpoint_range_cmd);
|
||||
install_element(node, &cfg_mgcp_rtp_bts_base_port_cmd);
|
||||
install_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
|
||||
|
||||
osmo_fsm_vty_add_cmds();
|
||||
}
|
||||
|
45
src/libosmo-mgcp/Makefile.am
Normal file
45
src/libosmo-mgcp/Makefile.am
Normal file
@@ -0,0 +1,45 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
noinst_LIBRARIES = \
|
||||
libosmo-mgcp.a \
|
||||
$(NULL)
|
||||
|
||||
noinst_HEADERS = \
|
||||
g711common.h \
|
||||
$(NULL)
|
||||
|
||||
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_endp.c \
|
||||
iuup_protocol.c \
|
||||
iuup_cn_node.c \
|
||||
$(NULL)
|
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;
|
||||
}
|
366
src/libosmo-mgcp/mgcp_conn.c
Normal file
366
src/libosmo-mgcp/mgcp_conn.c
Normal file
@@ -0,0 +1,366 @@
|
||||
/* Message connection list handling */
|
||||
|
||||
/*
|
||||
* (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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_common.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>
|
||||
|
||||
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)
|
||||
{
|
||||
#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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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_rtp->end;
|
||||
/* FIXME: Each new rate counter group requires an unique index. At the
|
||||
* moment we generate this index using this counter, but perhaps there
|
||||
* is a more concious way to assign the indexes. */
|
||||
static unsigned int rate_ctr_index = 0;
|
||||
|
||||
conn_rtp->type = MGCP_RTP_DEFAULT;
|
||||
conn_rtp->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->rtp_port = end->rtcp_port = 0;
|
||||
talloc_free(end->fmtp_extra);
|
||||
end->fmtp_extra = NULL;
|
||||
|
||||
/* Set default values */
|
||||
end->frames_per_packet = 0; /* unknown */
|
||||
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
|
||||
end->output_enabled = 0;
|
||||
end->maximum_packet_time = -1;
|
||||
|
||||
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index);
|
||||
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.
|
||||
* \param[in] ctx talloc context
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection
|
||||
* \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,
|
||||
enum mgcp_conn_type type, char *name)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
int rc;
|
||||
|
||||
/* Do not allow more then two connections */
|
||||
if (llist_count(&endp->conns) >= endp->type->max_conns)
|
||||
return NULL;
|
||||
|
||||
/* Create new connection and add it to the list */
|
||||
conn = talloc_zero(ctx, struct mgcp_conn);
|
||||
if (!conn)
|
||||
return NULL;
|
||||
conn->endp = endp;
|
||||
conn->type = type;
|
||||
conn->mode = MGCP_CONN_NONE;
|
||||
conn->mode_orig = MGCP_CONN_NONE;
|
||||
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_init(&conn->u.rtp, conn);
|
||||
break;
|
||||
default:
|
||||
/* NOTE: This should never be called with an
|
||||
* invalid type, its up to the programmer
|
||||
* to ensure propery types */
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
llist_add(&conn->entry, &endp->conns);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
/*! find a connection by its ID.
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection
|
||||
* \returns pointer to allocated connection, NULL if not found */
|
||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
const char *id_upper;
|
||||
const char *conn_id;
|
||||
|
||||
if (!id || !*id)
|
||||
return NULL;
|
||||
|
||||
/* Ignore leading zeros in needle */
|
||||
while (*id == '0')
|
||||
id++;
|
||||
|
||||
/* Use uppercase to compare identifiers, to avoid mismatches: RFC3435 2.1.3.2 "Names of
|
||||
* Connections" defines the id as a hex string, so clients may return lower case hex even though
|
||||
* we sent upper case hex in the CRCX response. */
|
||||
id_upper = osmo_str_toupper(id);
|
||||
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
/* Ignore leading zeros in haystack */
|
||||
for (conn_id=conn->id; *conn_id == '0'; conn_id++);
|
||||
|
||||
if (strcmp(conn_id, id_upper) == 0)
|
||||
return conn;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! find an RTP connection by its ID.
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection
|
||||
* \returns pointer to allocated connection, NULL if not found */
|
||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
|
||||
const char *id)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
conn = mgcp_conn_get(endp, id);
|
||||
if (!conn)
|
||||
return NULL;
|
||||
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP)
|
||||
return &conn->u.rtp;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
aggregate_rtp_conn_stats(struct mgcp_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, const char *id)
|
||||
{
|
||||
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:
|
||||
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
|
||||
* invalid type, its up to the programmer
|
||||
* to ensure propery types */
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
llist_del(&conn->entry);
|
||||
talloc_free(conn);
|
||||
}
|
||||
|
||||
/*! free oldest connection in the list.
|
||||
* \param[in] endp associated endpoint */
|
||||
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
if (llist_empty(&endp->conns))
|
||||
return;
|
||||
|
||||
conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
mgcp_conn_free(endp, conn->id);
|
||||
}
|
||||
|
||||
/*! free all connections at once.
|
||||
* \param[in] endp associated endpoint */
|
||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
struct mgcp_conn *conn_tmp;
|
||||
|
||||
/* Drop all items in the list */
|
||||
llist_for_each_entry_safe(conn, conn_tmp, &endp->conns, entry) {
|
||||
mgcp_conn_free(endp, conn->id);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*! dump basic connection information to human readable string.
|
||||
* \param[in] conn to dump
|
||||
* \returns human readable string */
|
||||
char *mgcp_conn_dump(struct mgcp_conn *conn)
|
||||
{
|
||||
static char str[sizeof(conn->name)+sizeof(conn->id)+256];
|
||||
|
||||
if (!conn) {
|
||||
snprintf(str, sizeof(str), "(null connection)");
|
||||
return str;
|
||||
}
|
||||
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
/* Dump RTP connection */
|
||||
snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
|
||||
"rtp:%u rtcp:%u)",
|
||||
conn->name,
|
||||
conn->id,
|
||||
inet_ntoa(conn->u.rtp.end.addr),
|
||||
ntohs(conn->u.rtp.end.rtp_port),
|
||||
ntohs(conn->u.rtp.end.rtcp_port));
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Should not happen, we should be able to dump
|
||||
* every possible connection type. */
|
||||
snprintf(str, sizeof(str), "(unknown connection type)");
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/*! find destination connection on a specific endpoint.
|
||||
* \param[in] conn to search a destination for
|
||||
* \returns destination connection, NULL on failure */
|
||||
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
struct mgcp_conn *partner_conn;
|
||||
endp = conn->endp;
|
||||
|
||||
/*! NOTE: This simply works by grabbing the first connection that is
|
||||
* not the supplied connection, which is suitable for endpoints that
|
||||
* do not serve more than two connections. */
|
||||
|
||||
llist_for_each_entry(partner_conn, &endp->conns, entry) {
|
||||
if (conn != partner_conn) {
|
||||
return partner_conn;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
56
src/libosmo-mgcp/mgcp_endp.c
Normal file
56
src/libosmo-mgcp/mgcp_endp.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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.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;
|
||||
}
|
514
src/libosmo-mgcp/mgcp_msg.c
Normal file
514
src/libosmo-mgcp/mgcp_msg.c
Normal file
@@ -0,0 +1,514 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* Message parser/generator utilities */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* (C) 2017 by sysmocom s.f.m.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 <limits.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#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
|
||||
* \param[in] len message mgcp message string length
|
||||
* \param[in] preamble string to display in logtext in front of each line */
|
||||
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
|
||||
{
|
||||
unsigned char line[80];
|
||||
unsigned char *ptr;
|
||||
unsigned int consumed = 0;
|
||||
unsigned int consumed_line = 0;
|
||||
unsigned int line_count = 0;
|
||||
|
||||
if (!log_check_level(DLMGCP, LOGL_DEBUG))
|
||||
return;
|
||||
|
||||
while (1) {
|
||||
memset(line, 0, sizeof(line));
|
||||
ptr = line;
|
||||
consumed_line = 0;
|
||||
do {
|
||||
if (*message != '\n' && *message != '\r') {
|
||||
*ptr = *message;
|
||||
ptr++;
|
||||
}
|
||||
message++;
|
||||
consumed++;
|
||||
consumed_line++;
|
||||
} while (*message != '\n' && consumed < len
|
||||
&& consumed_line < sizeof(line));
|
||||
|
||||
if (strlen((const char *)line)) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "%s: line #%02u: %s\n",
|
||||
preamble, line_count, line);
|
||||
line_count++;
|
||||
}
|
||||
|
||||
if (consumed >= len)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Parse connection mode.
|
||||
* \param[in] mode as string (recvonly, sendrecv, sendonly or loopback)
|
||||
* \param[in] endp pointer to endpoint (only used for log output)
|
||||
* \param[out] associated connection to be modified accordingly
|
||||
* \returns 0 on success, -1 on error */
|
||||
int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn *conn)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!mode) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x missing connection mode\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
}
|
||||
if (!conn)
|
||||
return -1;
|
||||
if (!endp)
|
||||
return -1;
|
||||
|
||||
if (strcmp(mode, "recvonly") == 0)
|
||||
conn->mode = MGCP_CONN_RECV_ONLY;
|
||||
else if (strcmp(mode, "sendrecv") == 0)
|
||||
conn->mode = MGCP_CONN_RECV_SEND;
|
||||
else if (strcmp(mode, "sendonly") == 0)
|
||||
conn->mode = MGCP_CONN_SEND_ONLY;
|
||||
else if (strcmp(mode, "loopback") == 0)
|
||||
conn->mode = MGCP_CONN_LOOPBACK;
|
||||
else {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x unknown connection mode: '%s'\n",
|
||||
ENDPOINT_NUMBER(endp), mode);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
/* Special handling für RTP connections */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
conn->u.rtp.end.output_enabled =
|
||||
conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint:0x%x conn:%s\n",
|
||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn));
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"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:0x%x output_enabled %d\n",
|
||||
ENDPOINT_NUMBER(endp), conn->u.rtp.end.output_enabled);
|
||||
}
|
||||
|
||||
/* The VTY might change the connection mode at any time, so we have
|
||||
* to hold a copy of the original connection mode */
|
||||
conn->mode_orig = conn->mode;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We have a null terminated string with the endpoint name here. We only
|
||||
* support two kinds. Simple ones as seen on the BSC level and the ones
|
||||
* seen on the trunk side. (helper function for find_endpoint()) */
|
||||
static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
|
||||
const char *mgcp)
|
||||
{
|
||||
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) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
endp = strtoul(rest + 1, &rest, 10);
|
||||
if (rest == NULL || rest[0] != '@') {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* signalling is on timeslot 1 */
|
||||
if (endp == 1)
|
||||
return NULL;
|
||||
|
||||
tcfg = mgcp_trunk_num(cfg, trunk);
|
||||
if (!tcfg) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n",
|
||||
trunk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!tcfg->endpoints) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Endpoints of trunk %d not allocated.\n", trunk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (endp < 1 || endp >= tcfg->number_endpoints) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n",
|
||||
mgcp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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,
|
||||
int *cause)
|
||||
{
|
||||
char *endptr = NULL;
|
||||
unsigned int gw = INT_MAX;
|
||||
const char *endpoint_number_str;
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
*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 < 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;
|
||||
}
|
||||
|
||||
/*! Analyze and parse the the hader of an MGCP messeage string.
|
||||
* \param[out] pdata caller provided memory to store the parsing results
|
||||
* \param[in] data mgcp message string
|
||||
* \returns when the status line was complete and transaction_id and
|
||||
* endp out parameters are set, -1 on error */
|
||||
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
{
|
||||
int i = 0;
|
||||
char *elem, *save = NULL;
|
||||
int cause;
|
||||
|
||||
/*! This function will parse the header part of the received
|
||||
* MGCP message. The parsing results are stored in pdata.
|
||||
* The function will also automatically search the pool with
|
||||
* available endpoints in order to find an endpoint that matches
|
||||
* the endpoint string in in the header */
|
||||
|
||||
OSMO_ASSERT(data);
|
||||
pdata->trans = "000000";
|
||||
|
||||
for (elem = strtok_r(data, " ", &save); elem;
|
||||
elem = strtok_r(NULL, " ", &save)) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
pdata->trans = elem;
|
||||
break;
|
||||
case 1:
|
||||
pdata->endp = find_endpoint(pdata->cfg, elem, &cause);
|
||||
if (!pdata->endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Unable to find Endpoint `%s'\n", elem);
|
||||
OSMO_ASSERT(cause < 0);
|
||||
return cause;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (strcmp("MGCP", elem)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MGCP header parsing error\n");
|
||||
return -510;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (strcmp("1.0", elem)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
|
||||
"not supported\n", elem);
|
||||
return -528;
|
||||
}
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i != 4) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
|
||||
pdata->trans = "000000";
|
||||
pdata->endp = NULL;
|
||||
return -510;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Extract OSMUX CID from an MGCP parameter line (string).
|
||||
* \param[in] line single parameter line from the MGCP message
|
||||
* \returns OSMUX CID, -1 on error */
|
||||
int mgcp_parse_osmux_cid(const char *line)
|
||||
{
|
||||
int osmux_cid;
|
||||
|
||||
if (sscanf(line + 2, "Osmux: %u", &osmux_cid) != 1)
|
||||
return -1;
|
||||
|
||||
if (osmux_cid > OSMUX_CID_MAX) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
|
||||
osmux_cid, OSMUX_CID_MAX);
|
||||
return -1;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
|
||||
|
||||
return osmux_cid;
|
||||
}
|
||||
|
||||
/*! Check MGCP parameter line (string) for plausibility.
|
||||
* \param[in] endp pointer to endpoint (only used for log output)
|
||||
* \param[in] line single parameter line from the MGCP message
|
||||
* \returns 1 when line seems plausible, 0 on error */
|
||||
int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
|
||||
{
|
||||
const size_t line_len = strlen(line);
|
||||
if (line[0] != '\0' && line_len < 2) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Wrong MGCP option format: '%s' on 0x%x\n",
|
||||
line, ENDPOINT_NUMBER(endp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: A couple more checks wouldn't hurt... */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*! Check if the specified callid seems plausible.
|
||||
* \param[in] endp pointer to endpoint
|
||||
* \param{in] callid to verify
|
||||
* \returns 1 when callid seems plausible, 0 on error */
|
||||
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
|
||||
{
|
||||
/*! This function compares the supplied callid with the called that is
|
||||
* stored in the endpoint structure. */
|
||||
|
||||
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)
|
||||
return -1;
|
||||
|
||||
if (strcmp(endp->callid, callid) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x CallIDs mismatch: '%s' != '%s'\n",
|
||||
ENDPOINT_NUMBER(endp), endp->callid, callid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Check if the specified connection id seems plausible.
|
||||
* \param[in] endp pointer to endpoint
|
||||
* \param{in] connection id to verify
|
||||
* \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)
|
||||
{
|
||||
/* For invalid conn_ids, return 510 "The transaction could not be executed, because some
|
||||
* unspecified protocol error was detected." */
|
||||
|
||||
/* Check for null identifiers */
|
||||
if (!conn_id) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid ConnectionIdentifier (missing)\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return 510;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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:0x%x no connection found under ConnectionIdentifier 0x%s\n",
|
||||
ENDPOINT_NUMBER(endp), conn_id);
|
||||
|
||||
/* 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.
|
||||
* \param[in] str MGCP message string, consisting of multiple lines
|
||||
* \param{in] saveptr pointer to next line in str
|
||||
* \returns line, NULL when done */
|
||||
char *mgcp_strline(char *str, char **saveptr)
|
||||
{
|
||||
char *result;
|
||||
|
||||
/*! The function must be called with *str set to the input string
|
||||
* for the first line. After that saveptr will be initalized.
|
||||
* all consecutive lines are extracted by calling the function
|
||||
* with str set to NULL. When done, the function will return NULL
|
||||
* to indicate that all lines have been parsed. */
|
||||
|
||||
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;
|
||||
}
|
1459
src/libosmo-mgcp/mgcp_network.c
Normal file
1459
src/libosmo-mgcp/mgcp_network.c
Normal file
File diff suppressed because it is too large
Load Diff
725
src/libosmo-mgcp/mgcp_osmux.c
Normal file
725
src/libosmo-mgcp/mgcp_osmux.c
Normal file
@@ -0,0 +1,725 @@
|
||||
/*
|
||||
* (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/mgcp/mgcp.h>
|
||||
#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;
|
||||
|
||||
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;
|
||||
|
||||
/* Deliver OSMUX batch to the remote end */
|
||||
static void osmux_deliver_cb(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);
|
||||
}
|
||||
|
||||
/* Lookup existing OSMUX handle for specified destination address. */
|
||||
static struct osmux_handle *
|
||||
osmux_handle_find_get(struct in_addr *addr, int rem_port)
|
||||
{
|
||||
struct osmux_handle *h;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Put down no longer needed OSMUX handle */
|
||||
static void osmux_handle_put(struct osmux_in_handle *in)
|
||||
{
|
||||
struct osmux_handle *h;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* Allocate free OSMUX handle */
|
||||
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;
|
||||
}
|
||||
|
||||
/* sequence number to start OSMUX message from */
|
||||
h->in->osmux_seq = 0;
|
||||
|
||||
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_cb;
|
||||
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;
|
||||
}
|
||||
|
||||
/* Lookup existing handle for a specified address, if the handle can not be
|
||||
* 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/*! send RTP packet through OSMUX connection.
|
||||
* \param[in] buf rtp data
|
||||
* \param[in] buf_len length of rtp data
|
||||
* \param[in] conn associated RTP connection
|
||||
* \returns 0 on success, -1 on ERROR */
|
||||
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
int ret;
|
||||
struct msgb *msg;
|
||||
|
||||
msg = msgb_alloc(4096, "RTP");
|
||||
if (!msg)
|
||||
return -1;
|
||||
|
||||
memcpy(msg->data, buf, buf_len);
|
||||
msgb_put(msg, buf_len);
|
||||
|
||||
while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.cid)) > 0) {
|
||||
/* batch full, build and deliver it */
|
||||
osmux_xfrm_input_deliver(conn->osmux.in);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Lookup the endpoint that corresponds to the specified address (port) */
|
||||
static struct mgcp_endpoint *
|
||||
endpoint_lookup(struct mgcp_config *cfg, int cid,
|
||||
struct in_addr *from_addr, int type)
|
||||
{
|
||||
struct mgcp_endpoint *endp = NULL;
|
||||
int i;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
struct mgcp_conn_rtp *conn_bts = NULL;
|
||||
|
||||
for (i=0; i<cfg->trunk.number_endpoints; i++) {
|
||||
struct in_addr *this;
|
||||
|
||||
endp = &cfg->trunk.endpoints[i];
|
||||
|
||||
#if 0
|
||||
if (!tmp->allocated)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
switch(type) {
|
||||
case MGCP_DEST_NET:
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
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);
|
||||
if (conn_bts)
|
||||
this = &conn_bts->end.addr;
|
||||
else
|
||||
this = NULL;
|
||||
break;
|
||||
default:
|
||||
/* Should not ever happen */
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (conn_net && this && conn_net->osmux.cid == cid
|
||||
&& this->s_addr == from_addr->s_addr)
|
||||
return endp;
|
||||
}
|
||||
|
||||
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 mgcp_conn_rtp *conn_net = NULL;
|
||||
struct mgcp_conn_rtp *conn_bts = NULL;
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_bts || !conn_net)
|
||||
return;
|
||||
|
||||
struct sockaddr_in addr = {
|
||||
.sin_addr = conn_net->end.addr,
|
||||
.sin_port = conn_net->end.rtp_port,
|
||||
};
|
||||
|
||||
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, msg, conn_bts, conn_net);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
|
||||
{
|
||||
struct mgcp_endpoint *endp = data;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
struct mgcp_conn_rtp *conn_bts = NULL;
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_bts || !conn_net)
|
||||
return;
|
||||
|
||||
struct sockaddr_in addr = {
|
||||
.sin_addr = conn_bts->end.addr,
|
||||
.sin_port = conn_bts->end.rtp_port,
|
||||
};
|
||||
|
||||
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, msg, conn_net, conn_bts);
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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 sockaddr_in addr;
|
||||
struct mgcp_config *cfg = ofd->data;
|
||||
uint32_t rem;
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||
if (!conn_bts)
|
||||
continue;
|
||||
|
||||
if (!endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find an endpoint for circuit_id=%d\n",
|
||||
osmuxh->circuit_id);
|
||||
goto out;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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, int endp_type)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
uint8_t osmux_cid;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
|
||||
if (osmux_legacy_dummy_parse_cid(addr, msg, &osmux_cid) < 0)
|
||||
goto out;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
endp_osmux_state_check(endp, conn, false);
|
||||
/* Only needed to punch hole in firewall, it can be dropped */
|
||||
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 sockaddr_in addr;
|
||||
struct mgcp_config *cfg = ofd->data;
|
||||
uint32_t rem;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
|
||||
msg = osmux_recv(ofd, &addr);
|
||||
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, MGCP_DEST_BTS);
|
||||
|
||||
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);
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_net)
|
||||
continue;
|
||||
|
||||
if (!endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find an endpoint for circuit_id=%d\n",
|
||||
osmuxh->circuit_id);
|
||||
goto out;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/*! enable OSXMUX circuit for a specified connection.
|
||||
* \param[in] endp mgcp endpoint (configuration)
|
||||
* \param[in] conn connection to disable
|
||||
* \param[in] addr IP address of remote OSMUX endpoint
|
||||
* \param[in] port portnumber of the remote OSMUX endpoint
|
||||
* \returns 0 on success, -1 on ERROR */
|
||||
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||
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 (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 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 / (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_ACTIVATING) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "conn:%s didn't negotiate Osmux, state %d\n",
|
||||
mgcp_conn_dump(conn->conn), conn->osmux.state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
return -1;
|
||||
}
|
||||
if (!osmux_xfrm_input_open_circuit(conn->osmux.in, conn->osmux.cid, osmux_dummy)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot open osmux circuit %u for conn:%s\n",
|
||||
conn->osmux.cid, mgcp_conn_dump(conn->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;
|
||||
}
|
||||
|
||||
conn->osmux.state = OSMUX_STATE_ENABLED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! disable OSXMUX circuit for a specified connection.
|
||||
* \param[in] conn connection to disable */
|
||||
void osmux_disable_conn(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
if (conn->osmux.state != OSMUX_STATE_ENABLED)
|
||||
return;
|
||||
|
||||
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;
|
||||
osmux_handle_put(conn->osmux.in);
|
||||
}
|
||||
|
||||
/*! relase OSXMUX cid, that had been allocated to this connection.
|
||||
* \param[in] conn connection with OSMUX cid to release */
|
||||
void osmux_release_cid(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
if (conn->osmux.state != OSMUX_STATE_ENABLED)
|
||||
return;
|
||||
|
||||
if (conn->osmux.allocated_cid >= 0)
|
||||
osmux_put_cid(conn->osmux.allocated_cid);
|
||||
conn->osmux.allocated_cid = -1;
|
||||
}
|
||||
|
||||
/*! allocate OSXMUX cid to connection.
|
||||
* \param[in] conn connection for which we allocate the OSMUX cid*/
|
||||
void osmux_allocate_cid(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
osmux_release_cid(conn);
|
||||
conn->osmux.allocated_cid = osmux_get_cid();
|
||||
}
|
||||
|
||||
/*! send RTP dummy packet to OSMUX connection port.
|
||||
* \param[in] endp mcgp endpoint that holds the RTP connection
|
||||
* \param[in] conn associated RTP connection
|
||||
* \returns bytes sent, -1 on error */
|
||||
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
char buf[1 + sizeof(uint8_t)];
|
||||
struct in_addr addr_unset = {};
|
||||
|
||||
/*! The dummy packet will not be sent via the actual OSMUX connection,
|
||||
* instead it is sent out of band to port where the remote OSMUX
|
||||
* multplexer is listening. The goal is to ensure that the connection
|
||||
* is kept open */
|
||||
|
||||
/*! 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. */
|
||||
|
||||
buf[0] = MGCP_DUMMY_LOAD;
|
||||
memcpy(&buf[1], &conn->osmux.cid, sizeof(conn->osmux.cid));
|
||||
|
||||
/* Wait until we have the connection information from MDCX */
|
||||
if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0)
|
||||
return 0;
|
||||
|
||||
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);
|
||||
|
||||
return mgcp_udp_send(osmux_fd.fd, &conn->end.addr,
|
||||
htons(endp->cfg->osmux_port), buf, sizeof(buf));
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
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;
|
||||
}
|
||||
|
||||
/*! take a free OSMUX cid.
|
||||
* \returns OSMUX cid */
|
||||
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;
|
||||
}
|
||||
|
||||
/*! put back a no longer used OSMUX cid.
|
||||
* \param[in] osmux_cid OSMUX cid */
|
||||
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));
|
||||
}
|
1737
src/libosmo-mgcp/mgcp_protocol.c
Normal file
1737
src/libosmo-mgcp/mgcp_protocol.c
Normal file
File diff suppressed because it is too large
Load Diff
402
src/libosmo-mgcp/mgcp_sdp.c
Normal file
402
src/libosmo-mgcp/mgcp_sdp.c
Normal file
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* 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/core/msgb.h>
|
||||
#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;
|
||||
/* 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;
|
||||
};
|
||||
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
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)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < used; ++i) {
|
||||
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",
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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 *str;
|
||||
char *str_ptr;
|
||||
char *pt_str;
|
||||
unsigned int pt;
|
||||
unsigned int count = 0;
|
||||
unsigned int i;
|
||||
|
||||
str = talloc_zero_size(ctx, strlen(sdp) + 1);
|
||||
str_ptr = str;
|
||||
strcpy(str_ptr, sdp);
|
||||
|
||||
str_ptr = strstr(str_ptr, "RTP/AVP ");
|
||||
if (!str_ptr)
|
||||
goto exit;
|
||||
|
||||
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.
|
||||
*
|
||||
* Note: In conn (conn->end) the function returns the packet duration,
|
||||
* rtp port, rtcp port and the codec information.
|
||||
* \returns 0 on success, -1 on failure. */
|
||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
|
||||
{
|
||||
struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_used = 0;
|
||||
char *line;
|
||||
unsigned int i;
|
||||
void *tmp_ctx = talloc_new(NULL);
|
||||
struct mgcp_rtp_end *rtp;
|
||||
|
||||
int payload;
|
||||
int ptime, ptime2 = 0;
|
||||
char audio_name[64];
|
||||
int port, rc;
|
||||
char ipv4[16];
|
||||
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(p);
|
||||
|
||||
rtp = &conn->end;
|
||||
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':
|
||||
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) {
|
||||
rtp->maximum_packet_time = ptime2;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
|
||||
if (rc == 1) {
|
||||
rtp->rtp_port = htons(port);
|
||||
rtp->rtcp_port = htons(port + 1);
|
||||
}
|
||||
|
||||
rc = pt_from_sdp(conn->conn, codecs,
|
||||
ARRAY_SIZE(codecs), line);
|
||||
if (rc > 0)
|
||||
codecs_used = rc;
|
||||
break;
|
||||
case 'c':
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
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.
|
||||
* \param[in] endp trunk endpoint.
|
||||
* \param[in] conn associated rtp connection.
|
||||
* \param[out] sdp msg buffer to append resulting SDP string data.
|
||||
* \param[in] addr IPV4 address string (e.g. 192.168.100.1).
|
||||
* \returns 0 on success, -1 on failure. */
|
||||
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
|
||||
const char *addr)
|
||||
{
|
||||
const char *fmtp_extra;
|
||||
const char *audio_name;
|
||||
int payload_type;
|
||||
int rc;
|
||||
int payload_types[1];
|
||||
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(sdp);
|
||||
OSMO_ASSERT(addr);
|
||||
|
||||
/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
|
||||
endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
|
||||
&payload_type, &audio_name,
|
||||
&fmtp_extra,
|
||||
(struct mgcp_conn_rtp *)conn);
|
||||
|
||||
rc = msgb_printf(sdp,
|
||||
"v=0\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);
|
||||
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
|
||||
if (payload_type >= 0) {
|
||||
|
||||
payload_types[0] = payload_type;
|
||||
rc = add_audio(sdp, payload_types, 1, conn->end.local_port);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
|
||||
if (endp->tcfg->audio_send_name) {
|
||||
rc = add_rtpmap(sdp, payload_type, audio_name);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
}
|
||||
|
||||
if (fmtp_extra) {
|
||||
rc = msgb_printf(sdp, "%s\r\n", fmtp_extra);
|
||||
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
}
|
||||
}
|
||||
if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
|
||||
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
|
||||
conn->end.packet_duration_ms);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
buffer_too_small:
|
||||
LOGP(DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
|
||||
return -1;
|
||||
}
|
138
src/libosmo-mgcp/mgcp_stat.c
Normal file
138
src/libosmo-mgcp/mgcp_stat.c
Normal file
@@ -0,0 +1,138 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* The statistics generator */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* (C) 2017 by sysmocom s.f.m.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 <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_conn_rtp *conn, uint32_t *expected, int *loss)
|
||||
{
|
||||
struct mgcp_rtp_state *state = &conn->state;
|
||||
struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
|
||||
|
||||
*expected = state->stats.cycles + state->stats.max_seq;
|
||||
*expected = *expected - state->stats.base_seq + 1;
|
||||
|
||||
if (!state->stats.initialized) {
|
||||
*expected = 0;
|
||||
*loss = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the sign is correct and use the biggest
|
||||
* positive/negative number that fits.
|
||||
*/
|
||||
*loss = *expected - packets_rx->current;
|
||||
if (*expected < packets_rx->current) {
|
||||
if (*loss > 0)
|
||||
*loss = INT_MIN;
|
||||
} else {
|
||||
if (*loss < 0)
|
||||
*loss = INT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function for mgcp_format_stats_rtp() to calculate jitter */
|
||||
uint32_t calc_jitter(struct mgcp_rtp_state *state)
|
||||
{
|
||||
if (!state->stats.initialized)
|
||||
return 0;
|
||||
return state->stats.jitter >> 4;
|
||||
}
|
||||
|
||||
/* Generate statistics for an RTP connection */
|
||||
static void mgcp_format_stats_rtp(char *str, size_t str_len,
|
||||
struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
uint32_t expected, jitter;
|
||||
int ploss;
|
||||
int nchars;
|
||||
|
||||
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=%" 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;
|
||||
|
||||
str += nchars;
|
||||
str_len -= nchars;
|
||||
|
||||
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;
|
||||
|
||||
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:
|
||||
str[str_len - 1] = '\0';
|
||||
}
|
||||
|
||||
/*! format statistics into an mgcp parameter string.
|
||||
* \param[out] str resulting string
|
||||
* \param[in] str_len length of the string buffer
|
||||
* \param[in] conn connection to evaluate */
|
||||
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn)
|
||||
{
|
||||
memset(str, 0, str_len);
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
/* NOTE: At the moment we only support generating statistics for
|
||||
* RTP connections. However, in the future we may also want to
|
||||
* generate statistics for other connection types as well. Lets
|
||||
* keep this option open: */
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
mgcp_format_stats_rtp(str, str_len, &conn->u.rtp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -8,24 +8,23 @@ AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(LIBBCG729_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
bin_PROGRAMS = \
|
||||
osmo-bsc_mgcp \
|
||||
osmo-mgw \
|
||||
$(NULL)
|
||||
|
||||
osmo_bsc_mgcp_SOURCES = \
|
||||
mgcp_main.c \
|
||||
osmo_mgw_SOURCES = \
|
||||
mgw_main.c \
|
||||
$(NULL)
|
||||
|
||||
osmo_bsc_mgcp_LDADD = \
|
||||
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
|
||||
osmo_mgw_LDADD = \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBBCG729_LIBS) \
|
||||
$(LIBRARY_GSM) \
|
||||
$(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
|
||||
@@ -32,9 +33,11 @@
|
||||
|
||||
#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/mgcp/mgcp.h>
|
||||
#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>
|
||||
@@ -43,38 +46,37 @@
|
||||
#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"
|
||||
|
||||
#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"
|
||||
/* FIXME: 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 =
|
||||
const char *osmomgw_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"
|
||||
"Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
|
||||
"Contributions by Pablo Neira Ayuso, Jacob Erlbeck, Neels Hofmeyr\r\n"
|
||||
"Philipp Maier\r\n\r\n"
|
||||
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
|
||||
"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;
|
||||
@@ -102,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;
|
||||
@@ -132,10 +134,14 @@ static void handle_options(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
/* simply remember this */
|
||||
/* Callback function to be called when the RSIP ("Reset in Progress") mgcp
|
||||
* command is received */
|
||||
static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
|
||||
{
|
||||
/* Set flag so that, when read_call_agent() is called next time
|
||||
* the reset can progress */
|
||||
reset_endpoints = 1;
|
||||
|
||||
reset_trunk = tcfg;
|
||||
|
||||
return 0;
|
||||
@@ -173,15 +179,19 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
|
||||
msgb_free(resp);
|
||||
}
|
||||
|
||||
/* reset endpoints */
|
||||
if (reset_endpoints) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Asked to reset endpoints: %d/%d\n",
|
||||
reset_trunk->trunk_nr, reset_trunk->trunk_type);
|
||||
|
||||
/* reset flag */
|
||||
reset_endpoints = 0;
|
||||
|
||||
/* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */
|
||||
/* 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;
|
||||
@@ -189,38 +199,38 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
static struct vty_app_info vty_info = {
|
||||
.name = "OpenBSC MGCP",
|
||||
.name = "OsmoMGW",
|
||||
.version = PACKAGE_VERSION,
|
||||
.go_parent_cb = mgcp_vty_go_parent,
|
||||
.is_config_node = mgcp_vty_is_config_node,
|
||||
@@ -228,39 +238,46 @@ static struct vty_app_info vty_info = {
|
||||
|
||||
static const struct log_info_cat log_categories[] = {
|
||||
/* DLMGCP is provided by the MGCP library */
|
||||
[DRTP] = {
|
||||
.name = "DRTP",
|
||||
.description = "RTP stream handling",
|
||||
.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;
|
||||
|
||||
#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_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();
|
||||
|
||||
@@ -275,61 +292,37 @@ 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;
|
||||
|
||||
/* set some callbacks */
|
||||
/* Set the reset callback function. This functions is called when the
|
||||
* 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,6 +1,7 @@
|
||||
SUBDIRS = \
|
||||
legacy_mgcp \
|
||||
mgcp_client \
|
||||
mgcp \
|
||||
iuup \
|
||||
$(NULL)
|
||||
|
||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||
@@ -25,7 +26,6 @@ EXTRA_DIST = \
|
||||
testsuite.at \
|
||||
$(srcdir)/package.m4 \
|
||||
$(TESTSUITE) \
|
||||
vty_test_runner.py \
|
||||
$(NULL)
|
||||
|
||||
TESTSUITE = $(srcdir)/testsuite
|
||||
@@ -38,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)"
|
||||
|
@@ -1,7 +1,6 @@
|
||||
enable_nat_test='@osmo_ac_build_nat@'
|
||||
enable_smpp_test='@osmo_ac_build_smpp@'
|
||||
enable_bsc_test='@osmo_ac_build_bsc@'
|
||||
enable_mgcp_transcoding_test='@osmo_ac_mgcp_transcoding@'
|
||||
enable_sgsn_test='@found_libgtp_and_libcares@'
|
||||
enable_oap_test='@found_libgtp_and_libcares@'
|
||||
enable_gtphub_test='@found_libgtp_and_libcares@'
|
||||
|
45
tests/iuup/Makefile.am
Normal file
45
tests/iuup/Makefile.am
Normal file
@@ -0,0 +1,45 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
-ggdb3 \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
iuup_test.ok \
|
||||
iuup_test.err \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
iuup_test \
|
||||
$(NULL)
|
||||
|
||||
iuup_test_SOURCES = \
|
||||
iuup_test.c \
|
||||
$(NULL)
|
||||
|
||||
iuup_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBRARY_DL) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
-lm \
|
||||
$(NULL)
|
||||
|
||||
update_exp:
|
||||
$(builddir)/iuup_test >$(srcdir)/iuup_test.ok 2>$(srcdir)/iuup_test.err
|
156
tests/iuup/iuup_test.c
Normal file
156
tests/iuup/iuup_test.c
Normal file
@@ -0,0 +1,156 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/mgcp/iuup_cn_node.h>
|
||||
#include <osmocom/mgcp/iuup_protocol.h>
|
||||
|
||||
void *ctx = NULL;
|
||||
|
||||
static const char *dump(struct msgb *msg)
|
||||
{
|
||||
return osmo_hexdump_nospc(msg->data, msg->len);
|
||||
}
|
||||
|
||||
struct msgb *msgb_from_hex(const char *label, const char *hex)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_headroom(4096 + OSMO_IUUP_HEADROOM,
|
||||
OSMO_IUUP_HEADROOM, label);
|
||||
unsigned char *rc;
|
||||
msg->l2h = msg->data;
|
||||
rc = msgb_put(msg, osmo_hexparse(hex, msg->data, msgb_tailroom(msg)));
|
||||
OSMO_ASSERT(rc == msg->l2h);
|
||||
return msg;
|
||||
}
|
||||
|
||||
const char *expect_rx_payload = NULL;
|
||||
int rx_payload(struct msgb *msg, void *node_priv)
|
||||
{
|
||||
printf("rx_payload() invoked by iuup_cn!\n");
|
||||
printf(" [IuUP] -RTP->\n");
|
||||
printf("%s\n", dump(msg));
|
||||
printf("node_priv=%p\n", node_priv);
|
||||
if (!expect_rx_payload) {
|
||||
printf("ERROR: did not expect rx_payload()\n");
|
||||
exit(-1);
|
||||
} else if (strcmp(expect_rx_payload, dump(msg))) {
|
||||
printf("ERROR: mismatches expected msg %s\n", expect_rx_payload);
|
||||
exit(-1);
|
||||
} else
|
||||
printf("ok: matches expected msg\n");
|
||||
expect_rx_payload = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *expect_tx_msg = NULL;
|
||||
int tx_msg(struct msgb *msg, void *node_priv)
|
||||
{
|
||||
printf("tx_msg() invoked by iuup_cn!\n");
|
||||
printf(" <-PDU- [IuUP]\n");
|
||||
printf("%s\n", dump(msg));
|
||||
printf("node_priv=%p\n", node_priv);
|
||||
if (!expect_tx_msg) {
|
||||
printf("ERROR: did not expect tx_msg()\n");
|
||||
exit(-1);
|
||||
} else if (strcmp(expect_tx_msg, dump(msg))) {
|
||||
printf("ERROR: mismatches expected msg %s\n", expect_tx_msg);
|
||||
exit(-1);
|
||||
} else
|
||||
printf("ok: matches expected msg\n");
|
||||
expect_tx_msg = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rx_pdu(struct osmo_iuup_cn *cn, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
printf(" -PDU-> [IuUP]\n");
|
||||
printf("%s\n", dump(msg));
|
||||
rc = osmo_iuup_cn_rx_pdu(cn, msg);
|
||||
printf("rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tx_payload(struct osmo_iuup_cn *cn, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
printf(" [IuUP] <-RTP-\n");
|
||||
printf("%s\n", dump(msg));
|
||||
rc = osmo_iuup_cn_tx_payload(cn, msg);
|
||||
printf("rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void test_cn_session()
|
||||
{
|
||||
void *node_priv = (void*)0x2342;
|
||||
|
||||
struct osmo_iuup_cn_cfg cfg = {
|
||||
.node_priv = node_priv,
|
||||
.rx_payload = rx_payload,
|
||||
.tx_msg = tx_msg,
|
||||
};
|
||||
|
||||
struct osmo_iuup_cn *cn = osmo_iuup_cn_init(ctx, &cfg, __func__);
|
||||
OSMO_ASSERT(cn);
|
||||
|
||||
printf("\nSend IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack\n");
|
||||
expect_tx_msg = "8060dc5219495e3f00010111" /* RTP header */
|
||||
"e4002400"; /* IuUP Init Ack */
|
||||
rx_pdu(cn,
|
||||
msgb_from_hex("IuUP-Init",
|
||||
"8060dc5219495e3f00010111" /* <- RTP header */
|
||||
"e000df99" /* <- IuUP header */
|
||||
"160051673c01270000820000001710000100" /* IuUP params */));
|
||||
|
||||
#define RTP_HEADER "8060944c6256042c00010102"
|
||||
#define IUUP_HEADER "0100e2b3"
|
||||
#define RTP_PAYLOAD "6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0"
|
||||
printf("\nReceive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet\n");
|
||||
printf("i.e. should strip away " IUUP_HEADER "\n");
|
||||
expect_rx_payload = RTP_HEADER "f03c" RTP_PAYLOAD;
|
||||
rx_pdu(cn,
|
||||
msgb_from_hex("IuUP-Data",
|
||||
RTP_HEADER IUUP_HEADER RTP_PAYLOAD));
|
||||
|
||||
printf("\nTransmit RTP. Expecting tx_msg() with inserted IuUP header\n");
|
||||
expect_tx_msg = RTP_HEADER "000002b3" RTP_PAYLOAD;
|
||||
tx_payload(cn,
|
||||
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
|
||||
|
||||
printf("\nMore RTP, each time the Frame Nr advances, causing a new header CRC.\n");
|
||||
expect_tx_msg = RTP_HEADER "0100e2b3" RTP_PAYLOAD;
|
||||
tx_payload(cn,
|
||||
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
|
||||
expect_tx_msg = RTP_HEADER "02007eb3" RTP_PAYLOAD;
|
||||
tx_payload(cn,
|
||||
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
|
||||
expect_tx_msg = RTP_HEADER "03009eb3" RTP_PAYLOAD;
|
||||
tx_payload(cn,
|
||||
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
|
||||
|
||||
printf("All done.\n");
|
||||
}
|
||||
|
||||
static const struct log_info_cat log_categories[] = {
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
.cat = log_categories,
|
||||
.num_cat = ARRAY_SIZE(log_categories),
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ctx = talloc_named_const(NULL, 0, __FILE__);
|
||||
void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
|
||||
osmo_init_logging2(ctx, &log_info);
|
||||
|
||||
test_cn_session();
|
||||
|
||||
talloc_free(msgb_ctx);
|
||||
return 0;
|
||||
}
|
0
tests/iuup/iuup_test.err
Normal file
0
tests/iuup/iuup_test.err
Normal file
58
tests/iuup/iuup_test.ok
Normal file
58
tests/iuup/iuup_test.ok
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
Send IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack
|
||||
-PDU-> [IuUP]
|
||||
8060dc5219495e3f00010111e000df99160051673c01270000820000001710000100
|
||||
tx_msg() invoked by iuup_cn!
|
||||
<-PDU- [IuUP]
|
||||
8060dc5219495e3f00010111e4002400
|
||||
node_priv=0x2342
|
||||
ok: matches expected msg
|
||||
rc=0
|
||||
|
||||
Receive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet
|
||||
i.e. should strip away 0100e2b3
|
||||
-PDU-> [IuUP]
|
||||
8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
rx_payload() invoked by iuup_cn!
|
||||
[IuUP] -RTP->
|
||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
node_priv=0x2342
|
||||
ok: matches expected msg
|
||||
rc=0
|
||||
|
||||
Transmit RTP. Expecting tx_msg() with inserted IuUP header
|
||||
[IuUP] <-RTP-
|
||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
tx_msg() invoked by iuup_cn!
|
||||
<-PDU- [IuUP]
|
||||
8060944c6256042c00010102000002b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
node_priv=0x2342
|
||||
ok: matches expected msg
|
||||
rc=0
|
||||
|
||||
More RTP, each time the Frame Nr advances, causing a new header CRC.
|
||||
[IuUP] <-RTP-
|
||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
tx_msg() invoked by iuup_cn!
|
||||
<-PDU- [IuUP]
|
||||
8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
node_priv=0x2342
|
||||
ok: matches expected msg
|
||||
rc=0
|
||||
[IuUP] <-RTP-
|
||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
tx_msg() invoked by iuup_cn!
|
||||
<-PDU- [IuUP]
|
||||
8060944c6256042c0001010202007eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
node_priv=0x2342
|
||||
ok: matches expected msg
|
||||
rc=0
|
||||
[IuUP] <-RTP-
|
||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
tx_msg() invoked by iuup_cn!
|
||||
<-PDU- [IuUP]
|
||||
8060944c6256042c0001010203009eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
node_priv=0x2342
|
||||
ok: matches expected msg
|
||||
rc=0
|
||||
All done.
|
@@ -1,61 +0,0 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
-ggdb3 \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(LIBBCG729_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
mgcp_test.ok \
|
||||
mgcp_transcoding_test.ok \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
mgcp_test \
|
||||
$(NULL)
|
||||
if BUILD_MGCP_TRANSCODING
|
||||
noinst_PROGRAMS += \
|
||||
mgcp_transcoding_test \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
mgcp_test_SOURCES = \
|
||||
mgcp_test.c \
|
||||
$(NULL)
|
||||
|
||||
mgcp_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBRARY_DL) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBRARY_GSM) \
|
||||
-lm \
|
||||
$(NULL)
|
||||
|
||||
mgcp_transcoding_test_SOURCES = \
|
||||
mgcp_transcoding_test.c \
|
||||
$(NULL)
|
||||
|
||||
mgcp_transcoding_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBBCG729_LIBS) \
|
||||
$(LIBRARY_DL) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBRARY_GSM) \
|
||||
-lm \
|
||||
$(NULL)
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user