mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-03 21:43:32 +00:00 
			
		
		
		
	Compare commits
	
		
			95 Commits
		
	
	
		
			1.4.0
			...
			pmaier/amr
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					63ce8ce2e9 | ||
| 
						 | 
					9fc8a02196 | ||
| 
						 | 
					e36b775eae | ||
| 
						 | 
					622dd61764 | ||
| 
						 | 
					e81c1176aa | ||
| 
						 | 
					ed0c1aa9d5 | ||
| 
						 | 
					ac69ea9cdf | ||
| 
						 | 
					0a40379214 | ||
| 
						 | 
					72bc8da537 | ||
| 
						 | 
					c3132fd508 | ||
| 
						 | 
					96c3107daf | ||
| 
						 | 
					ad21a0e166 | ||
| 
						 | 
					32d15cc8ba | ||
| 
						 | 
					1fd50e5306 | ||
| 
						 | 
					6802374574 | ||
| 
						 | 
					82c1d1eb20 | ||
| 
						 | 
					5a5169e15c | ||
| 
						 | 
					7c0b70a94e | ||
| 
						 | 
					ca6a8495e1 | ||
| 
						 | 
					c0a2196f91 | ||
| 
						 | 
					da026abc27 | ||
| 
						 | 
					27780313ac | ||
| 
						 | 
					e5ed946bdb | ||
| 
						 | 
					c319b79baa | ||
| 
						 | 
					2818db8b33 | ||
| 
						 | 
					0415de74b8 | ||
| 
						 | 
					4075b8805d | ||
| 
						 | 
					8ab3fbbaf2 | ||
| 
						 | 
					cc39218dce | ||
| 
						 | 
					8dbc9ed408 | ||
| 
						 | 
					ba25eab0fa | ||
| 
						 | 
					aa823bf24b | ||
| 
						 | 
					a714abfc91 | ||
| 
						 | 
					9270e91c09 | ||
| 
						 | 
					b7974e2fa4 | ||
| 
						 | 
					1e174875bf | ||
| 
						 | 
					c26b665c0c | ||
| 
						 | 
					0479b2a15f | ||
| 
						 | 
					ff6606cacb | ||
| 
						 | 
					407b1f186d | ||
| 
						 | 
					de2a4d7c22 | ||
| 
						 | 
					48aff62341 | ||
| 
						 | 
					852ba86949 | ||
| 
						 | 
					662fa421c6 | ||
| 
						 | 
					d14163e74f | ||
| 
						 | 
					11b4810142 | ||
| 
						 | 
					9ecceb651b | ||
| 
						 | 
					426a9d9103 | ||
| 
						 | 
					17bf603222 | ||
| 
						 | 
					bcd52e5724 | ||
| 
						 | 
					956242dcec | ||
| 
						 | 
					e686675f7d | ||
| 
						 | 
					4ca0b0d1fa | ||
| 
						 | 
					efd645e5a8 | ||
| 
						 | 
					f0504e86b3 | ||
| 
						 | 
					e28b673fba | ||
| 
						 | 
					a77eade744 | ||
| 
						 | 
					6531726a02 | ||
| 
						 | 
					31b4729f27 | ||
| 
						 | 
					a729db62a9 | ||
| 
						 | 
					5336f57f05 | ||
| 
						 | 
					eb72ff058f | ||
| 
						 | 
					8a91d2c04e | ||
| 
						 | 
					55e0dcf254 | ||
| 
						 | 
					23e7bf1c00 | ||
| 
						 | 
					10d487e13f | ||
| 
						 | 
					40f5033cf9 | ||
| 
						 | 
					b1bb1fa187 | ||
| 
						 | 
					1d121483f6 | ||
| 
						 | 
					124441af1f | ||
| 
						 | 
					b861db97ea | ||
| 
						 | 
					08e07046f9 | ||
| 
						 | 
					a5e0cf09a6 | ||
| 
						 | 
					f2388eab71 | ||
| 
						 | 
					120865833c | ||
| 
						 | 
					e6d8e91b3a | ||
| 
						 | 
					475f868b7f | ||
| 
						 | 
					0063ca2fb0 | ||
| 
						 | 
					610fda6108 | ||
| 
						 | 
					352eed09ae | ||
| 
						 | 
					0a89e92a5e | ||
| 
						 | 
					dde80f3f61 | ||
| 
						 | 
					9a7ccc3746 | ||
| 
						 | 
					910189d0a1 | ||
| 
						 | 
					8482e8374c | ||
| 
						 | 
					fee4fa9492 | ||
| 
						 | 
					4dba769577 | ||
| 
						 | 
					da895b1151 | ||
| 
						 | 
					a74c0ea2db | ||
| 
						 | 
					6a26c16eb0 | ||
| 
						 | 
					af8e00ffe8 | ||
| 
						 | 
					6931f9a7a4 | ||
| 
						 | 
					544448abea | ||
| 
						 | 
					e547bdd1ce | ||
| 
						 | 
					d1562a7766 | 
							
								
								
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -22,6 +22,7 @@ src/utils/smpp_mirror
 | 
			
		||||
*.pyc
 | 
			
		||||
*.gcda
 | 
			
		||||
*.gcno
 | 
			
		||||
*.pc
 | 
			
		||||
 | 
			
		||||
#configure
 | 
			
		||||
aclocal.m4
 | 
			
		||||
@@ -82,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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										89
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										89
									
								
								configure.ac
									
									
									
									
									
								
							@@ -39,10 +39,10 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
 | 
			
		||||
AC_SUBST(LIBRARY_DL)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.0.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.0.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.4.0)
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(sanitize,
 | 
			
		||||
	[AS_HELP_STRING(
 | 
			
		||||
@@ -74,22 +74,6 @@ then
 | 
			
		||||
	CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Enable/disable transcoding within osmo-bsc_mgcp?
 | 
			
		||||
AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [Build the MGCP gateway with internal transcoding enabled.])],
 | 
			
		||||
    [osmo_ac_mgcp_transcoding="$enableval"],[osmo_ac_mgcp_transcoding="no"])
 | 
			
		||||
AC_ARG_WITH([g729], [AS_HELP_STRING([--with-g729], [Enable G.729 encoding/decoding.])], [osmo_ac_with_g729="$withval"],[osmo_ac_with_g729="no"])
 | 
			
		||||
 | 
			
		||||
if test "$osmo_ac_mgcp_transcoding" = "yes" ; then
 | 
			
		||||
    AC_SEARCH_LIBS([gsm_create], [gsm], [LIBRARY_GSM="$LIBS";LIBS=""], [AC_MSG_ERROR([--enable-mgcp-transcoding: cannot find usable libgsm])])
 | 
			
		||||
    AC_SUBST(LIBRARY_GSM)
 | 
			
		||||
    if test "$osmo_ac_with_g729" = "yes" ; then
 | 
			
		||||
	PKG_CHECK_MODULES(LIBBCG729, libbcg729 >= 0.1, [AC_DEFINE([HAVE_BCG729], [1], [Use bgc729 decoder/encoder])])
 | 
			
		||||
    fi
 | 
			
		||||
    AC_DEFINE(BUILD_MGCP_TRANSCODING, 1, [Define if we want to build the MGCP gateway with transcoding support])
 | 
			
		||||
fi
 | 
			
		||||
AM_CONDITIONAL(BUILD_MGCP_TRANSCODING, test "x$osmo_ac_mgcp_transcoding" = "xyes")
 | 
			
		||||
AC_SUBST(osmo_ac_mgcp_transcoding)
 | 
			
		||||
 | 
			
		||||
dnl Checks for typedefs, structures and compiler characteristics
 | 
			
		||||
 | 
			
		||||
# The following test is taken from WebKit's webkit.m4
 | 
			
		||||
@@ -144,6 +128,64 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
 | 
			
		||||
AC_MSG_RESULT([$enable_ext_tests])
 | 
			
		||||
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"])
 | 
			
		||||
 | 
			
		||||
@@ -151,25 +193,22 @@ 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/libosmo-mgcp/Makefile
 | 
			
		||||
    src/osmo-bsc_mgcp/Makefile
 | 
			
		||||
    src/osmo-mgw/Makefile
 | 
			
		||||
    tests/Makefile
 | 
			
		||||
    tests/atlocal
 | 
			
		||||
    tests/legacy_mgcp/Makefile
 | 
			
		||||
    tests/mgcp_client/Makefile
 | 
			
		||||
    tests/mgcp/Makefile
 | 
			
		||||
    doc/Makefile
 | 
			
		||||
    doc/examples/Makefile
 | 
			
		||||
    doc/manuals/Makefile
 | 
			
		||||
    contrib/Makefile
 | 
			
		||||
    contrib/systemd/Makefile
 | 
			
		||||
    Makefile)
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1,3 @@
 | 
			
		||||
SUBDIRS = systemd
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = ipa.py
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,10 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
# jenkins build helper script for openbsc.  This is how we build on jenkins.osmocom.org
 | 
			
		||||
#
 | 
			
		||||
# environment variables:
 | 
			
		||||
# * WITH_MANUALS: build manual PDFs if set to "1"
 | 
			
		||||
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
 | 
			
		||||
	echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
 | 
			
		||||
@@ -23,10 +28,18 @@ verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
 | 
			
		||||
 | 
			
		||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
 | 
			
		||||
export LD_LIBRARY_PATH="$inst/lib"
 | 
			
		||||
export PATH="$inst/bin:$PATH"
 | 
			
		||||
 | 
			
		||||
osmo-build-dep.sh libosmo-abis
 | 
			
		||||
osmo-build-dep.sh libosmo-netif
 | 
			
		||||
 | 
			
		||||
# Additional configure options and depends
 | 
			
		||||
CONFIG=""
 | 
			
		||||
if [ "$WITH_MANUALS" = "1" ]; then
 | 
			
		||||
	osmo-build-dep.sh osmo-gsm-manuals
 | 
			
		||||
	CONFIG="--enable-manuals"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
set +x
 | 
			
		||||
echo
 | 
			
		||||
echo
 | 
			
		||||
@@ -37,13 +50,17 @@ set -x
 | 
			
		||||
 | 
			
		||||
cd "$base"
 | 
			
		||||
autoreconf --install --force
 | 
			
		||||
./configure $MGCP --enable-vty-tests --enable-external-tests --enable-werror
 | 
			
		||||
./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
 | 
			
		||||
							
								
								
									
										107
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										107
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,110 @@
 | 
			
		||||
osmo-mgw (1.5.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * gitignore: Filter *.pc
 | 
			
		||||
  * configure: Find correct libgsm's gsm.h header
 | 
			
		||||
  * vty: Fix typo writing bts-jitter-buffer-delay-{min,max}
 | 
			
		||||
  * Remove libosmo-legacy-mgcp and osmo-bsc-mgcp
 | 
			
		||||
  * debian: Remove dangling symlink to osmo-bsc-mgcp.service
 | 
			
		||||
  * Install systemd services with autotools
 | 
			
		||||
  * Install sample cfg file to /etc/osmocom
 | 
			
		||||
  * mgcp: Fix osmux_cid_bitmap static array size calculation
 | 
			
		||||
  * mgcp_osmux: Use define to calculate rtp_ssrc_winlen
 | 
			
		||||
  * osmux: Avoid initing output without enabling osmux
 | 
			
		||||
  * mgcp: Log endpoint nr consistently as hex
 | 
			
		||||
  * osmux_send_dummy: Avoid logging incorrectly and sending if osmux not enabled
 | 
			
		||||
  * osmux: Don't process regular osmux frames if disabled by cfg
 | 
			
		||||
  * osmux: Move parse_cid of legacy dummy frames to own function
 | 
			
		||||
  * osmux: Make func handling dummy frames independent of endp type
 | 
			
		||||
  * osmux: allow enabling osmux only on correct activating state
 | 
			
		||||
  * osmux: Improve checks around activating and using enabled osmux
 | 
			
		||||
  * osmux.h: Document enum osmux_state
 | 
			
		||||
  * osmux: Avoid processing further frames if conn not found
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * mgcp_client_fsm: allow ptmap in mgcp_client_fsm as well
 | 
			
		||||
  * mgcp_network: translate payload type numbers in RTP packets
 | 
			
		||||
  * mgcp_client: use IETF source port as for MGCP
 | 
			
		||||
  * mgcp_client: increment local port number when port is in use
 | 
			
		||||
  * mgcp_test: release endpoints after use
 | 
			
		||||
  * network: do not patch PT of RTCP packets
 | 
			
		||||
  * network: check packets before further processing
 | 
			
		||||
  * Cosmetic: remove misplaced line break
 | 
			
		||||
  * mgcp_sdp: restructure mgcp_write_response_sdp() (rtpmap)
 | 
			
		||||
  * mgcp_sdp: restructure mgcp_write_response_sdp() (audio)
 | 
			
		||||
  * mgcp_client: check local port only once
 | 
			
		||||
  * mgcp_client_fsm: switch to MGCP_CONN_RECV_SEND in add_audio()
 | 
			
		||||
  * mgcp_protocol: increase buffer space for codec name in LCO
 | 
			
		||||
  * osmo-mgw: Add vty reference manual
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * fix handling of "Wrong domain name" error
 | 
			
		||||
  * interpret domain '*' as 'allow any domain'
 | 
			
		||||
  * cosmetic: log: fix "CallIDs does not match"
 | 
			
		||||
  * fix 3G hack: allow any IP for loopback and 0.0.0.0
 | 
			
		||||
  * cosmetic: drop code dup in mgcp_client_fsm.c CRCX
 | 
			
		||||
  * add X-Osmo-IGN MGCP header to ignore CallID
 | 
			
		||||
  * X-Osmo-IGN: rather parse items by token, not char
 | 
			
		||||
  * mgcp_test: fix log of conn_id presence
 | 
			
		||||
  * mgcp_test: fix get_conn_id_from_response() CI length
 | 
			
		||||
  * mgcp_client_test: cosmetically re-arrange reply_to() args
 | 
			
		||||
  * mgcp_client_test: use "\r\n\r\n" instead of "\n\n"
 | 
			
		||||
  * mgcp_client_test: also verify received conn_id
 | 
			
		||||
  * mgcp_client_test: test long conn_id
 | 
			
		||||
  * mgcp_client: error on too long conn id
 | 
			
		||||
  * mgcp_common: rename to MGCP_CONN_ID_MAXLEN
 | 
			
		||||
  * doc: fix mgcp_verify_ci() return val doc
 | 
			
		||||
  * mgcp_verify_ci(): return meaningful error codes
 | 
			
		||||
  * fix mgcp_verify_ci(): off-by-one in max len check
 | 
			
		||||
  * generate shorter 'I:' conn IDs
 | 
			
		||||
  * mgcp_conn_get(): compare conn Id ('I:') case insensitively
 | 
			
		||||
  * mgcp_conn_get(): match conn Id ('I:') despite leading zeros
 | 
			
		||||
  * cosmetic: mgcp_test: fix get_conn_id_from_response()
 | 
			
		||||
  * comment: indicate struct type for mgcp_endpoint.conns
 | 
			
		||||
  * log: avoid logging early media as error
 | 
			
		||||
  * fix osmo-mgw -s; fixes osmo-mgw.service using -s
 | 
			
		||||
  * Importing history from osmo-gsm-manuals.git
 | 
			
		||||
  * OsmoMGW: update VTY reference
 | 
			
		||||
  * OsmoMGW: document the 'X-Osmo-IGN' MGCP extension
 | 
			
		||||
  * mgw: update vty reference
 | 
			
		||||
  * drop/replace very weird logging in mgcp_client.c
 | 
			
		||||
  * check_rtp: on IP:port errors, log the IP and port
 | 
			
		||||
  * osmo-mgw: err-log: include expected domain name
 | 
			
		||||
  * mgcp_client_vty: fix missing talloc_free
 | 
			
		||||
  * mgcp_client: drop a bunch of dead code
 | 
			
		||||
  * mgcp_client: logging tweaks
 | 
			
		||||
  * mgcp_client: make domain part of endpoint configurable
 | 
			
		||||
  * mgcp_client: tweak some log levels INFO -> {DEBUG,ERROR}
 | 
			
		||||
 | 
			
		||||
  [ Stefan Sperling ]
 | 
			
		||||
  * add VTY commands which show specific mgcp endpoints
 | 
			
		||||
  * add MGCP CRCX command statistics to osmo-mgw
 | 
			
		||||
  * show RTP TX/RX stats in 'mgcp show stats' output
 | 
			
		||||
  * use local variable for rate counters in handle_create_con()
 | 
			
		||||
  * add more mgcp crxc error counters
 | 
			
		||||
  * add MDCX command statistics to osmo-mgw
 | 
			
		||||
  * add aggregated rtp connection stats to osmo-mgw
 | 
			
		||||
  * add DLCX command statistics to osmo-mgw
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * debian/rules: Don't overwrite .tarball-version
 | 
			
		||||
  * check_rtp_origin(): Don't memcmp sockadd_in and in_addr
 | 
			
		||||
  * check_rtp_origin(): Avoid using memcmp for comparing integer types
 | 
			
		||||
  * vty-ref: Update URI of docbook 5.0 schema
 | 
			
		||||
 | 
			
		||||
  [ Daniel Willmann ]
 | 
			
		||||
  * mgw: Add new VTY reference
 | 
			
		||||
  * Add initial OsmoMGW manual
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * build manuals moved here from osmo-gsm-manuals.git
 | 
			
		||||
  * jenkins.sh: remove leftover MGCP env variable
 | 
			
		||||
  * Fix DISTCHECK_CONFIGURE_FLAGS override
 | 
			
		||||
  * contrib/jenkins.sh: build and publish manuals
 | 
			
		||||
  * contrib: fix makedistcheck with disabled systemd
 | 
			
		||||
 | 
			
		||||
 -- Harald Welte <laforge@gnumonks.org>  Sun, 20 Jan 2019 15:02:18 +0100
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.4.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -19,7 +19,7 @@ Multi-Arch: foreign
 | 
			
		||||
Depends: ${misc:Depends}, ${shlibs:Depends}
 | 
			
		||||
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
 | 
			
		||||
 | 
			
		||||
Package: libosmo-mgcp-client3
 | 
			
		||||
Package: libosmo-mgcp-client5
 | 
			
		||||
Section: libs
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
@@ -31,26 +31,5 @@ Package: libosmo-mgcp-client-dev
 | 
			
		||||
Section: libdevel
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
Depends: libosmo-mgcp-client3 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Depends: libosmo-mgcp-client5 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
 | 
			
		||||
 | 
			
		||||
Package: osmo-bsc-mgcp
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: foreign
 | 
			
		||||
Depends: libosmo-legacy-mgcp0, ${misc:Depends}, ${shlibs:Depends}
 | 
			
		||||
Description: OsmoBSC-MGCP: Osmocom's Legacy Media Gateway; use osmo-mgw instead.
 | 
			
		||||
 | 
			
		||||
Package: libosmo-legacy-mgcp0
 | 
			
		||||
Section: libs
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
Pre-Depends: ${misc:Pre-Depends}
 | 
			
		||||
Depends: ${misc:Depends}, ${shlibs:Depends}
 | 
			
		||||
Description: libosmo-legacy-mgcp: Osmocom's Legacy Media Gateway server library; use libosmo-mgcp instead.
 | 
			
		||||
 | 
			
		||||
Package: libosmo-legacy-mgcp-dev
 | 
			
		||||
Section: libdevel
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
Depends: libosmo-legacy-mgcp0 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Description: libosmo-legacy-mgcp: Osmocom's Legacy Media Gateway server library; use libosmo-mgcp instead.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										36
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							@@ -40,30 +40,6 @@ License:   GPL-2.0+
 | 
			
		||||
 You should have received a copy of the GNU General Public License
 | 
			
		||||
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
Files:     src/libosmo-legacy-mgcp/g711common.h
 | 
			
		||||
Copyright: 2000 Abramo Bagnara <abramo@alsa-project.org>
 | 
			
		||||
License:   GPL-2.0+
 | 
			
		||||
 Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org>
 | 
			
		||||
 .
 | 
			
		||||
 This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 it under the terms of the GNU General Public License as published by
 | 
			
		||||
 the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 (at your option) any later version.
 | 
			
		||||
 .
 | 
			
		||||
 This program is distributed in the hope that it will be useful,
 | 
			
		||||
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 GNU General Public License for more details.
 | 
			
		||||
 .
 | 
			
		||||
 You should have received a copy of the GNU General Public License
 | 
			
		||||
 along with this program; if not, write to the Free Software
 | 
			
		||||
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 .
 | 
			
		||||
 The FSF address in the above text is the old one.
 | 
			
		||||
 .
 | 
			
		||||
 On Debian systems, the complete text of the GNU General Public License
 | 
			
		||||
 Version 2 can be found in `/usr/share/common-licenses/GPL-2'.
 | 
			
		||||
 | 
			
		||||
Files:     tests/vty_test_runner.py
 | 
			
		||||
Copyright: 2013 Holger Hans Peter Freyther
 | 
			
		||||
           2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
 | 
			
		||||
@@ -87,15 +63,3 @@ License:   GPL-3.0+
 | 
			
		||||
Files:     osmoappdesc.py
 | 
			
		||||
Copyright: 2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
 | 
			
		||||
License:   GPL-3.0+
 | 
			
		||||
 | 
			
		||||
Files:     src/libosmo-legacy-mgcp/mgcp_osmux.c
 | 
			
		||||
Copyright: 2012-2013 On Waves ehf <http://www.on-waves.com>
 | 
			
		||||
           2012-2013 Pablo Neira Ayuso <pablo@gnumonks.org>
 | 
			
		||||
License:   AGPL-3.0+
 | 
			
		||||
 All rights not specifically granted under this license are reserved.
 | 
			
		||||
 .
 | 
			
		||||
 This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 under the terms of the GNU Affero General Public License as published by the
 | 
			
		||||
 Free Software Foundation; either version 3 of the License, or (at your
 | 
			
		||||
 option) any later version.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								debian/libosmo-legacy-mgcp-dev.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/libosmo-legacy-mgcp-dev.install
									
									
									
									
										vendored
									
									
								
							@@ -1,4 +0,0 @@
 | 
			
		||||
usr/include/osmocom/legacy_mgcp
 | 
			
		||||
usr/lib/*/libosmo-legacy-mgcp.so
 | 
			
		||||
usr/lib/*/libosmo-legacy-mgcp.a
 | 
			
		||||
usr/lib/*/pkgconfig/libosmo-legacy-mgcp.pc
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/libosmo-legacy-mgcp0.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/libosmo-legacy-mgcp0.install
									
									
									
									
										vendored
									
									
								
							@@ -1 +0,0 @@
 | 
			
		||||
usr/lib/*/libosmo-legacy-mgcp.so.*
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/osmo-bsc-mgcp.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/osmo-bsc-mgcp.install
									
									
									
									
										vendored
									
									
								
							@@ -1,2 +0,0 @@
 | 
			
		||||
usr/bin/osmo-bsc_mgcp
 | 
			
		||||
usr/share/doc/osmo-mgw/examples/osmo-bsc_mgcp/mgcp.cfg
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/osmo-bsc-mgcp.service
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-bsc-mgcp.service
									
									
									
									
										vendored
									
									
								
							@@ -1 +0,0 @@
 | 
			
		||||
../contrib/systemd/osmo-bsc-mgcp.service
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/osmo-mgw.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/osmo-mgw.install
									
									
									
									
										vendored
									
									
								
							@@ -1,2 +1,4 @@
 | 
			
		||||
etc/osmocom/osmo-mgw.cfg
 | 
			
		||||
lib/systemd/system/osmo-mgw.service
 | 
			
		||||
usr/bin/osmo-mgw
 | 
			
		||||
usr/share/doc/osmo-mgw/examples/osmo-mgw/osmo-mgw.cfg
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/osmo-mgw.service
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-mgw.service
									
									
									
									
										vendored
									
									
								
							@@ -1 +0,0 @@
 | 
			
		||||
../contrib/systemd/osmo-mgw.service
 | 
			
		||||
							
								
								
									
										5
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							@@ -29,8 +29,7 @@ CFLAGS += -g
 | 
			
		||||
override_dh_auto_test:
 | 
			
		||||
	dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
 | 
			
		||||
 | 
			
		||||
override_dh_autoreconf:
 | 
			
		||||
	echo $(VERSION) > .tarball-version
 | 
			
		||||
	dh_autoreconf
 | 
			
		||||
override_dh_auto_configure:
 | 
			
		||||
	dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system
 | 
			
		||||
 | 
			
		||||
# See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	examples \
 | 
			
		||||
	manuals \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,11 @@
 | 
			
		||||
OSMOCONF_FILES = \
 | 
			
		||||
	osmo-mgw/osmo-mgw.cfg
 | 
			
		||||
 | 
			
		||||
osmoconfdir = $(sysconfdir)/osmocom
 | 
			
		||||
osmoconf_DATA = $(OSMOCONF_FILES)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = $(OSMOCONF_FILES)
 | 
			
		||||
 | 
			
		||||
CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
 | 
			
		||||
 | 
			
		||||
dist-hook:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
!
 | 
			
		||||
! MGCP configuration example
 | 
			
		||||
!
 | 
			
		||||
mgcp
 | 
			
		||||
 !local ip 10.23.24.2
 | 
			
		||||
 !bts ip 10.24.24.1
 | 
			
		||||
 !bind ip 10.23.24.1
 | 
			
		||||
 bind port 2427
 | 
			
		||||
 rtp base 4000
 | 
			
		||||
 rtp force-ptime 20
 | 
			
		||||
 sdp audio payload number 98
 | 
			
		||||
 sdp audio payload name AMR/8000
 | 
			
		||||
 number endpoints 31
 | 
			
		||||
 no rtcp-omit
 | 
			
		||||
							
								
								
									
										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
											
										
									
								
							@@ -3,9 +3,6 @@ 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 \
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	legacy_mgcp \
 | 
			
		||||
	mgcp_client \
 | 
			
		||||
	mgcp \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	mgcp_transcode.h \
 | 
			
		||||
	vty.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
@@ -1,298 +0,0 @@
 | 
			
		||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2012 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef OPENBSC_MGCP_H
 | 
			
		||||
#define OPENBSC_MGCP_H
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/write_queue.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
 | 
			
		||||
#define RTP_PORT_DEFAULT 4000
 | 
			
		||||
#define RTP_PORT_NET_DEFAULT 16000
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Calculate the RTP audio port for the given multiplex
 | 
			
		||||
 * and the direction. This allows a semi static endpoint
 | 
			
		||||
 * to port calculation removing the need for the BSC
 | 
			
		||||
 * and the MediaGateway to communicate.
 | 
			
		||||
 *
 | 
			
		||||
 * Port usage explained:
 | 
			
		||||
 *       base + (multiplex * 2) + 0 == local port to wait for network packets
 | 
			
		||||
 *       base + (multiplex * 2) + 1 == local port for rtcp
 | 
			
		||||
 *
 | 
			
		||||
 * The above port will receive packets from the BTS that need
 | 
			
		||||
 * to be patched and forwarded to the network.
 | 
			
		||||
 * The above port will receive packets from the network that
 | 
			
		||||
 * need to be patched and forwarded to the BTS.
 | 
			
		||||
 *
 | 
			
		||||
 * We assume to have a static BTS IP address so we can differentiate
 | 
			
		||||
 * network and BTS.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
static inline int rtp_calculate_port(int multiplex, int base)
 | 
			
		||||
{
 | 
			
		||||
	return base + (multiplex * 2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Handling of MGCP Endpoints and the MGCP Config
 | 
			
		||||
 */
 | 
			
		||||
struct mgcp_endpoint;
 | 
			
		||||
struct mgcp_config;
 | 
			
		||||
struct mgcp_trunk_config;
 | 
			
		||||
struct mgcp_rtp_end;
 | 
			
		||||
 | 
			
		||||
#define MGCP_ENDP_CRCX 1
 | 
			
		||||
#define MGCP_ENDP_DLCX 2
 | 
			
		||||
#define MGCP_ENDP_MDCX 3
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * what to do with the msg?
 | 
			
		||||
 *	- continue as usual?
 | 
			
		||||
 *	- reject and send a failure code?
 | 
			
		||||
 *	- defer? do not send anything
 | 
			
		||||
 */
 | 
			
		||||
#define MGCP_POLICY_CONT	4
 | 
			
		||||
#define MGCP_POLICY_REJECT	5
 | 
			
		||||
#define MGCP_POLICY_DEFER	6
 | 
			
		||||
 | 
			
		||||
typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint);
 | 
			
		||||
typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state);
 | 
			
		||||
typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id);
 | 
			
		||||
typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
 | 
			
		||||
typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Return:
 | 
			
		||||
 *   <  0 in case no audio was processed
 | 
			
		||||
 *   >= 0 in case audio was processed. The remaining payload
 | 
			
		||||
 *   length will be returned.
 | 
			
		||||
 */
 | 
			
		||||
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
 | 
			
		||||
			       struct mgcp_rtp_end *dst_end,
 | 
			
		||||
			       char *data, int *len, int buf_size);
 | 
			
		||||
typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
 | 
			
		||||
				     struct mgcp_rtp_end *dst_end,
 | 
			
		||||
				     struct mgcp_rtp_end *src_end);
 | 
			
		||||
 | 
			
		||||
typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
 | 
			
		||||
				int *payload_type,
 | 
			
		||||
				const char**subtype_name,
 | 
			
		||||
				const char**fmtp_extra);
 | 
			
		||||
 | 
			
		||||
#define PORT_ALLOC_STATIC	0
 | 
			
		||||
#define PORT_ALLOC_DYNAMIC	1
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This holds information on how to allocate ports
 | 
			
		||||
 */
 | 
			
		||||
struct mgcp_port_range {
 | 
			
		||||
	int mode;
 | 
			
		||||
 | 
			
		||||
	/* addr or NULL to fall-back to default */
 | 
			
		||||
	char *bind_addr;
 | 
			
		||||
 | 
			
		||||
	/* pre-allocated from a base? */
 | 
			
		||||
	int base_port;
 | 
			
		||||
 | 
			
		||||
	/* dynamically allocated */
 | 
			
		||||
	int range_start;
 | 
			
		||||
	int range_end;
 | 
			
		||||
	int last_port;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define MGCP_KEEPALIVE_ONCE (-1)
 | 
			
		||||
 | 
			
		||||
struct mgcp_trunk_config {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
 | 
			
		||||
	struct mgcp_config *cfg;
 | 
			
		||||
 | 
			
		||||
	int trunk_nr;
 | 
			
		||||
	int trunk_type;
 | 
			
		||||
 | 
			
		||||
	char *audio_fmtp_extra;
 | 
			
		||||
	char *audio_name;
 | 
			
		||||
	int audio_payload;
 | 
			
		||||
	int audio_send_ptime;
 | 
			
		||||
	int audio_send_name;
 | 
			
		||||
	int audio_loop;
 | 
			
		||||
 | 
			
		||||
	int no_audio_transcoding;
 | 
			
		||||
 | 
			
		||||
	int omit_rtcp;
 | 
			
		||||
	int keepalive_interval;
 | 
			
		||||
 | 
			
		||||
	/* RTP patching */
 | 
			
		||||
	int force_constant_ssrc; /* 0: don't, 1: once */
 | 
			
		||||
	int force_aligned_timing;
 | 
			
		||||
 | 
			
		||||
	/* spec handling */
 | 
			
		||||
	int force_realloc;
 | 
			
		||||
 | 
			
		||||
	/* timer */
 | 
			
		||||
	struct osmo_timer_list keepalive_timer;
 | 
			
		||||
 | 
			
		||||
	unsigned int number_endpoints;
 | 
			
		||||
	struct mgcp_endpoint *endpoints;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum mgcp_role {
 | 
			
		||||
	MGCP_BSC = 0,
 | 
			
		||||
	MGCP_BSC_NAT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum mgcp_connection_mode {
 | 
			
		||||
	MGCP_CONN_NONE = 0,
 | 
			
		||||
	MGCP_CONN_RECV_ONLY = 1,
 | 
			
		||||
	MGCP_CONN_SEND_ONLY = 2,
 | 
			
		||||
	MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
 | 
			
		||||
	MGCP_CONN_LOOPBACK  = 4 | MGCP_CONN_RECV_SEND,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_config {
 | 
			
		||||
	int source_port;
 | 
			
		||||
	char *local_ip;
 | 
			
		||||
	char *source_addr;
 | 
			
		||||
	char *bts_ip;
 | 
			
		||||
	char *call_agent_addr;
 | 
			
		||||
 | 
			
		||||
	struct in_addr bts_in;
 | 
			
		||||
 | 
			
		||||
	/* transcoder handling */
 | 
			
		||||
	char *transcoder_ip;
 | 
			
		||||
	struct in_addr transcoder_in;
 | 
			
		||||
	int transcoder_remote_base;
 | 
			
		||||
 | 
			
		||||
	/* RTP processing */
 | 
			
		||||
	mgcp_processing rtp_processing_cb;
 | 
			
		||||
	mgcp_processing_setup setup_rtp_processing_cb;
 | 
			
		||||
 | 
			
		||||
	mgcp_get_format get_net_downlink_format_cb;
 | 
			
		||||
 | 
			
		||||
	struct osmo_wqueue gw_fd;
 | 
			
		||||
 | 
			
		||||
	struct mgcp_port_range bts_ports;
 | 
			
		||||
	struct mgcp_port_range net_ports;
 | 
			
		||||
	struct mgcp_port_range transcoder_ports;
 | 
			
		||||
	int endp_dscp;
 | 
			
		||||
 | 
			
		||||
	int bts_force_ptime;
 | 
			
		||||
 | 
			
		||||
	mgcp_change change_cb;
 | 
			
		||||
	mgcp_policy policy_cb;
 | 
			
		||||
	mgcp_reset reset_cb;
 | 
			
		||||
	mgcp_realloc realloc_cb;
 | 
			
		||||
	mgcp_rqnt rqnt_cb;
 | 
			
		||||
	void *data;
 | 
			
		||||
 | 
			
		||||
	uint32_t last_call_id;
 | 
			
		||||
 | 
			
		||||
	/* trunk handling */
 | 
			
		||||
	struct mgcp_trunk_config trunk;
 | 
			
		||||
	struct llist_head trunks;
 | 
			
		||||
 | 
			
		||||
	/* only used for start with a static configuration */
 | 
			
		||||
	int last_net_port;
 | 
			
		||||
	int last_bts_port;
 | 
			
		||||
 | 
			
		||||
	enum mgcp_role role;
 | 
			
		||||
 | 
			
		||||
	/* osmux translator: 0 means disabled, 1 means enabled */
 | 
			
		||||
	int osmux;
 | 
			
		||||
	/* addr to bind the server to */
 | 
			
		||||
	char *osmux_addr;
 | 
			
		||||
	/* The BSC-NAT may ask for enabling osmux on demand. This tells us if
 | 
			
		||||
	 * the osmux socket is already initialized.
 | 
			
		||||
	 */
 | 
			
		||||
	int osmux_init;
 | 
			
		||||
	/* osmux batch factor: from 1 to 4 maximum */
 | 
			
		||||
	int osmux_batch;
 | 
			
		||||
	/* osmux batch size (in bytes) */
 | 
			
		||||
	int osmux_batch_size;
 | 
			
		||||
	/* osmux port */
 | 
			
		||||
	uint16_t osmux_port;
 | 
			
		||||
	/* Pad circuit with dummy messages until we see the first voice
 | 
			
		||||
	 * message.
 | 
			
		||||
	 */
 | 
			
		||||
	uint16_t osmux_dummy;
 | 
			
		||||
 | 
			
		||||
	/* Use a jitterbuffer on the bts-side receiver */
 | 
			
		||||
	bool bts_use_jibuf;
 | 
			
		||||
	/* Minimum and maximum buffer size for the jitter buffer, in ms */
 | 
			
		||||
	uint32_t bts_jitter_delay_min;
 | 
			
		||||
	uint32_t bts_jitter_delay_max;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* config management */
 | 
			
		||||
struct mgcp_config *mgcp_config_alloc(void);
 | 
			
		||||
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
 | 
			
		||||
		      enum mgcp_role role);
 | 
			
		||||
int mgcp_vty_init(void);
 | 
			
		||||
int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg);
 | 
			
		||||
void mgcp_release_endp(struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_initialize_endp(struct mgcp_endpoint *endp);
 | 
			
		||||
int mgcp_reset_transcoder(struct mgcp_config *cfg);
 | 
			
		||||
void mgcp_format_stats(struct mgcp_endpoint *endp, char *stats, size_t size);
 | 
			
		||||
int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os, uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter);
 | 
			
		||||
 | 
			
		||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * format helper functions
 | 
			
		||||
 */
 | 
			
		||||
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
/* adc helper */
 | 
			
		||||
static inline int mgcp_timeslot_to_endpoint(int multiplex, int timeslot)
 | 
			
		||||
{
 | 
			
		||||
	if (timeslot == 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Timeslot should not be 0\n");
 | 
			
		||||
		timeslot = 255;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return timeslot + (32 * multiplex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void mgcp_endpoint_to_timeslot(int endpoint, int *multiplex, int *timeslot)
 | 
			
		||||
{
 | 
			
		||||
	*multiplex = endpoint / 32;
 | 
			
		||||
	*timeslot = endpoint % 32;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint);
 | 
			
		||||
int mgcp_send_reset_all(struct mgcp_config *cfg);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port);
 | 
			
		||||
int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, struct sockaddr_in *addr, char *buf, int rc);
 | 
			
		||||
int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,351 +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>
 | 
			
		||||
#include <osmocom/netif/jibuf.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;
 | 
			
		||||
 | 
			
		||||
	/* Jitter buffer */
 | 
			
		||||
	struct osmo_jibuf* bts_jb;
 | 
			
		||||
	/* Use a jitterbuffer on the bts-side receiver */
 | 
			
		||||
	bool bts_use_jibuf;
 | 
			
		||||
	/* Minimum and maximum buffer size for the jitter buffer, in ms */
 | 
			
		||||
	uint32_t bts_jitter_delay_min;
 | 
			
		||||
	uint32_t bts_jitter_delay_max;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Internal jitter buffer related
 | 
			
		||||
 */
 | 
			
		||||
void mgcp_dejitter_udp_send(struct msgb *msg, void *data);
 | 
			
		||||
@@ -1,90 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2014 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#ifndef OPENBSC_MGCP_TRANSCODE_H
 | 
			
		||||
#define OPENBSC_MGCP_TRANSCODE_H
 | 
			
		||||
 | 
			
		||||
#include "bscconfig.h"
 | 
			
		||||
 | 
			
		||||
#include <gsm.h>
 | 
			
		||||
#ifdef HAVE_BCG729
 | 
			
		||||
#include <bcg729/decoder.h>
 | 
			
		||||
#include <bcg729/encoder.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
enum audio_format {
 | 
			
		||||
	AF_INVALID,
 | 
			
		||||
	AF_S16,
 | 
			
		||||
	AF_L16,
 | 
			
		||||
	AF_GSM,
 | 
			
		||||
	AF_G729,
 | 
			
		||||
	AF_PCMA,
 | 
			
		||||
	AF_PCMU
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct mgcp_process_rtp_state {
 | 
			
		||||
	/* decoding */
 | 
			
		||||
	enum audio_format src_fmt;
 | 
			
		||||
	union {
 | 
			
		||||
		gsm gsm_handle;
 | 
			
		||||
#ifdef HAVE_BCG729
 | 
			
		||||
		bcg729DecoderChannelContextStruct *g729_dec;
 | 
			
		||||
#endif
 | 
			
		||||
	} src;
 | 
			
		||||
	size_t src_frame_size;
 | 
			
		||||
	size_t src_samples_per_frame;
 | 
			
		||||
 | 
			
		||||
	/* processing */
 | 
			
		||||
 | 
			
		||||
	/* encoding */
 | 
			
		||||
	enum audio_format dst_fmt;
 | 
			
		||||
	union {
 | 
			
		||||
		gsm gsm_handle;
 | 
			
		||||
#ifdef HAVE_BCG729
 | 
			
		||||
		bcg729EncoderChannelContextStruct *g729_enc;
 | 
			
		||||
#endif
 | 
			
		||||
	} dst;
 | 
			
		||||
	size_t dst_frame_size;
 | 
			
		||||
	size_t dst_samples_per_frame;
 | 
			
		||||
	int dst_packet_duration;
 | 
			
		||||
 | 
			
		||||
	int is_running;
 | 
			
		||||
	uint16_t next_seq;
 | 
			
		||||
	uint32_t next_time;
 | 
			
		||||
	int16_t samples[10*160];
 | 
			
		||||
	size_t sample_cnt;
 | 
			
		||||
	size_t sample_offs;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int mgcp_transcoding_setup(struct mgcp_endpoint *endp,
 | 
			
		||||
			   struct mgcp_rtp_end *dst_end,
 | 
			
		||||
			   struct mgcp_rtp_end *src_end);
 | 
			
		||||
 | 
			
		||||
void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp,
 | 
			
		||||
					  int *payload_type,
 | 
			
		||||
					  const char**audio_name,
 | 
			
		||||
					  const char**fmtp_extra);
 | 
			
		||||
 | 
			
		||||
int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
				 struct mgcp_rtp_end *dst_end,
 | 
			
		||||
				 char *data, int *len, int buf_size);
 | 
			
		||||
 | 
			
		||||
int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst);
 | 
			
		||||
#endif /* OPENBSC_MGCP_TRANSCODE_H */
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
#ifndef _OPENBSC_OSMUX_H_
 | 
			
		||||
#define _OPENBSC_OSMUX_H_
 | 
			
		||||
 | 
			
		||||
#include <osmocom/netif/osmux.h>
 | 
			
		||||
 | 
			
		||||
#define OSMUX_PORT	1984
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	OSMUX_ROLE_BSC = 0,
 | 
			
		||||
	OSMUX_ROLE_BSC_NAT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int osmux_init(int role, struct mgcp_config *cfg);
 | 
			
		||||
int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port);
 | 
			
		||||
void osmux_disable_endpoint(struct mgcp_endpoint *endp);
 | 
			
		||||
void osmux_allocate_cid(struct mgcp_endpoint *endp);
 | 
			
		||||
void osmux_release_cid(struct mgcp_endpoint *endp);
 | 
			
		||||
 | 
			
		||||
int osmux_xfrm_to_rtp(struct mgcp_endpoint *endp, int type, char *buf, int rc);
 | 
			
		||||
int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp);
 | 
			
		||||
 | 
			
		||||
int osmux_send_dummy(struct mgcp_endpoint *endp);
 | 
			
		||||
 | 
			
		||||
int osmux_get_cid(void);
 | 
			
		||||
void osmux_put_cid(uint8_t osmux_cid);
 | 
			
		||||
int osmux_used_cid(void);
 | 
			
		||||
 | 
			
		||||
enum osmux_state {
 | 
			
		||||
	OSMUX_STATE_DISABLED = 0,
 | 
			
		||||
	OSMUX_STATE_NEGOTIATING,
 | 
			
		||||
	OSMUX_STATE_ACTIVATING,
 | 
			
		||||
	OSMUX_STATE_ENABLED,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum osmux_usage {
 | 
			
		||||
	OSMUX_USAGE_OFF = 0,
 | 
			
		||||
	OSMUX_USAGE_ON = 1,
 | 
			
		||||
	OSMUX_USAGE_ONLY = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
 | 
			
		||||
enum mgcp_vty_node {
 | 
			
		||||
	MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
 | 
			
		||||
	TRUNK_NODE,
 | 
			
		||||
};
 | 
			
		||||
@@ -118,6 +118,55 @@ struct mgcp_port_range {
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
@@ -141,6 +190,8 @@ struct mgcp_trunk_config {
 | 
			
		||||
	/* RTP patching */
 | 
			
		||||
	int force_constant_ssrc; /* 0: don't, 1: once */
 | 
			
		||||
	int force_aligned_timing;
 | 
			
		||||
	bool rfc5993_hr_convert;
 | 
			
		||||
	bool amr_oa_bwe_convert;
 | 
			
		||||
 | 
			
		||||
	/* spec handling */
 | 
			
		||||
	int force_realloc;
 | 
			
		||||
@@ -155,6 +206,15 @@ struct mgcp_trunk_config {
 | 
			
		||||
	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 {
 | 
			
		||||
@@ -216,6 +276,9 @@ struct mgcp_config {
 | 
			
		||||
	uint16_t osmux_dummy;
 | 
			
		||||
	/* domain name of the media gateway */
 | 
			
		||||
	char domain[255+1];
 | 
			
		||||
 | 
			
		||||
	/* time after which inactive connections (CIs) get closed */
 | 
			
		||||
	int conn_timeout;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* config management */
 | 
			
		||||
 
 | 
			
		||||
@@ -4,3 +4,4 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
 | 
			
		||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name);
 | 
			
		||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
 | 
			
		||||
 
 | 
			
		||||
@@ -49,6 +49,14 @@ enum mgcp_connection_mode {
 | 
			
		||||
	MGCP_CONN_LOOPBACK  = 4 | MGCP_CONN_RECV_SEND,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:"
 | 
			
		||||
 | 
			
		||||
/* Values should be bitwise-OR-able */
 | 
			
		||||
enum mgcp_x_osmo_ign {
 | 
			
		||||
	MGCP_X_OSMO_IGN_NONE = 0,
 | 
			
		||||
	MGCP_X_OSMO_IGN_CALLID = 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Ensure that the msg->l2h is NUL terminated. */
 | 
			
		||||
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
@@ -71,9 +79,12 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
 | 
			
		||||
/* Maximum length of the comment field */
 | 
			
		||||
#define MGCP_COMMENT_MAXLEN 256
 | 
			
		||||
 | 
			
		||||
/* String length of Connection Identifiers
 | 
			
		||||
 * (see also RFC3435 2.1.3.2 Names of Connections) */
 | 
			
		||||
#define MGCP_CONN_ID_LENGTH 32+1
 | 
			
		||||
/* 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) */
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
/* RTP connection related counters */
 | 
			
		||||
@@ -35,7 +36,36 @@ enum {
 | 
			
		||||
        RTP_OCTETS_RX_CTR,
 | 
			
		||||
        RTP_PACKETS_TX_CTR,
 | 
			
		||||
        RTP_OCTETS_TX_CTR,
 | 
			
		||||
        RTP_DROPPED_PACKETS_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,
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@ struct mgcp_endpoint {
 | 
			
		||||
	/*! Local connection options (see mgcp_internal.h) */
 | 
			
		||||
	struct mgcp_lco local_options;
 | 
			
		||||
 | 
			
		||||
	/*! List with connections active on this endpoint */
 | 
			
		||||
	/*! List of struct mgcp_conn, of the connections active on this endpoint */
 | 
			
		||||
	struct llist_head conns;
 | 
			
		||||
 | 
			
		||||
	/*! Backpointer to the MGW configuration */
 | 
			
		||||
@@ -90,6 +90,9 @@ struct mgcp_endpoint {
 | 
			
		||||
	/*! 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 */
 | 
			
		||||
 
 | 
			
		||||
@@ -128,6 +128,8 @@ struct mgcp_rtp_end {
 | 
			
		||||
	int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
 | 
			
		||||
	/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
 | 
			
		||||
	int force_aligned_timing;
 | 
			
		||||
	bool rfc5993_hr_convert;
 | 
			
		||||
	bool amr_oa_bwe_convert;
 | 
			
		||||
 | 
			
		||||
	/* Each end has a separate socket for RTP and RTCP */
 | 
			
		||||
	struct osmo_fd rtp;
 | 
			
		||||
@@ -226,11 +228,14 @@ struct mgcp_conn {
 | 
			
		||||
	enum mgcp_connection_mode mode_orig;
 | 
			
		||||
 | 
			
		||||
	/*! connection id to identify the connection */
 | 
			
		||||
	char id[MGCP_CONN_ID_LENGTH];
 | 
			
		||||
	char id[MGCP_CONN_ID_MAXLEN];
 | 
			
		||||
 | 
			
		||||
	/*! human readable name (vty, logging) */
 | 
			
		||||
	char name[256];
 | 
			
		||||
 | 
			
		||||
	/*! activity tracker (for cleaning up inactive connections) */
 | 
			
		||||
	struct osmo_timer_list watchdog;
 | 
			
		||||
 | 
			
		||||
	/*! union with connection description */
 | 
			
		||||
	union {
 | 
			
		||||
		struct mgcp_conn_rtp rtp;
 | 
			
		||||
@@ -328,3 +333,4 @@ enum {
 | 
			
		||||
#define PTYPE_UNDEFINED (-1)
 | 
			
		||||
 | 
			
		||||
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
 | 
			
		||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);
 | 
			
		||||
 
 | 
			
		||||
@@ -24,10 +24,10 @@ void osmux_put_cid(uint8_t osmux_cid);
 | 
			
		||||
int osmux_used_cid(void);
 | 
			
		||||
 | 
			
		||||
enum osmux_state {
 | 
			
		||||
	OSMUX_STATE_DISABLED = 0,
 | 
			
		||||
	OSMUX_STATE_NEGOTIATING,
 | 
			
		||||
	OSMUX_STATE_ACTIVATING,
 | 
			
		||||
	OSMUX_STATE_ENABLED,
 | 
			
		||||
	OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */
 | 
			
		||||
	OSMUX_STATE_NEGOTIATING,  /* Osmux was locally requested in MGCP CRCX */
 | 
			
		||||
	OSMUX_STATE_ACTIVATING,   /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref osmux_enable_endpoint. */
 | 
			
		||||
	OSMUX_STATE_ENABLED,	  /* Osmux was initialized by \ref osmux_enable_endpoint and can process frames */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum osmux_usage {
 | 
			
		||||
@@ -35,4 +35,3 @@ enum osmux_usage {
 | 
			
		||||
	OSMUX_USAGE_ON = 1,
 | 
			
		||||
	OSMUX_USAGE_ONLY = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,14 @@
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_common.h>
 | 
			
		||||
 | 
			
		||||
/* See also: RFC 3435, chapter 3.5 Transmission over UDP */
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727
 | 
			
		||||
#define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
 | 
			
		||||
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
 | 
			
		||||
 | 
			
		||||
#define MGCP_CLIENT_MGW_STR "Configure MGCP connection to Media Gateway\n"
 | 
			
		||||
 | 
			
		||||
struct msgb;
 | 
			
		||||
struct vty;
 | 
			
		||||
struct mgcp_client;
 | 
			
		||||
@@ -19,9 +22,10 @@ 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[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef unsigned int mgcp_trans_id_t;
 | 
			
		||||
@@ -57,7 +61,7 @@ struct mgcp_response_head {
 | 
			
		||||
	int response_code;
 | 
			
		||||
	mgcp_trans_id_t trans_id;
 | 
			
		||||
	char comment[MGCP_COMMENT_MAXLEN];
 | 
			
		||||
	char conn_id[MGCP_CONN_ID_LENGTH];
 | 
			
		||||
	char conn_id[MGCP_CONN_ID_MAXLEN];
 | 
			
		||||
	char endpoint[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -87,6 +91,7 @@ enum mgcp_verb {
 | 
			
		||||
#define MGCP_MSG_PRESENCE_AUDIO_IP	0x0008
 | 
			
		||||
#define MGCP_MSG_PRESENCE_AUDIO_PORT	0x0010
 | 
			
		||||
#define MGCP_MSG_PRESENCE_CONN_MODE	0x0020
 | 
			
		||||
#define MGCP_MSG_PRESENCE_X_OSMO_IGN	0x8000
 | 
			
		||||
 | 
			
		||||
struct mgcp_msg {
 | 
			
		||||
	enum mgcp_verb verb;
 | 
			
		||||
@@ -103,6 +108,7 @@ struct mgcp_msg {
 | 
			
		||||
	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);
 | 
			
		||||
@@ -118,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. */
 | 
			
		||||
@@ -132,20 +138,6 @@ int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
 | 
			
		||||
 | 
			
		||||
enum mgcp_connection_mode;
 | 
			
		||||
 | 
			
		||||
struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
 | 
			
		||||
			   uint16_t rtp_endpoint, unsigned int call_id,
 | 
			
		||||
			   enum mgcp_connection_mode mode)
 | 
			
		||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
 | 
			
		||||
 | 
			
		||||
struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
 | 
			
		||||
			   uint16_t rtp_endpoint, const char *rtp_conn_addr,
 | 
			
		||||
			   uint16_t rtp_port, enum mgcp_connection_mode mode)
 | 
			
		||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
 | 
			
		||||
 | 
			
		||||
struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
 | 
			
		||||
			   unsigned int call_id)
 | 
			
		||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
 | 
			
		||||
 | 
			
		||||
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
 | 
			
		||||
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,19 @@ struct mgcp_conn_peer {
 | 
			
		||||
 | 
			
		||||
	/*! 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,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
prefix=@prefix@
 | 
			
		||||
exec_prefix=@exec_prefix@
 | 
			
		||||
libdir=@libdir@
 | 
			
		||||
includedir=@includedir@
 | 
			
		||||
 | 
			
		||||
Name: Osmocom legacy Media Gateway Control Protocol library
 | 
			
		||||
Description: C Utility Library
 | 
			
		||||
Version: @VERSION@
 | 
			
		||||
Libs: -L${libdir} -losmo-legacy-mgcp
 | 
			
		||||
Cflags: -I${includedir}/
 | 
			
		||||
@@ -17,11 +17,9 @@
 | 
			
		||||
 | 
			
		||||
app_configs = {
 | 
			
		||||
    "osmo-mgw": ["doc/examples/osmo-mgw/osmo-mgw.cfg"],
 | 
			
		||||
    "osmo-bsc_mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apps = [(4243, "src/osmo-mgw/osmo-mgw", "OsmoMGW", "osmo-mgw"),
 | 
			
		||||
        (4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "osmo-bsc_mgcp"),
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
vty_command = ["./src/osmo-mgw/osmo-mgw", "-c",
 | 
			
		||||
 
 | 
			
		||||
@@ -21,13 +21,11 @@ AM_LDFLAGS = \
 | 
			
		||||
 | 
			
		||||
# Libraries
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	libosmo-legacy-mgcp \
 | 
			
		||||
	libosmo-mgcp-client \
 | 
			
		||||
	libosmo-mgcp \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
# Programs
 | 
			
		||||
SUBDIRS += \
 | 
			
		||||
	osmo-bsc_mgcp \
 | 
			
		||||
	osmo-mgw \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,51 +0,0 @@
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	$(all_includes) \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	-I$(top_builddir) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(LIBBCG729_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(LIBBCG729_LIBS) \
 | 
			
		||||
	$(LIBRARY_GSM) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
# This is not at all related to the release version, but a range of supported
 | 
			
		||||
# API versions. Read TODO_RELEASE in the source tree's root!
 | 
			
		||||
LEGACY_MGCP_LIBVERSION=1:0:1
 | 
			
		||||
 | 
			
		||||
lib_LTLIBRARIES = \
 | 
			
		||||
	libosmo-legacy-mgcp.la \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	g711common.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_legacy_mgcp_la_SOURCES = \
 | 
			
		||||
	mgcp_common.c \
 | 
			
		||||
	mgcp_protocol.c \
 | 
			
		||||
	mgcp_network.c \
 | 
			
		||||
	mgcp_vty.c \
 | 
			
		||||
	mgcp_osmux.c \
 | 
			
		||||
	mgcp_sdp.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
if BUILD_MGCP_TRANSCODING
 | 
			
		||||
libosmo_legacy_mgcp_la_SOURCES += \
 | 
			
		||||
	mgcp_transcode.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
libosmo_legacy_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LEGACY_MGCP_LIBVERSION)
 | 
			
		||||
@@ -1,187 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  PCM - A-Law conversion
 | 
			
		||||
 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org>
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License as published by
 | 
			
		||||
 *  the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 *  (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static inline int val_seg(int val)
 | 
			
		||||
{
 | 
			
		||||
	int r = 0;
 | 
			
		||||
	val >>= 7; /*7 = 4 + 3*/
 | 
			
		||||
	if (val & 0xf0) {
 | 
			
		||||
		val >>= 4;
 | 
			
		||||
		r += 4;
 | 
			
		||||
	}
 | 
			
		||||
	if (val & 0x0c) {
 | 
			
		||||
		val >>= 2;
 | 
			
		||||
		r += 2;
 | 
			
		||||
	}
 | 
			
		||||
	if (val & 0x02)
 | 
			
		||||
		r += 1;
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
 | 
			
		||||
 *
 | 
			
		||||
 * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data.
 | 
			
		||||
 *
 | 
			
		||||
 *		Linear Input Code	Compressed Code
 | 
			
		||||
 *	------------------------	---------------
 | 
			
		||||
 *	0000000wxyza			000wxyz
 | 
			
		||||
 *	0000001wxyza			001wxyz
 | 
			
		||||
 *	000001wxyzab			010wxyz
 | 
			
		||||
 *	00001wxyzabc			011wxyz
 | 
			
		||||
 *	0001wxyzabcd			100wxyz
 | 
			
		||||
 *	001wxyzabcde			101wxyz
 | 
			
		||||
 *	01wxyzabcdef			110wxyz
 | 
			
		||||
 *	1wxyzabcdefg			111wxyz
 | 
			
		||||
 *
 | 
			
		||||
 * For further information see John C. Bellamy's Digital Telephony, 1982,
 | 
			
		||||
 * John Wiley & Sons, pps 98-111 and 472-476.
 | 
			
		||||
 * G711 is designed for 13 bits input signal, this function add extra shifting to take this into account.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static inline unsigned char s16_to_alaw(int pcm_val)
 | 
			
		||||
{
 | 
			
		||||
	int		mask;
 | 
			
		||||
	int		seg;
 | 
			
		||||
	unsigned char	aval;
 | 
			
		||||
 | 
			
		||||
	if (pcm_val >= 0) {
 | 
			
		||||
		mask = 0xD5;
 | 
			
		||||
	} else {
 | 
			
		||||
		mask = 0x55;
 | 
			
		||||
		pcm_val = -pcm_val;
 | 
			
		||||
		if (pcm_val > 0x7fff)
 | 
			
		||||
			pcm_val = 0x7fff;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (pcm_val < 256) /*256 = 32 << 3*/
 | 
			
		||||
		aval = pcm_val >> 4; /*4 = 1 + 3*/
 | 
			
		||||
	else {
 | 
			
		||||
		/* Convert the scaled magnitude to segment number. */
 | 
			
		||||
		seg = val_seg(pcm_val);
 | 
			
		||||
		aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
 | 
			
		||||
	}
 | 
			
		||||
	return aval ^ mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
static inline int alaw_to_s16(unsigned char a_val)
 | 
			
		||||
{
 | 
			
		||||
	int		t;
 | 
			
		||||
	int		seg;
 | 
			
		||||
 | 
			
		||||
	a_val ^= 0x55;
 | 
			
		||||
	t = a_val & 0x7f;
 | 
			
		||||
	if (t < 16)
 | 
			
		||||
		t = (t << 4) + 8;
 | 
			
		||||
	else {
 | 
			
		||||
		seg = (t >> 4) & 0x07;
 | 
			
		||||
		t = ((t & 0x0f) << 4) + 0x108;
 | 
			
		||||
		t <<= seg -1;
 | 
			
		||||
	}
 | 
			
		||||
	return ((a_val & 0x80) ? t : -t);
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
 * s16_to_ulaw() - Convert a linear PCM value to u-law
 | 
			
		||||
 *
 | 
			
		||||
 * In order to simplify the encoding process, the original linear magnitude
 | 
			
		||||
 * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
 | 
			
		||||
 * (33 - 8191). The result can be seen in the following encoding table:
 | 
			
		||||
 *
 | 
			
		||||
 *	Biased Linear Input Code	Compressed Code
 | 
			
		||||
 *	------------------------	---------------
 | 
			
		||||
 *	00000001wxyza			000wxyz
 | 
			
		||||
 *	0000001wxyzab			001wxyz
 | 
			
		||||
 *	000001wxyzabc			010wxyz
 | 
			
		||||
 *	00001wxyzabcd			011wxyz
 | 
			
		||||
 *	0001wxyzabcde			100wxyz
 | 
			
		||||
 *	001wxyzabcdef			101wxyz
 | 
			
		||||
 *	01wxyzabcdefg			110wxyz
 | 
			
		||||
 *	1wxyzabcdefgh			111wxyz
 | 
			
		||||
 *
 | 
			
		||||
 * Each biased linear code has a leading 1 which identifies the segment
 | 
			
		||||
 * number. The value of the segment number is equal to 7 minus the number
 | 
			
		||||
 * of leading 0's. The quantization interval is directly available as the
 | 
			
		||||
 * four bits wxyz.  * The trailing bits (a - h) are ignored.
 | 
			
		||||
 *
 | 
			
		||||
 * Ordinarily the complement of the resulting code word is used for
 | 
			
		||||
 * transmission, and so the code word is complemented before it is returned.
 | 
			
		||||
 *
 | 
			
		||||
 * For further information see John C. Bellamy's Digital Telephony, 1982,
 | 
			
		||||
 * John Wiley & Sons, pps 98-111 and 472-476.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static inline unsigned char s16_to_ulaw(int pcm_val)	/* 2's complement (16-bit range) */
 | 
			
		||||
{
 | 
			
		||||
	int mask;
 | 
			
		||||
	int seg;
 | 
			
		||||
	unsigned char uval;
 | 
			
		||||
 | 
			
		||||
	if (pcm_val < 0) {
 | 
			
		||||
		pcm_val = 0x84 - pcm_val;
 | 
			
		||||
		mask = 0x7f;
 | 
			
		||||
	} else {
 | 
			
		||||
		pcm_val += 0x84;
 | 
			
		||||
		mask = 0xff;
 | 
			
		||||
	}
 | 
			
		||||
	if (pcm_val > 0x7fff)
 | 
			
		||||
		pcm_val = 0x7fff;
 | 
			
		||||
 | 
			
		||||
	/* Convert the scaled magnitude to segment number. */
 | 
			
		||||
	seg = val_seg(pcm_val);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Combine the sign, segment, quantization bits;
 | 
			
		||||
	 * and complement the code word.
 | 
			
		||||
	 */
 | 
			
		||||
	uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
 | 
			
		||||
	return uval ^ mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM
 | 
			
		||||
 *
 | 
			
		||||
 * First, a biased linear code is derived from the code word. An unbiased
 | 
			
		||||
 * output can then be obtained by subtracting 33 from the biased code.
 | 
			
		||||
 *
 | 
			
		||||
 * Note that this function expects to be passed the complement of the
 | 
			
		||||
 * original code word. This is in keeping with ISDN conventions.
 | 
			
		||||
 */
 | 
			
		||||
static inline int ulaw_to_s16(unsigned char u_val)
 | 
			
		||||
{
 | 
			
		||||
	int t;
 | 
			
		||||
 | 
			
		||||
	/* Complement to obtain normal u-law value. */
 | 
			
		||||
	u_val = ~u_val;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Extract and bias the quantization bits. Then
 | 
			
		||||
	 * shift up by the segment number and subtract out the bias.
 | 
			
		||||
	 */
 | 
			
		||||
	t = ((u_val & 0x0f) << 3) + 0x84;
 | 
			
		||||
	t <<= (u_val & 0x70) >> 4;
 | 
			
		||||
 | 
			
		||||
	return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84));
 | 
			
		||||
}
 | 
			
		||||
@@ -1,54 +0,0 @@
 | 
			
		||||
/* Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
			
		||||
/* Implementations useful both for the MGCP GW as well as MGCP GW clients */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp.h>
 | 
			
		||||
 | 
			
		||||
const struct value_string mgcp_connection_mode_strs[] = {
 | 
			
		||||
	{ MGCP_CONN_NONE, "none" },
 | 
			
		||||
	{ MGCP_CONN_RECV_SEND, "sendrecv" },
 | 
			
		||||
	{ MGCP_CONN_SEND_ONLY, "sendonly" },
 | 
			
		||||
	{ MGCP_CONN_RECV_ONLY, "recvonly" },
 | 
			
		||||
	{ MGCP_CONN_LOOPBACK, "loopback" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Ensure that the msg->l2h is NUL terminated. */
 | 
			
		||||
int mgcp_msg_terminate_nul(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */
 | 
			
		||||
	if (tail[-1] == '\0')
 | 
			
		||||
		/* nothing to do */;
 | 
			
		||||
	else if (msgb_tailroom(msg) > 0)
 | 
			
		||||
		tail[0] = '\0';
 | 
			
		||||
	else if (tail[-1] == '\r' || tail[-1] == '\n')
 | 
			
		||||
		tail[-1] = '\0';
 | 
			
		||||
	else {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: "
 | 
			
		||||
		     "Length: %d, Buffer size: %d\n",
 | 
			
		||||
		     msgb_l2len(msg), msg->data_len);
 | 
			
		||||
		return -ENOTSUP;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,588 +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 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_sched(&endp->osmux.out, osmuxh);
 | 
			
		||||
	}
 | 
			
		||||
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 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_sched(&endp->osmux.out, osmuxh);
 | 
			
		||||
	}
 | 
			
		||||
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;
 | 
			
		||||
			osmux_xfrm_output_set_tx_cb(&endp->osmux.out,
 | 
			
		||||
							scheduled_tx_net_cb, endp);
 | 
			
		||||
			break;
 | 
			
		||||
		case MGCP_BSC:
 | 
			
		||||
			endp->type = MGCP_OSMUX_BSC;
 | 
			
		||||
			osmux_xfrm_output_set_tx_cb(&endp->osmux.out,
 | 
			
		||||
							scheduled_tx_bts_cb, endp);
 | 
			
		||||
			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);
 | 
			
		||||
 | 
			
		||||
	/* We are closing, we don't need pending RTP packets to be transmitted */
 | 
			
		||||
	osmux_xfrm_output_set_tx_cb(&endp->osmux.out, NULL, NULL);
 | 
			
		||||
	osmux_xfrm_output_flush(&endp->osmux.out);
 | 
			
		||||
 | 
			
		||||
	osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid);
 | 
			
		||||
	endp->osmux.state = OSMUX_STATE_DISABLED;
 | 
			
		||||
	endp->osmux.cid = -1;
 | 
			
		||||
	osmux_handle_put(endp->osmux.in);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void osmux_release_cid(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	if (endp->osmux.allocated_cid >= 0)
 | 
			
		||||
		osmux_put_cid(endp->osmux.allocated_cid);
 | 
			
		||||
	endp->osmux.allocated_cid = -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void osmux_allocate_cid(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	osmux_release_cid(endp);
 | 
			
		||||
	endp->osmux.allocated_cid = osmux_get_cid();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* We don't need to send the dummy load for osmux so often as another endpoint
 | 
			
		||||
 * may have already punched the hole in the firewall. This approach is simple
 | 
			
		||||
 * though.
 | 
			
		||||
 */
 | 
			
		||||
int osmux_send_dummy(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	char buf[1 + sizeof(uint8_t)];
 | 
			
		||||
	struct in_addr addr_unset = {};
 | 
			
		||||
 | 
			
		||||
	buf[0] = MGCP_DUMMY_LOAD;
 | 
			
		||||
	memcpy(&buf[1], &endp->osmux.cid, sizeof(endp->osmux.cid));
 | 
			
		||||
 | 
			
		||||
	/* Wait until we have the connection information from MDCX */
 | 
			
		||||
	if (memcmp(&endp->net_end.addr, &addr_unset, sizeof(addr_unset)) == 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (endp->osmux.state == OSMUX_STATE_ACTIVATING) {
 | 
			
		||||
		if (osmux_enable_endpoint(endp, &endp->net_end.addr,
 | 
			
		||||
					  htons(endp->cfg->osmux_port)) < 0) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Could not activate osmux in endpoint %d\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		}
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Osmux CID %u for %s:%u is now enabled\n",
 | 
			
		||||
		     endp->osmux.cid, inet_ntoa(endp->net_end.addr),
 | 
			
		||||
		     endp->cfg->osmux_port);
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG,
 | 
			
		||||
	     "sending OSMUX dummy load to %s CID %u\n",
 | 
			
		||||
	     inet_ntoa(endp->net_end.addr), endp->osmux.cid);
 | 
			
		||||
 | 
			
		||||
	return mgcp_udp_send(osmux_fd.fd, &endp->net_end.addr,
 | 
			
		||||
			     htons(endp->cfg->osmux_port), buf, sizeof(buf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* bsc-nat allocates/releases the Osmux circuit ID */
 | 
			
		||||
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8];
 | 
			
		||||
 | 
			
		||||
int osmux_used_cid(void)
 | 
			
		||||
{
 | 
			
		||||
	int i, j, used = 0;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
 | 
			
		||||
		for (j = 0; j < 8; j++) {
 | 
			
		||||
			if (osmux_cid_bitmap[i] & (1 << j))
 | 
			
		||||
				used += 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return used;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osmux_get_cid(void)
 | 
			
		||||
{
 | 
			
		||||
	int i, j;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
 | 
			
		||||
		for (j = 0; j < 8; j++) {
 | 
			
		||||
			if (osmux_cid_bitmap[i] & (1 << j))
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			osmux_cid_bitmap[i] |= (1 << j);
 | 
			
		||||
			LOGP(DLMGCP, LOGL_DEBUG,
 | 
			
		||||
			     "Allocating Osmux CID %u from pool\n", (i * 8) + j);
 | 
			
		||||
			return (i * 8) + j;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "All Osmux circuits are in use!\n");
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void osmux_put_cid(uint8_t osmux_cid)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid);
 | 
			
		||||
	osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8));
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,305 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Some SDP file parsing...
 | 
			
		||||
 *
 | 
			
		||||
 * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2014 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp_internal.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
struct sdp_rtp_map {
 | 
			
		||||
	/* the type */
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	/* null, static or later dynamic codec name */
 | 
			
		||||
	char *codec_name;
 | 
			
		||||
	/* A pointer to the original line for later parsing */
 | 
			
		||||
	char *map_line;
 | 
			
		||||
 | 
			
		||||
	int rate;
 | 
			
		||||
	int channels;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
 | 
			
		||||
			int payload_type, const char *audio_name)
 | 
			
		||||
{
 | 
			
		||||
	int rate = codec->rate;
 | 
			
		||||
	int channels = codec->channels;
 | 
			
		||||
	char audio_codec[64];
 | 
			
		||||
 | 
			
		||||
	talloc_free(codec->subtype_name);
 | 
			
		||||
	codec->subtype_name = NULL;
 | 
			
		||||
	talloc_free(codec->audio_name);
 | 
			
		||||
	codec->audio_name = NULL;
 | 
			
		||||
 | 
			
		||||
	if (payload_type != PTYPE_UNDEFINED)
 | 
			
		||||
		codec->payload_type = payload_type;
 | 
			
		||||
 | 
			
		||||
	if (!audio_name) {
 | 
			
		||||
		switch (payload_type) {
 | 
			
		||||
		case 0: audio_name = "PCMU/8000/1"; break;
 | 
			
		||||
		case 3: audio_name = "GSM/8000/1"; break;
 | 
			
		||||
		case 8: audio_name = "PCMA/8000/1"; break;
 | 
			
		||||
		case 18: audio_name = "G729/8000/1"; break;
 | 
			
		||||
		default:
 | 
			
		||||
			 /* Payload type is unknown, don't change rate and
 | 
			
		||||
			  * channels. */
 | 
			
		||||
			 /* TODO: return value? */
 | 
			
		||||
			 return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sscanf(audio_name, "%63[^/]/%d/%d",
 | 
			
		||||
		   audio_codec, &rate, &channels) < 1)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	codec->rate = rate;
 | 
			
		||||
	codec->channels = channels;
 | 
			
		||||
	codec->subtype_name = talloc_strdup(ctx, audio_codec);
 | 
			
		||||
	codec->audio_name = talloc_strdup(ctx, audio_name);
 | 
			
		||||
 | 
			
		||||
	if (!strcmp(audio_codec, "G729")) {
 | 
			
		||||
		codec->frame_duration_num = 10;
 | 
			
		||||
		codec->frame_duration_den = 1000;
 | 
			
		||||
	} else {
 | 
			
		||||
		codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
 | 
			
		||||
		codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (payload_type < 0) {
 | 
			
		||||
		payload_type = 96;
 | 
			
		||||
		if (rate == 8000 && channels == 1) {
 | 
			
		||||
			if (!strcmp(audio_codec, "GSM"))
 | 
			
		||||
				payload_type = 3;
 | 
			
		||||
			else if (!strcmp(audio_codec, "PCMA"))
 | 
			
		||||
				payload_type = 8;
 | 
			
		||||
			else if (!strcmp(audio_codec, "PCMU"))
 | 
			
		||||
				payload_type = 0;
 | 
			
		||||
			else if (!strcmp(audio_codec, "G729"))
 | 
			
		||||
				payload_type = 18;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		codec->payload_type = payload_type;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (channels != 1)
 | 
			
		||||
		LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
		     "Channels != 1 in SDP: '%s'\n", audio_name);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < used; ++i) {
 | 
			
		||||
		switch (codecs[i].payload_type) {
 | 
			
		||||
		case 0:
 | 
			
		||||
			codecs[i].codec_name = "PCMU";
 | 
			
		||||
			codecs[i].rate = 8000;
 | 
			
		||||
			codecs[i].channels = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		case 3:
 | 
			
		||||
			codecs[i].codec_name = "GSM";
 | 
			
		||||
			codecs[i].rate = 8000;
 | 
			
		||||
			codecs[i].channels = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		case 8:
 | 
			
		||||
			codecs[i].codec_name = "PCMA";
 | 
			
		||||
			codecs[i].rate = 8000;
 | 
			
		||||
			codecs[i].channels = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		case 18:
 | 
			
		||||
			codecs[i].codec_name = "G729";
 | 
			
		||||
			codecs[i].rate = 8000;
 | 
			
		||||
			codecs[i].channels = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, int payload, char *audio_name)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < used; ++i) {
 | 
			
		||||
		char audio_codec[64];
 | 
			
		||||
		int rate = -1;
 | 
			
		||||
		int channels = -1;
 | 
			
		||||
		if (codecs[i].payload_type != payload)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (sscanf(audio_name, "%63[^/]/%d/%d",
 | 
			
		||||
				audio_codec, &rate, &channels) < 1) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n", audio_name);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		codecs[i].map_line = talloc_strdup(ctx, audio_name);
 | 
			
		||||
		codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
 | 
			
		||||
		codecs[i].rate = rate;
 | 
			
		||||
		codecs[i].channels = channels;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, audio_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int is_codec_compatible(struct mgcp_endpoint *endp, struct sdp_rtp_map *codec)
 | 
			
		||||
{
 | 
			
		||||
	char *bts_codec;
 | 
			
		||||
	char audio_codec[64];
 | 
			
		||||
 | 
			
		||||
	if (!codec->codec_name)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * GSM, GSM/8000 and GSM/8000/1 should all be compatible.. let's go
 | 
			
		||||
	 * by name first.
 | 
			
		||||
	 */
 | 
			
		||||
	bts_codec = endp->tcfg->audio_name;
 | 
			
		||||
	if (sscanf(bts_codec, "%63[^/]/%*d/%*d", audio_codec) < 1)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	return strcasecmp(audio_codec, codec->codec_name) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
 | 
			
		||||
{
 | 
			
		||||
	struct sdp_rtp_map codecs[10];
 | 
			
		||||
	int codecs_used = 0;
 | 
			
		||||
	char *line;
 | 
			
		||||
	int maxptime = -1;
 | 
			
		||||
	int i;
 | 
			
		||||
	int codecs_assigned = 0;
 | 
			
		||||
	void *tmp_ctx = talloc_new(NULL);
 | 
			
		||||
 | 
			
		||||
	memset(&codecs, 0, sizeof(codecs));
 | 
			
		||||
 | 
			
		||||
	for_each_line(line, p->save) {
 | 
			
		||||
		switch (line[0]) {
 | 
			
		||||
		case 'o':
 | 
			
		||||
		case 's':
 | 
			
		||||
		case 't':
 | 
			
		||||
		case 'v':
 | 
			
		||||
			/* skip these SDP attributes */
 | 
			
		||||
			break;
 | 
			
		||||
		case 'a': {
 | 
			
		||||
			int payload;
 | 
			
		||||
			int ptime, ptime2 = 0;
 | 
			
		||||
			char audio_name[64];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
			if (sscanf(line, "a=rtpmap:%d %63s",
 | 
			
		||||
				   &payload, audio_name) == 2) {
 | 
			
		||||
				codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
 | 
			
		||||
			} else if (sscanf(line, "a=ptime:%d-%d",
 | 
			
		||||
					  &ptime, &ptime2) >= 1) {
 | 
			
		||||
				if (ptime2 > 0 && ptime2 != ptime)
 | 
			
		||||
					rtp->packet_duration_ms = 0;
 | 
			
		||||
				else
 | 
			
		||||
					rtp->packet_duration_ms = ptime;
 | 
			
		||||
			} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
 | 
			
		||||
				maxptime = ptime2;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case 'm': {
 | 
			
		||||
			int port, rc;
 | 
			
		||||
 | 
			
		||||
			rc = sscanf(line, "m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
 | 
			
		||||
					&port,
 | 
			
		||||
					&codecs[0].payload_type,
 | 
			
		||||
					&codecs[1].payload_type,
 | 
			
		||||
					&codecs[2].payload_type,
 | 
			
		||||
					&codecs[3].payload_type,
 | 
			
		||||
					&codecs[4].payload_type,
 | 
			
		||||
					&codecs[5].payload_type,
 | 
			
		||||
					&codecs[6].payload_type,
 | 
			
		||||
					&codecs[7].payload_type,
 | 
			
		||||
					&codecs[8].payload_type,
 | 
			
		||||
					&codecs[9].payload_type);
 | 
			
		||||
			if (rc >= 2) {
 | 
			
		||||
				rtp->rtp_port = htons(port);
 | 
			
		||||
				rtp->rtcp_port = htons(port + 1);
 | 
			
		||||
				codecs_used = rc - 1;
 | 
			
		||||
				codecs_initialize(tmp_ctx, codecs, codecs_used);
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case 'c': {
 | 
			
		||||
			char ipv4[16];
 | 
			
		||||
 | 
			
		||||
			if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
 | 
			
		||||
				inet_aton(ipv4, &rtp->addr);
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		default:
 | 
			
		||||
			if (p->endp)
 | 
			
		||||
				LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
				     "Unhandled SDP option: '%c'/%d on 0x%x\n",
 | 
			
		||||
				     line[0], line[0], ENDPOINT_NUMBER(p->endp));
 | 
			
		||||
			else
 | 
			
		||||
				LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
				     "Unhandled SDP option: '%c'/%d\n",
 | 
			
		||||
				     line[0], line[0]);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Now select the primary and alt_codec */
 | 
			
		||||
	for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
 | 
			
		||||
		struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
 | 
			
		||||
					&rtp->codec : &rtp->alt_codec;
 | 
			
		||||
 | 
			
		||||
		if (endp->tcfg->no_audio_transcoding &&
 | 
			
		||||
			!is_codec_compatible(endp, &codecs[i])) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n",
 | 
			
		||||
				codecs[i].codec_name);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		mgcp_set_audio_info(p->cfg, codec,
 | 
			
		||||
					codecs[i].payload_type,
 | 
			
		||||
					codecs[i].map_line);
 | 
			
		||||
		codecs_assigned += 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (codecs_assigned > 0) {
 | 
			
		||||
		/* TODO/XXX: Store this per codec and derive it on use */
 | 
			
		||||
		if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
 | 
			
		||||
				rtp->codec.frame_duration_num * 1500) {
 | 
			
		||||
			/* more than 1 frame */
 | 
			
		||||
			rtp->packet_duration_ms = 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
		     "Got media info via SDP: port %d, payload %d (%s), "
 | 
			
		||||
		     "duration %d, addr %s\n",
 | 
			
		||||
		     ntohs(rtp->rtp_port), rtp->codec.payload_type,
 | 
			
		||||
		     rtp->codec.subtype_name ? rtp->codec.subtype_name : "unknown",
 | 
			
		||||
		     rtp->packet_duration_ms, inet_ntoa(rtp->addr));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(tmp_ctx);
 | 
			
		||||
	return codecs_assigned > 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,611 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2014 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "g711common.h"
 | 
			
		||||
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp_transcode.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/netif/rtp.h>
 | 
			
		||||
 | 
			
		||||
int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_process_rtp_state *state = state_;
 | 
			
		||||
	if (dst)
 | 
			
		||||
		return (nsamples >= 0 ?
 | 
			
		||||
			nsamples / state->dst_samples_per_frame :
 | 
			
		||||
			1) * state->dst_frame_size;
 | 
			
		||||
	else
 | 
			
		||||
		return (nsamples >= 0 ?
 | 
			
		||||
			nsamples / state->src_samples_per_frame :
 | 
			
		||||
			1) * state->src_frame_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static enum audio_format get_audio_format(const struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	if (codec->subtype_name) {
 | 
			
		||||
		if (!strcasecmp("GSM", codec->subtype_name))
 | 
			
		||||
			return AF_GSM;
 | 
			
		||||
		if (!strcasecmp("PCMA", codec->subtype_name))
 | 
			
		||||
			return AF_PCMA;
 | 
			
		||||
		if (!strcasecmp("PCMU", codec->subtype_name))
 | 
			
		||||
			return AF_PCMU;
 | 
			
		||||
#ifdef HAVE_BCG729
 | 
			
		||||
		if (!strcasecmp("G729", codec->subtype_name))
 | 
			
		||||
			return AF_G729;
 | 
			
		||||
#endif
 | 
			
		||||
		if (!strcasecmp("L16", codec->subtype_name))
 | 
			
		||||
			return AF_L16;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (codec->payload_type) {
 | 
			
		||||
	case 0 /* PCMU */:
 | 
			
		||||
		return AF_PCMU;
 | 
			
		||||
	case 3 /* GSM */:
 | 
			
		||||
		return AF_GSM;
 | 
			
		||||
	case 8 /* PCMA */:
 | 
			
		||||
		return AF_PCMA;
 | 
			
		||||
#ifdef HAVE_BCG729
 | 
			
		||||
	case 18 /* G.729 */:
 | 
			
		||||
		return AF_G729;
 | 
			
		||||
#endif
 | 
			
		||||
	case 11 /* L16 */:
 | 
			
		||||
		return AF_L16;
 | 
			
		||||
	default:
 | 
			
		||||
		return AF_INVALID;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void l16_encode(short *sample, unsigned char *buf, size_t n)
 | 
			
		||||
{
 | 
			
		||||
	for (; n > 0; --n, ++sample, buf += 2) {
 | 
			
		||||
		buf[0] = sample[0] >> 8;
 | 
			
		||||
		buf[1] = sample[0] & 0xff;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void l16_decode(unsigned char *buf, short *sample, size_t n)
 | 
			
		||||
{
 | 
			
		||||
	for (; n > 0; --n, ++sample, buf += 2)
 | 
			
		||||
		sample[0] = ((short)buf[0] << 8) | buf[1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void alaw_encode(short *sample, unsigned char *buf, size_t n)
 | 
			
		||||
{
 | 
			
		||||
	for (; n > 0; --n)
 | 
			
		||||
		*(buf++) = s16_to_alaw(*(sample++));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void alaw_decode(unsigned char *buf, short *sample, size_t n)
 | 
			
		||||
{
 | 
			
		||||
	for (; n > 0; --n)
 | 
			
		||||
		*(sample++) = alaw_to_s16(*(buf++));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ulaw_encode(short *sample, unsigned char *buf, size_t n)
 | 
			
		||||
{
 | 
			
		||||
	for (; n > 0; --n)
 | 
			
		||||
		*(buf++) = s16_to_ulaw(*(sample++));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ulaw_decode(unsigned char *buf, short *sample, size_t n)
 | 
			
		||||
{
 | 
			
		||||
	for (; n > 0; --n)
 | 
			
		||||
		*(sample++) = ulaw_to_s16(*(buf++));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int processing_state_destructor(struct mgcp_process_rtp_state *state)
 | 
			
		||||
{
 | 
			
		||||
	switch (state->src_fmt) {
 | 
			
		||||
	case AF_GSM:
 | 
			
		||||
		if (state->src.gsm_handle)
 | 
			
		||||
			gsm_destroy(state->src.gsm_handle);
 | 
			
		||||
		break;
 | 
			
		||||
#ifdef HAVE_BCG729
 | 
			
		||||
	case AF_G729:
 | 
			
		||||
		if (state->src.g729_dec)
 | 
			
		||||
			closeBcg729DecoderChannel(state->src.g729_dec);
 | 
			
		||||
		break;
 | 
			
		||||
#endif
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	switch (state->dst_fmt) {
 | 
			
		||||
	case AF_GSM:
 | 
			
		||||
		if (state->dst.gsm_handle)
 | 
			
		||||
			gsm_destroy(state->dst.gsm_handle);
 | 
			
		||||
		break;
 | 
			
		||||
#ifdef HAVE_BCG729
 | 
			
		||||
	case AF_G729:
 | 
			
		||||
		if (state->dst.g729_enc)
 | 
			
		||||
			closeBcg729EncoderChannel(state->dst.g729_enc);
 | 
			
		||||
		break;
 | 
			
		||||
#endif
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mgcp_transcoding_setup(struct mgcp_endpoint *endp,
 | 
			
		||||
			   struct mgcp_rtp_end *dst_end,
 | 
			
		||||
			   struct mgcp_rtp_end *src_end)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_process_rtp_state *state;
 | 
			
		||||
	enum audio_format src_fmt, dst_fmt;
 | 
			
		||||
	const struct mgcp_rtp_codec *dst_codec = &dst_end->codec;
 | 
			
		||||
 | 
			
		||||
	/* cleanup first */
 | 
			
		||||
	if (dst_end->rtp_process_data) {
 | 
			
		||||
		talloc_free(dst_end->rtp_process_data);
 | 
			
		||||
		dst_end->rtp_process_data = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!src_end)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	const struct mgcp_rtp_codec *src_codec = &src_end->codec;
 | 
			
		||||
 | 
			
		||||
	if (endp->tcfg->no_audio_transcoding) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
			"Transcoding disabled on endpoint 0x%x\n",
 | 
			
		||||
			ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	src_fmt = get_audio_format(src_codec);
 | 
			
		||||
	dst_fmt = get_audio_format(dst_codec);
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
	     "Checking transcoding: %s (%d) -> %s (%d)\n",
 | 
			
		||||
	     src_codec->subtype_name, src_codec->payload_type,
 | 
			
		||||
	     dst_codec->subtype_name, dst_codec->payload_type);
 | 
			
		||||
 | 
			
		||||
	if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) {
 | 
			
		||||
		if (!src_codec->subtype_name || !dst_codec->subtype_name)
 | 
			
		||||
			/* Not enough info, do nothing */
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		if (strcasecmp(src_codec->subtype_name, dst_codec->subtype_name) == 0)
 | 
			
		||||
			/* Nothing to do */
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Cannot transcode: %s codec not supported (%s -> %s).\n",
 | 
			
		||||
		     src_fmt != AF_INVALID ? "destination" : "source",
 | 
			
		||||
		     src_codec->audio_name, dst_codec->audio_name);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (src_codec->rate && dst_codec->rate && src_codec->rate != dst_codec->rate) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Cannot transcode: rate conversion (%d -> %d) not supported.\n",
 | 
			
		||||
		     src_codec->rate, dst_codec->rate);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state = talloc_zero(endp->tcfg->cfg, struct mgcp_process_rtp_state);
 | 
			
		||||
	talloc_set_destructor(state, processing_state_destructor);
 | 
			
		||||
	dst_end->rtp_process_data = state;
 | 
			
		||||
 | 
			
		||||
	state->src_fmt = src_fmt;
 | 
			
		||||
 | 
			
		||||
	switch (state->src_fmt) {
 | 
			
		||||
	case AF_L16:
 | 
			
		||||
	case AF_S16:
 | 
			
		||||
		state->src_frame_size = 80 * sizeof(short);
 | 
			
		||||
		state->src_samples_per_frame = 80;
 | 
			
		||||
		break;
 | 
			
		||||
	case AF_GSM:
 | 
			
		||||
		state->src_frame_size = sizeof(gsm_frame);
 | 
			
		||||
		state->src_samples_per_frame = 160;
 | 
			
		||||
		state->src.gsm_handle = gsm_create();
 | 
			
		||||
		if (!state->src.gsm_handle) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Failed to initialize GSM decoder.\n");
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
#ifdef HAVE_BCG729
 | 
			
		||||
	case AF_G729:
 | 
			
		||||
		state->src_frame_size = 10;
 | 
			
		||||
		state->src_samples_per_frame = 80;
 | 
			
		||||
		state->src.g729_dec = initBcg729DecoderChannel();
 | 
			
		||||
		if (!state->src.g729_dec) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Failed to initialize G.729 decoder.\n");
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
#endif
 | 
			
		||||
	case AF_PCMU:
 | 
			
		||||
	case AF_PCMA:
 | 
			
		||||
		state->src_frame_size = 80;
 | 
			
		||||
		state->src_samples_per_frame = 80;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state->dst_fmt = dst_fmt;
 | 
			
		||||
 | 
			
		||||
	switch (state->dst_fmt) {
 | 
			
		||||
	case AF_L16:
 | 
			
		||||
	case AF_S16:
 | 
			
		||||
		state->dst_frame_size = 80*sizeof(short);
 | 
			
		||||
		state->dst_samples_per_frame = 80;
 | 
			
		||||
		break;
 | 
			
		||||
	case AF_GSM:
 | 
			
		||||
		state->dst_frame_size = sizeof(gsm_frame);
 | 
			
		||||
		state->dst_samples_per_frame = 160;
 | 
			
		||||
		state->dst.gsm_handle = gsm_create();
 | 
			
		||||
		if (!state->dst.gsm_handle) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Failed to initialize GSM encoder.\n");
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
#ifdef HAVE_BCG729
 | 
			
		||||
	case AF_G729:
 | 
			
		||||
		state->dst_frame_size = 10;
 | 
			
		||||
		state->dst_samples_per_frame = 80;
 | 
			
		||||
		state->dst.g729_enc = initBcg729EncoderChannel();
 | 
			
		||||
		if (!state->dst.g729_enc) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Failed to initialize G.729 decoder.\n");
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
#endif
 | 
			
		||||
	case AF_PCMU:
 | 
			
		||||
	case AF_PCMA:
 | 
			
		||||
		state->dst_frame_size = 80;
 | 
			
		||||
		state->dst_samples_per_frame = 80;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (dst_end->force_output_ptime)
 | 
			
		||||
		state->dst_packet_duration = mgcp_rtp_packet_duration(endp, dst_end);
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_INFO,
 | 
			
		||||
	     "Initialized RTP processing on: 0x%x "
 | 
			
		||||
	     "conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp),
 | 
			
		||||
	     src_fmt, src_codec->payload_type, src_codec->rate, src_end->fmtp_extra,
 | 
			
		||||
	     dst_fmt, dst_codec->payload_type, dst_codec->rate, dst_end->fmtp_extra);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp,
 | 
			
		||||
					  int *payload_type,
 | 
			
		||||
					  const char**audio_name,
 | 
			
		||||
					  const char**fmtp_extra)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_process_rtp_state *state = endp->net_end.rtp_process_data;
 | 
			
		||||
	struct mgcp_rtp_codec *net_codec = &endp->net_end.codec;
 | 
			
		||||
	struct mgcp_rtp_codec *bts_codec = &endp->bts_end.codec;
 | 
			
		||||
 | 
			
		||||
	if (!state || net_codec->payload_type < 0) {
 | 
			
		||||
		*payload_type = bts_codec->payload_type;
 | 
			
		||||
		*audio_name = bts_codec->audio_name;
 | 
			
		||||
		*fmtp_extra = endp->bts_end.fmtp_extra;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*payload_type = net_codec->payload_type;
 | 
			
		||||
	*audio_name = net_codec->audio_name;
 | 
			
		||||
	*fmtp_extra = endp->net_end.fmtp_extra;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int decode_audio(struct mgcp_process_rtp_state *state,
 | 
			
		||||
			uint8_t **src, size_t *nbytes)
 | 
			
		||||
{
 | 
			
		||||
	while (*nbytes >= state->src_frame_size) {
 | 
			
		||||
		if (state->sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(state->samples)) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Sample buffer too small: %zu > %zu.\n",
 | 
			
		||||
			     state->sample_cnt + state->src_samples_per_frame,
 | 
			
		||||
			     ARRAY_SIZE(state->samples));
 | 
			
		||||
			return -ENOSPC;
 | 
			
		||||
		}
 | 
			
		||||
		switch (state->src_fmt) {
 | 
			
		||||
		case AF_GSM:
 | 
			
		||||
			if (gsm_decode(state->src.gsm_handle,
 | 
			
		||||
				       (gsm_byte *)*src, state->samples + state->sample_cnt) < 0) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
				     "Failed to decode GSM.\n");
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
#ifdef HAVE_BCG729
 | 
			
		||||
		case AF_G729:
 | 
			
		||||
			bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt);
 | 
			
		||||
			break;
 | 
			
		||||
#endif
 | 
			
		||||
		case AF_PCMU:
 | 
			
		||||
			ulaw_decode(*src, state->samples + state->sample_cnt,
 | 
			
		||||
				    state->src_samples_per_frame);
 | 
			
		||||
			break;
 | 
			
		||||
		case AF_PCMA:
 | 
			
		||||
			alaw_decode(*src, state->samples + state->sample_cnt,
 | 
			
		||||
				    state->src_samples_per_frame);
 | 
			
		||||
			break;
 | 
			
		||||
		case AF_S16:
 | 
			
		||||
			memmove(state->samples + state->sample_cnt, *src,
 | 
			
		||||
				state->src_frame_size);
 | 
			
		||||
			break;
 | 
			
		||||
		case AF_L16:
 | 
			
		||||
			l16_decode(*src, state->samples + state->sample_cnt,
 | 
			
		||||
				   state->src_samples_per_frame);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		*src        += state->src_frame_size;
 | 
			
		||||
		*nbytes     -= state->src_frame_size;
 | 
			
		||||
		state->sample_cnt += state->src_samples_per_frame;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int encode_audio(struct mgcp_process_rtp_state *state,
 | 
			
		||||
			uint8_t *dst, size_t buf_size, size_t max_samples)
 | 
			
		||||
{
 | 
			
		||||
	int nbytes = 0;
 | 
			
		||||
	size_t nsamples = 0;
 | 
			
		||||
	/* Encode samples into dst */
 | 
			
		||||
	while (nsamples + state->dst_samples_per_frame <= max_samples) {
 | 
			
		||||
		if (nbytes + state->dst_frame_size > buf_size) {
 | 
			
		||||
			if (nbytes > 0)
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			/* Not even one frame fits into the buffer */
 | 
			
		||||
			LOGP(DLMGCP, LOGL_INFO,
 | 
			
		||||
			     "Encoding (RTP) buffer too small: %zu > %zu.\n",
 | 
			
		||||
			     nbytes + state->dst_frame_size, buf_size);
 | 
			
		||||
			return -ENOSPC;
 | 
			
		||||
		}
 | 
			
		||||
		switch (state->dst_fmt) {
 | 
			
		||||
		case AF_GSM:
 | 
			
		||||
			gsm_encode(state->dst.gsm_handle,
 | 
			
		||||
				   state->samples + state->sample_offs, dst);
 | 
			
		||||
			break;
 | 
			
		||||
#ifdef HAVE_BCG729
 | 
			
		||||
		case AF_G729:
 | 
			
		||||
			bcg729Encoder(state->dst.g729_enc,
 | 
			
		||||
				      state->samples + state->sample_offs, dst);
 | 
			
		||||
			break;
 | 
			
		||||
#endif
 | 
			
		||||
		case AF_PCMU:
 | 
			
		||||
			ulaw_encode(state->samples + state->sample_offs, dst,
 | 
			
		||||
				    state->src_samples_per_frame);
 | 
			
		||||
			break;
 | 
			
		||||
		case AF_PCMA:
 | 
			
		||||
			alaw_encode(state->samples + state->sample_offs, dst,
 | 
			
		||||
				    state->src_samples_per_frame);
 | 
			
		||||
			break;
 | 
			
		||||
		case AF_S16:
 | 
			
		||||
			memmove(dst, state->samples + state->sample_offs,
 | 
			
		||||
				state->dst_frame_size);
 | 
			
		||||
			break;
 | 
			
		||||
		case AF_L16:
 | 
			
		||||
			l16_encode(state->samples + state->sample_offs, dst,
 | 
			
		||||
				   state->src_samples_per_frame);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		dst        += state->dst_frame_size;
 | 
			
		||||
		nbytes     += state->dst_frame_size;
 | 
			
		||||
		state->sample_offs += state->dst_samples_per_frame;
 | 
			
		||||
		nsamples   += state->dst_samples_per_frame;
 | 
			
		||||
	}
 | 
			
		||||
	state->sample_cnt -= nsamples;
 | 
			
		||||
	return nbytes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct mgcp_rtp_end *source_for_dest(struct mgcp_endpoint *endp,
 | 
			
		||||
					struct mgcp_rtp_end *dst_end)
 | 
			
		||||
{
 | 
			
		||||
	if (&endp->bts_end == dst_end)
 | 
			
		||||
		return &endp->net_end;
 | 
			
		||||
	else if (&endp->net_end == dst_end)
 | 
			
		||||
		return &endp->bts_end;
 | 
			
		||||
	OSMO_ASSERT(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * With some modems we get offered multiple codecs
 | 
			
		||||
 * and we have selected one of them. It might not
 | 
			
		||||
 * be the right one and we need to detect this with
 | 
			
		||||
 * the first audio packets. One difficulty is that
 | 
			
		||||
 * we patch the rtp payload type in place, so we
 | 
			
		||||
 * need to discuss this.
 | 
			
		||||
 */
 | 
			
		||||
struct mgcp_process_rtp_state *check_transcode_state(
 | 
			
		||||
				struct mgcp_endpoint *endp,
 | 
			
		||||
				struct mgcp_rtp_end *dst_end,
 | 
			
		||||
				struct rtp_hdr *rtp_hdr)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_rtp_end *src_end;
 | 
			
		||||
 | 
			
		||||
	/* Only deal with messages from net to bts */
 | 
			
		||||
	if (&endp->bts_end != dst_end)
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	src_end = source_for_dest(endp, dst_end);
 | 
			
		||||
 | 
			
		||||
	/* Already patched */
 | 
			
		||||
	if (rtp_hdr->payload_type == dst_end->codec.payload_type)
 | 
			
		||||
		goto done;
 | 
			
		||||
	/* The payload we expect */
 | 
			
		||||
	if (rtp_hdr->payload_type == src_end->codec.payload_type)
 | 
			
		||||
		goto done;
 | 
			
		||||
	/* The matching alternate payload type? Then switch */
 | 
			
		||||
	if (rtp_hdr->payload_type == src_end->alt_codec.payload_type) {
 | 
			
		||||
		struct mgcp_config *cfg = endp->cfg;
 | 
			
		||||
		struct mgcp_rtp_codec tmp_codec = src_end->alt_codec;
 | 
			
		||||
		src_end->alt_codec = src_end->codec;
 | 
			
		||||
		src_end->codec = tmp_codec;
 | 
			
		||||
		cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end);
 | 
			
		||||
		cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return dst_end->rtp_process_data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
				struct mgcp_rtp_end *dst_end,
 | 
			
		||||
			     char *data, int *len, int buf_size)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_process_rtp_state *state;
 | 
			
		||||
	const size_t rtp_hdr_size = sizeof(struct rtp_hdr);
 | 
			
		||||
	struct rtp_hdr *rtp_hdr = (struct rtp_hdr *) data;
 | 
			
		||||
	char *payload_data = (char *) &rtp_hdr->data[0];
 | 
			
		||||
	int payload_len = *len - rtp_hdr_size;
 | 
			
		||||
	uint8_t *src = (uint8_t *)payload_data;
 | 
			
		||||
	uint8_t *dst = (uint8_t *)payload_data;
 | 
			
		||||
	size_t nbytes = payload_len;
 | 
			
		||||
	size_t nsamples;
 | 
			
		||||
	size_t max_samples;
 | 
			
		||||
	uint32_t ts_no;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	state = check_transcode_state(endp, dst_end, rtp_hdr);
 | 
			
		||||
	if (!state)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (state->src_fmt == state->dst_fmt) {
 | 
			
		||||
		if (!state->dst_packet_duration)
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		/* TODO: repackage without transcoding */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* If the remaining samples do not fit into a fixed ptime,
 | 
			
		||||
	 * a) discard them, if the next packet is much later
 | 
			
		||||
	 * b) add silence and * send it, if the current packet is not
 | 
			
		||||
	 *    yet too late
 | 
			
		||||
	 * c) append the sample data, if the timestamp matches exactly
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	/* TODO: check payload type (-> G.711 comfort noise) */
 | 
			
		||||
 | 
			
		||||
	if (payload_len > 0) {
 | 
			
		||||
		ts_no = ntohl(rtp_hdr->timestamp);
 | 
			
		||||
		if (!state->is_running) {
 | 
			
		||||
			state->next_seq = ntohs(rtp_hdr->sequence);
 | 
			
		||||
			state->next_time = ts_no;
 | 
			
		||||
			state->is_running = 1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		if (state->sample_cnt > 0) {
 | 
			
		||||
			int32_t delta = ts_no - state->next_time;
 | 
			
		||||
			/* TODO: check sequence? reordering? packet loss? */
 | 
			
		||||
 | 
			
		||||
			if (delta > state->sample_cnt) {
 | 
			
		||||
				/* There is a time gap between the last packet
 | 
			
		||||
				 * and the current one. Just discard the
 | 
			
		||||
				 * partial data that is left in the buffer.
 | 
			
		||||
				 * TODO: This can be improved by adding silence
 | 
			
		||||
				 * instead if the delta is small enough.
 | 
			
		||||
				 */
 | 
			
		||||
				LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
					"0x%x dropping sample buffer due delta=%d sample_cnt=%zu\n",
 | 
			
		||||
					ENDPOINT_NUMBER(endp), delta, state->sample_cnt);
 | 
			
		||||
				state->sample_cnt = 0;
 | 
			
		||||
				state->next_time = ts_no;
 | 
			
		||||
			} else if (delta < 0) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
				     "RTP time jumps backwards, delta = %d, "
 | 
			
		||||
				     "discarding buffered samples\n",
 | 
			
		||||
				     delta);
 | 
			
		||||
				state->sample_cnt = 0;
 | 
			
		||||
				state->sample_offs = 0;
 | 
			
		||||
				return -EAGAIN;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* Make sure the samples start without offset */
 | 
			
		||||
			if (state->sample_offs && state->sample_cnt)
 | 
			
		||||
				memmove(&state->samples[0],
 | 
			
		||||
					&state->samples[state->sample_offs],
 | 
			
		||||
					state->sample_cnt *
 | 
			
		||||
					sizeof(state->samples[0]));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		state->sample_offs = 0;
 | 
			
		||||
 | 
			
		||||
		/* Append decoded audio to samples */
 | 
			
		||||
		decode_audio(state, &src, &nbytes);
 | 
			
		||||
 | 
			
		||||
		if (nbytes > 0)
 | 
			
		||||
			LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
			     "Skipped audio frame in RTP packet: %zu octets\n",
 | 
			
		||||
			     nbytes);
 | 
			
		||||
	} else
 | 
			
		||||
		ts_no = state->next_time;
 | 
			
		||||
 | 
			
		||||
	if (state->sample_cnt < state->dst_packet_duration)
 | 
			
		||||
		return -EAGAIN;
 | 
			
		||||
 | 
			
		||||
	max_samples =
 | 
			
		||||
		state->dst_packet_duration ?
 | 
			
		||||
		state->dst_packet_duration : state->sample_cnt;
 | 
			
		||||
 | 
			
		||||
	nsamples = state->sample_cnt;
 | 
			
		||||
 | 
			
		||||
	rc = encode_audio(state, dst, buf_size, max_samples);
 | 
			
		||||
	/*
 | 
			
		||||
	 * There were no samples to encode?
 | 
			
		||||
	 * TODO: how does this work for comfort noise?
 | 
			
		||||
	 */
 | 
			
		||||
	if (rc == 0)
 | 
			
		||||
		return -ENOMSG;
 | 
			
		||||
	/* Any other error during the encoding */
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	nsamples -= state->sample_cnt;
 | 
			
		||||
 | 
			
		||||
	*len = rtp_hdr_size + rc;
 | 
			
		||||
	rtp_hdr->sequence = htons(state->next_seq);
 | 
			
		||||
	rtp_hdr->timestamp = htonl(ts_no);
 | 
			
		||||
 | 
			
		||||
	state->next_seq += 1;
 | 
			
		||||
	state->next_time = ts_no + nsamples;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * XXX: At this point we should always have consumed
 | 
			
		||||
	 * samples. So doing OSMO_ASSERT(nsamples > 0) and returning
 | 
			
		||||
	 * rtp_hdr_size should be fine.
 | 
			
		||||
	 */
 | 
			
		||||
	return nsamples ? rtp_hdr_size : 0;
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -20,7 +20,7 @@ AM_LDFLAGS = \
 | 
			
		||||
 | 
			
		||||
# This is not at all related to the release version, but a range of supported
 | 
			
		||||
# API versions. Read TODO_RELEASE in the source tree's root!
 | 
			
		||||
MGCP_CLIENT_LIBVERSION=4:0:1
 | 
			
		||||
MGCP_CLIENT_LIBVERSION=5:0:0
 | 
			
		||||
 | 
			
		||||
lib_LTLIBRARIES = \
 | 
			
		||||
	libosmo-mgcp-client.la \
 | 
			
		||||
 
 | 
			
		||||
@@ -190,75 +190,9 @@ void mgcp_client_conf_init(struct mgcp_client_conf *conf)
 | 
			
		||||
		.local_port = -1,
 | 
			
		||||
		.remote_addr = NULL,
 | 
			
		||||
		.remote_port = -1,
 | 
			
		||||
		.first_endpoint = 0,
 | 
			
		||||
		.last_endpoint = 0,
 | 
			
		||||
		.bts_base = 0,
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Test if a given endpoint id is currently in use */
 | 
			
		||||
static bool endpoint_in_use(uint16_t id, struct mgcp_client *client)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_inuse_endpoint *endpoint;
 | 
			
		||||
	llist_for_each_entry(endpoint, &client->inuse_endpoints, entry) {
 | 
			
		||||
		if (endpoint->id == id)
 | 
			
		||||
			return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Pick next free endpoint ID.
 | 
			
		||||
 *  \param[in,out] client MGCP client descriptor.
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on error. */
 | 
			
		||||
int mgcp_client_next_endpoint(struct mgcp_client *client)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	uint16_t first_endpoint = client->actual.first_endpoint;
 | 
			
		||||
	uint16_t last_endpoint = client->actual.last_endpoint;
 | 
			
		||||
	struct mgcp_inuse_endpoint *endpoint;
 | 
			
		||||
 | 
			
		||||
	/* Use the maximum permitted range if the VTY
 | 
			
		||||
	 * configuration does not specify a range */
 | 
			
		||||
	if (client->actual.last_endpoint == 0) {
 | 
			
		||||
		first_endpoint = 1;
 | 
			
		||||
		last_endpoint = 65534;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Test the permitted endpoint range for an endpoint
 | 
			
		||||
	 * number that is not in use. When a suitable endpoint
 | 
			
		||||
	 * number can be found, seize it by adding it to the
 | 
			
		||||
	 * inuse list. */
 | 
			
		||||
	for (i=first_endpoint;i<last_endpoint;i++)
 | 
			
		||||
	{
 | 
			
		||||
		if (endpoint_in_use(i,client) == false) {
 | 
			
		||||
			endpoint = talloc_zero(client, struct mgcp_inuse_endpoint);
 | 
			
		||||
			endpoint->id = i;
 | 
			
		||||
			llist_add_tail(&endpoint->entry, &client->inuse_endpoints);
 | 
			
		||||
			return endpoint->id;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* All endpoints are busy! */
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Release a seized endpoint ID to make it available again for other calls.
 | 
			
		||||
 *  \param[in] id Endpoint ID
 | 
			
		||||
 *  \param[in,out] client MGCP client descriptor. */
 | 
			
		||||
/* Release a seized endpoint id to make it available again for other calls */
 | 
			
		||||
void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_inuse_endpoint *endpoint;
 | 
			
		||||
	struct mgcp_inuse_endpoint *endpoint_tmp;
 | 
			
		||||
	llist_for_each_entry_safe(endpoint, endpoint_tmp, &client->inuse_endpoints, entry) {
 | 
			
		||||
		if (endpoint->id == id) {
 | 
			
		||||
			llist_del(&endpoint->entry);
 | 
			
		||||
			talloc_free(endpoint);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mgcp_client_handle_response(struct mgcp_client *mgcp,
 | 
			
		||||
					struct mgcp_response_pending *pending,
 | 
			
		||||
					struct mgcp_response *response)
 | 
			
		||||
@@ -271,7 +205,7 @@ static void mgcp_client_handle_response(struct mgcp_client *mgcp,
 | 
			
		||||
	if (pending->response_cb)
 | 
			
		||||
		pending->response_cb(response, pending->priv);
 | 
			
		||||
	else
 | 
			
		||||
		LOGP(DLMGCP, LOGL_INFO, "MGCP response ignored (NULL cb)\n");
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "MGCP response ignored (NULL cb)\n");
 | 
			
		||||
	talloc_free(pending);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -550,6 +484,7 @@ static int mgcp_parse_head_param(char *result, unsigned int result_len,
 | 
			
		||||
				 char label, const char *line)
 | 
			
		||||
{
 | 
			
		||||
	char label_string[4];
 | 
			
		||||
	size_t rc;
 | 
			
		||||
 | 
			
		||||
	/* Detect empty parameters */
 | 
			
		||||
	if (strlen(line) < 4)
 | 
			
		||||
@@ -562,7 +497,14 @@ static int mgcp_parse_head_param(char *result, unsigned int result_len,
 | 
			
		||||
 | 
			
		||||
	/* Copy payload part of the string to destinations (the label string
 | 
			
		||||
	 * is always 3 chars long) */
 | 
			
		||||
	osmo_strlcpy(result, line + 3, result_len);
 | 
			
		||||
	rc = osmo_strlcpy(result, line + 3, result_len);
 | 
			
		||||
	if (rc >= result_len) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Failed to parse MGCP response (parameter label: %c):"
 | 
			
		||||
		     " the received conn ID is too long: %zu, maximum is %u characters\n",
 | 
			
		||||
		     label, rc, result_len - 1);
 | 
			
		||||
		return -ENOSPC;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
response_parse_failure:
 | 
			
		||||
@@ -709,11 +651,13 @@ static int mgcp_do_read(struct osmo_fd *fd)
 | 
			
		||||
 | 
			
		||||
	ret = read(fd->fd, msg->data, 4096 - 128);
 | 
			
		||||
	if (ret <= 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno));
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Failed to read: %s: %d='%s'\n", osmo_sock_get_name2(fd->fd),
 | 
			
		||||
		     errno, strerror(errno));
 | 
			
		||||
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return -1;
 | 
			
		||||
	} else if (ret > 4096 - 128) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Too much data: %d\n", ret);
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Too much data: %s: %d\n", osmo_sock_get_name2(fd->fd), ret);
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -727,26 +671,15 @@ static int mgcp_do_read(struct osmo_fd *fd)
 | 
			
		||||
static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	static char strbuf[4096];
 | 
			
		||||
	unsigned int l = msg->len < sizeof(strbuf) ? msg->len : sizeof(strbuf);
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	osmo_strlcpy(strbuf, (const char*)msg->data, l);
 | 
			
		||||
	for (i = 0; i < sizeof(strbuf); i++) {
 | 
			
		||||
		if (strbuf[i] == '\n' || strbuf[i] == '\r') {
 | 
			
		||||
			strbuf[i] = '\0';
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	DEBUGP(DLMGCP, "Tx MGCP msg to MGCP GW: '%s'\n", strbuf);
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len);
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "Tx MGCP: %s: len=%u '%s'...\n",
 | 
			
		||||
	     osmo_sock_get_name2(fd->fd), msg->len, osmo_escape_str((const char*)msg->data, OSMO_MIN(42, msg->len)));
 | 
			
		||||
 | 
			
		||||
	ret = write(fd->fd, msg->data, msg->len);
 | 
			
		||||
	if (ret != msg->len)
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Failed to forward message to MGCP"
 | 
			
		||||
		     " GW: %s\n", strerror(errno));
 | 
			
		||||
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
 | 
			
		||||
		     osmo_sock_get_name2(fd->fd), errno, strerror(errno),
 | 
			
		||||
		     msg->len, osmo_escape_str((const char*)msg->data, OSMO_MIN(42, msg->len)));
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -772,13 +705,55 @@ struct mgcp_client *mgcp_client_init(void *ctx,
 | 
			
		||||
	mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
 | 
			
		||||
		MGCP_CLIENT_REMOTE_PORT_DEFAULT;
 | 
			
		||||
 | 
			
		||||
	mgcp->actual.first_endpoint = conf->first_endpoint > 0 ? (uint16_t)conf->first_endpoint : 0;
 | 
			
		||||
	mgcp->actual.last_endpoint = conf->last_endpoint > 0 ? (uint16_t)conf->last_endpoint : 0;
 | 
			
		||||
	mgcp->actual.bts_base = conf->bts_base > 0 ? (uint16_t)conf->bts_base : 4000;
 | 
			
		||||
	if (osmo_strlcpy(mgcp->actual.endpoint_domain_name, conf->endpoint_domain_name,
 | 
			
		||||
			 sizeof(mgcp->actual.endpoint_domain_name))
 | 
			
		||||
	    >= sizeof(mgcp->actual.endpoint_domain_name)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "MGCP client: endpoint domain name is too long, max length is %zu: '%s'\n",
 | 
			
		||||
		     sizeof(mgcp->actual.endpoint_domain_name) - 1, conf->endpoint_domain_name);
 | 
			
		||||
		talloc_free(mgcp);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DLMGCP, LOGL_NOTICE, "MGCP client: using endpoint domain '@%s'\n", mgcp_client_endpoint_domain(mgcp));
 | 
			
		||||
 | 
			
		||||
	return mgcp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int init_socket(struct mgcp_client *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct osmo_wqueue *wq;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	wq = &mgcp->wq;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < 100; i++) {
 | 
			
		||||
 | 
			
		||||
		/* Initalize socket with the currently configured port
 | 
			
		||||
		 * number */
 | 
			
		||||
		rc = osmo_sock_init2_ofd(&wq->bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
 | 
			
		||||
					 mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
 | 
			
		||||
					 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
 | 
			
		||||
		if (rc > 0)
 | 
			
		||||
			return rc;
 | 
			
		||||
 | 
			
		||||
		/* If there is a different port than the default port
 | 
			
		||||
		 * configured then we assume that the user has choosen
 | 
			
		||||
		 * that port conciously and we will not try to resolve
 | 
			
		||||
		 * this by silently choosing a different port. */
 | 
			
		||||
		if (mgcp->actual.local_port != MGCP_CLIENT_LOCAL_PORT_DEFAULT && i == 0)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		/* Choose a new port number to try next */
 | 
			
		||||
		LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
		     "MGCPGW failed to bind to %s:%u, retrying with port %u\n",
 | 
			
		||||
		     mgcp->actual.local_addr, mgcp->actual.local_port, mgcp->actual.local_port + 1);
 | 
			
		||||
		mgcp->actual.local_port++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_FATAL, "MGCPGW failed to find a port to bind on %i times.\n", i);
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Initalize client connection (opens socket only, no request is sent yet)
 | 
			
		||||
 *  \param[in,out] mgcp MGCP client descriptor.
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on error. */
 | 
			
		||||
@@ -795,10 +770,7 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
 | 
			
		||||
 | 
			
		||||
	wq = &mgcp->wq;
 | 
			
		||||
 | 
			
		||||
	rc = osmo_sock_init2_ofd(&wq->bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
 | 
			
		||||
				 mgcp->actual.local_addr, mgcp->actual.local_port,
 | 
			
		||||
				 mgcp->actual.remote_addr, mgcp->actual.remote_port,
 | 
			
		||||
				 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
 | 
			
		||||
	rc = init_socket(mgcp);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_FATAL,
 | 
			
		||||
		     "Failed to initialize socket %s:%u -> %s:%u for MGCP GW: %s\n",
 | 
			
		||||
@@ -816,9 +788,7 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
 | 
			
		||||
	wq->read_cb = mgcp_do_read;
 | 
			
		||||
	wq->write_cb = mgcp_do_write;
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s:%u -> %s:%u\n",
 | 
			
		||||
	     mgcp->actual.local_addr, mgcp->actual.local_port,
 | 
			
		||||
	     mgcp->actual.remote_addr, mgcp->actual.remote_port);
 | 
			
		||||
	LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s\n", osmo_sock_get_name2(wq->bfd.fd));
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
error_close_fd:
 | 
			
		||||
@@ -851,6 +821,32 @@ uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
 | 
			
		||||
	return mgcp->remote_addr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* To compose endpoint names, usually for CRCX, use this as domain name.
 | 
			
		||||
 * For example, snprintf("rtpbridge\*@%s", mgcp_client_endpoint_domain(mgcp)). */
 | 
			
		||||
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	return mgcp->actual.endpoint_domain_name[0] ? mgcp->actual.endpoint_domain_name : "mgw";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	static char endpoint[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
#define RTPBRIDGE_WILDCARD_FMT "rtpbridge/*@%s"
 | 
			
		||||
	rc = snprintf(endpoint, sizeof(endpoint), RTPBRIDGE_WILDCARD_FMT, mgcp_client_endpoint_domain(mgcp));
 | 
			
		||||
	if (rc > sizeof(endpoint) - 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '" RTPBRIDGE_WILDCARD_FMT "'\n",
 | 
			
		||||
		     sizeof(endpoint) - 1, mgcp_client_endpoint_domain(mgcp));
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	if (rc < 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP endpoint name\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	return endpoint;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct mgcp_response_pending * mgcp_client_pending_add(
 | 
			
		||||
					struct mgcp_client *mgcp,
 | 
			
		||||
					mgcp_trans_id_t trans_id,
 | 
			
		||||
@@ -908,7 +904,7 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		goto mgcp_tx_error;
 | 
			
		||||
	} else
 | 
			
		||||
		LOGP(DLMGCP, LOGL_INFO, "Queued %u bytes for MGCP GW\n",
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "Queued %u bytes for MGCP GW\n",
 | 
			
		||||
		     msgb_l2len(msg));
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
@@ -932,10 +928,10 @@ int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id)
 | 
			
		||||
	struct mgcp_response_pending *pending = mgcp_client_response_pending_get(mgcp, trans_id);
 | 
			
		||||
	if (!pending) {
 | 
			
		||||
		/*! Note: it is not harmful to cancel a transaction twice. */
 | 
			
		||||
		LOGP(DLMGCP, LOGL_INFO, "Cannot cancel, no such transaction: %u\n", trans_id);
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Cannot cancel, no such transaction: %u\n", trans_id);
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DLMGCP, LOGL_INFO, "Canceled transaction %u\n", trans_id);
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "Canceled transaction %u\n", trans_id);
 | 
			
		||||
	talloc_free(pending);
 | 
			
		||||
	return 0;
 | 
			
		||||
	/*! We don't really need to clean up the wqueue: In all sane cases, the msgb has already been sent
 | 
			
		||||
@@ -947,54 +943,6 @@ int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id)
 | 
			
		||||
	 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id,
 | 
			
		||||
				      const char *buf, int len)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
 | 
			
		||||
	if (len > (4096 - 128)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Cannot send to MGCP GW:"
 | 
			
		||||
		     " message too large: %d\n", len);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
 | 
			
		||||
	OSMO_ASSERT(msg);
 | 
			
		||||
 | 
			
		||||
	char *dst = (char*)msgb_put(msg, len);
 | 
			
		||||
	memcpy(dst, buf, len);
 | 
			
		||||
	msg->l2h = msg->data;
 | 
			
		||||
	msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
 | 
			
		||||
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct msgb *mgcp_msg_from_str(mgcp_trans_id_t trans_id,
 | 
			
		||||
				      const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	static char compose[4096 - 128];
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	int len;
 | 
			
		||||
	OSMO_ASSERT(fmt);
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	len = vsnprintf(compose, sizeof(compose), fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
	if (len >= sizeof(compose)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Message too large: trans_id=%u len=%d\n",
 | 
			
		||||
		     trans_id, len);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	if (len < 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Failed to compose message: trans_id=%u len=%d\n",
 | 
			
		||||
		     trans_id, len);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	return mgcp_msg_from_buf(trans_id, compose, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	/* avoid zero trans_id to distinguish from unset trans_id */
 | 
			
		||||
@@ -1003,52 +951,6 @@ static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
 | 
			
		||||
	return mgcp->next_trans_id ++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
 | 
			
		||||
			   uint16_t rtp_endpoint, unsigned int call_id,
 | 
			
		||||
			   enum mgcp_connection_mode mode)
 | 
			
		||||
{
 | 
			
		||||
	mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
 | 
			
		||||
	return mgcp_msg_from_str(trans_id,
 | 
			
		||||
		 "CRCX %u %x@mgw MGCP 1.0\r\n"
 | 
			
		||||
		 "C: %x\r\n"
 | 
			
		||||
		 "L: p:20, a:AMR, nt:IN\r\n"
 | 
			
		||||
		 "M: %s\r\n"
 | 
			
		||||
		 ,
 | 
			
		||||
		 trans_id,
 | 
			
		||||
		 rtp_endpoint,
 | 
			
		||||
		 call_id,
 | 
			
		||||
		 mgcp_client_cmode_name(mode));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
 | 
			
		||||
			   uint16_t rtp_endpoint, const char *rtp_conn_addr,
 | 
			
		||||
			   uint16_t rtp_port, enum mgcp_connection_mode mode)
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
	mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
 | 
			
		||||
	return mgcp_msg_from_str(trans_id,
 | 
			
		||||
		 "MDCX %u %x@mgw MGCP 1.0\r\n"
 | 
			
		||||
		 "M: %s\r\n"
 | 
			
		||||
		 "\r\n"
 | 
			
		||||
		 "c=IN IP4 %s\r\n"
 | 
			
		||||
		 "m=audio %u RTP/AVP 255\r\n"
 | 
			
		||||
		 ,
 | 
			
		||||
		 trans_id,
 | 
			
		||||
		 rtp_endpoint,
 | 
			
		||||
		 mgcp_client_cmode_name(mode),
 | 
			
		||||
		 rtp_conn_addr,
 | 
			
		||||
		 rtp_port);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
 | 
			
		||||
			   unsigned int call_id)
 | 
			
		||||
{
 | 
			
		||||
	mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
 | 
			
		||||
	return mgcp_msg_from_str(trans_id,
 | 
			
		||||
				 "DLCX %u %x@mgw MGCP 1.0\r\n"
 | 
			
		||||
				 "C: %x\r\n", trans_id, rtp_endpoint, call_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MGCP_CRCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
 | 
			
		||||
			     MGCP_MSG_PRESENCE_CALL_ID | \
 | 
			
		||||
			     MGCP_MSG_PRESENCE_CONN_MODE)
 | 
			
		||||
@@ -1280,6 +1182,13 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
 | 
			
		||||
		    msgb_printf(msg, "M: %s\r\n",
 | 
			
		||||
				mgcp_client_cmode_name(mgcp_msg->conn_mode));
 | 
			
		||||
 | 
			
		||||
	/* Add X-Osmo-IGN */
 | 
			
		||||
	if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_X_OSMO_IGN)
 | 
			
		||||
	    && (mgcp_msg->x_osmo_ign != 0))
 | 
			
		||||
		rc +=
 | 
			
		||||
		    msgb_printf(msg, MGCP_X_OSMO_IGN_HEADER "%s\r\n",
 | 
			
		||||
				mgcp_msg->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID ? " C": "");
 | 
			
		||||
 | 
			
		||||
	/* Add session description protocol (SDP) */
 | 
			
		||||
	if (use_sdp
 | 
			
		||||
	    && (mgcp_msg->verb == MGCP_VERB_CRCX
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,7 @@ struct mgcp_ctx {
 | 
			
		||||
	bool mgw_trans_pending;
 | 
			
		||||
 | 
			
		||||
	/* Connection ID which has been assigned by he MGW */
 | 
			
		||||
	char conn_id[MGCP_CONN_ID_LENGTH];
 | 
			
		||||
	char conn_id[MGCP_CONN_ID_MAXLEN];
 | 
			
		||||
 | 
			
		||||
	/* Local RTP connection info, the MGW will send outgoing traffic to the
 | 
			
		||||
	 * ip/port specified here. The Address does not have to be choosen right
 | 
			
		||||
@@ -104,44 +104,34 @@ static const struct value_string fsm_mgcp_client_evt_names[] = {
 | 
			
		||||
	{0, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct msgb *make_crcx_msg_bind(struct mgcp_ctx *mgcp_ctx)
 | 
			
		||||
static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_msg mgcp_msg;
 | 
			
		||||
 | 
			
		||||
	mgcp_msg = (struct mgcp_msg) {
 | 
			
		||||
	*mgcp_msg = (struct mgcp_msg) {
 | 
			
		||||
		.verb = MGCP_VERB_CRCX,
 | 
			
		||||
		.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
 | 
			
		||||
		.call_id = mgcp_ctx->conn_peer_local.call_id,
 | 
			
		||||
		.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 = mgcp_ctx->conn_peer_local.ptime,
 | 
			
		||||
		.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
 | 
			
		||||
		.ptime = info->ptime,
 | 
			
		||||
		.codecs_len = info->codecs_len,
 | 
			
		||||
		.ptmap_len = info->ptmap_len
 | 
			
		||||
	};
 | 
			
		||||
	osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
 | 
			
		||||
	memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
 | 
			
		||||
	osmo_strlcpy(mgcp_msg->endpoint, info->endpoint, MGCP_ENDPOINT_MAXLEN);
 | 
			
		||||
	memcpy(mgcp_msg->codecs, info->codecs, sizeof(mgcp_msg->codecs));
 | 
			
		||||
	memcpy(mgcp_msg->ptmap, info->ptmap, sizeof(mgcp_msg->ptmap));
 | 
			
		||||
 | 
			
		||||
	return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
 | 
			
		||||
	if (info->x_osmo_ign) {
 | 
			
		||||
		mgcp_msg->x_osmo_ign = info->x_osmo_ign;
 | 
			
		||||
		mgcp_msg->presence |= MGCP_MSG_PRESENCE_X_OSMO_IGN;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct msgb *make_crcx_msg_bind_connect(struct mgcp_ctx *mgcp_ctx)
 | 
			
		||||
static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_msg mgcp_msg;
 | 
			
		||||
 | 
			
		||||
	mgcp_msg = (struct mgcp_msg) {
 | 
			
		||||
		.verb = MGCP_VERB_CRCX,
 | 
			
		||||
		.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
 | 
			
		||||
			     MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
 | 
			
		||||
			     MGCP_MSG_PRESENCE_AUDIO_PORT),
 | 
			
		||||
		.call_id = mgcp_ctx->conn_peer_local.call_id,
 | 
			
		||||
		.conn_mode = MGCP_CONN_RECV_SEND,
 | 
			
		||||
		.audio_ip = mgcp_ctx->conn_peer_local.addr,
 | 
			
		||||
		.audio_port = mgcp_ctx->conn_peer_local.port,
 | 
			
		||||
		.ptime = mgcp_ctx->conn_peer_local.ptime,
 | 
			
		||||
		.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
 | 
			
		||||
	};
 | 
			
		||||
	osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
 | 
			
		||||
	memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
 | 
			
		||||
 | 
			
		||||
	return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
 | 
			
		||||
	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)
 | 
			
		||||
@@ -158,10 +148,12 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
 | 
			
		||||
		.audio_ip = mgcp_ctx->conn_peer_local.addr,
 | 
			
		||||
		.audio_port = mgcp_ctx->conn_peer_local.port,
 | 
			
		||||
		.ptime = mgcp_ctx->conn_peer_local.ptime,
 | 
			
		||||
		.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
 | 
			
		||||
		.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
 | 
			
		||||
@@ -191,6 +183,7 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_ctx *mgcp_ctx = data;
 | 
			
		||||
	struct mgcp_client *mgcp;
 | 
			
		||||
	struct mgcp_msg mgcp_msg;
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
@@ -203,10 +196,10 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
		LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: creating connection on MGW endpoint:%s...\n",
 | 
			
		||||
			 mgcp_ctx->conn_peer_local.endpoint);
 | 
			
		||||
 | 
			
		||||
		make_crcx_msg(&mgcp_msg, &mgcp_ctx->conn_peer_local);
 | 
			
		||||
		if (mgcp_ctx->conn_peer_local.port)
 | 
			
		||||
			msg = make_crcx_msg_bind_connect(mgcp_ctx);
 | 
			
		||||
		else
 | 
			
		||||
			msg = make_crcx_msg_bind(mgcp_ctx);
 | 
			
		||||
			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);
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client.h>
 | 
			
		||||
 | 
			
		||||
#define MGW_STR "Configure MGCP connection to Media Gateway\n"
 | 
			
		||||
#define MGW_STR MGCP_CLIENT_MGW_STR
 | 
			
		||||
 | 
			
		||||
void *global_mgcp_client_ctx = NULL;
 | 
			
		||||
struct mgcp_client_conf *global_mgcp_client_conf = NULL;
 | 
			
		||||
@@ -43,8 +43,9 @@ DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
 | 
			
		||||
	if (!global_mgcp_client_conf)
 | 
			
		||||
		return CMD_ERR_NOTHING_TODO;
 | 
			
		||||
	OSMO_ASSERT(global_mgcp_client_ctx);
 | 
			
		||||
	global_mgcp_client_conf->local_addr =
 | 
			
		||||
		talloc_strdup(global_mgcp_client_ctx, argv[0]);
 | 
			
		||||
	osmo_talloc_replace_string(global_mgcp_client_ctx,
 | 
			
		||||
				   (char**)&global_mgcp_client_conf->local_addr,
 | 
			
		||||
				   argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
 | 
			
		||||
@@ -75,8 +76,9 @@ DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
 | 
			
		||||
	if (!global_mgcp_client_conf)
 | 
			
		||||
		return CMD_ERR_NOTHING_TODO;
 | 
			
		||||
	OSMO_ASSERT(global_mgcp_client_ctx);
 | 
			
		||||
	global_mgcp_client_conf->remote_addr =
 | 
			
		||||
		talloc_strdup(global_mgcp_client_ctx, argv[0]);
 | 
			
		||||
	osmo_talloc_replace_string(global_mgcp_client_ctx,
 | 
			
		||||
				   (char**)&global_mgcp_client_conf->remote_addr,
 | 
			
		||||
				   argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
 | 
			
		||||
@@ -99,24 +101,15 @@ ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
 | 
			
		||||
		 MGW_STR "remote bind to connect to MGCP gateway with\n"
 | 
			
		||||
		 "remote bind port\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
 | 
			
		||||
DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
 | 
			
		||||
      "mgw endpoint-range <1-65534> <1-65534>",
 | 
			
		||||
      MGW_STR "usable range of endpoint identifiers\n"
 | 
			
		||||
      "set first usable endpoint identifier\n"
 | 
			
		||||
      "set last usable endpoint identifier\n")
 | 
			
		||||
      MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
 | 
			
		||||
      "-\n" "-\n")
 | 
			
		||||
{
 | 
			
		||||
	uint16_t first_endpoint = atoi(argv[0]);
 | 
			
		||||
	uint16_t last_endpoint = atoi(argv[1]);
 | 
			
		||||
 | 
			
		||||
	if (last_endpoint < first_endpoint) {
 | 
			
		||||
		vty_out(vty, "last endpoint must be greater than first endpoint!%s",
 | 
			
		||||
	vty_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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	global_mgcp_client_conf->first_endpoint = first_endpoint;
 | 
			
		||||
	global_mgcp_client_conf->last_endpoint = last_endpoint;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
 | 
			
		||||
      "mgcpgw endpoint-range <1-65534> <1-65534>",
 | 
			
		||||
@@ -126,14 +119,15 @@ ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
 | 
			
		||||
 | 
			
		||||
#define BTS_START_STR "First UDP port allocated for the BTS side\n"
 | 
			
		||||
#define UDP_PORT_STR "UDP Port number\n"
 | 
			
		||||
DEFUN(cfg_mgw_rtp_bts_base_port,
 | 
			
		||||
DEFUN_DEPRECATED(cfg_mgw_rtp_bts_base_port,
 | 
			
		||||
      cfg_mgw_rtp_bts_base_port_cmd,
 | 
			
		||||
      "mgw bts-base <0-65534>",
 | 
			
		||||
      MGW_STR
 | 
			
		||||
      BTS_START_STR
 | 
			
		||||
      UDP_PORT_STR)
 | 
			
		||||
      "DEPRECATED: there is no explicit BTS side in current osmo-mgw\n" "-\n")
 | 
			
		||||
{
 | 
			
		||||
	global_mgcp_client_conf->bts_base = atoi(argv[0]);
 | 
			
		||||
	vty_out(vty, "Please do not use legacy config 'mgw bts-base'"
 | 
			
		||||
		" (there is no explicit BTS side in an MGW anymore)%s",
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
 | 
			
		||||
@@ -143,13 +137,26 @@ ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
 | 
			
		||||
      BTS_START_STR
 | 
			
		||||
      UDP_PORT_STR)
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_endpoint_domain_name,
 | 
			
		||||
      cfg_mgw_endpoint_domain_name_cmd,
 | 
			
		||||
      "mgw endpoint-domain NAME",
 | 
			
		||||
      MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
 | 
			
		||||
      "Domain name, should be alphanumeric.\n")
 | 
			
		||||
{
 | 
			
		||||
	if (osmo_strlcpy(global_mgcp_client_conf->endpoint_domain_name, argv[0],
 | 
			
		||||
			 sizeof(global_mgcp_client_conf->endpoint_domain_name))
 | 
			
		||||
	    >= sizeof(global_mgcp_client_conf->endpoint_domain_name)) {
 | 
			
		||||
		vty_out(vty, "%% Error: 'mgw endpoint-domain' name too long, max length is %zu: '%s'%s",
 | 
			
		||||
			sizeof(global_mgcp_client_conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mgcp_client_config_write(struct vty *vty, const char *indent)
 | 
			
		||||
{
 | 
			
		||||
	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)
 | 
			
		||||
@@ -169,18 +176,9 @@ int mgcp_client_config_write(struct vty *vty, const char *indent)
 | 
			
		||||
		vty_out(vty, "%smgw remote-port %u%s", indent,
 | 
			
		||||
			(uint16_t)port, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	first_endpoint = global_mgcp_client_conf->first_endpoint;
 | 
			
		||||
	last_endpoint = global_mgcp_client_conf->last_endpoint;
 | 
			
		||||
	if (last_endpoint != 0) {
 | 
			
		||||
		vty_out(vty, "%smgw endpoint-range %u %u%s", indent,
 | 
			
		||||
			first_endpoint, last_endpoint, VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bts_base = global_mgcp_client_conf->bts_base;
 | 
			
		||||
	if (bts_base) {
 | 
			
		||||
		vty_out(vty, "%smgw bts-base %u%s", indent,
 | 
			
		||||
			bts_base, VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
	if (global_mgcp_client_conf->endpoint_domain_name[0])
 | 
			
		||||
		vty_out(vty, "%smgw endpoint-domain %s%s", indent,
 | 
			
		||||
			global_mgcp_client_conf->endpoint_domain_name, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
@@ -196,6 +194,7 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
 | 
			
		||||
	install_element(node, &cfg_mgw_remote_port_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgw_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);
 | 
			
		||||
 
 | 
			
		||||
@@ -341,3 +341,70 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,24 +29,15 @@
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_desc 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_rx", "Outbound rtp octets."},
 | 
			
		||||
	[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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(rate_ctr_desc),
 | 
			
		||||
	.ctr_desc = rate_ctr_desc
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(mgcp_conn_rate_ctr_desc),
 | 
			
		||||
	.ctr_desc = mgcp_conn_rate_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -55,10 +46,11 @@ const static struct rate_ctr_group_desc rate_ctr_group_desc = {
 | 
			
		||||
 * 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[16];
 | 
			
		||||
	uint8_t id_bin[MGCP_CONN_ID_GEN_LEN / 2];
 | 
			
		||||
	char *id_hex;
 | 
			
		||||
 | 
			
		||||
	/* Generate a connection id that is unique for the current endpoint.
 | 
			
		||||
@@ -78,7 +70,7 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
 | 
			
		||||
		/* ensure that the generated conn_id is unique
 | 
			
		||||
		 * for this endpoint */
 | 
			
		||||
		if (!mgcp_conn_get_rtp(endp, id_hex)) {
 | 
			
		||||
			osmo_strlcpy(id, id_hex, MGCP_CONN_ID_LENGTH);
 | 
			
		||||
			osmo_strlcpy(id, id_hex, MGCP_CONN_ID_MAXLEN);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -134,6 +126,23 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
	rate_ctr_group_free(conn_rtp->rate_ctr_group);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgcp_conn_watchdog_cb(void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn = data;
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x CI:%s connection timed out!\n", ENDPOINT_NUMBER(conn->endp), conn->id);
 | 
			
		||||
	mgcp_conn_free(conn->endp, conn->id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	int timeout = conn->endp->cfg->conn_timeout;
 | 
			
		||||
	if (!timeout)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x CI:%s watchdog kicked\n", ENDPOINT_NUMBER(conn->endp), conn->id);
 | 
			
		||||
	osmo_timer_schedule(&conn->watchdog, timeout, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! allocate a new connection list entry.
 | 
			
		||||
 *  \param[in] ctx talloc context
 | 
			
		||||
 *  \param[in] endp associated endpoint
 | 
			
		||||
@@ -176,6 +185,9 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Initialize watchdog */
 | 
			
		||||
	osmo_timer_setup(&conn->watchdog, mgcp_conn_watchdog_cb, conn);
 | 
			
		||||
	mgcp_conn_watchdog_kick(conn);
 | 
			
		||||
	llist_add(&conn->entry, &endp->conns);
 | 
			
		||||
 | 
			
		||||
	return conn;
 | 
			
		||||
@@ -188,9 +200,26 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	const char *id_upper;
 | 
			
		||||
	const char *conn_id;
 | 
			
		||||
 | 
			
		||||
	if (!id || !*id)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Ignore leading zeros in needle */
 | 
			
		||||
	while (*id == '0')
 | 
			
		||||
		id++;
 | 
			
		||||
 | 
			
		||||
	/* Use uppercase to compare identifiers, to avoid mismatches: RFC3435 2.1.3.2 "Names of
 | 
			
		||||
	 * Connections" defines the id as a hex string, so clients may return lower case hex even though
 | 
			
		||||
	 * we sent upper case hex in the CRCX response. */
 | 
			
		||||
	id_upper = osmo_str_toupper(id);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(conn, &endp->conns, entry) {
 | 
			
		||||
		if (strncmp(conn->id, id, sizeof(conn->id)) == 0)
 | 
			
		||||
		/* Ignore leading zeros in haystack */
 | 
			
		||||
		for (conn_id=conn->id; *conn_id == '0'; conn_id++);
 | 
			
		||||
 | 
			
		||||
		if (strcmp(conn_id, id_upper) == 0)
 | 
			
		||||
			return conn;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -216,6 +245,27 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
	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 */
 | 
			
		||||
@@ -235,6 +285,7 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
 | 
			
		||||
	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:
 | 
			
		||||
@@ -244,6 +295,7 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	osmo_timer_del(&conn->watchdog);
 | 
			
		||||
	llist_del(&conn->entry);
 | 
			
		||||
	talloc_free(conn);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -219,8 +219,14 @@ static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
 | 
			
		||||
	if (!domain_to_check)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(domain_to_check+1, cfg->domain) != 0)
 | 
			
		||||
	/* Accept any domain if configured as "*" */
 | 
			
		||||
	if (!strcmp(cfg->domain, "*"))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(domain_to_check+1, cfg->domain) != 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s', expecting '%s'\n", mgcp, cfg->domain);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -240,7 +246,7 @@ static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
 | 
			
		||||
 | 
			
		||||
	/* Check if the domainname in the request is correct */
 | 
			
		||||
	if (check_domain_name(cfg, mgcp)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s'\n", mgcp);
 | 
			
		||||
		*cause = -500;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -320,6 +326,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
 | 
			
		||||
			if (!pdata->endp) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
				     "Unable to find Endpoint `%s'\n", elem);
 | 
			
		||||
				OSMO_ASSERT(cause < 0);
 | 
			
		||||
				return cause;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
@@ -401,6 +408,11 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
 | 
			
		||||
 | 
			
		||||
	if (!endp)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	/* Accept any CallID for "X-Osmo-IGN: C" */
 | 
			
		||||
	if (endp->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (!callid)
 | 
			
		||||
		return -1;
 | 
			
		||||
	if (!endp->callid)
 | 
			
		||||
@@ -408,7 +420,7 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
 | 
			
		||||
 | 
			
		||||
	if (strcmp(endp->callid, callid) != 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x CallIDs does not match '%s' != '%s'\n",
 | 
			
		||||
		     "endpoint:0x%x CallIDs mismatch: '%s' != '%s'\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), endp->callid, callid);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -419,15 +431,19 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
 | 
			
		||||
/*! Check if the specified connection id seems plausible.
 | 
			
		||||
  * \param[in] endp pointer to endpoint
 | 
			
		||||
  * \param{in] connection id to verify
 | 
			
		||||
  * \returns 1 when connection id seems plausible, 0 on error */
 | 
			
		||||
  * \returns 0 when connection id is valid and exists, an RFC3435 error code on error.
 | 
			
		||||
  */
 | 
			
		||||
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
 | 
			
		||||
{
 | 
			
		||||
	/* 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 -1;
 | 
			
		||||
		return 510;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check for empty connection identifiers */
 | 
			
		||||
@@ -435,15 +451,15 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x invalid ConnectionIdentifier (empty)\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return -1;
 | 
			
		||||
		return 510;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check for over long connection identifiers */
 | 
			
		||||
	if (strlen(conn_id) > MGCP_CONN_ID_LENGTH) {
 | 
			
		||||
	if (strlen(conn_id) > (MGCP_CONN_ID_MAXLEN-1)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x invalid ConnectionIdentifier (too long) 0x%s\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), conn_id);
 | 
			
		||||
		return -1;
 | 
			
		||||
		     "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 */
 | 
			
		||||
@@ -454,7 +470,9 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
 | 
			
		||||
	     "endpoint:0x%x no connection found under ConnectionIdentifier 0x%s\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), conn_id);
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
	/* When the conn_id was not found, return error code 515 "The transaction refers to an incorrect
 | 
			
		||||
	 * connection-id (may have been already deleted)." */
 | 
			
		||||
	return 515;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Extract individual lines from MCGP message.
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,9 @@
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/byteswap.h>
 | 
			
		||||
#include <osmocom/netif/rtp.h>
 | 
			
		||||
#include <osmocom/netif/amr.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
@@ -40,7 +42,10 @@
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
#include <osmocom/mgcp/debug.h>
 | 
			
		||||
#include <osmocom/codec/codec.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define RTP_SEQ_MOD		(1 << 16)
 | 
			
		||||
#define RTP_MAX_DROPOUT		3000
 | 
			
		||||
@@ -474,6 +479,28 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
 | 
			
		||||
	state->stats.max_seq = seq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* There may be different payload type numbers negotiated for two connections.
 | 
			
		||||
 * Patch the payload type of an RTP packet so that it uses the payload type
 | 
			
		||||
 * that is valid for the destination connection (conn_dst) */
 | 
			
		||||
static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
 | 
			
		||||
			 struct mgcp_conn_rtp *conn_dst, char *data, int len)
 | 
			
		||||
{
 | 
			
		||||
	struct rtp_hdr *rtp_hdr;
 | 
			
		||||
	uint8_t pt_in;
 | 
			
		||||
	int pt_out;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(len >= sizeof(struct rtp_hdr));
 | 
			
		||||
	rtp_hdr = (struct rtp_hdr *)data;
 | 
			
		||||
 | 
			
		||||
	pt_in = rtp_hdr->payload_type;
 | 
			
		||||
	pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
 | 
			
		||||
	if (pt_out < 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	rtp_hdr->payload_type = (uint8_t) pt_out;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* The RFC 3550 Appendix A assumes there are multiple sources but
 | 
			
		||||
 * some of the supported endpoints (e.g. the nanoBTS) can only handle
 | 
			
		||||
 * one source and this code will patch RTP header to appear as if there
 | 
			
		||||
@@ -626,6 +653,75 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* There are different dialects used to format and transfer voice data. When
 | 
			
		||||
 * the receiving end expects GSM-HR data to be formated after RFC 5993, this
 | 
			
		||||
 * function is used to convert between RFC 5993 and TS 101318, which we normally
 | 
			
		||||
 * use. */
 | 
			
		||||
static void rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
 | 
			
		||||
{
 | 
			
		||||
	/* NOTE: *data has an overall length of RTP_BUF_SIZE, so there is
 | 
			
		||||
	 * plenty of space available to store the slightly larger, converted
 | 
			
		||||
	 * data */
 | 
			
		||||
 | 
			
		||||
	struct rtp_hdr *rtp_hdr;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(*len >= sizeof(struct rtp_hdr));
 | 
			
		||||
	rtp_hdr = (struct rtp_hdr *)data;
 | 
			
		||||
 | 
			
		||||
	if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr)) {
 | 
			
		||||
		/* TS 101318 encoding => RFC 5993 encoding */
 | 
			
		||||
		memmove(rtp_hdr->data + 1, rtp_hdr->data, GSM_HR_BYTES);
 | 
			
		||||
		rtp_hdr->data[0] = 0x00;
 | 
			
		||||
		(*len) += 1;
 | 
			
		||||
 | 
			
		||||
	} else if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr) + 1) {
 | 
			
		||||
		/* RFC 5993 encoding => TS 101318 encoding */
 | 
			
		||||
		memmove(rtp_hdr->data, rtp_hdr->data + 1, GSM_HR_BYTES);
 | 
			
		||||
		(*len) -= 1;
 | 
			
		||||
	} else {
 | 
			
		||||
		/* It is possible that multiple payloads occur in one RTP
 | 
			
		||||
		 * packet. This is not supported yet. */
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x cannot figure out how to convert RTP packet\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* For AMR RTP two framing modes are defined RFC3267. There is a bandwith
 | 
			
		||||
 * efficient encoding scheme where all fields are packed together one after
 | 
			
		||||
 * another and an octet aligned mode where all fields are aligned to octet
 | 
			
		||||
 * boundaries. This function is used to convert between the two modes */
 | 
			
		||||
static void amr_oa_bwe_convert(struct mgcp_endpoint *endp, char *data, int *len)
 | 
			
		||||
{
 | 
			
		||||
	/* NOTE: *data has an overall length of RTP_BUF_SIZE, so there is
 | 
			
		||||
	 * plenty of space available to store the slightly larger, converted
 | 
			
		||||
	 * data */
 | 
			
		||||
 | 
			
		||||
	struct rtp_hdr *rtp_hdr;
 | 
			
		||||
	unsigned int payload_len;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(*len >= sizeof(struct rtp_hdr));
 | 
			
		||||
	rtp_hdr = (struct rtp_hdr *)data;
 | 
			
		||||
 | 
			
		||||
	payload_len = *len - sizeof(struct rtp_hdr);
 | 
			
		||||
 | 
			
		||||
	if (osmo_amr_is_oa(rtp_hdr->data, payload_len))
 | 
			
		||||
		rc = osmo_amr_oa_to_bwe(rtp_hdr->data, payload_len);
 | 
			
		||||
	else
 | 
			
		||||
		rc = osmo_amr_bwe_to_oa(rtp_hdr->data, payload_len,
 | 
			
		||||
					RTP_BUF_SIZE);
 | 
			
		||||
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x AMR RTP packet conversion failed\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*len = rc + sizeof(struct rtp_hdr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Forward data to a debug tap. This is debug function that is intended for
 | 
			
		||||
 * debugging the voice traffic with tools like gstreamer */
 | 
			
		||||
static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
 | 
			
		||||
@@ -665,6 +761,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
			
		||||
	struct mgcp_rtp_end *rtp_end;
 | 
			
		||||
	struct mgcp_rtp_state *rtp_state;
 | 
			
		||||
	char *dest_name;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(conn_src);
 | 
			
		||||
	OSMO_ASSERT(conn_dst);
 | 
			
		||||
@@ -684,6 +781,23 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), tcfg->audio_loop, conn_src->conn->mode,
 | 
			
		||||
	     conn_src->conn->mode == MGCP_CONN_LOOPBACK ? " (loopback)" : "");
 | 
			
		||||
 | 
			
		||||
	/* FIXME: It is legal that the payload type on the egress connection is
 | 
			
		||||
	 * different from the payload type that has been negotiated on the
 | 
			
		||||
	 * ingress connection. Essentially the codecs are the same so we can
 | 
			
		||||
	 * match them and patch the payload type. However, if we can not find
 | 
			
		||||
	 * the codec pendant (everything ist equal except the PT), we are of
 | 
			
		||||
	 * course unable to patch the payload type. A situation like this
 | 
			
		||||
	 * should not occur if transcoding is consequently avoided. Until
 | 
			
		||||
	 * we have transcoding support in osmo-mgw we can not resolve this. */
 | 
			
		||||
	if (is_rtp) {
 | 
			
		||||
		rc = mgcp_patch_pt(conn_src, conn_dst, buf, len);
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
			     "endpoint:0x%x can not patch PT because no suitable egress codec was found.\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Note: In case of loopback configuration, both, the source and the
 | 
			
		||||
	 * destination will point to the same connection. */
 | 
			
		||||
	rtp_end = &conn_dst->end;
 | 
			
		||||
@@ -715,6 +829,17 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
			
		||||
			if (addr)
 | 
			
		||||
				mgcp_patch_and_count(endp, rtp_state, rtp_end,
 | 
			
		||||
						     addr, buf, buflen);
 | 
			
		||||
 | 
			
		||||
			if (rtp_end->rfc5993_hr_convert
 | 
			
		||||
			    && strcmp(conn_src->end.codec->subtype_name,
 | 
			
		||||
				      "GSM-HR-08") == 0)
 | 
			
		||||
				rfc5993_hr_convert(endp, buf, &buflen);
 | 
			
		||||
 | 
			
		||||
			else if (rtp_end->amr_oa_bwe_convert
 | 
			
		||||
			    && strcmp(conn_src->end.codec->subtype_name,
 | 
			
		||||
				      "AMR") == 0)
 | 
			
		||||
				amr_oa_bwe_convert(endp, buf, &buflen);
 | 
			
		||||
 | 
			
		||||
			LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
			     "endpoint:0x%x process/send to %s %s "
 | 
			
		||||
			     "rtp_port:%u rtcp_port:%u\n",
 | 
			
		||||
@@ -830,10 +955,37 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn,
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	endp = conn->conn->endp;
 | 
			
		||||
 | 
			
		||||
	if (conn->end.addr.s_addr == 0) {
 | 
			
		||||
		switch (conn->conn->mode) {
 | 
			
		||||
		case MGCP_CONN_LOOPBACK:
 | 
			
		||||
			/* HACK: for IuUP, we want to reply with an IuUP Initialization ACK upon the first RTP
 | 
			
		||||
			 * message received. We currently hackishly accomplish that by putting the endpoint in
 | 
			
		||||
			 * loopback mode and patching over the looped back RTP message to make it look like an
 | 
			
		||||
			 * ack. We don't know the femto cell's IP address and port until the RAB Assignment
 | 
			
		||||
			 * Response is received, but the nano3G expects an IuUP Initialization Ack before it even
 | 
			
		||||
			 * sends the RAB Assignment Response. Hence, if the remote address is 0.0.0.0 and the
 | 
			
		||||
			 * MGCP port is in loopback mode, allow looping back the packet to any source. */
 | 
			
		||||
			LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
			     "endpoint:0x%x In loopback mode and remote address not set:"
 | 
			
		||||
			     " allowing data from address: %s\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr));
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			/* Receiving early media before the endpoint is configured. Instead of logging
 | 
			
		||||
			 * this as an error that occurs on every call, keep it more low profile to not
 | 
			
		||||
			 * confuse humans with expected errors. */
 | 
			
		||||
			LOGP(DRTP, LOGL_INFO,
 | 
			
		||||
			     "endpoint:0x%x I:%s Rx RTP from %s, but remote address not set:"
 | 
			
		||||
			     " dropping early media\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp), conn->conn->id, inet_ntoa(addr->sin_addr));
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Note: Check if the inbound RTP data comes from the same host to
 | 
			
		||||
	 * which we send our outgoing RTP traffic. */
 | 
			
		||||
	if (memcmp(&addr->sin_addr, &conn->end.addr, sizeof(addr->sin_addr))
 | 
			
		||||
	    != 0) {
 | 
			
		||||
	if (conn->end.addr.s_addr != addr->sin_addr.s_addr) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x data from wrong address: %s, ",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr));
 | 
			
		||||
@@ -875,28 +1027,75 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn)
 | 
			
		||||
	 * and IP-address for outgoing data. */
 | 
			
		||||
	if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0 && conn->end.rtp_port == 0) {
 | 
			
		||||
		LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
		     "endpoint:0x%x destination IP-address and rtp port is (not yet) known\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		     "endpoint:0x%x destination IP-address and rtp port is (not yet) known (%s:%u)\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), inet_ntoa(conn->end.addr), conn->end.rtp_port);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x destination IP-address is invalid\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		     "endpoint:0x%x destination IP-address is invalid (%s:%u)\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), inet_ntoa(conn->end.addr), conn->end.rtp_port);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (conn->end.rtp_port == 0) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x destination rtp port is invalid\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		     "endpoint:0x%x destination rtp port is invalid (%s:%u)\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), inet_ntoa(conn->end.addr), conn->end.rtp_port);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Do some basic checks to make sure that the RTCP packets we are going to
 | 
			
		||||
 * process are not complete garbage */
 | 
			
		||||
static int check_rtcp(char *buf, unsigned int buf_size)
 | 
			
		||||
{
 | 
			
		||||
	struct rtcp_hdr *hdr;
 | 
			
		||||
	unsigned int len;
 | 
			
		||||
	uint8_t type;
 | 
			
		||||
 | 
			
		||||
	/* RTPC packets that are just a header without data do not make
 | 
			
		||||
	 * any sense. */
 | 
			
		||||
	if (buf_size < sizeof(struct rtcp_hdr))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Make sure that the length of the received packet does not exceed
 | 
			
		||||
	 * the available buffer size */
 | 
			
		||||
	hdr = (struct rtcp_hdr *)buf;
 | 
			
		||||
	len = (osmo_ntohs(hdr->length) + 1) * 4;
 | 
			
		||||
	if (len > buf_size)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Make sure we accept only packets that have a proper packet type set
 | 
			
		||||
	 * See also: http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
 | 
			
		||||
	type = hdr->type;
 | 
			
		||||
	if ((type < 192 || type > 195) && (type < 200 || type > 213))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Do some basic checks to make sure that the RTP packets we are going to
 | 
			
		||||
 * process are not complete garbage */
 | 
			
		||||
static int check_rtp(char *buf, unsigned int buf_size)
 | 
			
		||||
{
 | 
			
		||||
	/* RTP packets that are just a header without data do not make
 | 
			
		||||
	 * any sense. */
 | 
			
		||||
	if (buf_size < sizeof(struct rtp_hdr))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: Add more checks, the reason why we do not check more than
 | 
			
		||||
	 * the length is because we currently handle IUUP packets as RTP
 | 
			
		||||
	 * packets, so they must pass this check, if we weould be more
 | 
			
		||||
	 * strict here, we would possibly break 3G. (see also FIXME note
 | 
			
		||||
	 * below */
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Receive RTP data from a specified source connection and dispatch it to a
 | 
			
		||||
 * destination connection. */
 | 
			
		||||
static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
 | 
			
		||||
@@ -917,8 +1116,29 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
 | 
			
		||||
	rc = receive_from(endp, fd->fd, addr, buf, buf_size);
 | 
			
		||||
	if (rc <= 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: The way how we detect the protocol looks odd. We should look
 | 
			
		||||
	 * into the packet header. Also we should introduce a packet type
 | 
			
		||||
	 * MGCP_PROTO_IUUP because currently we handle IUUP packets like RTP
 | 
			
		||||
	 * packets which is problematic. */
 | 
			
		||||
	*proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
 | 
			
		||||
 | 
			
		||||
	if (*proto == MGCP_PROTO_RTP) {
 | 
			
		||||
		if (check_rtp(buf, rc) < 0) {
 | 
			
		||||
			LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
			     "endpoint:0x%x invalid RTP packet received -- packet tossed\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp));
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (*proto == MGCP_PROTO_RTCP) {
 | 
			
		||||
		if (check_rtcp(buf, rc) < 0) {
 | 
			
		||||
			LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
			     "endpoint:0x%x invalid RTCP packet received -- packet tossed\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp));
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x ", ENDPOINT_NUMBER(endp));
 | 
			
		||||
	LOGPC(DRTP, LOGL_DEBUG, "receiving from %s %s %d\n",
 | 
			
		||||
	      conn->conn->name, inet_ntoa(addr->sin_addr),
 | 
			
		||||
@@ -1108,9 +1328,10 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
	if (len < 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	mgcp_conn_watchdog_kick(conn_src->conn);
 | 
			
		||||
 | 
			
		||||
	/* Check if the connection is in loopback mode, if yes, just send the
 | 
			
		||||
	 * incoming data back to the origin */
 | 
			
		||||
 | 
			
		||||
	if (conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
 | 
			
		||||
		/* When we are in loopback mode, we loop back all incoming
 | 
			
		||||
		 * packets back to their origin. We will use the originating
 | 
			
		||||
 
 | 
			
		||||
@@ -316,6 +316,48 @@ static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr)
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Updates endp osmux state and returns 0 if it can process messages, -1 otherwise */
 | 
			
		||||
static int endp_osmux_state_check(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
 | 
			
		||||
				  bool sending)
 | 
			
		||||
{
 | 
			
		||||
	switch(conn->osmux.state) {
 | 
			
		||||
	case OSMUX_STATE_ACTIVATING:
 | 
			
		||||
	if (osmux_enable_conn(endp, conn, &conn->end.addr, htons(endp->cfg->osmux_port)) < 0) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
				"Could not enable osmux for conn:%s\n",
 | 
			
		||||
				mgcp_conn_dump(conn->conn));
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		    "Osmux CID %u for %s:%u is now enabled\n",
 | 
			
		||||
		    conn->osmux.cid, inet_ntoa(conn->end.addr),
 | 
			
		||||
		    endp->cfg->osmux_port);
 | 
			
		||||
		return 0;
 | 
			
		||||
	case OSMUX_STATE_ENABLED:
 | 
			
		||||
		return 0;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Osmux %s in conn %s without full negotiation, state %d\n",
 | 
			
		||||
		     sending ? "sent" : "received",
 | 
			
		||||
		     mgcp_conn_dump(conn->conn), conn->osmux.state);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int osmux_legacy_dummy_parse_cid(struct sockaddr_in *addr, struct msgb *msg,
 | 
			
		||||
					uint8_t *osmux_cid)
 | 
			
		||||
{
 | 
			
		||||
	if (msg->len < 1 + sizeof(osmux_cid)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Discarding truncated Osmux dummy load\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* extract the osmux CID from the dummy message */
 | 
			
		||||
	memcpy(osmux_cid, &msg->data[1], sizeof(*osmux_cid));
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define osmux_chunk_length(msg, rem) (rem - msg->len);
 | 
			
		||||
 | 
			
		||||
int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
@@ -331,6 +373,12 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
	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;
 | 
			
		||||
@@ -346,7 +394,7 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
		/* FIXME: Get rid of CONN_ID_XXX! */
 | 
			
		||||
		conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
 | 
			
		||||
		if (!conn_bts)
 | 
			
		||||
			goto out;
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (!endp) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
@@ -354,12 +402,13 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
			     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++;
 | 
			
		||||
		rem = msg->len;
 | 
			
		||||
 | 
			
		||||
			osmux_xfrm_output_sched(&conn_bts->osmux.out, osmuxh);
 | 
			
		||||
		}
 | 
			
		||||
		rem = msg->len;
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -367,54 +416,29 @@ out:
 | 
			
		||||
 | 
			
		||||
/* This is called from the bsc-nat */
 | 
			
		||||
static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
 | 
			
		||||
			      struct msgb *msg)
 | 
			
		||||
			      struct msgb *msg, int endp_type)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	uint8_t osmux_cid;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_net = NULL;
 | 
			
		||||
	struct mgcp_conn_rtp *conn = NULL;
 | 
			
		||||
 | 
			
		||||
	if (msg->len < 1 + sizeof(osmux_cid)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Discarding truncated Osmux dummy load\n");
 | 
			
		||||
	if (osmux_legacy_dummy_parse_cid(addr, msg, &osmux_cid) < 0)
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n",
 | 
			
		||||
	     inet_ntoa(addr->sin_addr));
 | 
			
		||||
 | 
			
		||||
	if (!cfg->osmux) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "bsc wants to use Osmux but bsc-nat did not request it\n");
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* extract the osmux CID from the dummy message */
 | 
			
		||||
	memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid));
 | 
			
		||||
 | 
			
		||||
	endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS);
 | 
			
		||||
	endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, endp_type);
 | 
			
		||||
	if (!endp) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Cannot find endpoint for Osmux CID %d\n", osmux_cid);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
 | 
			
		||||
	if (!conn_net)
 | 
			
		||||
	/* FIXME: Get rid of CONN_ID_XXX! */
 | 
			
		||||
	conn = mgcp_conn_get_rtp(endp, endp_type == MGCP_DEST_BTS ? CONN_ID_NET : CONN_ID_BTS);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (conn_net->osmux.state == OSMUX_STATE_ENABLED)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (osmux_enable_conn(endp, conn_net, &addr->sin_addr, addr->sin_port) < 0 ) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Could not enable osmux in endpoint %d\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr),
 | 
			
		||||
	     ntohs(addr->sin_port));
 | 
			
		||||
	endp_osmux_state_check(endp, conn, false);
 | 
			
		||||
	/* Only needed to punch hole in firewall, it can be dropped */
 | 
			
		||||
out:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -433,9 +457,15 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	if (!cfg->osmux) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "bsc wants to use Osmux but bsc-nat did not request it\n");
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* not any further processing dummy messages */
 | 
			
		||||
	if (msg->data[0] == MGCP_DUMMY_LOAD)
 | 
			
		||||
		return osmux_handle_dummy(cfg, &addr, msg);
 | 
			
		||||
		return osmux_handle_dummy(cfg, &addr, msg, MGCP_DEST_BTS);
 | 
			
		||||
 | 
			
		||||
	rem = msg->len;
 | 
			
		||||
	while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
 | 
			
		||||
@@ -448,7 +478,7 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
		/* FIXME: Get rid of CONN_ID_XXX! */
 | 
			
		||||
		conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
 | 
			
		||||
		if (!conn_net)
 | 
			
		||||
			goto out;
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (!endp) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
@@ -456,12 +486,13 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
			     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++;
 | 
			
		||||
		rem = msg->len;
 | 
			
		||||
 | 
			
		||||
			osmux_xfrm_output_sched(&conn_net->osmux.out, osmuxh);
 | 
			
		||||
		}
 | 
			
		||||
		rem = msg->len;
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -515,25 +546,21 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
 | 
			
		||||
	 *  used to reconstruct the RTP flow from osmux. The RTP SSRC is
 | 
			
		||||
	 *  allocated based on the circuit ID (conn_net->osmux.cid), which is unique
 | 
			
		||||
	 *  in the local scope to the BSC/BSC-NAT. We use it to divide the RTP
 | 
			
		||||
	 *  SSRC space (2^32) by the 256 possible circuit IDs, then randomly
 | 
			
		||||
	 *  SSRC space (2^32) by the OSMUX_CID_MAX + 1 possible circuit IDs, then randomly
 | 
			
		||||
	 *  select one value from that window. Thus, we have no chance to have
 | 
			
		||||
	 *  overlapping RTP SSRC traveling to the BTSes behind the BSC,
 | 
			
		||||
	 *  similarly, for flows traveling to the MSC.
 | 
			
		||||
	 */
 | 
			
		||||
	static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256;
 | 
			
		||||
	static const uint32_t rtp_ssrc_winlen = UINT32_MAX / (OSMUX_CID_MAX + 1);
 | 
			
		||||
	uint16_t osmux_dummy = endp->cfg->osmux_dummy;
 | 
			
		||||
 | 
			
		||||
	/* Check if osmux is enabled for the specified connection */
 | 
			
		||||
	if (conn->osmux.state == OSMUX_STATE_DISABLED) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "OSMUX not enabled for conn:%s\n",
 | 
			
		||||
		     mgcp_conn_dump(conn->conn));
 | 
			
		||||
	if (conn->osmux.state != OSMUX_STATE_ACTIVATING) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "conn:%s didn't negotiate Osmux, state %d\n",
 | 
			
		||||
		     mgcp_conn_dump(conn->conn), conn->osmux.state);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	osmux_xfrm_output_init(&conn->osmux.out,
 | 
			
		||||
			       (conn->osmux.cid * rtp_ssrc_winlen) +
 | 
			
		||||
			       (random() % rtp_ssrc_winlen));
 | 
			
		||||
 | 
			
		||||
	conn->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
 | 
			
		||||
	if (!conn->osmux.in) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle for conn:%s\n",
 | 
			
		||||
@@ -546,6 +573,10 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	osmux_xfrm_output_init(&conn->osmux.out,
 | 
			
		||||
			       (conn->osmux.cid * rtp_ssrc_winlen) +
 | 
			
		||||
			       (random() % rtp_ssrc_winlen));
 | 
			
		||||
 | 
			
		||||
	switch (endp->cfg->role) {
 | 
			
		||||
		case MGCP_BSC_NAT:
 | 
			
		||||
			conn->type = MGCP_OSMUX_BSC_NAT;
 | 
			
		||||
@@ -635,18 +666,9 @@ int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
 | 
			
		||||
	if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (conn->osmux.state == OSMUX_STATE_ACTIVATING) {
 | 
			
		||||
		if (osmux_enable_conn(endp, conn, &conn->end.addr,
 | 
			
		||||
				      htons(endp->cfg->osmux_port)) < 0) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Could not activate osmux for conn:%s\n",
 | 
			
		||||
			     mgcp_conn_dump(conn->conn));
 | 
			
		||||
		}
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Osmux CID %u for %s:%u is now enabled\n",
 | 
			
		||||
		     conn->osmux.cid, inet_ntoa(conn->end.addr),
 | 
			
		||||
		     endp->cfg->osmux_port);
 | 
			
		||||
	}
 | 
			
		||||
	if (endp_osmux_state_check(endp, conn, true) < 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG,
 | 
			
		||||
	     "sending OSMUX dummy load to %s CID %u\n",
 | 
			
		||||
	     inet_ntoa(conn->end.addr), conn->osmux.cid);
 | 
			
		||||
@@ -655,8 +677,8 @@ int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
 | 
			
		||||
			     htons(endp->cfg->osmux_port), buf, sizeof(buf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! bsc-nat allocates/releases the OSMUX cids (Circuit IDs). */
 | 
			
		||||
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8];
 | 
			
		||||
/* bsc-nat allocates/releases the Osmux circuit ID. +7 to round up to 8 bit boundary. */
 | 
			
		||||
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1 + 7) / 8];
 | 
			
		||||
 | 
			
		||||
/*! count the number of taken OSMUX cids.
 | 
			
		||||
 *  \returns number of OSMUX cids in use */
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/core/stats.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
@@ -41,6 +42,7 @@
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_sdp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
 | 
			
		||||
struct mgcp_request {
 | 
			
		||||
	char *name;
 | 
			
		||||
@@ -51,6 +53,84 @@ struct mgcp_request {
 | 
			
		||||
#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
 | 
			
		||||
	{ .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
 | 
			
		||||
	[MGCP_CRCX_SUCCESS] = {"crcx:success", "CRCX command processed successfully."},
 | 
			
		||||
	[MGCP_CRCX_FAIL_BAD_ACTION] = {"crcx:bad_action", "bad action in CRCX command."},
 | 
			
		||||
	[MGCP_CRCX_FAIL_UNHANDLED_PARAM] = {"crcx:unhandled_param", "unhandled parameter in CRCX command."},
 | 
			
		||||
	[MGCP_CRCX_FAIL_MISSING_CALLID] = {"crcx:missing_callid", "missing CallId in CRCX command."},
 | 
			
		||||
	[MGCP_CRCX_FAIL_INVALID_MODE] = {"crcx:invalid_mode", "invalid connection mode in CRCX command."},
 | 
			
		||||
	[MGCP_CRCX_FAIL_LIMIT_EXCEEDED] = {"crcx:limit_exceeded", "limit of concurrent connections was reached."},
 | 
			
		||||
	[MGCP_CRCX_FAIL_UNKNOWN_CALLID] = {"crcx:unkown_callid", "unknown CallId in CRCX command."},
 | 
			
		||||
	[MGCP_CRCX_FAIL_ALLOC_CONN] = {"crcx:alloc_conn_fail", "connection allocation failure."},
 | 
			
		||||
	[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC] = {"crcx:no_remote_conn_desc", "no opposite end specified for connection."},
 | 
			
		||||
	[MGCP_CRCX_FAIL_START_RTP] = {"crcx:start_rtp_failure", "failure to start RTP processing."},
 | 
			
		||||
	[MGCP_CRCX_FAIL_REJECTED_BY_POLICY] = {"crcx:conn_rejected", "connection rejected by policy."},
 | 
			
		||||
	[MGCP_CRCX_FAIL_NO_OSMUX] = {"crcx:no_osmux", "no osmux offered by peer."},
 | 
			
		||||
	[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS] = {"crcx:conn_opt", "connection options invalid."},
 | 
			
		||||
	[MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = {"crcx:codec_nego", "codec negotiation failure."},
 | 
			
		||||
	[MGCP_CRCX_FAIL_BIND_PORT] = {"crcx:bind_port", "port bind failure."},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "crcx",
 | 
			
		||||
	.group_description = "crxc statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(mgcp_crcx_ctr_desc),
 | 
			
		||||
	.ctr_desc = mgcp_crcx_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
 | 
			
		||||
	[MGCP_MDCX_SUCCESS] = {"mdcx:success", "MDCX command processed successfully."},
 | 
			
		||||
	[MGCP_MDCX_FAIL_WILDCARD] = {"mdcx:wildcard", "wildcard endpoint names in MDCX commands are unsupported."},
 | 
			
		||||
	[MGCP_MDCX_FAIL_NO_CONN] = {"mdcx:no_conn", "endpoint specified in MDCX command has no active connections."},
 | 
			
		||||
	[MGCP_MDCX_FAIL_INVALID_CALLID] = {"mdcx:callid", "invalid CallId specified in MDCX command."},
 | 
			
		||||
	[MGCP_MDCX_FAIL_INVALID_CONNID] = {"mdcx:connid", "invalid connection ID specified in MDCX command."},
 | 
			
		||||
	[MGCP_MDCX_FAIL_UNHANDLED_PARAM] = {"crcx:unhandled_param", "unhandled parameter in MDCX command."},
 | 
			
		||||
	[MGCP_MDCX_FAIL_NO_CONNID] = {"mdcx:no_connid", "no connection ID specified in MDCX command."},
 | 
			
		||||
	[MGCP_MDCX_FAIL_CONN_NOT_FOUND] = {"mdcx:conn_not_found", "connection specified in MDCX command does not exist."},
 | 
			
		||||
	[MGCP_MDCX_FAIL_INVALID_MODE] = {"mdcx:invalid_mode", "invalid connection mode in MDCX command."},
 | 
			
		||||
	[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = {"mdcx:conn_opt", "connection options invalid."},
 | 
			
		||||
	[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] = {"mdcx:no_remote_conn_desc", "no opposite end specified for connection."},
 | 
			
		||||
	[MGCP_MDCX_FAIL_START_RTP] = {"mdcx:start_rtp_failure", "failure to start RTP processing."},
 | 
			
		||||
	[MGCP_MDCX_FAIL_REJECTED_BY_POLICY] = {"mdcx:conn_rejected", "connection rejected by policy."},
 | 
			
		||||
	[MGCP_MDCX_DEFERRED_BY_POLICY] = {"mdcx:conn_deferred", "connection deferred by policy."},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "mdcx",
 | 
			
		||||
	.group_description = "mdcx statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(mgcp_mdcx_ctr_desc),
 | 
			
		||||
	.ctr_desc = mgcp_mdcx_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
 | 
			
		||||
	[MGCP_DLCX_SUCCESS] = {"dlcx:success", "DLCX command processed successfully."},
 | 
			
		||||
	[MGCP_DLCX_FAIL_WILDCARD] = {"dlcx:wildcard", "wildcard names in DLCX commands are unsupported."},
 | 
			
		||||
	[MGCP_DLCX_FAIL_NO_CONN] = {"dlcx:no_conn", "endpoint specified in DLCX command has no active connections."},
 | 
			
		||||
	[MGCP_DLCX_FAIL_INVALID_CALLID] = {"dlcx:callid", "CallId specified in DLCX command mismatches endpoint's CallId ."},
 | 
			
		||||
	[MGCP_DLCX_FAIL_INVALID_CONNID] = {"dlcx:connid", "connection ID specified in DLCX command does not exist on endpoint."},
 | 
			
		||||
	[MGCP_DLCX_FAIL_UNHANDLED_PARAM] = {"dlcx:unhandled_param", "unhandled parameter in DLCX command."},
 | 
			
		||||
	[MGCP_DLCX_FAIL_REJECTED_BY_POLICY] = {"dlcx:rejected", "connection deletion rejected by policy."},
 | 
			
		||||
	[MGCP_DLCX_DEFERRED_BY_POLICY] = {"dlcx:deferred", "connection deletion deferred by policy."},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "dlcx",
 | 
			
		||||
	.group_description = "dlcx statistics",
 | 
			
		||||
	.class_id = OSMO_STATS_CLASS_GLOBAL,
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(mgcp_dlcx_ctr_desc),
 | 
			
		||||
	.ctr_desc = mgcp_dlcx_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
 | 
			
		||||
	.group_name_prefix = "all_rtp_conn",
 | 
			
		||||
	.group_description = "aggregated statistics for all rtp connections",
 | 
			
		||||
	.class_id = 1,
 | 
			
		||||
	.num_ctr = ARRAY_SIZE(all_rtp_conn_rate_ctr_desc),
 | 
			
		||||
	.ctr_desc = all_rtp_conn_rate_ctr_desc
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data);
 | 
			
		||||
static struct msgb *handle_create_con(struct mgcp_parse_data *data);
 | 
			
		||||
static struct msgb *handle_delete_con(struct mgcp_parse_data *data);
 | 
			
		||||
@@ -518,7 +598,7 @@ static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
 | 
			
		||||
				 const char *options)
 | 
			
		||||
{
 | 
			
		||||
	char *p_opt, *a_opt;
 | 
			
		||||
	char codec[9];
 | 
			
		||||
	char codec[17];
 | 
			
		||||
 | 
			
		||||
	if (!options)
 | 
			
		||||
		return 0;
 | 
			
		||||
@@ -544,7 +624,7 @@ static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
 | 
			
		||||
	 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
 | 
			
		||||
	 * codec only. */
 | 
			
		||||
	a_opt = strstr(lco->string, "a:");
 | 
			
		||||
	if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1) {
 | 
			
		||||
	if (a_opt && sscanf(a_opt, "a:%16[^,]", codec) == 1) {
 | 
			
		||||
		talloc_free(lco->codec);
 | 
			
		||||
		lco->codec = talloc_strdup(ctx, codec);
 | 
			
		||||
	}
 | 
			
		||||
@@ -572,6 +652,8 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
 | 
			
		||||
 | 
			
		||||
	rtp->force_aligned_timing = tcfg->force_aligned_timing;
 | 
			
		||||
	rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
 | 
			
		||||
	rtp->rfc5993_hr_convert = tcfg->rfc5993_hr_convert;
 | 
			
		||||
	rtp->amr_oa_bwe_convert = tcfg->amr_oa_bwe_convert;
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG,
 | 
			
		||||
	     "Configuring RTP endpoint: local port %d%s%s\n",
 | 
			
		||||
@@ -674,13 +756,37 @@ error:
 | 
			
		||||
	return 534;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
 | 
			
		||||
{
 | 
			
		||||
	char *saveptr = NULL;
 | 
			
		||||
 | 
			
		||||
	if (strncmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
 | 
			
		||||
		return false;
 | 
			
		||||
	line += strlen(MGCP_X_OSMO_IGN_HEADER);
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		char *token = strtok_r(line, " ", &saveptr);
 | 
			
		||||
		line = NULL;
 | 
			
		||||
		if (!token)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (!strcmp(token, "C"))
 | 
			
		||||
			endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
 | 
			
		||||
		else
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR, "endpoint 0x%x: received unknown X-Osmo-IGN item '%s'\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp), token);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* CRCX command handler, processes the received command */
 | 
			
		||||
static struct msgb *handle_create_con(struct mgcp_parse_data *p)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk_config *tcfg;
 | 
			
		||||
	struct mgcp_trunk_config *tcfg = p->endp->tcfg;
 | 
			
		||||
	struct mgcp_endpoint *endp = p->endp;
 | 
			
		||||
	struct rate_ctr_group *rate_ctrs = tcfg->mgcp_crcx_ctr_group;
 | 
			
		||||
	int error_code = 400;
 | 
			
		||||
 | 
			
		||||
	const char *local_options = NULL;
 | 
			
		||||
	const char *callid = NULL;
 | 
			
		||||
	const char *mode = NULL;
 | 
			
		||||
@@ -709,19 +815,26 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
 | 
			
		||||
			/* It is illegal to send a connection identifier
 | 
			
		||||
			 * together with a CRCX, the MGW will assign the
 | 
			
		||||
			 * connection identifier by itself on CRCX */
 | 
			
		||||
			rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_BAD_ACTION]);
 | 
			
		||||
			return create_err_response(NULL, 523, "CRCX", p->trans);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'M':
 | 
			
		||||
			mode = (const char *)line + 3;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'X':
 | 
			
		||||
			/* If osmoux is disabled, just skip setting it up */
 | 
			
		||||
			if (strncmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
 | 
			
		||||
				/* If osmux is disabled, just skip setting it up */
 | 
			
		||||
				if (!p->endp->cfg->osmux)
 | 
			
		||||
					break;
 | 
			
		||||
			if (strncmp("Osmux: ", line + 2, strlen("Osmux: ")) ==
 | 
			
		||||
			    0)
 | 
			
		||||
				osmux_cid = mgcp_osmux_setup(endp, line);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (parse_x_osmo_ign(endp, line))
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			/* Ignore unknown X-headers */
 | 
			
		||||
			break;
 | 
			
		||||
		case '\0':
 | 
			
		||||
			have_sdp = 1;
 | 
			
		||||
			goto mgcp_header_done;
 | 
			
		||||
@@ -729,19 +842,19 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
 | 
			
		||||
			LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
			     "CRCX: endpoint:%x unhandled option: '%c'/%d\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp), *line, *line);
 | 
			
		||||
			rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_UNHANDLED_PARAM]);
 | 
			
		||||
			return create_err_response(NULL, 539, "CRCX", p->trans);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
mgcp_header_done:
 | 
			
		||||
	tcfg = p->endp->tcfg;
 | 
			
		||||
 | 
			
		||||
	/* Check parameters */
 | 
			
		||||
	if (!callid) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "CRCX: endpoint:%x insufficient parameters, missing callid\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_MISSING_CALLID]);
 | 
			
		||||
		return create_err_response(endp, 516, "CRCX", p->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -749,6 +862,7 @@ mgcp_header_done:
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "CRCX: endpoint:%x insufficient parameters, missing mode\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_INVALID_MODE]);
 | 
			
		||||
		return create_err_response(endp, 517, "CRCX", p->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -765,6 +879,7 @@ mgcp_header_done:
 | 
			
		||||
		} else {
 | 
			
		||||
			/* There is no more room for a connection, leave
 | 
			
		||||
			 * everything as it is and return with an error */
 | 
			
		||||
			rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_LIMIT_EXCEEDED]);
 | 
			
		||||
			return create_err_response(endp, 540, "CRCX", p->trans);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -782,6 +897,7 @@ mgcp_header_done:
 | 
			
		||||
		else {
 | 
			
		||||
			/* This is not our call, leave everything as it is and
 | 
			
		||||
			 * return with an error. */
 | 
			
		||||
			rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_UNKNOWN_CALLID]);
 | 
			
		||||
			return create_err_response(endp, 400, "CRCX", p->trans);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -797,6 +913,7 @@ mgcp_header_done:
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "CRCX: endpoint:0x%x unable to allocate RTP connection\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_ALLOC_CONN]);
 | 
			
		||||
		goto error2;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
@@ -805,6 +922,7 @@ mgcp_header_done:
 | 
			
		||||
 | 
			
		||||
	if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
 | 
			
		||||
		error_code = 517;
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_INVALID_MODE]);
 | 
			
		||||
		goto error2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -818,6 +936,7 @@ mgcp_header_done:
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "CRCX: endpoint:0x%x osmux only and no osmux offered\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_NO_OSMUX]);
 | 
			
		||||
		goto error2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -830,6 +949,7 @@ mgcp_header_done:
 | 
			
		||||
			     "CRCX: endpoint:%x inavlid local connection options!\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp));
 | 
			
		||||
			error_code = rc;
 | 
			
		||||
			rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS]);
 | 
			
		||||
			goto error2;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -839,6 +959,7 @@ mgcp_header_done:
 | 
			
		||||
	mgcp_codec_summary(conn);
 | 
			
		||||
	if (rc) {
 | 
			
		||||
		error_code = rc;
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_CODEC_NEGOTIATION]);
 | 
			
		||||
		goto error2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -860,10 +981,12 @@ mgcp_header_done:
 | 
			
		||||
		     "CRCX: endpoint:%x selected connection mode type requires an opposite end!\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		error_code = 527;
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC]);
 | 
			
		||||
		goto error2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (allocate_port(endp, conn) != 0) {
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_BIND_PORT]);
 | 
			
		||||
		goto error2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -871,6 +994,7 @@ mgcp_header_done:
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "CRCX: endpoint:0x%x could not start RTP processing!\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_START_RTP]);
 | 
			
		||||
		goto error2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -885,6 +1009,7 @@ mgcp_header_done:
 | 
			
		||||
			     "CRCX: endpoint:0x%x CRCX rejected by policy\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp));
 | 
			
		||||
			mgcp_endp_release(endp);
 | 
			
		||||
			rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_REJECTED_BY_POLICY]);
 | 
			
		||||
			return create_err_response(endp, 400, "CRCX", p->trans);
 | 
			
		||||
			break;
 | 
			
		||||
		case MGCP_POLICY_DEFER:
 | 
			
		||||
@@ -912,6 +1037,7 @@ mgcp_header_done:
 | 
			
		||||
	LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
	     "CRCX: endpoint:0x%x connection successfully created\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp));
 | 
			
		||||
	rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_SUCCESS]);
 | 
			
		||||
	return create_response_with_sdp(endp, conn, "CRCX", p->trans, true);
 | 
			
		||||
error2:
 | 
			
		||||
	mgcp_endp_release(endp);
 | 
			
		||||
@@ -928,7 +1054,9 @@ error2:
 | 
			
		||||
/* MDCX command handler, processes the received command */
 | 
			
		||||
static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk_config *tcfg = p->endp->tcfg;
 | 
			
		||||
	struct mgcp_endpoint *endp = p->endp;
 | 
			
		||||
	struct rate_ctr_group *rate_ctrs = tcfg->mgcp_mdcx_ctr_group;
 | 
			
		||||
	int error_code = 500;
 | 
			
		||||
	int silent = 0;
 | 
			
		||||
	int have_sdp = 0;
 | 
			
		||||
@@ -946,6 +1074,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "MDCX: endpoint:0x%x wildcarded endpoint names not supported.\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_WILDCARD]);
 | 
			
		||||
		return create_err_response(endp, 507, "MDCX", p->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -953,6 +1082,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "MDCX: endpoint:0x%x endpoint is not holding a connection.\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_NO_CONN]);
 | 
			
		||||
		return create_err_response(endp, 400, "MDCX", p->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -963,14 +1093,15 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
 | 
			
		||||
		switch (line[0]) {
 | 
			
		||||
		case 'C':
 | 
			
		||||
			if (mgcp_verify_call_id(endp, line + 3) != 0) {
 | 
			
		||||
				rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_CALLID]);
 | 
			
		||||
				error_code = 516;
 | 
			
		||||
				goto error3;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case 'I':
 | 
			
		||||
			conn_id = (const char *)line + 3;
 | 
			
		||||
			if (mgcp_verify_ci(endp, conn_id) != 0) {
 | 
			
		||||
				error_code = 515;
 | 
			
		||||
			if ((error_code = mgcp_verify_ci(endp, conn_id))) {
 | 
			
		||||
				rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_CONNID]);
 | 
			
		||||
				goto error3;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
@@ -991,6 +1122,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
 | 
			
		||||
			LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
			     "MDCX: endpoint:0x%x Unhandled MGCP option: '%c'/%d\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp), line[0], line[0]);
 | 
			
		||||
			rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_UNHANDLED_PARAM]);
 | 
			
		||||
			return create_err_response(NULL, 539, "MDCX", p->trans);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
@@ -1001,15 +1133,21 @@ mgcp_header_done:
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "MDCX: endpoint:0x%x insufficient parameters, missing ci (connectionIdentifier)\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_NO_CONNID]);
 | 
			
		||||
		return create_err_response(endp, 515, "MDCX", p->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn = mgcp_conn_get_rtp(endp, conn_id);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_CONN_NOT_FOUND]);
 | 
			
		||||
		return create_err_response(endp, 400, "MDCX", p->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mgcp_conn_watchdog_kick(conn->conn);
 | 
			
		||||
 | 
			
		||||
	if (mode) {
 | 
			
		||||
		if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
 | 
			
		||||
			rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_MODE]);
 | 
			
		||||
			error_code = 517;
 | 
			
		||||
			goto error3;
 | 
			
		||||
		}
 | 
			
		||||
@@ -1022,9 +1160,10 @@ mgcp_header_done:
 | 
			
		||||
					  &endp->local_options, local_options);
 | 
			
		||||
		if (rc != 0) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "MDCX: endpoint:%x inavlid local connection options!\n",
 | 
			
		||||
			     "MDCX: endpoint:%x invalid local connection options!\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp));
 | 
			
		||||
			error_code = rc;
 | 
			
		||||
			rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS]);
 | 
			
		||||
			goto error3;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -1045,12 +1184,15 @@ mgcp_header_done:
 | 
			
		||||
		     "MDCX: endpoint:%x selected connection mode type requires an opposite end!\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		error_code = 527;
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC]);
 | 
			
		||||
		goto error3;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (setup_rtp_processing(endp, conn) != 0)
 | 
			
		||||
	if (setup_rtp_processing(endp, conn) != 0) {
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_START_RTP]);
 | 
			
		||||
		goto error3;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/* policy CB */
 | 
			
		||||
@@ -1063,6 +1205,7 @@ mgcp_header_done:
 | 
			
		||||
			LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
			     "MDCX: endpoint:0x%x rejected by policy\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp));
 | 
			
		||||
			rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_REJECTED_BY_POLICY]);
 | 
			
		||||
			if (silent)
 | 
			
		||||
				goto out_silent;
 | 
			
		||||
			return create_err_response(endp, 400, "MDCX", p->trans);
 | 
			
		||||
@@ -1070,8 +1213,9 @@ mgcp_header_done:
 | 
			
		||||
		case MGCP_POLICY_DEFER:
 | 
			
		||||
			/* stop processing */
 | 
			
		||||
			LOGP(DLMGCP, LOGL_DEBUG,
 | 
			
		||||
			     "MDCX: endpoint:0x%x defered by policy\n",
 | 
			
		||||
			     "MDCX: endpoint:0x%x deferred by policy\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp));
 | 
			
		||||
			rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_DEFERRED_BY_POLICY]);
 | 
			
		||||
			return NULL;
 | 
			
		||||
			break;
 | 
			
		||||
		case MGCP_POLICY_CONT:
 | 
			
		||||
@@ -1096,6 +1240,7 @@ mgcp_header_done:
 | 
			
		||||
	    && endp->tcfg->keepalive_interval != MGCP_KEEPALIVE_NEVER)
 | 
			
		||||
		send_dummy(endp, conn);
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_SUCCESS]);
 | 
			
		||||
	if (silent)
 | 
			
		||||
		goto out_silent;
 | 
			
		||||
 | 
			
		||||
@@ -1115,7 +1260,9 @@ out_silent:
 | 
			
		||||
/* DLCX command handler, processes the received command */
 | 
			
		||||
static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk_config *tcfg = p->endp->tcfg;
 | 
			
		||||
	struct mgcp_endpoint *endp = p->endp;
 | 
			
		||||
	struct rate_ctr_group *rate_ctrs = tcfg->mgcp_dlcx_ctr_group;
 | 
			
		||||
	int error_code = 400;
 | 
			
		||||
	int silent = 0;
 | 
			
		||||
	char *line;
 | 
			
		||||
@@ -1132,6 +1279,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "DLCX: endpoint:0x%x wildcarded endpoint names not supported.\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_WILDCARD]);
 | 
			
		||||
		return create_err_response(endp, 507, "DLCX", p->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1139,6 +1287,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "DLCX: endpoint:0x%x endpoint is not holding a connection.\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_NO_CONN]);
 | 
			
		||||
		return create_err_response(endp, 515, "DLCX", p->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1150,13 +1299,14 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
 | 
			
		||||
		case 'C':
 | 
			
		||||
			if (mgcp_verify_call_id(endp, line + 3) != 0) {
 | 
			
		||||
				error_code = 516;
 | 
			
		||||
				rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_INVALID_CALLID]);
 | 
			
		||||
				goto error3;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case 'I':
 | 
			
		||||
			conn_id = (const char *)line + 3;
 | 
			
		||||
			if (mgcp_verify_ci(endp, conn_id) != 0) {
 | 
			
		||||
				error_code = 515;
 | 
			
		||||
			if ((error_code = mgcp_verify_ci(endp, conn_id))) {
 | 
			
		||||
				rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_INVALID_CONNID]);
 | 
			
		||||
				goto error3;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
@@ -1167,6 +1317,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
 | 
			
		||||
			LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
			     "DLCX: endpoint:0x%x Unhandled MGCP option: '%c'/%d\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp), line[0], line[0]);
 | 
			
		||||
			rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_UNHANDLED_PARAM]);
 | 
			
		||||
			return create_err_response(NULL, 539, "DLCX", p->trans);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
@@ -1182,12 +1333,14 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
 | 
			
		||||
			LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
			     "DLCX: endpoint:0x%x rejected by policy\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp));
 | 
			
		||||
			rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_REJECTED_BY_POLICY]);
 | 
			
		||||
			if (silent)
 | 
			
		||||
				goto out_silent;
 | 
			
		||||
			return create_err_response(endp, 400, "DLCX", p->trans);
 | 
			
		||||
			break;
 | 
			
		||||
		case MGCP_POLICY_DEFER:
 | 
			
		||||
			/* stop processing */
 | 
			
		||||
			rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_DEFERRED_BY_POLICY]);
 | 
			
		||||
			return NULL;
 | 
			
		||||
			break;
 | 
			
		||||
		case MGCP_POLICY_CONT:
 | 
			
		||||
@@ -1200,9 +1353,13 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
 | 
			
		||||
	 * wildcarded DLCX and drop all connections at once. (See also
 | 
			
		||||
	 * RFC3435 Section F.7) */
 | 
			
		||||
	if (!conn_id) {
 | 
			
		||||
		int num_conns = llist_count(&endp->conns);
 | 
			
		||||
		LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
		     "DLCX: endpoint:0x%x missing ci (connectionIdentifier), will remove all connections at once\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		     "DLCX: endpoint:0x%x missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
 | 
			
		||||
		     num_conns, ENDPOINT_NUMBER(endp));
 | 
			
		||||
 | 
			
		||||
		if (num_conns > 0)
 | 
			
		||||
			rate_ctr_add(&rate_ctrs->ctr[MGCP_DLCX_SUCCESS], num_conns);
 | 
			
		||||
 | 
			
		||||
		mgcp_endp_release(endp);
 | 
			
		||||
 | 
			
		||||
@@ -1214,9 +1371,10 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
 | 
			
		||||
 | 
			
		||||
	/* Find the connection */
 | 
			
		||||
	conn = mgcp_conn_get_rtp(endp, conn_id);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_INVALID_CONNID]);
 | 
			
		||||
		goto error3;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	/* save the statistics of the current connection */
 | 
			
		||||
	mgcp_format_stats(stats, sizeof(stats), conn->conn);
 | 
			
		||||
 | 
			
		||||
@@ -1241,6 +1399,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
 | 
			
		||||
		p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
 | 
			
		||||
				  MGCP_ENDP_DLCX);
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_SUCCESS]);
 | 
			
		||||
	if (silent)
 | 
			
		||||
		goto out_silent;
 | 
			
		||||
	return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats);
 | 
			
		||||
@@ -1370,6 +1529,45 @@ void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval)
 | 
			
		||||
				    tcfg->keepalive_interval, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group)
 | 
			
		||||
{
 | 
			
		||||
	rate_ctr_group_free(rate_ctr_group);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void alloc_mgcp_rate_counters(struct mgcp_trunk_config *trunk, void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	/* FIXME: Each new rate counter group requires a unique index. At the
 | 
			
		||||
	 * moment we generate an index using a counter, but perhaps there is
 | 
			
		||||
	 * a better way of assigning indices? */
 | 
			
		||||
	static unsigned int crcx_rate_ctr_index = 0;
 | 
			
		||||
	static unsigned int mdcx_rate_ctr_index = 0;
 | 
			
		||||
	static unsigned int dlcx_rate_ctr_index = 0;
 | 
			
		||||
	static unsigned int all_rtp_conn_rate_ctr_index = 0;
 | 
			
		||||
 | 
			
		||||
	if (trunk->mgcp_crcx_ctr_group == NULL) {
 | 
			
		||||
		trunk->mgcp_crcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index);
 | 
			
		||||
		talloc_set_destructor(trunk->mgcp_crcx_ctr_group, free_rate_counter_group);
 | 
			
		||||
		crcx_rate_ctr_index++;
 | 
			
		||||
	}
 | 
			
		||||
	if (trunk->mgcp_mdcx_ctr_group == NULL) {
 | 
			
		||||
		trunk->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index);
 | 
			
		||||
		talloc_set_destructor(trunk->mgcp_mdcx_ctr_group, free_rate_counter_group);
 | 
			
		||||
		mdcx_rate_ctr_index++;
 | 
			
		||||
	}
 | 
			
		||||
	if (trunk->mgcp_dlcx_ctr_group == NULL) {
 | 
			
		||||
		trunk->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index);
 | 
			
		||||
		talloc_set_destructor(trunk->mgcp_dlcx_ctr_group, free_rate_counter_group);
 | 
			
		||||
		dlcx_rate_ctr_index++;
 | 
			
		||||
	}
 | 
			
		||||
	if (trunk->all_rtp_conn_stats == NULL) {
 | 
			
		||||
		trunk->all_rtp_conn_stats = rate_ctr_group_alloc(ctx, &all_rtp_conn_rate_ctr_group_desc,
 | 
			
		||||
								 all_rtp_conn_rate_ctr_index);
 | 
			
		||||
		talloc_set_destructor(trunk->all_rtp_conn_stats, free_rate_counter_group);
 | 
			
		||||
		all_rtp_conn_rate_ctr_index++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! allocate configuration with default values.
 | 
			
		||||
 *  (called once at startup by main function) */
 | 
			
		||||
struct mgcp_config *mgcp_config_alloc(void)
 | 
			
		||||
@@ -1407,6 +1605,7 @@ struct mgcp_config *mgcp_config_alloc(void)
 | 
			
		||||
	cfg->trunk.audio_send_name = 1;
 | 
			
		||||
	cfg->trunk.omit_rtcp = 0;
 | 
			
		||||
	mgcp_trunk_set_keepalive(&cfg->trunk, MGCP_KEEPALIVE_ONCE);
 | 
			
		||||
	alloc_mgcp_rate_counters(&cfg->trunk, cfg);
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&cfg->trunks);
 | 
			
		||||
 | 
			
		||||
@@ -1438,7 +1637,9 @@ struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr)
 | 
			
		||||
	trunk->vty_number_endpoints = 33;
 | 
			
		||||
	trunk->omit_rtcp = 0;
 | 
			
		||||
	mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
 | 
			
		||||
	alloc_mgcp_rate_counters(trunk, trunk);
 | 
			
		||||
	llist_add_tail(&trunk->entry, &cfg->trunks);
 | 
			
		||||
 | 
			
		||||
	return trunk;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1483,6 +1684,8 @@ int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tcfg->number_endpoints = tcfg->vty_number_endpoints;
 | 
			
		||||
	alloc_mgcp_rate_counters(tcfg, tcfg->cfg);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -286,6 +286,50 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
	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.
 | 
			
		||||
@@ -300,6 +344,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
	const char *audio_name;
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int payload_types[1];
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(endp);
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
@@ -323,17 +368,14 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
		goto buffer_too_small;
 | 
			
		||||
 | 
			
		||||
	if (payload_type >= 0) {
 | 
			
		||||
		rc = msgb_printf(sdp, "m=audio %d RTP/AVP %d\r\n",
 | 
			
		||||
				 conn->end.local_port, payload_type);
 | 
			
		||||
 | 
			
		||||
		payload_types[0] = payload_type;
 | 
			
		||||
		rc = add_audio(sdp, payload_types, 1, conn->end.local_port);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			goto buffer_too_small;
 | 
			
		||||
 | 
			
		||||
		/* FIXME: Check if the payload type is from the static range,
 | 
			
		||||
		 * if yes, omitthe a=rtpmap since it is unnecessary */
 | 
			
		||||
		if (audio_name && endp->tcfg->audio_send_name && (payload_type >= 96 && payload_type <= 127)) {
 | 
			
		||||
			rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n",
 | 
			
		||||
					 payload_type, audio_name);
 | 
			
		||||
 | 
			
		||||
		if (endp->tcfg->audio_send_name) {
 | 
			
		||||
			rc = add_rtpmap(sdp, payload_type, audio_name);
 | 
			
		||||
			if (rc < 0)
 | 
			
		||||
				goto buffer_too_small;
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/vty/misc.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
@@ -31,10 +32,14 @@
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
 | 
			
		||||
#define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
 | 
			
		||||
#define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
 | 
			
		||||
#define RTP_KEEPALIVE_STR "Send dummy UDP packet to net RTP destination\n"
 | 
			
		||||
#define RTP_TS101318_RFC5993_CONV_STR "Convert GSM-HR from TS101318 to RFC5993 and vice versa\n"
 | 
			
		||||
#define RTP_AMR_OA_BWE_CONV_STR "Convert AMR from octet-aligned to bandwith-efficient mode and vice versa\n"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct mgcp_config *g_cfg = NULL;
 | 
			
		||||
 | 
			
		||||
@@ -94,13 +99,17 @@ static int config_write_mgcp(struct vty *vty)
 | 
			
		||||
	else
 | 
			
		||||
		vty_out(vty, "  no rtcp-omit%s", VTY_NEWLINE);
 | 
			
		||||
	if (g_cfg->trunk.force_constant_ssrc
 | 
			
		||||
	    || g_cfg->trunk.force_aligned_timing) {
 | 
			
		||||
	    || g_cfg->trunk.force_aligned_timing
 | 
			
		||||
	    || g_cfg->trunk.rfc5993_hr_convert) {
 | 
			
		||||
		vty_out(vty, "  %srtp-patch ssrc%s",
 | 
			
		||||
			g_cfg->trunk.force_constant_ssrc ? "" : "no ",
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, "  %srtp-patch timestamp%s",
 | 
			
		||||
			g_cfg->trunk.force_aligned_timing ? "" : "no ",
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, "  %srtp-patch rfc5993hr%s",
 | 
			
		||||
			g_cfg->trunk.rfc5993_hr_convert ? "" : "no ",
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
	} else
 | 
			
		||||
		vty_out(vty, "  no rtp-patch%s", VTY_NEWLINE);
 | 
			
		||||
	if (g_cfg->trunk.audio_payload != -1)
 | 
			
		||||
@@ -152,6 +161,10 @@ static int config_write_mgcp(struct vty *vty)
 | 
			
		||||
		vty_out(vty, "  osmux dummy %s%s",
 | 
			
		||||
			g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (g_cfg->conn_timeout)
 | 
			
		||||
		vty_out(vty, "  conn-timeout %u%s", g_cfg->conn_timeout, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -160,11 +173,19 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
 | 
			
		||||
	struct mgcp_rtp_state *state = &conn->state;
 | 
			
		||||
	struct mgcp_rtp_end *end = &conn->end;
 | 
			
		||||
	struct mgcp_rtp_codec *codec = end->codec;
 | 
			
		||||
	struct rate_ctr *tx_packets, *tx_bytes;
 | 
			
		||||
	struct rate_ctr *rx_packets, *rx_bytes;
 | 
			
		||||
	struct rate_ctr *dropped_packets;
 | 
			
		||||
 | 
			
		||||
	tx_packets = &conn->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR];
 | 
			
		||||
	tx_bytes = &conn->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR];
 | 
			
		||||
	rx_packets = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
 | 
			
		||||
	rx_bytes = &conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR];
 | 
			
		||||
	dropped_packets = &conn->rate_ctr_group->ctr[RTP_DROPPED_PACKETS_CTR];
 | 
			
		||||
 | 
			
		||||
	vty_out(vty,
 | 
			
		||||
		"   Packets Sent: %" PRIu64 " (%" PRIu64 " bytes total)%s"
 | 
			
		||||
		"   Packets Received: %" PRIu64 " (%" PRIu64 " bytes total)%s"
 | 
			
		||||
		"   Timestamp Errs: %" PRIu64 "->%" PRIu64 "%s"
 | 
			
		||||
		"   Dropped Packets: %" PRIu64 "%s"
 | 
			
		||||
		"   Payload Type: %d Rate: %u Channels: %d %s"
 | 
			
		||||
@@ -172,6 +193,8 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
 | 
			
		||||
		"   FPP: %d Packet Duration: %u%s"
 | 
			
		||||
		"   FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"
 | 
			
		||||
		"   Output-Enabled: %d Force-PTIME: %d%s",
 | 
			
		||||
		tx_packets->current, tx_bytes->current, VTY_NEWLINE,
 | 
			
		||||
		rx_packets->current, rx_bytes->current, VTY_NEWLINE,
 | 
			
		||||
		state->in_stream.err_ts_ctr->current,
 | 
			
		||||
		state->out_stream.err_ts_ctr->current,
 | 
			
		||||
	        VTY_NEWLINE,
 | 
			
		||||
@@ -184,13 +207,47 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
 | 
			
		||||
		end->force_output_ptime, VTY_NEWLINE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
 | 
			
		||||
		       int verbose)
 | 
			
		||||
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, int epidx,
 | 
			
		||||
			  int trunk_nr, enum mgcp_trunk_type trunk_type, int show_stats)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "%s trunk nr %d with %d endpoints:%s",
 | 
			
		||||
	vty_out(vty, "%s trunk %d endpoint %s%.2x:%s",
 | 
			
		||||
		trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", trunk_nr,
 | 
			
		||||
		trunk_type == MGCP_TRUNK_VIRTUAL ? MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK : "",
 | 
			
		||||
		epidx, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	if (llist_empty(&endp->conns)) {
 | 
			
		||||
		vty_out(vty, "   No active connections%s", VTY_NEWLINE);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(conn, &endp->conns, entry) {
 | 
			
		||||
		vty_out(vty, "   CONN: %s%s", mgcp_conn_dump(conn), VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
		if (show_stats) {
 | 
			
		||||
			if (endp->cfg->conn_timeout) {
 | 
			
		||||
				struct timeval remaining;
 | 
			
		||||
				osmo_timer_remaining(&conn->watchdog, NULL, &remaining);
 | 
			
		||||
				vty_out(vty, "   Currently remaining timeout (seconds): %d.%06d%s",
 | 
			
		||||
					(int)remaining.tv_sec, (int)remaining.tv_usec, VTY_NEWLINE);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* FIXME: Also add verbosity for other
 | 
			
		||||
			 * connection types (E1) as soon as
 | 
			
		||||
			 * the implementation is available */
 | 
			
		||||
			if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
				dump_rtp_end(vty, &conn->u.rtp);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg, int show_stats)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "%s trunk %d with %d endpoints:%s",
 | 
			
		||||
		cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
 | 
			
		||||
		cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
@@ -201,29 +258,35 @@ static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
 | 
			
		||||
 | 
			
		||||
	for (i = 1; i < cfg->number_endpoints; ++i) {
 | 
			
		||||
		struct mgcp_endpoint *endp = &cfg->endpoints[i];
 | 
			
		||||
 | 
			
		||||
		vty_out(vty, "Endpoint 0x%.2x:%s", i, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
		llist_for_each_entry(conn, &endp->conns, entry) {
 | 
			
		||||
			vty_out(vty, "   CONN: %s%s",
 | 
			
		||||
				mgcp_conn_dump(conn), VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
			if (verbose) {
 | 
			
		||||
				/* FIXME: Also add verbosity for other
 | 
			
		||||
				 * connection types (E1) as soon as
 | 
			
		||||
				 * the implementation is available */
 | 
			
		||||
				if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
					dump_rtp_end(vty, &conn->u.rtp);
 | 
			
		||||
		dump_endpoint(vty, endp, i, cfg->trunk_nr, cfg->trunk_type, show_stats);
 | 
			
		||||
		if (i < cfg->number_endpoints - 1)
 | 
			
		||||
			vty_out(vty, "%s", VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (show_stats && cfg->mgcp_crcx_ctr_group) {
 | 
			
		||||
		vty_out(vty, "   %s:%s", cfg->mgcp_crcx_ctr_group->desc->group_description, VTY_NEWLINE);
 | 
			
		||||
		vty_out_rate_ctr_group_fmt(vty, "   %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_crcx_ctr_group);
 | 
			
		||||
	}
 | 
			
		||||
	if (show_stats && cfg->mgcp_dlcx_ctr_group) {
 | 
			
		||||
		vty_out(vty, "   %s:%s", cfg->mgcp_dlcx_ctr_group->desc->group_description, VTY_NEWLINE);
 | 
			
		||||
		vty_out_rate_ctr_group_fmt(vty, "   %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_dlcx_ctr_group);
 | 
			
		||||
	}
 | 
			
		||||
	if (show_stats && cfg->mgcp_mdcx_ctr_group) {
 | 
			
		||||
		vty_out(vty, "   %s:%s", cfg->mgcp_mdcx_ctr_group->desc->group_description, VTY_NEWLINE);
 | 
			
		||||
		vty_out_rate_ctr_group_fmt(vty, "   %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_mdcx_ctr_group);
 | 
			
		||||
	}
 | 
			
		||||
	if (show_stats && cfg->all_rtp_conn_stats) {
 | 
			
		||||
		vty_out(vty, "   %s:%s", cfg->all_rtp_conn_stats->desc->group_description, VTY_NEWLINE);
 | 
			
		||||
		vty_out_rate_ctr_group_fmt(vty, "   %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->all_rtp_conn_stats);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define SHOW_MGCP_STR "Display information about the MGCP Media Gateway\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(show_mcgp, show_mgcp_cmd,
 | 
			
		||||
      "show mgcp [stats]",
 | 
			
		||||
      SHOW_STR
 | 
			
		||||
      "Display information about the MGCP Media Gateway\n"
 | 
			
		||||
      SHOW_MGCP_STR
 | 
			
		||||
      "Include Statistics\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk_config *trunk;
 | 
			
		||||
@@ -241,6 +304,72 @@ DEFUN(show_mcgp, show_mgcp_cmd,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
dump_mgcp_endpoint(struct vty *vty, struct mgcp_trunk_config *trunk, const char *epname)
 | 
			
		||||
{
 | 
			
		||||
	const size_t virt_prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
 | 
			
		||||
	unsigned long epidx;
 | 
			
		||||
	char *endp;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, virt_prefix_len) == 0)
 | 
			
		||||
		epname += virt_prefix_len;
 | 
			
		||||
	errno = 0;
 | 
			
		||||
	epidx = strtoul(epname, &endp, 16);
 | 
			
		||||
	if (epname[0] == '\0' || *endp != '\0') {
 | 
			
		||||
		vty_out(vty, "endpoint name '%s' is not a hex number%s", epname, VTY_NEWLINE);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if ((errno == ERANGE && epidx == ULONG_MAX) /* parsed value out of range */
 | 
			
		||||
	    || epidx >= trunk->number_endpoints) {
 | 
			
		||||
		vty_out(vty, "endpoint %.2lx not configured on trunk %d%s", epidx, trunk->trunk_nr, VTY_NEWLINE);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < trunk->number_endpoints; ++i) {
 | 
			
		||||
		struct mgcp_endpoint *endp = &trunk->endpoints[i];
 | 
			
		||||
		if (i == epidx) {
 | 
			
		||||
			dump_endpoint(vty, endp, i, trunk->trunk_nr, trunk->trunk_type, true);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(show_mcgp_endpoint, show_mgcp_endpoint_cmd,
 | 
			
		||||
      "show mgcp endpoint NAME",
 | 
			
		||||
      SHOW_STR
 | 
			
		||||
      SHOW_MGCP_STR
 | 
			
		||||
      "Display information about an endpoint\n" "The name of the endpoint\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk_config *trunk;
 | 
			
		||||
 | 
			
		||||
	dump_mgcp_endpoint(vty, &g_cfg->trunk, argv[0]);
 | 
			
		||||
	llist_for_each_entry(trunk, &g_cfg->trunks, entry)
 | 
			
		||||
		dump_mgcp_endpoint(vty, trunk, argv[0]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(show_mcgp_trunk_endpoint, show_mgcp_trunk_endpoint_cmd,
 | 
			
		||||
      "show mgcp trunk <0-64> endpoint NAME",
 | 
			
		||||
      SHOW_STR
 | 
			
		||||
      SHOW_MGCP_STR
 | 
			
		||||
      "Display information about a trunk\n" "Trunk number\n"
 | 
			
		||||
      "Display information about an endpoint\n" "The name of the endpoint\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk_config *trunk;
 | 
			
		||||
	int trunkidx = atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	trunk = find_trunk(g_cfg, trunkidx);
 | 
			
		||||
	if (!trunk) {
 | 
			
		||||
		vty_out(vty, "trunk %d not found%s", trunkidx, VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dump_mgcp_endpoint(vty, trunk, argv[1]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp, cfg_mgcp_cmd, "mgcp", "Configure the MGCP")
 | 
			
		||||
{
 | 
			
		||||
	vty->node = MGCP_NODE;
 | 
			
		||||
@@ -600,11 +729,44 @@ DEFUN(cfg_mgcp_no_patch_rtp_ts,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_patch_rtp_rfc5993hr,
 | 
			
		||||
      cfg_mgcp_patch_rtp_rfc5993hr_cmd,
 | 
			
		||||
      "rtp-patch rfc5993hr", RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->trunk.rfc5993_hr_convert = true;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_no_patch_rtp_rfc5993hr,
 | 
			
		||||
      cfg_mgcp_no_patch_rtp_rfc5993hr_cmd,
 | 
			
		||||
      "no rtp-patch rfc5993hr", NO_STR RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->trunk.rfc5993_hr_convert = false;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_patch_rtp_amr_oa_bwe,
 | 
			
		||||
      cfg_mgcp_patch_rtp_amr_oa_bwe_cmd,
 | 
			
		||||
      "rtp-patch amr-oa-bwe", RTP_PATCH_STR RTP_AMR_OA_BWE_CONV_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->trunk.amr_oa_bwe_convert = true;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_no_patch_rtp_amr_oa_bwe,
 | 
			
		||||
      cfg_mgcp_no_patch_rtp_amr_oa_bwe_cmd,
 | 
			
		||||
      "no rtp-patch amr-oa-bwe", NO_STR RTP_PATCH_STR RTP_AMR_OA_BWE_CONV_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->trunk.amr_oa_bwe_convert = false;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_no_patch_rtp,
 | 
			
		||||
      cfg_mgcp_no_patch_rtp_cmd, "no rtp-patch", NO_STR RTP_PATCH_STR)
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->trunk.force_constant_ssrc = 0;
 | 
			
		||||
	g_cfg->trunk.force_aligned_timing = 0;
 | 
			
		||||
	g_cfg->trunk.rfc5993_hr_convert = false;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -701,13 +863,17 @@ static int config_write_trunk(struct vty *vty)
 | 
			
		||||
			vty_out(vty, "  rtcp-omit%s", VTY_NEWLINE);
 | 
			
		||||
		else
 | 
			
		||||
			vty_out(vty, "  no rtcp-omit%s", VTY_NEWLINE);
 | 
			
		||||
		if (trunk->force_constant_ssrc || trunk->force_aligned_timing) {
 | 
			
		||||
		if (trunk->force_constant_ssrc || trunk->force_aligned_timing
 | 
			
		||||
		    || g_cfg->trunk.rfc5993_hr_convert) {
 | 
			
		||||
			vty_out(vty, "  %srtp-patch ssrc%s",
 | 
			
		||||
				trunk->force_constant_ssrc ? "" : "no ",
 | 
			
		||||
				VTY_NEWLINE);
 | 
			
		||||
			vty_out(vty, "  %srtp-patch timestamp%s",
 | 
			
		||||
				trunk->force_aligned_timing ? "" : "no ",
 | 
			
		||||
				VTY_NEWLINE);
 | 
			
		||||
			vty_out(vty, "  %srtp-patch rfc5993hr%s",
 | 
			
		||||
				trunk->rfc5993_hr_convert ? "" : "no ",
 | 
			
		||||
				VTY_NEWLINE);
 | 
			
		||||
		} else
 | 
			
		||||
			vty_out(vty, "  no rtp-patch%s", VTY_NEWLINE);
 | 
			
		||||
		if (trunk->audio_fmtp_extra)
 | 
			
		||||
@@ -873,12 +1039,49 @@ DEFUN(cfg_trunk_no_patch_rtp_ts,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_trunk_patch_rtp_rfc5993hr,
 | 
			
		||||
      cfg_trunk_patch_rtp_rfc5993hr_cmd,
 | 
			
		||||
      "rtp-patch rfc5993hr", RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk_config *trunk = vty->index;
 | 
			
		||||
	trunk->rfc5993_hr_convert = true;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_trunk_no_patch_rtp_rfc5993hr,
 | 
			
		||||
      cfg_trunk_no_patch_rtp_rfc5993hr_cmd,
 | 
			
		||||
      "no rtp-patch rfc5993hr", NO_STR RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk_config *trunk = vty->index;
 | 
			
		||||
	trunk->rfc5993_hr_convert = false;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_trunk_patch_rtp_amr_oa_bwe,
 | 
			
		||||
      cfg_trunk_patch_rtp_amr_oa_bwe_cmd,
 | 
			
		||||
      "rtp-patch amr-oa-bwe", RTP_PATCH_STR RTP_AMR_OA_BWE_CONV_STR)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk_config *trunk = vty->index;
 | 
			
		||||
	trunk->amr_oa_bwe_convert = true;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_trunk_no_patch_rtp_amr_oa_bwe,
 | 
			
		||||
      cfg_trunk_no_patch_rtp_amr_oa_bwe_cmd,
 | 
			
		||||
      "no rtp-patch amr-oa-bwe", NO_STR RTP_PATCH_STR RTP_AMR_OA_BWE_CONV_STR)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk_config *trunk = vty->index;
 | 
			
		||||
	trunk->amr_oa_bwe_convert = false;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_trunk_no_patch_rtp,
 | 
			
		||||
      cfg_trunk_no_patch_rtp_cmd, "no rtp-patch", NO_STR RTP_PATCH_STR)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk_config *trunk = vty->index;
 | 
			
		||||
	trunk->force_constant_ssrc = 0;
 | 
			
		||||
	trunk->force_aligned_timing = 0;
 | 
			
		||||
	trunk->rfc5993_hr_convert = false;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1208,15 +1411,31 @@ DEFUN(cfg_mgcp_osmux_dummy,
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_domain,
 | 
			
		||||
      cfg_mgcp_domain_cmd,
 | 
			
		||||
      "domain NAME", "domain\n" "qualified domain name\n")
 | 
			
		||||
      "domain NAME",
 | 
			
		||||
      "Set the domain part expected in MGCP messages' endpoint names\n"
 | 
			
		||||
      "Qualified domain name expected in MGCP endpoint names, or '*' to accept any domain\n")
 | 
			
		||||
{
 | 
			
		||||
	osmo_strlcpy(g_cfg->domain, argv[0], sizeof(g_cfg->domain));
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_conn_timeout,
 | 
			
		||||
      cfg_mgcp_conn_timeout_cmd,
 | 
			
		||||
      "conn-timeout <1-65534>",
 | 
			
		||||
      "Set a time after which inactive connections (CIs) are closed. This can be used to work around interoperability"
 | 
			
		||||
      " problems causing connections to stay open forever, and slowly exhausting all available ports. Do not enable"
 | 
			
		||||
      " when LCLS is used (connections in LCLS state appear to be inactive)!\n"
 | 
			
		||||
      "Timeout value (sec.)\n")
 | 
			
		||||
{
 | 
			
		||||
	g_cfg->conn_timeout = strtoul(argv[0], NULL, 10);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mgcp_vty_init(void)
 | 
			
		||||
{
 | 
			
		||||
	install_element_ve(&show_mgcp_cmd);
 | 
			
		||||
	install_element_ve(&show_mgcp_endpoint_cmd);
 | 
			
		||||
	install_element_ve(&show_mgcp_trunk_endpoint_cmd);
 | 
			
		||||
	install_element(ENABLE_NODE, &loop_conn_cmd);
 | 
			
		||||
	install_element(ENABLE_NODE, &tap_rtp_cmd);
 | 
			
		||||
	install_element(ENABLE_NODE, &free_endp_cmd);
 | 
			
		||||
@@ -1262,6 +1481,10 @@ int mgcp_vty_init(void)
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_ts_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ts_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_rfc5993hr_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_rfc5993hr_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_amr_oa_bwe_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_amr_oa_bwe_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_sdp_fmtp_extra_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_send_ptime_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_ptime_cmd);
 | 
			
		||||
@@ -1276,6 +1499,7 @@ int mgcp_vty_init(void)
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_domain_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_conn_timeout_cmd);
 | 
			
		||||
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
 | 
			
		||||
	install_node(&trunk_node, config_write_trunk);
 | 
			
		||||
@@ -1292,6 +1516,10 @@ int mgcp_vty_init(void)
 | 
			
		||||
	install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ssrc_cmd);
 | 
			
		||||
	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ssrc_cmd);
 | 
			
		||||
	install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ts_cmd);
 | 
			
		||||
	install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_rfc5993hr_cmd);
 | 
			
		||||
	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_rfc5993hr_cmd);
 | 
			
		||||
	install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_amr_oa_bwe_cmd);
 | 
			
		||||
	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_amr_oa_bwe_cmd);
 | 
			
		||||
	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ts_cmd);
 | 
			
		||||
	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_cmd);
 | 
			
		||||
	install_element(TRUNK_NODE, &cfg_trunk_sdp_fmtp_extra_cmd);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,31 +0,0 @@
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	$(all_includes) \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	-I$(top_builddir) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(LIBBCG729_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
bin_PROGRAMS = \
 | 
			
		||||
	osmo-bsc_mgcp \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
osmo_bsc_mgcp_SOURCES = \
 | 
			
		||||
	mgcp_main.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
osmo_bsc_mgcp_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(LIBBCG729_LIBS) \
 | 
			
		||||
	$(LIBRARY_GSM) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
@@ -1,351 +0,0 @@
 | 
			
		||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
			
		||||
/* The main method to drive it as a standalone process      */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2011 by On-Waves
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/legacy_mgcp/vty.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/core/stats.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/telnet_interface.h>
 | 
			
		||||
#include <osmocom/vty/logging.h>
 | 
			
		||||
#include <osmocom/vty/ports.h>
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/vty/stats.h>
 | 
			
		||||
 | 
			
		||||
#include "../../bscconfig.h"
 | 
			
		||||
 | 
			
		||||
#ifdef BUILD_MGCP_TRANSCODING
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp_transcode.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
 | 
			
		||||
#warning "Make use of the rtp proxy code"
 | 
			
		||||
 | 
			
		||||
static struct mgcp_config *cfg;
 | 
			
		||||
static struct mgcp_trunk_config *reset_trunk;
 | 
			
		||||
static int reset_endpoints = 0;
 | 
			
		||||
static int daemonize = 0;
 | 
			
		||||
 | 
			
		||||
const char *openbsc_copyright =
 | 
			
		||||
	"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
 | 
			
		||||
	"Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n"
 | 
			
		||||
	"Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\r\n"
 | 
			
		||||
	"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
 | 
			
		||||
	"This is free software: you are free to change and redistribute it.\r\n"
 | 
			
		||||
	"There is NO WARRANTY, to the extent permitted by law.\r\n";
 | 
			
		||||
 | 
			
		||||
static char *config_file = "mgcp.cfg";
 | 
			
		||||
 | 
			
		||||
/* used by msgb and mgcp */
 | 
			
		||||
void *tall_bsc_ctx = NULL;
 | 
			
		||||
 | 
			
		||||
static void print_help()
 | 
			
		||||
{
 | 
			
		||||
	printf("Some useful help...\n");
 | 
			
		||||
	printf(" -h --help is printing this text.\n");
 | 
			
		||||
	printf(" -c --config-file filename The config file to use.\n");
 | 
			
		||||
	printf(" -s --disable-color\n");
 | 
			
		||||
	printf(" -D --daemonize Fork the process into a background daemon\n");
 | 
			
		||||
	printf(" -V --version Print the version number\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_options(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	while (1) {
 | 
			
		||||
		int option_index = 0, c;
 | 
			
		||||
		static struct option long_options[] = {
 | 
			
		||||
			{"help", 0, 0, 'h'},
 | 
			
		||||
			{"config-file", 1, 0, 'c'},
 | 
			
		||||
			{"daemonize", 0, 0, 'D'},
 | 
			
		||||
			{"version", 0, 0, 'V'},
 | 
			
		||||
			{"disable-color", 0, 0, 's'},
 | 
			
		||||
			{0, 0, 0, 0},
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		c = getopt_long(argc, argv, "hc:VD", long_options, &option_index);
 | 
			
		||||
 | 
			
		||||
		if (c == -1)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		switch(c) {
 | 
			
		||||
		case 'h':
 | 
			
		||||
			print_help();
 | 
			
		||||
			exit(0);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'c':
 | 
			
		||||
			config_file = talloc_strdup(tall_bsc_ctx, optarg);
 | 
			
		||||
			break;
 | 
			
		||||
		case 's':
 | 
			
		||||
			log_set_use_color(osmo_stderr_target, 0);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'V':
 | 
			
		||||
			print_version(1);
 | 
			
		||||
			exit(0);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'D':
 | 
			
		||||
			daemonize = 1;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* ignore */
 | 
			
		||||
			break;
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* simply remember this */
 | 
			
		||||
static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
 | 
			
		||||
{
 | 
			
		||||
	reset_endpoints = 1;
 | 
			
		||||
	reset_trunk = tcfg;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int read_call_agent(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr_in addr;
 | 
			
		||||
	socklen_t slen = sizeof(addr);
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	struct msgb *resp;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	msg = (struct msgb *) fd->data;
 | 
			
		||||
 | 
			
		||||
	/* read one less so we can use it as a \0 */
 | 
			
		||||
	int rc = recvfrom(cfg->gw_fd.bfd.fd, msg->data, msg->data_len - 1, 0,
 | 
			
		||||
		(struct sockaddr *) &addr, &slen);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		perror("Gateway failed to read");
 | 
			
		||||
		return -1;
 | 
			
		||||
	} else if (slen > sizeof(addr)) {
 | 
			
		||||
		fprintf(stderr, "Gateway received message from outerspace: %zu %zu\n",
 | 
			
		||||
			(size_t) slen, sizeof(addr));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* handle message now */
 | 
			
		||||
	msg->l2h = msgb_put(msg, rc);
 | 
			
		||||
	resp = mgcp_handle_message(cfg, msg);
 | 
			
		||||
	msgb_reset(msg);
 | 
			
		||||
 | 
			
		||||
	if (resp) {
 | 
			
		||||
		sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
 | 
			
		||||
		msgb_free(resp);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (reset_endpoints) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
		     "Asked to reset endpoints: %d/%d\n",
 | 
			
		||||
		     reset_trunk->trunk_nr, reset_trunk->trunk_type);
 | 
			
		||||
		reset_endpoints = 0;
 | 
			
		||||
 | 
			
		||||
		/* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */
 | 
			
		||||
		for (i = 1; i < reset_trunk->number_endpoints; ++i)
 | 
			
		||||
			mgcp_release_endp(&reset_trunk->endpoints[i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mgcp_vty_is_config_node(struct vty *vty, int node)
 | 
			
		||||
{
 | 
			
		||||
	switch (node) {
 | 
			
		||||
	case CONFIG_NODE:
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mgcp_vty_go_parent(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	switch (vty->node) {
 | 
			
		||||
	case TRUNK_NODE:
 | 
			
		||||
		vty->node = MGCP_NODE;
 | 
			
		||||
		vty->index = NULL;
 | 
			
		||||
		break;
 | 
			
		||||
	case MGCP_NODE:
 | 
			
		||||
	default:
 | 
			
		||||
		if (mgcp_vty_is_config_node(vty, vty->node))
 | 
			
		||||
			vty->node = CONFIG_NODE;
 | 
			
		||||
		else
 | 
			
		||||
			vty->node = ENABLE_NODE;
 | 
			
		||||
 | 
			
		||||
		vty->index = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vty->node;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct vty_app_info vty_info = {
 | 
			
		||||
	.name 		= "OpenBSC MGCP",
 | 
			
		||||
	.version	= PACKAGE_VERSION,
 | 
			
		||||
	.go_parent_cb	= mgcp_vty_go_parent,
 | 
			
		||||
	.is_config_node	= mgcp_vty_is_config_node,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct log_info_cat log_categories[] = {
 | 
			
		||||
	/* DLMGCP is provided by the MGCP library */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct log_info log_info = {
 | 
			
		||||
	.cat = log_categories,
 | 
			
		||||
	.num_cat = ARRAY_SIZE(log_categories),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr_in addr;
 | 
			
		||||
	int on = 1, rc;
 | 
			
		||||
 | 
			
		||||
	tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
 | 
			
		||||
	msgb_talloc_ctx_init(tall_bsc_ctx, 0);
 | 
			
		||||
 | 
			
		||||
	osmo_init_ignore_signals();
 | 
			
		||||
	osmo_init_logging2(tall_bsc_ctx, &log_info);
 | 
			
		||||
 | 
			
		||||
	cfg = mgcp_config_alloc();
 | 
			
		||||
	if (!cfg)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
#ifdef BUILD_MGCP_TRANSCODING
 | 
			
		||||
	cfg->setup_rtp_processing_cb = &mgcp_transcoding_setup;
 | 
			
		||||
	cfg->rtp_processing_cb = &mgcp_transcoding_process_rtp;
 | 
			
		||||
	cfg->get_net_downlink_format_cb = &mgcp_transcoding_net_downlink_format;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	cfg->trunk.force_realloc = 1;
 | 
			
		||||
 | 
			
		||||
	vty_info.copyright = openbsc_copyright;
 | 
			
		||||
	vty_init(&vty_info);
 | 
			
		||||
	logging_vty_add_cmds(NULL);
 | 
			
		||||
	osmo_stats_vty_add_cmds(&log_info);
 | 
			
		||||
	mgcp_vty_init();
 | 
			
		||||
 | 
			
		||||
	handle_options(argc, argv);
 | 
			
		||||
 | 
			
		||||
	rate_ctr_init(tall_bsc_ctx);
 | 
			
		||||
	osmo_stats_init(tall_bsc_ctx);
 | 
			
		||||
 | 
			
		||||
	rc = mgcp_parse_config(config_file, cfg, MGCP_BSC);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	/* start telnet after reading config for vty_get_bind_addr() */
 | 
			
		||||
	rc = telnet_init_dynif(tall_bsc_ctx, NULL,
 | 
			
		||||
			       vty_get_bind_addr(), OSMO_VTY_PORT_BSC_MGCP);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	/* set some callbacks */
 | 
			
		||||
	cfg->reset_cb = mgcp_rsip_cb;
 | 
			
		||||
 | 
			
		||||
	/* we need to bind a socket */
 | 
			
		||||
	if (rc == 0) {
 | 
			
		||||
		cfg->gw_fd.bfd.when = BSC_FD_READ;
 | 
			
		||||
		cfg->gw_fd.bfd.cb = read_call_agent;
 | 
			
		||||
		cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
 | 
			
		||||
		if (cfg->gw_fd.bfd.fd < 0) {
 | 
			
		||||
			perror("Gateway failed to listen");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
 | 
			
		||||
 | 
			
		||||
		memset(&addr, 0, sizeof(addr));
 | 
			
		||||
		addr.sin_family = AF_INET;
 | 
			
		||||
		addr.sin_port = htons(cfg->source_port);
 | 
			
		||||
		inet_aton(cfg->source_addr, &addr.sin_addr);
 | 
			
		||||
 | 
			
		||||
		if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 | 
			
		||||
			perror("Gateway failed to bind");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
 | 
			
		||||
		if (!cfg->gw_fd.bfd.data) {
 | 
			
		||||
			fprintf(stderr, "Gateway memory error.\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (cfg->call_agent_addr) {
 | 
			
		||||
			addr.sin_port = htons(2727);
 | 
			
		||||
			inet_aton(cfg->call_agent_addr, &addr.sin_addr);
 | 
			
		||||
			if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n",
 | 
			
		||||
				     cfg->call_agent_addr, errno);
 | 
			
		||||
				close(cfg->gw_fd.bfd.fd);
 | 
			
		||||
				cfg->gw_fd.bfd.fd = -1;
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_FATAL, "Failed to register the fd\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* initialisation */
 | 
			
		||||
	srand(time(NULL));
 | 
			
		||||
 | 
			
		||||
	if (daemonize) {
 | 
			
		||||
		rc = osmo_daemonize();
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			perror("Error during daemonize");
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* main loop */
 | 
			
		||||
	while (1) {
 | 
			
		||||
		osmo_select_main(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -104,7 +104,7 @@ static void handle_options(int argc, char **argv)
 | 
			
		||||
			{0, 0, 0, 0},
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		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;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	legacy_mgcp \
 | 
			
		||||
	mgcp_client \
 | 
			
		||||
	mgcp \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
@@ -26,7 +25,6 @@ EXTRA_DIST = \
 | 
			
		||||
	testsuite.at \
 | 
			
		||||
	$(srcdir)/package.m4 \
 | 
			
		||||
	$(TESTSUITE) \
 | 
			
		||||
	vty_test_runner.py \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
TESTSUITE = $(srcdir)/testsuite
 | 
			
		||||
@@ -39,7 +37,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@'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,61 +0,0 @@
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	$(all_includes) \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	-I$(top_srcdir) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	-ggdb3 \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(LIBBCG729_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	mgcp_test.ok \
 | 
			
		||||
	mgcp_transcoding_test.ok \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS = \
 | 
			
		||||
	mgcp_test \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
if BUILD_MGCP_TRANSCODING
 | 
			
		||||
noinst_PROGRAMS += \
 | 
			
		||||
	mgcp_transcoding_test \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
mgcp_test_SOURCES = \
 | 
			
		||||
	mgcp_test.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
mgcp_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBRARY_DL) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(LIBRARY_GSM) \
 | 
			
		||||
	-lm  \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
mgcp_transcoding_test_SOURCES = \
 | 
			
		||||
	mgcp_transcoding_test.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
mgcp_transcoding_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBBCG729_LIBS) \
 | 
			
		||||
	$(LIBRARY_DL) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(LIBRARY_GSM) \
 | 
			
		||||
	-lm \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,480 +0,0 @@
 | 
			
		||||
line: 'one CR'
 | 
			
		||||
line: 'two CR'
 | 
			
		||||
line: ''
 | 
			
		||||
line: 'one CRLF'
 | 
			
		||||
line: 'two CRLF'
 | 
			
		||||
line: ''
 | 
			
		||||
line: 'one LF'
 | 
			
		||||
line: 'two LF'
 | 
			
		||||
line: ''
 | 
			
		||||
line: 'mixed (4 lines)'
 | 
			
		||||
line: ''
 | 
			
		||||
line: ''
 | 
			
		||||
line: ''
 | 
			
		||||
Testing AUEP1
 | 
			
		||||
Testing AUEP2
 | 
			
		||||
Testing MDCX1
 | 
			
		||||
Testing MDCX2
 | 
			
		||||
Testing CRCX
 | 
			
		||||
Dummy packets: 1
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetetization period: 20-20
 | 
			
		||||
Connection mode: 1: RECV
 | 
			
		||||
Testing MDCX3
 | 
			
		||||
Dummy packets: 1
 | 
			
		||||
Packet duration not set
 | 
			
		||||
Requested packetization period not set
 | 
			
		||||
Connection mode not set
 | 
			
		||||
Testing MDCX4
 | 
			
		||||
Dummy packets: 1
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetetization period: 20-20
 | 
			
		||||
Connection mode: 3: SEND RECV
 | 
			
		||||
Testing MDCX4_PT1
 | 
			
		||||
Dummy packets: 1
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetetization period: 20-40
 | 
			
		||||
Connection mode: 3: SEND RECV
 | 
			
		||||
Testing MDCX4_PT2
 | 
			
		||||
Dummy packets: 1
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetetization period: 20-20
 | 
			
		||||
Connection mode: 3: SEND RECV
 | 
			
		||||
Testing MDCX4_PT3
 | 
			
		||||
Dummy packets: 1
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetization period not set
 | 
			
		||||
Connection mode: 3: SEND RECV
 | 
			
		||||
Testing MDCX4_SO
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetetization period: 20-20
 | 
			
		||||
Connection mode: 2: SEND
 | 
			
		||||
Testing MDCX4_RO
 | 
			
		||||
Dummy packets: 1
 | 
			
		||||
Packet duration not set
 | 
			
		||||
Requested packetetization period: 20-20
 | 
			
		||||
Connection mode: 1: RECV
 | 
			
		||||
Testing DLCX
 | 
			
		||||
Detected packet duration: 20
 | 
			
		||||
Requested packetization period not set
 | 
			
		||||
Connection mode: 0: NONE
 | 
			
		||||
Testing CRCX_ZYN
 | 
			
		||||
Dummy packets: 1
 | 
			
		||||
Packet duration not set
 | 
			
		||||
Requested packetization period not set
 | 
			
		||||
Connection mode: 1: RECV
 | 
			
		||||
Testing EMPTY
 | 
			
		||||
Testing SHORT1
 | 
			
		||||
Testing SHORT2
 | 
			
		||||
Testing SHORT3
 | 
			
		||||
Testing SHORT4
 | 
			
		||||
Testing RQNT1
 | 
			
		||||
Testing RQNT2
 | 
			
		||||
Testing DLCX
 | 
			
		||||
Detected packet duration: 20
 | 
			
		||||
Requested packetization period not set
 | 
			
		||||
Connection mode: 0: NONE
 | 
			
		||||
Testing CRCX
 | 
			
		||||
Dummy packets: 1
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetetization period: 20-20
 | 
			
		||||
Connection mode: 1: RECV
 | 
			
		||||
Testing MDCX3
 | 
			
		||||
Dummy packets: 1
 | 
			
		||||
Packet duration not set
 | 
			
		||||
Requested packetization period not set
 | 
			
		||||
Connection mode not set
 | 
			
		||||
Testing DLCX
 | 
			
		||||
Detected packet duration: 20
 | 
			
		||||
Requested packetization period not set
 | 
			
		||||
Connection mode: 0: NONE
 | 
			
		||||
Testing CRCX
 | 
			
		||||
Re-transmitting CRCX
 | 
			
		||||
Testing RQNT1
 | 
			
		||||
Re-transmitting RQNT1
 | 
			
		||||
Testing RQNT2
 | 
			
		||||
Re-transmitting RQNT2
 | 
			
		||||
Testing MDCX3
 | 
			
		||||
Re-transmitting MDCX3
 | 
			
		||||
Testing DLCX
 | 
			
		||||
Re-transmitting DLCX
 | 
			
		||||
Testing packet loss calculation.
 | 
			
		||||
Testing stat parsing
 | 
			
		||||
Parsing result: 0
 | 
			
		||||
Parsing result: 0
 | 
			
		||||
Testing packet error detection, patch SSRC.
 | 
			
		||||
Output SSRC changed to 11223344
 | 
			
		||||
In TS: 0, dTS: 0, Seq: 0
 | 
			
		||||
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = 0
 | 
			
		||||
In TS: 160, dTS: 160, Seq: 1
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = 0
 | 
			
		||||
In TS: 320, dTS: 160, Seq: 2
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = 0
 | 
			
		||||
In TS: 320, dTS: 160, Seq: 3
 | 
			
		||||
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 10, Transit = 160
 | 
			
		||||
In TS: 480, dTS: 160, Seq: 4
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 9, Transit = 160
 | 
			
		||||
In TS: 640, dTS: 160, Seq: 5
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 8, Transit = 160
 | 
			
		||||
In TS: 960, dTS: 320, Seq: 6
 | 
			
		||||
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 18, Transit = 0
 | 
			
		||||
In TS: 1120, dTS: 160, Seq: 7
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 17, Transit = 0
 | 
			
		||||
In TS: 1280, dTS: 160, Seq: 8
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 16, Transit = 0
 | 
			
		||||
In TS: 1400, dTS: 120, Seq: 9
 | 
			
		||||
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
 | 
			
		||||
Stats: Jitter = 17, Transit = 40
 | 
			
		||||
In TS: 1560, dTS: 160, Seq: 10
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 16, Transit = 40
 | 
			
		||||
In TS: 1720, dTS: 160, Seq: 11
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 15, Transit = 40
 | 
			
		||||
In TS: 34688, dTS: 0, Seq: 12
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -32768
 | 
			
		||||
In TS: 34848, dTS: 160, Seq: 13
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -32768
 | 
			
		||||
In TS: 35008, dTS: 160, Seq: 14
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -32768
 | 
			
		||||
In TS: 35128, dTS: 120, Seq: 15
 | 
			
		||||
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
 | 
			
		||||
Stats: Jitter = 2, Transit = -32728
 | 
			
		||||
In TS: 35288, dTS: 160, Seq: 16
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 2, Transit = -32728
 | 
			
		||||
In TS: 35448, dTS: 160, Seq: 17
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 2, Transit = -32728
 | 
			
		||||
In TS: 35768, dTS: 160, Seq: 19
 | 
			
		||||
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 12, Transit = -32888
 | 
			
		||||
In TS: 35928, dTS: 160, Seq: 20
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 11, Transit = -32888
 | 
			
		||||
In TS: 36088, dTS: 160, Seq: 21
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 20, Transit = -33048
 | 
			
		||||
In TS: 36088, dTS: 160, Seq: 21
 | 
			
		||||
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 29, Transit = -32888
 | 
			
		||||
In TS: 36248, dTS: 160, Seq: 22
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 27, Transit = -32888
 | 
			
		||||
In TS: 36408, dTS: 160, Seq: 23
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 25, Transit = -32888
 | 
			
		||||
In TS: 36568, dTS: 160, Seq: 23
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
 | 
			
		||||
Stats: Jitter = 24, Transit = -32888
 | 
			
		||||
In TS: 36728, dTS: 160, Seq: 24
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 22, Transit = -32888
 | 
			
		||||
In TS: 36888, dTS: 160, Seq: 25
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 21, Transit = -32888
 | 
			
		||||
In TS: 160000, dTS: 0, Seq: 1000
 | 
			
		||||
Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
In TS: 160160, dTS: 160, Seq: 1001
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
In TS: 160320, dTS: 160, Seq: 1002
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
Testing packet error detection.
 | 
			
		||||
Output SSRC changed to 11223344
 | 
			
		||||
In TS: 0, dTS: 0, Seq: 0
 | 
			
		||||
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = 0
 | 
			
		||||
In TS: 160, dTS: 160, Seq: 1
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = 0
 | 
			
		||||
In TS: 320, dTS: 160, Seq: 2
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = 0
 | 
			
		||||
In TS: 320, dTS: 160, Seq: 3
 | 
			
		||||
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 10, Transit = 160
 | 
			
		||||
In TS: 480, dTS: 160, Seq: 4
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 9, Transit = 160
 | 
			
		||||
In TS: 640, dTS: 160, Seq: 5
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 8, Transit = 160
 | 
			
		||||
In TS: 960, dTS: 320, Seq: 6
 | 
			
		||||
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 18, Transit = 0
 | 
			
		||||
In TS: 1120, dTS: 160, Seq: 7
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 17, Transit = 0
 | 
			
		||||
In TS: 1280, dTS: 160, Seq: 8
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 16, Transit = 0
 | 
			
		||||
In TS: 1400, dTS: 120, Seq: 9
 | 
			
		||||
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
 | 
			
		||||
Stats: Jitter = 17, Transit = 40
 | 
			
		||||
In TS: 1560, dTS: 160, Seq: 10
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 16, Transit = 40
 | 
			
		||||
In TS: 1720, dTS: 160, Seq: 11
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 15, Transit = 40
 | 
			
		||||
Output SSRC changed to 10203040
 | 
			
		||||
In TS: 34688, dTS: 0, Seq: 12
 | 
			
		||||
Out TS change: 32968, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -32768
 | 
			
		||||
In TS: 34848, dTS: 160, Seq: 13
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -32768
 | 
			
		||||
In TS: 35008, dTS: 160, Seq: 14
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -32768
 | 
			
		||||
In TS: 35128, dTS: 120, Seq: 15
 | 
			
		||||
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
 | 
			
		||||
Stats: Jitter = 2, Transit = -32728
 | 
			
		||||
In TS: 35288, dTS: 160, Seq: 16
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 2, Transit = -32728
 | 
			
		||||
In TS: 35448, dTS: 160, Seq: 17
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 2, Transit = -32728
 | 
			
		||||
In TS: 35768, dTS: 160, Seq: 19
 | 
			
		||||
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 12, Transit = -32888
 | 
			
		||||
In TS: 35928, dTS: 160, Seq: 20
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 11, Transit = -32888
 | 
			
		||||
In TS: 36088, dTS: 160, Seq: 21
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 20, Transit = -33048
 | 
			
		||||
In TS: 36088, dTS: 160, Seq: 21
 | 
			
		||||
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 29, Transit = -32888
 | 
			
		||||
In TS: 36248, dTS: 160, Seq: 22
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 27, Transit = -32888
 | 
			
		||||
In TS: 36408, dTS: 160, Seq: 23
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 25, Transit = -32888
 | 
			
		||||
In TS: 36568, dTS: 160, Seq: 23
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
 | 
			
		||||
Stats: Jitter = 24, Transit = -32888
 | 
			
		||||
In TS: 36728, dTS: 160, Seq: 24
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 22, Transit = -32888
 | 
			
		||||
In TS: 36888, dTS: 160, Seq: 25
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 21, Transit = -32888
 | 
			
		||||
Output SSRC changed to 50607080
 | 
			
		||||
In TS: 160000, dTS: 0, Seq: 1000
 | 
			
		||||
Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
In TS: 160160, dTS: 160, Seq: 1001
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
In TS: 160320, dTS: 160, Seq: 1002
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
Testing packet error detection, patch timestamps.
 | 
			
		||||
Output SSRC changed to 11223344
 | 
			
		||||
In TS: 0, dTS: 0, Seq: 0
 | 
			
		||||
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = 0
 | 
			
		||||
In TS: 160, dTS: 160, Seq: 1
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = 0
 | 
			
		||||
In TS: 320, dTS: 160, Seq: 2
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = 0
 | 
			
		||||
In TS: 320, dTS: 160, Seq: 3
 | 
			
		||||
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 10, Transit = 160
 | 
			
		||||
In TS: 480, dTS: 160, Seq: 4
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 9, Transit = 160
 | 
			
		||||
In TS: 640, dTS: 160, Seq: 5
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 8, Transit = 160
 | 
			
		||||
In TS: 960, dTS: 320, Seq: 6
 | 
			
		||||
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 18, Transit = 0
 | 
			
		||||
In TS: 1120, dTS: 160, Seq: 7
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 17, Transit = 0
 | 
			
		||||
In TS: 1280, dTS: 160, Seq: 8
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 16, Transit = 0
 | 
			
		||||
In TS: 1400, dTS: 120, Seq: 9
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
 | 
			
		||||
Stats: Jitter = 17, Transit = 40
 | 
			
		||||
In TS: 1560, dTS: 160, Seq: 10
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 16, Transit = 40
 | 
			
		||||
In TS: 1720, dTS: 160, Seq: 11
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 15, Transit = 40
 | 
			
		||||
Output SSRC changed to 10203040
 | 
			
		||||
In TS: 34688, dTS: 0, Seq: 12
 | 
			
		||||
Out TS change: 32968, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -32768
 | 
			
		||||
In TS: 34848, dTS: 160, Seq: 13
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -32768
 | 
			
		||||
In TS: 35008, dTS: 160, Seq: 14
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -32768
 | 
			
		||||
In TS: 35128, dTS: 120, Seq: 15
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
 | 
			
		||||
Stats: Jitter = 2, Transit = -32728
 | 
			
		||||
In TS: 35288, dTS: 160, Seq: 16
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 2, Transit = -32728
 | 
			
		||||
In TS: 35448, dTS: 160, Seq: 17
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 2, Transit = -32728
 | 
			
		||||
In TS: 35768, dTS: 160, Seq: 19
 | 
			
		||||
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 12, Transit = -32888
 | 
			
		||||
In TS: 35928, dTS: 160, Seq: 20
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 11, Transit = -32888
 | 
			
		||||
In TS: 36088, dTS: 160, Seq: 21
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 20, Transit = -33048
 | 
			
		||||
In TS: 36088, dTS: 160, Seq: 21
 | 
			
		||||
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 29, Transit = -32888
 | 
			
		||||
In TS: 36248, dTS: 160, Seq: 22
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 27, Transit = -32888
 | 
			
		||||
In TS: 36408, dTS: 160, Seq: 23
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 25, Transit = -32888
 | 
			
		||||
In TS: 36568, dTS: 160, Seq: 23
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
 | 
			
		||||
Stats: Jitter = 24, Transit = -32888
 | 
			
		||||
In TS: 36728, dTS: 160, Seq: 24
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 22, Transit = -32888
 | 
			
		||||
In TS: 36888, dTS: 160, Seq: 25
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 21, Transit = -32888
 | 
			
		||||
Output SSRC changed to 50607080
 | 
			
		||||
In TS: 160000, dTS: 0, Seq: 1000
 | 
			
		||||
Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
In TS: 160160, dTS: 160, Seq: 1001
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
In TS: 160320, dTS: 160, Seq: 1002
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
Testing packet error detection, patch SSRC, patch timestamps.
 | 
			
		||||
Output SSRC changed to 11223344
 | 
			
		||||
In TS: 0, dTS: 0, Seq: 0
 | 
			
		||||
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = 0
 | 
			
		||||
In TS: 160, dTS: 160, Seq: 1
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = 0
 | 
			
		||||
In TS: 320, dTS: 160, Seq: 2
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = 0
 | 
			
		||||
In TS: 320, dTS: 160, Seq: 3
 | 
			
		||||
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 10, Transit = 160
 | 
			
		||||
In TS: 480, dTS: 160, Seq: 4
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 9, Transit = 160
 | 
			
		||||
In TS: 640, dTS: 160, Seq: 5
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 8, Transit = 160
 | 
			
		||||
In TS: 960, dTS: 320, Seq: 6
 | 
			
		||||
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 18, Transit = 0
 | 
			
		||||
In TS: 1120, dTS: 160, Seq: 7
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 17, Transit = 0
 | 
			
		||||
In TS: 1280, dTS: 160, Seq: 8
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 16, Transit = 0
 | 
			
		||||
In TS: 1400, dTS: 120, Seq: 9
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
 | 
			
		||||
Stats: Jitter = 17, Transit = 40
 | 
			
		||||
In TS: 1560, dTS: 160, Seq: 10
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 16, Transit = 40
 | 
			
		||||
In TS: 1720, dTS: 160, Seq: 11
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 15, Transit = 40
 | 
			
		||||
In TS: 34688, dTS: 0, Seq: 12
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -32768
 | 
			
		||||
In TS: 34848, dTS: 160, Seq: 13
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -32768
 | 
			
		||||
In TS: 35008, dTS: 160, Seq: 14
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -32768
 | 
			
		||||
In TS: 35128, dTS: 120, Seq: 15
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
 | 
			
		||||
Stats: Jitter = 2, Transit = -32728
 | 
			
		||||
In TS: 35288, dTS: 160, Seq: 16
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 2, Transit = -32728
 | 
			
		||||
In TS: 35448, dTS: 160, Seq: 17
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 2, Transit = -32728
 | 
			
		||||
In TS: 35768, dTS: 160, Seq: 19
 | 
			
		||||
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 12, Transit = -32888
 | 
			
		||||
In TS: 35928, dTS: 160, Seq: 20
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 11, Transit = -32888
 | 
			
		||||
In TS: 36088, dTS: 160, Seq: 21
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 20, Transit = -33048
 | 
			
		||||
In TS: 36088, dTS: 160, Seq: 21
 | 
			
		||||
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 29, Transit = -32888
 | 
			
		||||
In TS: 36248, dTS: 160, Seq: 22
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 27, Transit = -32888
 | 
			
		||||
In TS: 36408, dTS: 160, Seq: 23
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 25, Transit = -32888
 | 
			
		||||
In TS: 36568, dTS: 160, Seq: 23
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
 | 
			
		||||
Stats: Jitter = 24, Transit = -32888
 | 
			
		||||
In TS: 36728, dTS: 160, Seq: 24
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 22, Transit = -32888
 | 
			
		||||
In TS: 36888, dTS: 160, Seq: 25
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 21, Transit = -32888
 | 
			
		||||
In TS: 160000, dTS: 0, Seq: 1000
 | 
			
		||||
Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
In TS: 160160, dTS: 160, Seq: 1001
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
In TS: 160320, dTS: 160, Seq: 1002
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
Testing multiple payload types
 | 
			
		||||
Testing no sequence flow on initial packet
 | 
			
		||||
Testing no rtpmap name
 | 
			
		||||
Done
 | 
			
		||||
@@ -1,662 +0,0 @@
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <err.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/netif/rtp.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp_internal.h>
 | 
			
		||||
 | 
			
		||||
#include "bscconfig.h"
 | 
			
		||||
#ifndef BUILD_MGCP_TRANSCODING
 | 
			
		||||
#error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp_transcode.h>
 | 
			
		||||
 | 
			
		||||
uint8_t *audio_frame_l16[] = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtp_packets {
 | 
			
		||||
	float t;
 | 
			
		||||
	int len;
 | 
			
		||||
	char *data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtp_packets audio_packets_l16[] = {
 | 
			
		||||
	/* RTP: SeqNo=1, TS=160 */
 | 
			
		||||
	{0.020000, 332,
 | 
			
		||||
		"\x80\x0B\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
		"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtp_packets audio_packets_gsm[] = {
 | 
			
		||||
	/* RTP: SeqNo=1, TS=160 */
 | 
			
		||||
	{0.020000, 45,
 | 
			
		||||
		"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
 | 
			
		||||
		"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
 | 
			
		||||
		"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
 | 
			
		||||
		"\xDE"
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtp_packets audio_packets_gsm_invalid_size[] = {
 | 
			
		||||
	/* RTP: SeqNo=1, TS=160 */
 | 
			
		||||
	{0.020000, 41,
 | 
			
		||||
		"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
 | 
			
		||||
		"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
 | 
			
		||||
		"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
 | 
			
		||||
		"\xDE"
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtp_packets audio_packets_gsm_invalid_data[] = {
 | 
			
		||||
	/* RTP: SeqNo=1, TS=160 */
 | 
			
		||||
	{0.020000, 45,
 | 
			
		||||
		"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
 | 
			
		||||
		"\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE"
 | 
			
		||||
		"\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE"
 | 
			
		||||
		"\xEE"
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtp_packets audio_packets_gsm_invalid_ptype[] = {
 | 
			
		||||
	/* RTP: SeqNo=1, TS=160 */
 | 
			
		||||
	{0.020000, 45,
 | 
			
		||||
		"\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
 | 
			
		||||
		"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
 | 
			
		||||
		"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
 | 
			
		||||
		"\xDE"
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtp_packets audio_packets_g729[] = {
 | 
			
		||||
	/* RTP: SeqNo=1, TS=160 */
 | 
			
		||||
	{0.020000, 32,
 | 
			
		||||
		"\x80\x12\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
 | 
			
		||||
		"\xAF\xC2\x81\x40\x00\xFA\xCE\xA4\x21\x7C\xC5\xC3\x4F\xA5\x98\xF5"
 | 
			
		||||
		"\xB2\x95\xC4\xAD"
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtp_packets audio_packets_pcma[] = {
 | 
			
		||||
	/* RTP: SeqNo=1, TS=160 */
 | 
			
		||||
	{0.020000, 172,
 | 
			
		||||
		"\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
 | 
			
		||||
		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
 | 
			
		||||
		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
 | 
			
		||||
		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
 | 
			
		||||
		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
 | 
			
		||||
		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
 | 
			
		||||
		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
 | 
			
		||||
		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
 | 
			
		||||
		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
 | 
			
		||||
		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
 | 
			
		||||
		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
 | 
			
		||||
	},
 | 
			
		||||
	/* RTP: SeqNo=26527, TS=232640 */
 | 
			
		||||
	{0.020000, 92,
 | 
			
		||||
		"\x80\x08\x67\x9f\x00\x03\x8c\xc0\x04\xaa\x67\x9f\xd5\xd5\xd5\xd5"
 | 
			
		||||
		"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
 | 
			
		||||
		"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
 | 
			
		||||
		"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
 | 
			
		||||
		"\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\xd5\xd5\x55\x55\xd5\xd5\x55\x55"
 | 
			
		||||
		"\xd5\xd5\xd5\x55\x55\xd5\xd5\xd5\x55\x55\xd5\xd5"
 | 
			
		||||
	},
 | 
			
		||||
	/* RTP: SeqNo=26528, TS=232720 */
 | 
			
		||||
	{0.020000, 92,
 | 
			
		||||
		"\x80\x08\x67\xa0\x00\x03\x8d\x10\x04\xaa\x67\x9f\x55\xd5\xd5\x55"
 | 
			
		||||
		"\xd5\x55\xd5\xd5\xd5\x55\xd5\x55\xd5\xd5\x55\xd5\x55\xd5\x55\xd5"
 | 
			
		||||
		"\x55\x55\xd5\x55\xd5\xd5\x55\x55\x55\x55\x55\xd5\xd5\x55\xd5\xd5"
 | 
			
		||||
		"\xd5\x55\xd5\xd5\xd5\x55\x54\x55\xd5\xd5\x55\xd5\xd5\xd5\xd5\x55"
 | 
			
		||||
		"\x54\x55\xd5\x55\xd5\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\xd4"
 | 
			
		||||
		"\xd5\x54\x55\xd5\xd4\xd5\x54\xd5\x55\xd5\xd5\xd5"
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int audio_name_to_type(const char *name)
 | 
			
		||||
{
 | 
			
		||||
	if (!strcasecmp(name, "gsm"))
 | 
			
		||||
		return 3;
 | 
			
		||||
#ifdef HAVE_BCG729
 | 
			
		||||
	else if (!strcasecmp(name, "g729"))
 | 
			
		||||
		return 18;
 | 
			
		||||
#endif
 | 
			
		||||
	else if (!strcasecmp(name, "pcma"))
 | 
			
		||||
		return 8;
 | 
			
		||||
	else if (!strcasecmp(name, "l16"))
 | 
			
		||||
		return 11;
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst);
 | 
			
		||||
 | 
			
		||||
static int given_configured_endpoint(int in_samples, int out_samples,
 | 
			
		||||
				const char *srcfmt, const char *dstfmt,
 | 
			
		||||
				void **out_ctx, struct mgcp_endpoint **out_endp)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct mgcp_rtp_end *dst_end;
 | 
			
		||||
	struct mgcp_rtp_end *src_end;
 | 
			
		||||
	struct mgcp_config *cfg;
 | 
			
		||||
	struct mgcp_trunk_config *tcfg;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
 | 
			
		||||
	cfg = mgcp_config_alloc();
 | 
			
		||||
	tcfg = talloc_zero(cfg, struct mgcp_trunk_config);
 | 
			
		||||
	endp = talloc_zero(tcfg, struct mgcp_endpoint);
 | 
			
		||||
 | 
			
		||||
	cfg->setup_rtp_processing_cb = mgcp_transcoding_setup;
 | 
			
		||||
	cfg->rtp_processing_cb = mgcp_transcoding_process_rtp;
 | 
			
		||||
	cfg->get_net_downlink_format_cb = mgcp_transcoding_net_downlink_format;
 | 
			
		||||
 | 
			
		||||
	tcfg->endpoints = endp;
 | 
			
		||||
	tcfg->number_endpoints = 1;
 | 
			
		||||
	tcfg->cfg = cfg;
 | 
			
		||||
	endp->tcfg = tcfg;
 | 
			
		||||
	endp->cfg = cfg;
 | 
			
		||||
	mgcp_initialize_endp(endp);
 | 
			
		||||
 | 
			
		||||
	dst_end = &endp->bts_end;
 | 
			
		||||
	dst_end->codec.payload_type = audio_name_to_type(dstfmt);
 | 
			
		||||
 | 
			
		||||
	src_end = &endp->net_end;
 | 
			
		||||
	src_end->codec.payload_type = audio_name_to_type(srcfmt);
 | 
			
		||||
 | 
			
		||||
	if (out_samples) {
 | 
			
		||||
		dst_end->codec.frame_duration_den = dst_end->codec.rate;
 | 
			
		||||
		dst_end->codec.frame_duration_num = out_samples;
 | 
			
		||||
		dst_end->frames_per_packet = 1;
 | 
			
		||||
		dst_end->force_output_ptime = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = mgcp_transcoding_setup(endp, dst_end, src_end);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		printf("setup failed: %s", strerror(-rc));
 | 
			
		||||
		abort();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*out_ctx = cfg;
 | 
			
		||||
	*out_endp = endp;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int transcode_test(const char *srcfmt, const char *dstfmt,
 | 
			
		||||
			  uint8_t *src_pkts, size_t src_pkt_size)
 | 
			
		||||
{
 | 
			
		||||
	char buf[4096] = {0x80, 0};
 | 
			
		||||
	void *ctx;
 | 
			
		||||
 | 
			
		||||
	struct mgcp_rtp_end *dst_end;
 | 
			
		||||
	struct mgcp_process_rtp_state *state;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	int in_size;
 | 
			
		||||
	const int in_samples = 160;
 | 
			
		||||
	int len, cont;
 | 
			
		||||
 | 
			
		||||
	printf("== Transcoding test ==\n");
 | 
			
		||||
	printf("converting %s -> %s\n", srcfmt, dstfmt);
 | 
			
		||||
 | 
			
		||||
	given_configured_endpoint(in_samples, 0, srcfmt, dstfmt, &ctx, &endp);
 | 
			
		||||
 | 
			
		||||
	dst_end = &endp->bts_end;
 | 
			
		||||
	state = dst_end->rtp_process_data;
 | 
			
		||||
	OSMO_ASSERT(state != NULL);
 | 
			
		||||
 | 
			
		||||
	in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0);
 | 
			
		||||
	OSMO_ASSERT(sizeof(buf) >= in_size + 12);
 | 
			
		||||
 | 
			
		||||
	memcpy(buf, src_pkts, src_pkt_size);
 | 
			
		||||
 | 
			
		||||
	len = src_pkt_size;
 | 
			
		||||
 | 
			
		||||
	cont = mgcp_transcoding_process_rtp(endp, dst_end,
 | 
			
		||||
					    buf, &len, sizeof(buf));
 | 
			
		||||
	if (cont < 0) {
 | 
			
		||||
		printf("Nothing encoded due: %s\n", strerror(-cont));
 | 
			
		||||
		talloc_free(ctx);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (len < 24) {
 | 
			
		||||
		printf("encoded: %s\n", osmo_hexdump((unsigned char *)buf, len));
 | 
			
		||||
	} else {
 | 
			
		||||
		const char *str = osmo_hexdump((unsigned char *)buf, len);
 | 
			
		||||
		int i = 0;
 | 
			
		||||
		const int prefix = 4;
 | 
			
		||||
		const int cutlen = 48;
 | 
			
		||||
		int nchars = 0;
 | 
			
		||||
 | 
			
		||||
		printf("encoded:\n");
 | 
			
		||||
		do {
 | 
			
		||||
			nchars = printf("%*s%-.*s", prefix, "", cutlen, str + i);
 | 
			
		||||
			i += nchars - prefix;
 | 
			
		||||
			printf("\n");
 | 
			
		||||
		} while (nchars - prefix >= cutlen);
 | 
			
		||||
	}
 | 
			
		||||
	printf("counted: %d\n", cont);
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_rtp_seq_state(void)
 | 
			
		||||
{
 | 
			
		||||
	char buf[4096];
 | 
			
		||||
	int len;
 | 
			
		||||
	int cont;
 | 
			
		||||
	void *ctx;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	struct mgcp_process_rtp_state *state;
 | 
			
		||||
	struct rtp_hdr *hdr;
 | 
			
		||||
	uint32_t ts_no;
 | 
			
		||||
	uint16_t seq_no;
 | 
			
		||||
 | 
			
		||||
	given_configured_endpoint(160, 0, "pcma", "l16", &ctx, &endp);
 | 
			
		||||
	state = endp->bts_end.rtp_process_data;
 | 
			
		||||
	OSMO_ASSERT(!state->is_running);
 | 
			
		||||
	OSMO_ASSERT(state->next_seq == 0);
 | 
			
		||||
	OSMO_ASSERT(state->next_time == 0);
 | 
			
		||||
 | 
			
		||||
	/* initialize packet */
 | 
			
		||||
	len = audio_packets_pcma[0].len;
 | 
			
		||||
	memcpy(buf, audio_packets_pcma[0].data, len);
 | 
			
		||||
	cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, len);
 | 
			
		||||
	OSMO_ASSERT(cont >= 0);
 | 
			
		||||
	OSMO_ASSERT(state->is_running);
 | 
			
		||||
	OSMO_ASSERT(state->next_seq == 2);
 | 
			
		||||
	OSMO_ASSERT(state->next_time == 240);
 | 
			
		||||
 | 
			
		||||
	/* verify that the right timestamp was written */
 | 
			
		||||
	OSMO_ASSERT(len == audio_packets_pcma[0].len);
 | 
			
		||||
	hdr = (struct rtp_hdr *) &buf[0];
 | 
			
		||||
 | 
			
		||||
	memcpy(&ts_no, &hdr->timestamp, sizeof(ts_no));
 | 
			
		||||
	OSMO_ASSERT(htonl(ts_no) == 160);
 | 
			
		||||
	memcpy(&seq_no, &hdr->sequence, sizeof(seq_no));
 | 
			
		||||
	OSMO_ASSERT(htons(seq_no) == 1);
 | 
			
		||||
	/* Check the right sequence number is written */
 | 
			
		||||
	state->next_seq = 1234;
 | 
			
		||||
	len = audio_packets_pcma[0].len;
 | 
			
		||||
	memcpy(buf, audio_packets_pcma[0].data, len);
 | 
			
		||||
	cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, len);
 | 
			
		||||
	OSMO_ASSERT(cont >= 0);
 | 
			
		||||
	OSMO_ASSERT(len == audio_packets_pcma[0].len);
 | 
			
		||||
	hdr = (struct rtp_hdr *) &buf[0];
 | 
			
		||||
 | 
			
		||||
	memcpy(&seq_no, &hdr->sequence, sizeof(seq_no));
 | 
			
		||||
	OSMO_ASSERT(htons(seq_no) == 1234);
 | 
			
		||||
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_transcode_result(void)
 | 
			
		||||
{
 | 
			
		||||
	char buf[4096];
 | 
			
		||||
	int len, res;
 | 
			
		||||
	void *ctx;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	struct mgcp_process_rtp_state *state;
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		/* from GSM to PCMA and same ptime */
 | 
			
		||||
		given_configured_endpoint(160, 0, "gsm", "pcma", &ctx, &endp);
 | 
			
		||||
		state = endp->bts_end.rtp_process_data;
 | 
			
		||||
 | 
			
		||||
		/* result */
 | 
			
		||||
		len = audio_packets_gsm[0].len;
 | 
			
		||||
		memcpy(buf, audio_packets_gsm[0].data, len);
 | 
			
		||||
		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
 | 
			
		||||
		OSMO_ASSERT(res == sizeof(struct rtp_hdr));
 | 
			
		||||
		OSMO_ASSERT(state->sample_cnt == 0);
 | 
			
		||||
 | 
			
		||||
		len = res;
 | 
			
		||||
		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
 | 
			
		||||
		OSMO_ASSERT(res == -ENOMSG);
 | 
			
		||||
 | 
			
		||||
		talloc_free(ctx);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		/* from GSM to PCMA and same ptime */
 | 
			
		||||
		given_configured_endpoint(160, 160, "gsm", "pcma", &ctx, &endp);
 | 
			
		||||
		state = endp->bts_end.rtp_process_data;
 | 
			
		||||
 | 
			
		||||
		/* result */
 | 
			
		||||
		len = audio_packets_gsm[0].len;
 | 
			
		||||
		memcpy(buf, audio_packets_gsm[0].data, len);
 | 
			
		||||
		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
 | 
			
		||||
		OSMO_ASSERT(res == sizeof(struct rtp_hdr));
 | 
			
		||||
		OSMO_ASSERT(state->sample_cnt == 0);
 | 
			
		||||
 | 
			
		||||
		len = res;
 | 
			
		||||
		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
 | 
			
		||||
		OSMO_ASSERT(res == -EAGAIN);
 | 
			
		||||
 | 
			
		||||
		talloc_free(ctx);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		/* from PCMA to GSM and wrong different ptime */
 | 
			
		||||
		given_configured_endpoint(80, 160, "pcma", "gsm", &ctx, &endp);
 | 
			
		||||
		state = endp->bts_end.rtp_process_data;
 | 
			
		||||
 | 
			
		||||
		/* Add the first sample */
 | 
			
		||||
		len = audio_packets_pcma[1].len;
 | 
			
		||||
		memcpy(buf, audio_packets_pcma[1].data, len);
 | 
			
		||||
		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
 | 
			
		||||
		OSMO_ASSERT(state->sample_cnt == 80);
 | 
			
		||||
		OSMO_ASSERT(state->next_time == 232640);
 | 
			
		||||
		OSMO_ASSERT(res < 0);
 | 
			
		||||
 | 
			
		||||
		/* Add the second sample and it should be consumable */
 | 
			
		||||
		len = audio_packets_pcma[2].len;
 | 
			
		||||
		memcpy(buf, audio_packets_pcma[2].data, len);
 | 
			
		||||
		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
 | 
			
		||||
		OSMO_ASSERT(state->sample_cnt == 0);
 | 
			
		||||
		OSMO_ASSERT(state->next_time == 232640 + 80 + 160);
 | 
			
		||||
		OSMO_ASSERT(res == sizeof(struct rtp_hdr));
 | 
			
		||||
 | 
			
		||||
		talloc_free(ctx);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		/* from PCMA to GSM with a big time jump */
 | 
			
		||||
		struct rtp_hdr *hdr;
 | 
			
		||||
		uint32_t ts;
 | 
			
		||||
 | 
			
		||||
		given_configured_endpoint(80, 160, "pcma", "gsm", &ctx, &endp);
 | 
			
		||||
		state = endp->bts_end.rtp_process_data;
 | 
			
		||||
 | 
			
		||||
		/* Add the first sample */
 | 
			
		||||
		len = audio_packets_pcma[1].len;
 | 
			
		||||
		memcpy(buf, audio_packets_pcma[1].data, len);
 | 
			
		||||
		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
 | 
			
		||||
		OSMO_ASSERT(state->sample_cnt == 80);
 | 
			
		||||
		OSMO_ASSERT(state->next_time == 232640);
 | 
			
		||||
		OSMO_ASSERT(state->next_seq == 26527);
 | 
			
		||||
		OSMO_ASSERT(res < 0);
 | 
			
		||||
 | 
			
		||||
		/* Add a skip to the packet to force a 'resync' */
 | 
			
		||||
		len = audio_packets_pcma[2].len;
 | 
			
		||||
		memcpy(buf, audio_packets_pcma[2].data, len);
 | 
			
		||||
		hdr = (struct rtp_hdr *) &buf[0];
 | 
			
		||||
		/* jump the time and add alignment error */
 | 
			
		||||
		ts = ntohl(hdr->timestamp) + 123 * 80 + 2;
 | 
			
		||||
		hdr->timestamp = htonl(ts);
 | 
			
		||||
		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
 | 
			
		||||
		OSMO_ASSERT(res < 0);
 | 
			
		||||
		OSMO_ASSERT(state->sample_cnt == 80);
 | 
			
		||||
		OSMO_ASSERT(state->next_time == ts);
 | 
			
		||||
		OSMO_ASSERT(state->next_seq == 26527);
 | 
			
		||||
		/* TODO: this can create alignment errors */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		/* Now attempt to consume 160 samples */
 | 
			
		||||
		len = audio_packets_pcma[2].len;
 | 
			
		||||
		memcpy(buf, audio_packets_pcma[2].data, len);
 | 
			
		||||
		hdr = (struct rtp_hdr *) &buf[0];
 | 
			
		||||
		ts += 80;
 | 
			
		||||
		hdr->timestamp = htonl(ts);
 | 
			
		||||
		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
 | 
			
		||||
		OSMO_ASSERT(res == 12);
 | 
			
		||||
		OSMO_ASSERT(state->sample_cnt == 0);
 | 
			
		||||
		OSMO_ASSERT(state->next_time == ts + 160);
 | 
			
		||||
		OSMO_ASSERT(state->next_seq == 26528);
 | 
			
		||||
 | 
			
		||||
		talloc_free(ctx);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_transcode_change(void)
 | 
			
		||||
{
 | 
			
		||||
	char buf[4096] = {0x80, 0};
 | 
			
		||||
	void *ctx;
 | 
			
		||||
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	struct mgcp_process_rtp_state *state;
 | 
			
		||||
	struct rtp_hdr *hdr;
 | 
			
		||||
 | 
			
		||||
	int len, res;
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		/* from GSM to PCMA and same ptime */
 | 
			
		||||
		printf("Testing Initial L16->GSM, PCMA->GSM\n");
 | 
			
		||||
		given_configured_endpoint(160, 0, "l16", "gsm", &ctx, &endp);
 | 
			
		||||
		endp->net_end.alt_codec = endp->net_end.codec;
 | 
			
		||||
		endp->net_end.alt_codec.payload_type = audio_name_to_type("pcma");
 | 
			
		||||
		state = endp->bts_end.rtp_process_data;
 | 
			
		||||
 | 
			
		||||
		/* initial transcoding work */
 | 
			
		||||
		OSMO_ASSERT(state->src_fmt == AF_L16);
 | 
			
		||||
		OSMO_ASSERT(state->dst_fmt == AF_GSM);
 | 
			
		||||
		OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 8);
 | 
			
		||||
		OSMO_ASSERT(endp->net_end.codec.payload_type == 11);
 | 
			
		||||
 | 
			
		||||
		/* result */
 | 
			
		||||
		len = audio_packets_pcma[0].len;
 | 
			
		||||
		memcpy(buf, audio_packets_pcma[0].data, len);
 | 
			
		||||
		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
 | 
			
		||||
		state = endp->bts_end.rtp_process_data;
 | 
			
		||||
		OSMO_ASSERT(res == sizeof(struct rtp_hdr));
 | 
			
		||||
		OSMO_ASSERT(state->sample_cnt == 0);
 | 
			
		||||
		OSMO_ASSERT(state->src_fmt == AF_PCMA);
 | 
			
		||||
		OSMO_ASSERT(state->dst_fmt == AF_GSM);
 | 
			
		||||
		OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 11);
 | 
			
		||||
		OSMO_ASSERT(endp->net_end.codec.payload_type == 8);
 | 
			
		||||
 | 
			
		||||
		len = res;
 | 
			
		||||
		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
 | 
			
		||||
		OSMO_ASSERT(res == -ENOMSG);
 | 
			
		||||
		OSMO_ASSERT(state == endp->bts_end.rtp_process_data);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		/* now check that comfort noise doesn't change anything */
 | 
			
		||||
		len = audio_packets_pcma[1].len;
 | 
			
		||||
		memcpy(buf, audio_packets_pcma[1].data, len);
 | 
			
		||||
		hdr = (struct rtp_hdr *) buf;
 | 
			
		||||
		hdr->payload_type = 12;
 | 
			
		||||
		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
 | 
			
		||||
		OSMO_ASSERT(state == endp->bts_end.rtp_process_data);
 | 
			
		||||
		OSMO_ASSERT(state->sample_cnt == 80);
 | 
			
		||||
		OSMO_ASSERT(state->src_fmt == AF_PCMA);
 | 
			
		||||
		OSMO_ASSERT(state->dst_fmt == AF_GSM);
 | 
			
		||||
		OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 11);
 | 
			
		||||
		OSMO_ASSERT(endp->net_end.codec.payload_type == 8);
 | 
			
		||||
 | 
			
		||||
		talloc_free(ctx);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int test_repacking(int in_samples, int out_samples, int no_transcode)
 | 
			
		||||
{
 | 
			
		||||
	char buf[4096] = {0x80, 0};
 | 
			
		||||
	int cc;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	void *ctx;
 | 
			
		||||
 | 
			
		||||
	struct mgcp_process_rtp_state *state;
 | 
			
		||||
	int in_cnt;
 | 
			
		||||
	int out_size;
 | 
			
		||||
	int in_size;
 | 
			
		||||
	uint32_t ts = 0;
 | 
			
		||||
	uint16_t seq = 0;
 | 
			
		||||
	const char *srcfmt = "pcma";
 | 
			
		||||
	const char *dstfmt = no_transcode ? "pcma" : "l16";
 | 
			
		||||
 | 
			
		||||
	printf("== Transcoding test ==\n");
 | 
			
		||||
	printf("converting %s -> %s\n", srcfmt, dstfmt);
 | 
			
		||||
 | 
			
		||||
	given_configured_endpoint(in_samples, out_samples, srcfmt, dstfmt, &ctx, &endp);
 | 
			
		||||
 | 
			
		||||
	state = endp->bts_end.rtp_process_data;
 | 
			
		||||
	OSMO_ASSERT(state != NULL);
 | 
			
		||||
 | 
			
		||||
	in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0);
 | 
			
		||||
	OSMO_ASSERT(sizeof(buf) >= in_size + 12);
 | 
			
		||||
 | 
			
		||||
	out_size = mgcp_transcoding_get_frame_size(state, -1, 1);
 | 
			
		||||
	OSMO_ASSERT(sizeof(buf) >= out_size + 12);
 | 
			
		||||
 | 
			
		||||
	buf[1] = endp->net_end.codec.payload_type;
 | 
			
		||||
	*(uint16_t*)(buf+2) = htons(1);
 | 
			
		||||
	*(uint32_t*)(buf+4) = htonl(0);
 | 
			
		||||
	*(uint32_t*)(buf+8) = htonl(0xaabbccdd);
 | 
			
		||||
 | 
			
		||||
	for (in_cnt = 0; in_cnt < 16; in_cnt++) {
 | 
			
		||||
		int cont;
 | 
			
		||||
		int len;
 | 
			
		||||
 | 
			
		||||
		/* fake PCMA data */
 | 
			
		||||
		printf("generating %d %s input samples\n", in_samples, srcfmt);
 | 
			
		||||
		for (cc = 0; cc < in_samples; cc++)
 | 
			
		||||
			buf[12+cc] = cc;
 | 
			
		||||
 | 
			
		||||
		*(uint16_t*)(buf+2) = htonl(seq);
 | 
			
		||||
		*(uint32_t*)(buf+4) = htonl(ts);
 | 
			
		||||
 | 
			
		||||
		seq += 1;
 | 
			
		||||
		ts += in_samples;
 | 
			
		||||
 | 
			
		||||
		cc += 12; /* include RTP header */
 | 
			
		||||
 | 
			
		||||
		len = cc;
 | 
			
		||||
 | 
			
		||||
		do {
 | 
			
		||||
			cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end,
 | 
			
		||||
							    buf, &len, sizeof(buf));
 | 
			
		||||
			if (cont == -EAGAIN) {
 | 
			
		||||
				fprintf(stderr, "Got EAGAIN\n");
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (cont < 0) {
 | 
			
		||||
				printf("processing failed: %s", strerror(-cont));
 | 
			
		||||
				abort();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			len -= 12; /* ignore RTP header */
 | 
			
		||||
 | 
			
		||||
			printf("got %d %s output frames (%d octets) count=%d\n",
 | 
			
		||||
			       len / out_size, dstfmt, len, cont);
 | 
			
		||||
 | 
			
		||||
			len = cont;
 | 
			
		||||
		} while (len > 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct log_info_cat log_categories[] = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct log_info log_info = {
 | 
			
		||||
        .cat = log_categories,
 | 
			
		||||
        .num_cat = ARRAY_SIZE(log_categories),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	void *ctx = talloc_named_const(NULL, 0, "mgcp_transcoding_test");
 | 
			
		||||
	osmo_init_logging2(ctx, &log_info);
 | 
			
		||||
 | 
			
		||||
	printf("=== Transcoding Good Cases ===\n");
 | 
			
		||||
 | 
			
		||||
	transcode_test("l16", "l16",
 | 
			
		||||
		       (uint8_t *)audio_packets_l16[0].data,
 | 
			
		||||
		       audio_packets_l16[0].len);
 | 
			
		||||
	transcode_test("l16", "gsm",
 | 
			
		||||
		       (uint8_t *)audio_packets_l16[0].data,
 | 
			
		||||
		       audio_packets_l16[0].len);
 | 
			
		||||
	transcode_test("l16", "pcma",
 | 
			
		||||
		       (uint8_t *)audio_packets_l16[0].data,
 | 
			
		||||
		       audio_packets_l16[0].len);
 | 
			
		||||
	transcode_test("gsm", "l16",
 | 
			
		||||
		       (uint8_t *)audio_packets_gsm[0].data,
 | 
			
		||||
		       audio_packets_gsm[0].len);
 | 
			
		||||
	transcode_test("gsm", "gsm",
 | 
			
		||||
		       (uint8_t *)audio_packets_gsm[0].data,
 | 
			
		||||
		       audio_packets_gsm[0].len);
 | 
			
		||||
	transcode_test("gsm", "pcma",
 | 
			
		||||
		       (uint8_t *)audio_packets_gsm[0].data,
 | 
			
		||||
		       audio_packets_gsm[0].len);
 | 
			
		||||
	transcode_test("pcma", "l16",
 | 
			
		||||
		       (uint8_t *)audio_packets_pcma[0].data,
 | 
			
		||||
		       audio_packets_pcma[0].len);
 | 
			
		||||
	transcode_test("pcma", "gsm",
 | 
			
		||||
		       (uint8_t *)audio_packets_pcma[0].data,
 | 
			
		||||
		       audio_packets_pcma[0].len);
 | 
			
		||||
	transcode_test("pcma", "pcma",
 | 
			
		||||
		       (uint8_t *)audio_packets_pcma[0].data,
 | 
			
		||||
		       audio_packets_pcma[0].len);
 | 
			
		||||
 | 
			
		||||
	printf("=== Transcoding Bad Cases ===\n");
 | 
			
		||||
 | 
			
		||||
	printf("Invalid size:\n");
 | 
			
		||||
	rc = transcode_test("gsm", "pcma",
 | 
			
		||||
		       (uint8_t *)audio_packets_gsm_invalid_size[0].data,
 | 
			
		||||
		       audio_packets_gsm_invalid_size[0].len);
 | 
			
		||||
	OSMO_ASSERT(rc < 0);
 | 
			
		||||
 | 
			
		||||
	printf("Invalid data:\n");
 | 
			
		||||
	rc = transcode_test("gsm", "pcma",
 | 
			
		||||
		       (uint8_t *)audio_packets_gsm_invalid_data[0].data,
 | 
			
		||||
		       audio_packets_gsm_invalid_data[0].len);
 | 
			
		||||
	OSMO_ASSERT(rc < 0);
 | 
			
		||||
 | 
			
		||||
	printf("Invalid payload type:\n");
 | 
			
		||||
	rc = transcode_test("gsm", "pcma",
 | 
			
		||||
		       (uint8_t *)audio_packets_gsm_invalid_ptype[0].data,
 | 
			
		||||
		       audio_packets_gsm_invalid_ptype[0].len);
 | 
			
		||||
	OSMO_ASSERT(rc == 0);
 | 
			
		||||
 | 
			
		||||
	printf("=== Repacking ===\n");
 | 
			
		||||
 | 
			
		||||
	test_repacking(160, 160, 0);
 | 
			
		||||
	test_repacking(160, 160, 1);
 | 
			
		||||
	test_repacking(160, 80, 0);
 | 
			
		||||
	test_repacking(160, 80, 1);
 | 
			
		||||
	test_repacking(160, 320, 0);
 | 
			
		||||
	test_repacking(160, 320, 1);
 | 
			
		||||
	test_repacking(160, 240, 0);
 | 
			
		||||
	test_repacking(160, 240, 1);
 | 
			
		||||
	test_repacking(160, 100, 0);
 | 
			
		||||
	test_repacking(160, 100, 1);
 | 
			
		||||
	test_rtp_seq_state();
 | 
			
		||||
	test_transcode_result();
 | 
			
		||||
	test_transcode_change();
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,539 +0,0 @@
 | 
			
		||||
=== Transcoding Good Cases ===
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting l16 -> l16
 | 
			
		||||
encoded:
 | 
			
		||||
    80 0b 00 01 00 00 00 a0 11 22 33 44 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 
 | 
			
		||||
    5a 9e 40 13 00 00 bf ed a5 62 bf ed 
 | 
			
		||||
counted: 0
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting l16 -> gsm
 | 
			
		||||
encoded:
 | 
			
		||||
    80 0b 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9 
 | 
			
		||||
    62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc 
 | 
			
		||||
    69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de 
 | 
			
		||||
counted: 12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting l16 -> pcma
 | 
			
		||||
encoded:
 | 
			
		||||
    80 0b 00 01 00 00 00 a0 11 22 33 44 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 
 | 
			
		||||
counted: 12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting gsm -> l16
 | 
			
		||||
encoded:
 | 
			
		||||
    80 03 00 01 00 00 00 a0 11 22 33 44 00 00 54 00 
 | 
			
		||||
    59 f0 34 20 c4 c8 b9 f8 e2 18 f1 e8 f2 28 f0 e0 
 | 
			
		||||
    46 08 4f 80 2c a0 a9 c8 80 00 c0 58 3f 80 63 c0 
 | 
			
		||||
    24 b8 fa b8 f6 88 0b a0 c8 70 a8 b0 c8 c0 3b a8 
 | 
			
		||||
    66 a0 2e 38 d1 f8 98 98 aa 18 e8 30 26 a0 37 40 
 | 
			
		||||
    37 e8 17 00 ee 50 b7 80 b1 88 de 28 18 40 45 b0 
 | 
			
		||||
    4f 48 21 d8 df 78 ae 68 ab 98 d6 b8 18 18 48 90 
 | 
			
		||||
    4e 70 27 40 e8 10 b5 b0 ac 80 d4 60 14 50 48 48 
 | 
			
		||||
    50 10 2a 00 ec 08 ba 00 af 58 d1 c0 10 60 45 c8 
 | 
			
		||||
    54 10 30 78 f1 a8 bb 18 ad 48 ce 30 0a e8 3f 30 
 | 
			
		||||
    4f 10 32 18 f6 18 bf 20 ac 30 cd 80 0b d0 43 d8 
 | 
			
		||||
    55 e0 34 a0 f5 78 bc 98 ad 98 cd c8 0a 80 40 58 
 | 
			
		||||
    51 c0 35 40 f9 60 c1 68 ac c8 cb 38 08 00 40 98 
 | 
			
		||||
    51 e0 34 d8 fa 28 c2 f0 ae 40 c7 70 02 d0 3c a8 
 | 
			
		||||
    54 78 38 a0 fc 68 c2 08 ad 50 c7 78 01 60 39 c0 
 | 
			
		||||
    51 38 3a e8 00 e8 c6 38 ab d8 c4 00 fe 08 39 18 
 | 
			
		||||
    50 30 39 50 01 d8 ca 70 b1 80 c4 c8 fc 58 36 40 
 | 
			
		||||
    51 d8 3b 08 02 80 c8 58 b0 60 c5 a8 fb d0 33 e8 
 | 
			
		||||
    4e 80 3c e0 06 10 cb 90 ae 48 c2 60 f9 58 34 08 
 | 
			
		||||
    4d a0 3a a8 06 48 cf 80 b4 60 c3 e8 f7 90 30 18 
 | 
			
		||||
    4d a0 3b 98 07 90 cf 18 b4 68 c4 88 
 | 
			
		||||
counted: 12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting gsm -> gsm
 | 
			
		||||
encoded:
 | 
			
		||||
    80 03 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9 
 | 
			
		||||
    62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc 
 | 
			
		||||
    69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de 
 | 
			
		||||
counted: 0
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting gsm -> pcma
 | 
			
		||||
encoded:
 | 
			
		||||
    80 03 00 01 00 00 00 a0 11 22 33 44 d5 a0 a3 bf 
 | 
			
		||||
    38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60 
 | 
			
		||||
    17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82 
 | 
			
		||||
    04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6 
 | 
			
		||||
    02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd 
 | 
			
		||||
    19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf 
 | 
			
		||||
    10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf 
 | 
			
		||||
    62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8 
 | 
			
		||||
    db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8 
 | 
			
		||||
    f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8 
 | 
			
		||||
    ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38 
 | 
			
		||||
counted: 12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting pcma -> l16
 | 
			
		||||
encoded:
 | 
			
		||||
    80 08 00 01 00 00 00 a0 11 22 33 44 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 
 | 
			
		||||
    5a 00 42 00 00 08 be 00 a6 00 be 00 
 | 
			
		||||
counted: 12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting pcma -> gsm
 | 
			
		||||
encoded:
 | 
			
		||||
    80 08 00 01 00 00 00 a0 11 22 33 44 d4 b9 f4 5d 
 | 
			
		||||
    d9 50 5a e1 a0 cd 76 ea 52 0e 87 53 ad d4 ea a2 
 | 
			
		||||
    0a 63 ca e9 60 79 e2 2a 25 d2 c0 f3 39 
 | 
			
		||||
counted: 12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting pcma -> pcma
 | 
			
		||||
encoded:
 | 
			
		||||
    80 08 00 01 00 00 00 a0 11 22 33 44 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 
 | 
			
		||||
    d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 
 | 
			
		||||
counted: 0
 | 
			
		||||
=== Transcoding Bad Cases ===
 | 
			
		||||
Invalid size:
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting gsm -> pcma
 | 
			
		||||
Nothing encoded due: No message of desired type
 | 
			
		||||
Invalid data:
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting gsm -> pcma
 | 
			
		||||
Nothing encoded due: No message of desired type
 | 
			
		||||
Invalid payload type:
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting gsm -> pcma
 | 
			
		||||
encoded:
 | 
			
		||||
    80 08 00 01 00 00 00 a0 11 22 33 44 d5 a0 a3 bf 
 | 
			
		||||
    38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60 
 | 
			
		||||
    17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82 
 | 
			
		||||
    04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6 
 | 
			
		||||
    02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd 
 | 
			
		||||
    19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf 
 | 
			
		||||
    10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf 
 | 
			
		||||
    62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8 
 | 
			
		||||
    db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8 
 | 
			
		||||
    f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8 
 | 
			
		||||
    ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38 
 | 
			
		||||
counted: 12
 | 
			
		||||
=== Repacking ===
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting pcma -> l16
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 l16 output frames (320 octets) count=12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting pcma -> pcma
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 2 pcma output frames (160 octets) count=12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting pcma -> l16
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting pcma -> pcma
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting pcma -> l16
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 l16 output frames (640 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 l16 output frames (640 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 l16 output frames (640 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 l16 output frames (640 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 l16 output frames (640 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 l16 output frames (640 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 l16 output frames (640 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 l16 output frames (640 octets) count=12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting pcma -> pcma
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 pcma output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 pcma output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 pcma output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 pcma output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 pcma output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 pcma output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 pcma output frames (320 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 4 pcma output frames (320 octets) count=12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting pcma -> l16
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 l16 output frames (480 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 l16 output frames (480 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 l16 output frames (480 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 l16 output frames (480 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 l16 output frames (480 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 l16 output frames (480 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 l16 output frames (480 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 l16 output frames (480 octets) count=12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting pcma -> pcma
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 pcma output frames (240 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 pcma output frames (240 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 pcma output frames (240 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 pcma output frames (240 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 pcma output frames (240 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 pcma output frames (240 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 pcma output frames (240 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 3 pcma output frames (240 octets) count=12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting pcma -> l16
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
got 1 l16 output frames (160 octets) count=12
 | 
			
		||||
== Transcoding test ==
 | 
			
		||||
converting pcma -> pcma
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
generating 160 pcma input samples
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
got 1 pcma output frames (80 octets) count=12
 | 
			
		||||
Testing Initial L16->GSM, PCMA->GSM
 | 
			
		||||
@@ -37,6 +37,7 @@
 | 
			
		||||
#include <dlfcn.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
 | 
			
		||||
char *strline_r(char *str, char **saveptr);
 | 
			
		||||
 | 
			
		||||
@@ -229,6 +230,12 @@ static void test_strline(void)
 | 
			
		||||
	"I: %s\r\n" \
 | 
			
		||||
	"L: p:20, a:AMR, nt:IN\r\n"
 | 
			
		||||
 | 
			
		||||
#define MDCX_TOO_LONG_CI \
 | 
			
		||||
	"MDCX 18983222 1@mgw MGCP 1.0\r\n" \
 | 
			
		||||
	"I: 123456789012345678901234567890123\n"
 | 
			
		||||
 | 
			
		||||
#define MDCX_TOO_LONG_CI_RET "510 18983222 FAIL\r\n"
 | 
			
		||||
 | 
			
		||||
#define SHORT2	"CRCX 1"
 | 
			
		||||
#define SHORT2_RET "510 000000 FAIL\r\n"
 | 
			
		||||
#define SHORT3	"CRCX 1 1@mgw"
 | 
			
		||||
@@ -309,6 +316,32 @@ static void test_strline(void)
 | 
			
		||||
	"a=rtpmap:97 GSM-EFR/8000\r\n" \
 | 
			
		||||
	"a=ptime:20\r\n"
 | 
			
		||||
 | 
			
		||||
#define CRCX_X_OSMO_IGN \
 | 
			
		||||
	"CRCX 2 1@mgw MGCP 1.0\r\n" \
 | 
			
		||||
	"M: recvonly\r\n" \
 | 
			
		||||
	"C: 2\r\n" \
 | 
			
		||||
	"L: p:20\r\n" \
 | 
			
		||||
	"X-Osmo-IGN: C foo\r\n" \
 | 
			
		||||
	"\r\n" \
 | 
			
		||||
	"v=0\r\n" \
 | 
			
		||||
	"c=IN IP4 123.12.12.123\r\n" \
 | 
			
		||||
	"m=audio 5904 RTP/AVP 97\r\n" \
 | 
			
		||||
	"a=rtpmap:97 GSM-EFR/8000\r\n" \
 | 
			
		||||
	"a=ptime:40\r\n"
 | 
			
		||||
 | 
			
		||||
#define CRCX_X_OSMO_IGN_RET \
 | 
			
		||||
	"200 2 OK\r\n" \
 | 
			
		||||
	"I: %s\r\n" \
 | 
			
		||||
	"\r\n" \
 | 
			
		||||
	"v=0\r\n" \
 | 
			
		||||
	"o=- %s 23 IN IP4 0.0.0.0\r\n" \
 | 
			
		||||
	"s=-\r\n" \
 | 
			
		||||
	"c=IN IP4 0.0.0.0\r\n" \
 | 
			
		||||
	"t=0 0\r\n" \
 | 
			
		||||
	"m=audio 16010 RTP/AVP 97\r\n" \
 | 
			
		||||
	"a=rtpmap:97 GSM-EFR/8000\r\n" \
 | 
			
		||||
	"a=ptime:40\r\n"
 | 
			
		||||
 | 
			
		||||
#define DLCX \
 | 
			
		||||
	"DLCX 7 1@mgw MGCP 1.0\r\n" \
 | 
			
		||||
	"I: %s\r\n" \
 | 
			
		||||
@@ -482,6 +515,8 @@ static const struct mgcp_test tests[] = {
 | 
			
		||||
	 "a=fmtp:126 0/1/2"},
 | 
			
		||||
	{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
 | 
			
		||||
	{"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
 | 
			
		||||
	{"CRCX", CRCX_X_OSMO_IGN, CRCX_X_OSMO_IGN_RET, 97},
 | 
			
		||||
	{"MDCX_TOO_LONG_CI", MDCX_TOO_LONG_CI, MDCX_TOO_LONG_CI_RET},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct mgcp_test retransmit[] = {
 | 
			
		||||
@@ -515,7 +550,7 @@ static int last_endpoint = -1;
 | 
			
		||||
static int mgcp_test_policy_cb(struct mgcp_trunk_config *cfg, int endpoint,
 | 
			
		||||
			       int state, const char *transactio_id)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stderr, "Policy CB got state %d on endpoint %d\n",
 | 
			
		||||
	fprintf(stderr, "Policy CB got state %d on endpoint 0x%x\n",
 | 
			
		||||
		state, endpoint);
 | 
			
		||||
	last_endpoint = endpoint;
 | 
			
		||||
	return MGCP_POLICY_CONT;
 | 
			
		||||
@@ -572,45 +607,42 @@ static void test_values(void)
 | 
			
		||||
		    MGCP_CONN_RECV_SEND);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Extract a connection ID from a response (CRCX) */
 | 
			
		||||
/* Extract a connection ID from a response and return in conn_id;
 | 
			
		||||
 * if there is none, return -EINVAL and leave conn_id unchanged. */
 | 
			
		||||
static int get_conn_id_from_response(uint8_t *resp, char *conn_id,
 | 
			
		||||
				     unsigned int conn_id_len)
 | 
			
		||||
				     size_t conn_id_buflen)
 | 
			
		||||
{
 | 
			
		||||
	char *conn_id_ptr;
 | 
			
		||||
	int i;
 | 
			
		||||
	bool got_conn_id = false;
 | 
			
		||||
	const char *conn_id_start;
 | 
			
		||||
	const char *conn_id_end;
 | 
			
		||||
	int conn_id_len;
 | 
			
		||||
 | 
			
		||||
	/* First try to get the conn_id from the I: parameter */
 | 
			
		||||
	conn_id_ptr = strstr((char *)resp, "I: ");
 | 
			
		||||
	if (conn_id_ptr) {
 | 
			
		||||
		memset(conn_id, 0, conn_id_len);
 | 
			
		||||
		memcpy(conn_id, conn_id_ptr + 3, 32);
 | 
			
		||||
		got_conn_id = true;
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Alternatively try to extract the conn_id from the o=- SDP
 | 
			
		||||
		 * parameter */
 | 
			
		||||
		conn_id_ptr = strstr((char *)resp, "o=- ");
 | 
			
		||||
		if(conn_id_ptr) {
 | 
			
		||||
			memset(conn_id, 0, conn_id_len);
 | 
			
		||||
			memcpy(conn_id, conn_id_ptr + 4, 32);
 | 
			
		||||
			got_conn_id = true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	const char *header_I = "\r\nI: ";
 | 
			
		||||
	const char *header_o = "\r\no=- ";
 | 
			
		||||
 | 
			
		||||
	if (got_conn_id) {
 | 
			
		||||
		for (i = 0; i < conn_id_len; i++) {
 | 
			
		||||
			if (conn_id[i] == '\n' || conn_id[i] == '\r')
 | 
			
		||||
				conn_id[i] = '\0';
 | 
			
		||||
		}
 | 
			
		||||
	/* Try to get the conn_id from the 'I:' or 'o=-' parameter */
 | 
			
		||||
	if ((conn_id_start = strstr((char *)resp, header_I))) {
 | 
			
		||||
		conn_id_start += strlen(header_I);
 | 
			
		||||
		conn_id_end = strstr(conn_id_start, "\r\n");
 | 
			
		||||
	} else if ((conn_id_start = strstr((char *)resp, header_o))) {
 | 
			
		||||
		conn_id_start += strlen(header_o);
 | 
			
		||||
		conn_id_end = strchr(conn_id_start, ' ');
 | 
			
		||||
	} else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (conn_id_end)
 | 
			
		||||
		conn_id_len = conn_id_end - conn_id_start;
 | 
			
		||||
	else
 | 
			
		||||
		conn_id_len = strlen(conn_id_start);
 | 
			
		||||
	OSMO_ASSERT(conn_id_len <= conn_id_buflen - 1);
 | 
			
		||||
 | 
			
		||||
	/* A valid conn_id must at least contain one digit, and must
 | 
			
		||||
	 * not exceed a length of 32 digits */
 | 
			
		||||
		OSMO_ASSERT(strlen(conn_id) <= 32);
 | 
			
		||||
		OSMO_ASSERT(strlen(conn_id) > 0);
 | 
			
		||||
	OSMO_ASSERT(conn_id_len <= 32);
 | 
			
		||||
	OSMO_ASSERT(conn_id_len > 0);
 | 
			
		||||
 | 
			
		||||
	strncpy(conn_id, conn_id_start, conn_id_len);
 | 
			
		||||
	conn_id[conn_id_len] = '\0';
 | 
			
		||||
	return 0;
 | 
			
		||||
	}
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check response, automatically patch connection ID if needed */
 | 
			
		||||
@@ -710,7 +742,7 @@ static void test_messages(void)
 | 
			
		||||
		if (msg) {
 | 
			
		||||
			rc = get_conn_id_from_response(msg->data, last_conn_id,
 | 
			
		||||
						       sizeof(last_conn_id));
 | 
			
		||||
			if (rc)
 | 
			
		||||
			if (rc == 0)
 | 
			
		||||
				printf("(response contains a connection id)\n");
 | 
			
		||||
			else
 | 
			
		||||
				printf("(response does not contain a connection id)\n");
 | 
			
		||||
@@ -781,7 +813,7 @@ static void test_messages(void)
 | 
			
		||||
			OSMO_ASSERT(last_endpoint != -1);
 | 
			
		||||
			endp = &cfg->trunk.endpoints[last_endpoint];
 | 
			
		||||
 | 
			
		||||
			fprintf(stderr, "endpoint %d: "
 | 
			
		||||
			fprintf(stderr, "endpoint 0x%x: "
 | 
			
		||||
				"payload type %d (expected %d)\n",
 | 
			
		||||
				last_endpoint,
 | 
			
		||||
				conn->end.codec->payload_type, t->ptype);
 | 
			
		||||
@@ -938,6 +970,7 @@ static void test_packet_loss_calc(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	struct mgcp_endpoint endp;
 | 
			
		||||
	struct mgcp_config cfg = {0};
 | 
			
		||||
	struct mgcp_trunk_config trunk;
 | 
			
		||||
 | 
			
		||||
	printf("Testing packet loss calculation.\n");
 | 
			
		||||
@@ -945,6 +978,7 @@ static void test_packet_loss_calc(void)
 | 
			
		||||
	memset(&endp, 0, sizeof(endp));
 | 
			
		||||
	memset(&trunk, 0, sizeof(trunk));
 | 
			
		||||
 | 
			
		||||
	endp.cfg = &cfg;
 | 
			
		||||
	endp.type = &ep_typeset.rtp;
 | 
			
		||||
	trunk.vty_number_endpoints = 1;
 | 
			
		||||
	trunk.endpoints = &endp;
 | 
			
		||||
@@ -1165,6 +1199,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
 | 
			
		||||
 | 
			
		||||
	struct mgcp_trunk_config trunk;
 | 
			
		||||
	struct mgcp_endpoint endp;
 | 
			
		||||
	struct mgcp_config cfg = {0};
 | 
			
		||||
	struct mgcp_rtp_state state;
 | 
			
		||||
	struct mgcp_rtp_end *rtp;
 | 
			
		||||
	struct sockaddr_in addr = { 0 };
 | 
			
		||||
@@ -1192,6 +1227,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
 | 
			
		||||
	state.in_stream.err_ts_ctr = &test_ctr_in;
 | 
			
		||||
	state.out_stream.err_ts_ctr = &test_ctr_out;
 | 
			
		||||
 | 
			
		||||
	endp.cfg = &cfg;
 | 
			
		||||
	endp.type = &ep_typeset.rtp;
 | 
			
		||||
 | 
			
		||||
	trunk.vty_number_endpoints = 1;
 | 
			
		||||
@@ -1265,6 +1301,7 @@ static void test_multilple_codec(void)
 | 
			
		||||
	struct in_addr addr;
 | 
			
		||||
	struct mgcp_conn_rtp *conn = NULL;
 | 
			
		||||
	char conn_id[256];
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	printf("Testing multiple payload types\n");
 | 
			
		||||
 | 
			
		||||
@@ -1398,6 +1435,9 @@ static void test_multilple_codec(void)
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
	OSMO_ASSERT(conn->end.codec->payload_type == 0);
 | 
			
		||||
 | 
			
		||||
	for (i = 1; i < cfg->trunk.number_endpoints; i++)
 | 
			
		||||
		mgcp_endp_release(&cfg->trunk.endpoints[i]);
 | 
			
		||||
 | 
			
		||||
	talloc_free(cfg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1616,6 +1656,140 @@ static void test_check_local_cx_options(void *ctx)
 | 
			
		||||
	OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_mgcp_codec_pt_translate_pars(struct mgcp_rtp_codec *c)
 | 
			
		||||
{
 | 
			
		||||
	c->rate = 8000;
 | 
			
		||||
	c->channels = 1;
 | 
			
		||||
	c->frame_duration_num = 23;
 | 
			
		||||
	c->frame_duration_den = 42;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_mgcp_codec_pt_translate(void)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn_rtp conn_src;
 | 
			
		||||
	struct mgcp_conn_rtp conn_dst;
 | 
			
		||||
	int pt_dst;
 | 
			
		||||
 | 
			
		||||
	/* Setup a realistic set of codec configurations on both
 | 
			
		||||
	 * ends. AMR and HR will use different payload types. PCMU
 | 
			
		||||
	 * must use 0 on both ends since this is not a dynamic payload
 | 
			
		||||
	 * type */
 | 
			
		||||
	test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[0]);
 | 
			
		||||
	test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[0]);
 | 
			
		||||
	test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[1]);
 | 
			
		||||
	test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[1]);
 | 
			
		||||
	test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[2]);
 | 
			
		||||
	test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[2]);
 | 
			
		||||
	conn_src.end.codecs[0].payload_type = 112;
 | 
			
		||||
	conn_dst.end.codecs[0].payload_type = 96;
 | 
			
		||||
	conn_src.end.codecs[1].payload_type = 0;
 | 
			
		||||
	conn_dst.end.codecs[1].payload_type = 0;
 | 
			
		||||
	conn_src.end.codecs[2].payload_type = 111;
 | 
			
		||||
	conn_dst.end.codecs[2].payload_type = 97;
 | 
			
		||||
	conn_src.end.codecs[0].audio_name = "AMR/8000/1";
 | 
			
		||||
	conn_dst.end.codecs[0].audio_name = "AMR/8000/1";
 | 
			
		||||
	conn_src.end.codecs[1].audio_name = "PCMU/8000/1";
 | 
			
		||||
	conn_dst.end.codecs[1].audio_name = "PCMU/8000/1";
 | 
			
		||||
	conn_src.end.codecs[2].audio_name = "GSM-HR-08/8000/1";
 | 
			
		||||
	conn_dst.end.codecs[2].audio_name = "GSM-HR-08/8000/1";
 | 
			
		||||
	conn_src.end.codecs[0].subtype_name = "AMR";
 | 
			
		||||
	conn_dst.end.codecs[0].subtype_name = "AMR";
 | 
			
		||||
	conn_src.end.codecs[1].subtype_name = "PCMU";
 | 
			
		||||
	conn_dst.end.codecs[1].subtype_name = "PCMU";
 | 
			
		||||
	conn_src.end.codecs[2].subtype_name = "GSM-HR-08";
 | 
			
		||||
	conn_dst.end.codecs[2].subtype_name = "GSM-HR-08";
 | 
			
		||||
	conn_src.end.codecs_assigned = 3;
 | 
			
		||||
	conn_dst.end.codecs_assigned = 3;
 | 
			
		||||
 | 
			
		||||
	/* We expect the function to find the PT we must use when we send the
 | 
			
		||||
	 * packet out to the destination. All we know is the context for both
 | 
			
		||||
	 * connections and the payload type from the source packet */
 | 
			
		||||
	pt_dst =
 | 
			
		||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
			
		||||
				    conn_src.end.codecs[0].payload_type);
 | 
			
		||||
	OSMO_ASSERT(pt_dst == conn_dst.end.codecs[0].payload_type);
 | 
			
		||||
	pt_dst =
 | 
			
		||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
			
		||||
				    conn_src.end.codecs[1].payload_type);
 | 
			
		||||
	OSMO_ASSERT(pt_dst == conn_dst.end.codecs[1].payload_type);
 | 
			
		||||
	pt_dst =
 | 
			
		||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
			
		||||
				    conn_src.end.codecs[2].payload_type);
 | 
			
		||||
	OSMO_ASSERT(pt_dst == conn_dst.end.codecs[2].payload_type);
 | 
			
		||||
 | 
			
		||||
	/* Try some constellations that must fail */
 | 
			
		||||
	pt_dst = mgcp_codec_pt_translate(&conn_src, &conn_dst, 123);
 | 
			
		||||
	OSMO_ASSERT(pt_dst == -EINVAL);
 | 
			
		||||
	conn_src.end.codecs_assigned = 0;
 | 
			
		||||
	conn_dst.end.codecs_assigned = 3;
 | 
			
		||||
	pt_dst =
 | 
			
		||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
			
		||||
				    conn_src.end.codecs[0].payload_type);
 | 
			
		||||
	OSMO_ASSERT(pt_dst == -EINVAL);
 | 
			
		||||
	pt_dst =
 | 
			
		||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
			
		||||
				    conn_src.end.codecs[1].payload_type);
 | 
			
		||||
	OSMO_ASSERT(pt_dst == -EINVAL);
 | 
			
		||||
	pt_dst =
 | 
			
		||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
			
		||||
				    conn_src.end.codecs[2].payload_type);
 | 
			
		||||
	OSMO_ASSERT(pt_dst == -EINVAL);
 | 
			
		||||
	conn_src.end.codecs_assigned = 3;
 | 
			
		||||
	conn_dst.end.codecs_assigned = 0;
 | 
			
		||||
	pt_dst =
 | 
			
		||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
			
		||||
				    conn_src.end.codecs[0].payload_type);
 | 
			
		||||
	OSMO_ASSERT(pt_dst == -EINVAL);
 | 
			
		||||
	pt_dst =
 | 
			
		||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
			
		||||
				    conn_src.end.codecs[1].payload_type);
 | 
			
		||||
	OSMO_ASSERT(pt_dst == -EINVAL);
 | 
			
		||||
	pt_dst =
 | 
			
		||||
	    mgcp_codec_pt_translate(&conn_src, &conn_dst,
 | 
			
		||||
				    conn_src.end.codecs[2].payload_type);
 | 
			
		||||
	OSMO_ASSERT(pt_dst == -EINVAL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_conn_id_matching()
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint endp = {};
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	struct mgcp_conn *conn_match;
 | 
			
		||||
	int i;
 | 
			
		||||
	const char *conn_id_generated = "000023AB";
 | 
			
		||||
	const char *conn_id_request[] = {
 | 
			
		||||
		"23AB",
 | 
			
		||||
		"0023AB",
 | 
			
		||||
		"000023AB",
 | 
			
		||||
		"00000023AB",
 | 
			
		||||
		"23ab",
 | 
			
		||||
		"0023ab",
 | 
			
		||||
		"000023ab",
 | 
			
		||||
		"00000023ab",
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	printf("\nTesting %s\n", __func__);
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&endp.conns);
 | 
			
		||||
 | 
			
		||||
	conn = talloc_zero(NULL, struct mgcp_conn);
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
	osmo_strlcpy(conn->id, conn_id_generated, sizeof(conn->id));
 | 
			
		||||
	llist_add(&conn->entry, &endp.conns);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(conn_id_request); i++) {
 | 
			
		||||
		const char *needle = conn_id_request[i];
 | 
			
		||||
		printf("needle='%s' ", needle);
 | 
			
		||||
		conn_match = mgcp_conn_get(&endp, needle);
 | 
			
		||||
		OSMO_ASSERT(conn_match);
 | 
			
		||||
		printf("found '%s'\n", conn_match->id);
 | 
			
		||||
		OSMO_ASSERT(conn_match == conn);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_del(&conn->entry);
 | 
			
		||||
	talloc_free(conn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
 | 
			
		||||
@@ -1639,6 +1813,8 @@ int main(int argc, char **argv)
 | 
			
		||||
	test_osmux_cid();
 | 
			
		||||
	test_get_lco_identifier();
 | 
			
		||||
	test_check_local_cx_options(ctx);
 | 
			
		||||
	test_mgcp_codec_pt_translate();
 | 
			
		||||
	test_conn_id_matching();
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
 | 
			
		||||
	OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing AUEP2
 | 
			
		||||
@@ -34,7 +34,7 @@ AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX1
 | 
			
		||||
@@ -46,7 +46,7 @@ MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX2
 | 
			
		||||
@@ -58,7 +58,7 @@ MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX
 | 
			
		||||
@@ -79,7 +79,7 @@ a=ptime:40
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
@@ -93,7 +93,7 @@ I: %s
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
@@ -117,7 +117,7 @@ a=ptime:40
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
@@ -141,7 +141,7 @@ a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
@@ -165,7 +165,7 @@ a=ptime:40
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
@@ -189,7 +189,7 @@ a=ptime:40
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
@@ -213,7 +213,7 @@ a=ptime:40
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
@@ -228,7 +228,7 @@ L: p:20, a:AMR, nt:IN
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20, a:AMR, nt:IN
 | 
			
		||||
L: p:20, a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -243,7 +243,7 @@ C: 2
 | 
			
		||||
---------8<---------
 | 
			
		||||
DLCX 7 1@mgw MGCP 1.0
 | 
			
		||||
I: %s
 | 
			
		||||
C: 2
 | 
			
		||||
C: 2
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
@@ -256,7 +256,7 @@ v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
@@ -277,7 +277,7 @@ CRCX
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing SHORT1
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
@@ -288,7 +288,7 @@ CRCX 1
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing SHORT2
 | 
			
		||||
@@ -299,7 +299,7 @@ CRCX 1 1@mgw
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing SHORT3
 | 
			
		||||
@@ -310,7 +310,7 @@ CRCX 1 1@mgw MGCP
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing SHORT4
 | 
			
		||||
@@ -324,7 +324,7 @@ S: D/9
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing RQNT1
 | 
			
		||||
Testing RQNT1
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
RQNT 186908780 1@mgw MGCP 1.0
 | 
			
		||||
@@ -338,7 +338,7 @@ R: D/[0-9#*](N), G/ft, fxr/t38
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing RQNT2
 | 
			
		||||
Testing RQNT2
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
RQNT 186908781 1@mgw MGCP 1.0
 | 
			
		||||
@@ -352,7 +352,7 @@ C: 2
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing DLCX
 | 
			
		||||
Testing DLCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
DLCX 7 1@mgw MGCP 1.0
 | 
			
		||||
@@ -373,7 +373,7 @@ a=ptime:40
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
@@ -387,7 +387,7 @@ I: %s
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX3
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
@@ -402,7 +402,7 @@ C: 2
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing DLCX
 | 
			
		||||
Testing DLCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
DLCX 7 1@mgw MGCP 1.0
 | 
			
		||||
@@ -416,9 +416,45 @@ C: 2
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX
 | 
			
		||||
Testing CRCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 6@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
X-Osmo-IGN: C foo
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX_TOO_LONG_CI
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983222 1@mgw MGCP 1.0
 | 
			
		||||
@@ -1133,4 +1169,14 @@ p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
Testing get_lco_identifier()
 | 
			
		||||
p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU'
 | 
			
		||||
'XXXX,p:10,a:PCMU' -> 'p:10,a:PCMU'
 | 
			
		||||
'10,a:PCMU' -> 'a:PCMU'
 | 
			
		||||
'10, a:PCMU' -> 'a:PCMU'
 | 
			
		||||
'10,a: PCMU' -> 'a: PCMU'
 | 
			
		||||
'10 ,a: PCMU' -> 'a: PCMU'
 | 
			
		||||
', a:PCMU' -> 'a:PCMU'
 | 
			
		||||
' a:PCMU' -> 'a:PCMU'
 | 
			
		||||
'' -> '(null)'
 | 
			
		||||
p10, aPCMU -> (null)
 | 
			
		||||
'10,a :PCMU' -> '(null)'
 | 
			
		||||
 
 | 
			
		||||
@@ -77,14 +77,14 @@ static struct mgcp_client_conf conf;
 | 
			
		||||
struct mgcp_client *mgcp = NULL;
 | 
			
		||||
 | 
			
		||||
static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
 | 
			
		||||
		     int conn_id, const char *params)
 | 
			
		||||
		    const char *params)
 | 
			
		||||
{
 | 
			
		||||
	static char compose[4096 - 128];
 | 
			
		||||
	int len;
 | 
			
		||||
 | 
			
		||||
	len = snprintf(compose, sizeof(compose),
 | 
			
		||||
		       "%d %u %s\r\nI: %d\n\n%s",
 | 
			
		||||
		       code, trans_id, comment, conn_id, params);
 | 
			
		||||
		       "%d %u %s\r\n%s",
 | 
			
		||||
		       code, trans_id, comment, params);
 | 
			
		||||
	OSMO_ASSERT(len < sizeof(compose));
 | 
			
		||||
	OSMO_ASSERT(len > 0);
 | 
			
		||||
 | 
			
		||||
@@ -102,6 +102,7 @@ void test_response_cb(struct mgcp_response *response, void *priv)
 | 
			
		||||
	printf("response cb received:\n");
 | 
			
		||||
	printf("  head.response_code = %d\n", response->head.response_code);
 | 
			
		||||
	printf("  head.trans_id = %u\n", response->head.trans_id);
 | 
			
		||||
	printf("  head.conn_id = %s\n", response->head.conn_id);
 | 
			
		||||
	printf("  head.comment = %s\n", response->head.comment);
 | 
			
		||||
	printf("  audio_port = %u\n", response->audio_port);
 | 
			
		||||
	printf("  audio_ip = %s\n", response->audio_ip);
 | 
			
		||||
@@ -134,32 +135,6 @@ mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
 | 
			
		||||
	return trans_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_crcx(void)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	mgcp_trans_id_t trans_id;
 | 
			
		||||
 | 
			
		||||
	printf("\n===== %s =====\n", __func__);
 | 
			
		||||
 | 
			
		||||
	if (mgcp)
 | 
			
		||||
		talloc_free(mgcp);
 | 
			
		||||
	mgcp = mgcp_client_init(ctx, &conf);
 | 
			
		||||
 | 
			
		||||
	msg = mgcp_msg_crcx(mgcp, 23, 42, MGCP_CONN_LOOPBACK);
 | 
			
		||||
	trans_id = dummy_mgcp_send(msg);
 | 
			
		||||
 | 
			
		||||
	reply_to(trans_id, 200, "OK", 1,
 | 
			
		||||
		"v=0\r\n"
 | 
			
		||||
		"o=- 1 23 IN IP4 10.9.1.120\r\n"
 | 
			
		||||
		"s=-\r\n"
 | 
			
		||||
		"c=IN IP4 10.9.1.120\r\n"
 | 
			
		||||
		"t=0 0\r\n"
 | 
			
		||||
		"m=audio 16002 RTP/AVP 110 96\r\n"
 | 
			
		||||
		"a=rtpmap:110 AMR/8000\r\n"
 | 
			
		||||
		"a=rtpmap:96 GSM-EFR/8000\r\n"
 | 
			
		||||
		"a=ptime:20\r\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_mgcp_msg(void)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
@@ -180,7 +155,8 @@ void test_mgcp_msg(void)
 | 
			
		||||
		.codecs_len = 1,
 | 
			
		||||
		.ptmap[0].codec = CODEC_GSMEFR_8000_1,
 | 
			
		||||
		.ptmap[0].pt = 96,
 | 
			
		||||
		.ptmap_len = 1
 | 
			
		||||
		.ptmap_len = 1,
 | 
			
		||||
		.x_osmo_ign = MGCP_X_OSMO_IGN_CALLID,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (mgcp)
 | 
			
		||||
@@ -268,6 +244,16 @@ void test_mgcp_msg(void)
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	printf("%s\n", (char *)msg->data);
 | 
			
		||||
 | 
			
		||||
	printf("Generate X-Osmo-IGN message:\n");
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	mgcp_msg.verb = MGCP_VERB_CRCX;
 | 
			
		||||
	mgcp_msg.presence =
 | 
			
		||||
	    (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
 | 
			
		||||
	     MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE
 | 
			
		||||
	     | MGCP_MSG_PRESENCE_X_OSMO_IGN);
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	printf("%s\n", (char *)msg->data);
 | 
			
		||||
 | 
			
		||||
	printf("Overfolow test:\n");
 | 
			
		||||
	mgcp_msg.verb = MGCP_VERB_MDCX;
 | 
			
		||||
	mgcp_msg.presence =
 | 
			
		||||
@@ -324,7 +310,7 @@ void test_mgcp_client_cancel()
 | 
			
		||||
	OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == 0);
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "- late response gets discarded\n");
 | 
			
		||||
	OSMO_ASSERT(reply_to(trans_id, 200, "OK", 1, "v=0\r\n") == -ENOENT);
 | 
			
		||||
	OSMO_ASSERT(reply_to(trans_id, 200, "OK", "I: 1\r\n\r\nv=0\r\n") == -ENOENT);
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "- canceling again does nothing\n");
 | 
			
		||||
	OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == -ENOENT);
 | 
			
		||||
@@ -551,7 +537,6 @@ int main(int argc, char **argv)
 | 
			
		||||
 | 
			
		||||
	mgcp_client_conf_init(&conf);
 | 
			
		||||
 | 
			
		||||
	test_crcx();
 | 
			
		||||
	test_mgcp_msg();
 | 
			
		||||
	test_mgcp_client_cancel();
 | 
			
		||||
	test_sdp_section_start();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
DLMGCP MGCP client: using endpoint domain '@mgw'
 | 
			
		||||
DLMGCP message buffer to small, can not generate MGCP message
 | 
			
		||||
 | 
			
		||||
test_mgcp_client_cancel():
 | 
			
		||||
DLMGCP MGCP client: using endpoint domain '@mgw'
 | 
			
		||||
- composed msg with trans_id=1
 | 
			
		||||
- not in queue yet, cannot cancel yet
 | 
			
		||||
DLMGCP Cannot cancel, no such transaction: 1
 | 
			
		||||
 
 | 
			
		||||
@@ -1,45 +1,4 @@
 | 
			
		||||
 | 
			
		||||
===== test_crcx =====
 | 
			
		||||
composed:
 | 
			
		||||
-----
 | 
			
		||||
CRCX 1 17@mgw MGCP 1.0
 | 
			
		||||
C: 2a
 | 
			
		||||
L: p:20, a:AMR, nt:IN
 | 
			
		||||
M: loopback
 | 
			
		||||
 | 
			
		||||
-----
 | 
			
		||||
composed response:
 | 
			
		||||
-----
 | 
			
		||||
200 1 OK
 | 
			
		||||
I: 1
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
o=- 1 23 IN IP4 10.9.1.120
 | 
			
		||||
s=-
 | 
			
		||||
c=IN IP4 10.9.1.120
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 16002 RTP/AVP 110 96
 | 
			
		||||
a=rtpmap:110 AMR/8000
 | 
			
		||||
a=rtpmap:96 GSM-EFR/8000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
-----
 | 
			
		||||
response cb received:
 | 
			
		||||
  head.response_code = 200
 | 
			
		||||
  head.trans_id = 1
 | 
			
		||||
  head.comment = OK
 | 
			
		||||
  audio_port = 16002
 | 
			
		||||
  audio_ip = 10.9.1.120
 | 
			
		||||
  ptime = 20
 | 
			
		||||
  codecs_len = 2
 | 
			
		||||
  codecs[0] = 112
 | 
			
		||||
  codecs[1] = 110
 | 
			
		||||
  ptmap_len = 2
 | 
			
		||||
  ptmap[0].codec = 112
 | 
			
		||||
  ptmap[0].pt = 110
 | 
			
		||||
  ptmap[1].codec = 110
 | 
			
		||||
  ptmap[1].pt = 96
 | 
			
		||||
 | 
			
		||||
Generated CRCX message:
 | 
			
		||||
CRCX 1 23@mgw MGCP 1.0
 | 
			
		||||
C: 2f
 | 
			
		||||
@@ -117,6 +76,14 @@ AUEP 8 23@mgw MGCP 1.0
 | 
			
		||||
Generated RSIP message:
 | 
			
		||||
RSIP 9 23@mgw MGCP 1.0
 | 
			
		||||
 | 
			
		||||
Generate X-Osmo-IGN message:
 | 
			
		||||
CRCX 11 23@mgw MGCP 1.0
 | 
			
		||||
C: 2f
 | 
			
		||||
I: 11
 | 
			
		||||
L: p:20, a:GSM, nt:IN
 | 
			
		||||
M: sendrecv
 | 
			
		||||
X-Osmo-IGN: C
 | 
			
		||||
 | 
			
		||||
Overfolow test:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,6 @@
 | 
			
		||||
AT_INIT
 | 
			
		||||
AT_BANNER([Regression tests.])
 | 
			
		||||
 | 
			
		||||
AT_SETUP([legacy_mgcp])
 | 
			
		||||
AT_KEYWORDS([legacy_mgcp])
 | 
			
		||||
cat $abs_srcdir/legacy_mgcp/mgcp_test.ok > expout
 | 
			
		||||
AT_CHECK([$abs_top_builddir/tests/legacy_mgcp/mgcp_test], [], [expout], [ignore])
 | 
			
		||||
AT_CLEANUP
 | 
			
		||||
 | 
			
		||||
AT_SETUP([legacy_mgcp-trans])
 | 
			
		||||
AT_KEYWORDS([legacy_mgcp-trans])
 | 
			
		||||
AT_CHECK([test "$enable_mgcp_transcoding_test" == yes || exit 77])
 | 
			
		||||
cat $abs_srcdir/legacy_mgcp/mgcp_transcoding_test.ok > expout
 | 
			
		||||
AT_CHECK([$abs_top_builddir/tests/legacy_mgcp/mgcp_transcoding_test], [], [expout], [ignore])
 | 
			
		||||
AT_CLEANUP
 | 
			
		||||
 | 
			
		||||
AT_SETUP([mgcp_client])
 | 
			
		||||
AT_KEYWORDS([mgcp_client])
 | 
			
		||||
cat $abs_srcdir/mgcp_client/mgcp_client_test.ok > expout
 | 
			
		||||
 
 | 
			
		||||
@@ -1,171 +0,0 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
 | 
			
		||||
# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
 | 
			
		||||
# (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
# This program is distributed in the hope that it will be useful,
 | 
			
		||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
import os, sys
 | 
			
		||||
import time
 | 
			
		||||
import unittest
 | 
			
		||||
import socket
 | 
			
		||||
import subprocess
 | 
			
		||||
 | 
			
		||||
import osmopy.obscvty as obscvty
 | 
			
		||||
import osmopy.osmoutil as osmoutil
 | 
			
		||||
 | 
			
		||||
# add $top_srcdir/contrib to find ipa.py
 | 
			
		||||
sys.path.append(os.path.join(sys.path[0], '..', 'contrib'))
 | 
			
		||||
 | 
			
		||||
from ipa import IPA
 | 
			
		||||
 | 
			
		||||
# to be able to find $top_srcdir/doc/...
 | 
			
		||||
confpath = os.path.join(sys.path[0], '..')
 | 
			
		||||
 | 
			
		||||
class TestVTYBase(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def checkForEndAndExit(self):
 | 
			
		||||
        res = self.vty.command("list")
 | 
			
		||||
        #print ('looking for "exit"\n')
 | 
			
		||||
        self.assert_(res.find('  exit\r') > 0)
 | 
			
		||||
        #print 'found "exit"\nlooking for "end"\n'
 | 
			
		||||
        self.assert_(res.find('  end\r') > 0)
 | 
			
		||||
        #print 'found "end"\n'
 | 
			
		||||
 | 
			
		||||
    def vty_command(self):
 | 
			
		||||
        raise Exception("Needs to be implemented by a subclass")
 | 
			
		||||
 | 
			
		||||
    def vty_app(self):
 | 
			
		||||
        raise Exception("Needs to be implemented by a subclass")
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        osmo_vty_cmd = self.vty_command()[:]
 | 
			
		||||
        config_index = osmo_vty_cmd.index('-c')
 | 
			
		||||
        if config_index:
 | 
			
		||||
            cfi = config_index + 1
 | 
			
		||||
            osmo_vty_cmd[cfi] = os.path.join(confpath, osmo_vty_cmd[cfi])
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            self.proc = osmoutil.popen_devnull(osmo_vty_cmd)
 | 
			
		||||
        except OSError:
 | 
			
		||||
            print >> sys.stderr, "Current directory: %s" % os.getcwd()
 | 
			
		||||
            print >> sys.stderr, "Consider setting -b"
 | 
			
		||||
 | 
			
		||||
        appstring = self.vty_app()[2]
 | 
			
		||||
        appport = self.vty_app()[0]
 | 
			
		||||
        self.vty = obscvty.VTYInteract(appstring, "127.0.0.1", appport)
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        if self.vty:
 | 
			
		||||
            self.vty._close_socket()
 | 
			
		||||
        self.vty = None
 | 
			
		||||
        osmoutil.end_proc(self.proc)
 | 
			
		||||
 | 
			
		||||
class TestVTYMGCP(TestVTYBase):
 | 
			
		||||
    def vty_command(self):
 | 
			
		||||
        return ["./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "-c",
 | 
			
		||||
                "doc/examples/osmo-bsc_mgcp/mgcp.cfg"]
 | 
			
		||||
 | 
			
		||||
    def vty_app(self):
 | 
			
		||||
        return (4243, "./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp")
 | 
			
		||||
 | 
			
		||||
    def testForcePtime(self):
 | 
			
		||||
        self.vty.enable()
 | 
			
		||||
        res = self.vty.command("show running-config")
 | 
			
		||||
        self.assert_(res.find('  rtp force-ptime 20\r') > 0)
 | 
			
		||||
        self.assertEquals(res.find('  no rtp force-ptime\r'), -1)
 | 
			
		||||
 | 
			
		||||
        self.vty.command("configure terminal")
 | 
			
		||||
        self.vty.command("mgcp")
 | 
			
		||||
        self.vty.command("no rtp force-ptime")
 | 
			
		||||
        res = self.vty.command("show running-config")
 | 
			
		||||
        self.assertEquals(res.find('  rtp force-ptime 20\r'), -1)
 | 
			
		||||
        self.assertEquals(res.find('  no rtp force-ptime\r'), -1)
 | 
			
		||||
 | 
			
		||||
    def testOmitAudio(self):
 | 
			
		||||
        self.vty.enable()
 | 
			
		||||
        res = self.vty.command("show running-config")
 | 
			
		||||
        self.assert_(res.find('  sdp audio-payload send-name\r') > 0)
 | 
			
		||||
        self.assertEquals(res.find('  no sdp audio-payload send-name\r'), -1)
 | 
			
		||||
 | 
			
		||||
        self.vty.command("configure terminal")
 | 
			
		||||
        self.vty.command("mgcp")
 | 
			
		||||
        self.vty.command("no sdp audio-payload send-name")
 | 
			
		||||
        res = self.vty.command("show running-config")
 | 
			
		||||
        self.assertEquals(res.find('  rtp sdp audio-payload send-name\r'), -1)
 | 
			
		||||
        self.assert_(res.find('  no sdp audio-payload send-name\r') > 0)
 | 
			
		||||
 | 
			
		||||
        # TODO: test it for the trunk!
 | 
			
		||||
 | 
			
		||||
    def testBindAddr(self):
 | 
			
		||||
        self.vty.enable()
 | 
			
		||||
 | 
			
		||||
        self.vty.command("configure terminal")
 | 
			
		||||
        self.vty.command("mgcp")
 | 
			
		||||
 | 
			
		||||
        # enable.. disable bts-bind-ip
 | 
			
		||||
        self.vty.command("rtp bts-bind-ip 254.253.252.250")
 | 
			
		||||
        res = self.vty.command("show running-config")
 | 
			
		||||
        self.assert_(res.find('rtp bts-bind-ip 254.253.252.250') > 0)
 | 
			
		||||
        self.vty.command("no rtp bts-bind-ip")
 | 
			
		||||
        res = self.vty.command("show running-config")
 | 
			
		||||
        self.assertEquals(res.find('  rtp bts-bind-ip'), -1)
 | 
			
		||||
 | 
			
		||||
        # enable.. disable net-bind-ip
 | 
			
		||||
        self.vty.command("rtp net-bind-ip 254.253.252.250")
 | 
			
		||||
        res = self.vty.command("show running-config")
 | 
			
		||||
        self.assert_(res.find('rtp net-bind-ip 254.253.252.250') > 0)
 | 
			
		||||
        self.vty.command("no rtp net-bind-ip")
 | 
			
		||||
        res = self.vty.command("show running-config")
 | 
			
		||||
        self.assertEquals(res.find('  rtp net-bind-ip'), -1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    import argparse
 | 
			
		||||
    import sys
 | 
			
		||||
 | 
			
		||||
    workdir = '.'
 | 
			
		||||
 | 
			
		||||
    parser = argparse.ArgumentParser()
 | 
			
		||||
    parser.add_argument("-v", "--verbose", dest="verbose",
 | 
			
		||||
                        action="store_true", help="verbose mode")
 | 
			
		||||
    parser.add_argument("-p", "--pythonconfpath", dest="p",
 | 
			
		||||
                        help="searchpath for config")
 | 
			
		||||
    parser.add_argument("-w", "--workdir", dest="w",
 | 
			
		||||
                        help="Working directory")
 | 
			
		||||
    parser.add_argument("test_name", nargs="*", help="(parts of) test names to run, case-insensitive")
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
    verbose_level = 1
 | 
			
		||||
    if args.verbose:
 | 
			
		||||
        verbose_level = 2
 | 
			
		||||
 | 
			
		||||
    if args.w:
 | 
			
		||||
        workdir = args.w
 | 
			
		||||
 | 
			
		||||
    if args.p:
 | 
			
		||||
        confpath = args.p
 | 
			
		||||
 | 
			
		||||
    print "confpath %s, workdir %s" % (confpath, workdir)
 | 
			
		||||
    os.chdir(workdir)
 | 
			
		||||
    print "Running tests for specific VTY commands"
 | 
			
		||||
    suite = unittest.TestSuite()
 | 
			
		||||
    suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestVTYMGCP))
 | 
			
		||||
 | 
			
		||||
    if args.test_name:
 | 
			
		||||
        osmoutil.pick_tests(suite, *args.test_name)
 | 
			
		||||
 | 
			
		||||
    res = unittest.TextTestRunner(verbosity=verbose_level, stream=sys.stdout).run(suite)
 | 
			
		||||
    sys.exit(len(res.errors) + len(res.failures))
 | 
			
		||||
 | 
			
		||||
# vim: shiftwidth=4 expandtab nocin ai
 | 
			
		||||
		Reference in New Issue
	
	Block a user