mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 08:12:01 +00:00
Compare commits
110 Commits
efistokl/i
...
1.6.0
Author | SHA1 | Date | |
---|---|---|---|
|
50e52e45f9 | ||
|
a2d10216ea | ||
|
f54eb96338 | ||
|
a2b1c5e6f6 | ||
|
c5c1430a1c | ||
|
f7de9aea7b | ||
|
199c5e965e | ||
|
06a49fc04b | ||
|
6349bd422a | ||
|
796a4a1325 | ||
|
2cf6652ba2 | ||
|
9b508f6b53 | ||
|
6e26c70f46 | ||
|
9a34592c09 | ||
|
7eb6f2cb56 | ||
|
83fd8a5692 | ||
|
17058484d1 | ||
|
166077ea48 | ||
|
0c6c3c1da6 | ||
|
fe9a1fe03b | ||
|
1e4a34d45e | ||
|
189f29e939 | ||
|
d2ce444008 | ||
|
ef9420adf5 | ||
|
cb96e05a91 | ||
|
f6387dc766 | ||
|
1b1d7ed98f | ||
|
df7d97e4b4 | ||
|
85978dadab | ||
|
9aaaab6b3b | ||
|
c1ad553d86 | ||
|
c1bf4694e7 | ||
|
1442c5a99e | ||
|
dac2ca7e21 | ||
|
375252279c | ||
|
295570c631 | ||
|
a93c6e9263 | ||
|
30907dc9d8 | ||
|
14f8a08f44 | ||
|
c63f15a9a7 | ||
|
ca538fc5eb | ||
|
6be2c49538 | ||
|
91088c305f | ||
|
900cd6518a | ||
|
fa810e8ccd | ||
|
497611ae84 | ||
|
2b89617aad | ||
|
b542b0457b | ||
|
120568651a | ||
|
f1d301a9a6 | ||
|
c9a6280c94 | ||
|
b5583cde41 | ||
|
6c1cd63a57 | ||
|
0d7ba56f5c | ||
|
3ac604e3ad | ||
|
9fb8ddf00e | ||
|
182ca3bad4 | ||
|
f027f17dcb | ||
|
ac772d8b0c | ||
|
9d9f44ac71 | ||
|
84274e4e9b | ||
|
c5479fe086 | ||
|
538d2c53d9 | ||
|
8de58e79b8 | ||
|
5e8d7995d1 | ||
|
3239f6212e | ||
|
209eb9f103 | ||
|
ef6304e4a1 | ||
|
21cdbfc196 | ||
|
fc8067348b | ||
|
9f11dc5616 | ||
|
ca0818c760 | ||
|
c12bfb7ffc | ||
|
228e591589 | ||
|
58128258b0 | ||
|
e7ae69a7be | ||
|
7e37ce6008 | ||
|
dbe09dd515 | ||
|
217d31d62f | ||
|
47642f245d | ||
|
cb760bdebe | ||
|
e3f8bca424 | ||
|
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 |
48
.gitignore
vendored
48
.gitignore
vendored
@@ -2,20 +2,13 @@ debian/*.log
|
||||
*.o
|
||||
*.lo
|
||||
*.a
|
||||
*.la
|
||||
.deps
|
||||
Makefile
|
||||
Makefile.in
|
||||
bscconfig.h
|
||||
bscconfig.h.in
|
||||
openbsc.pc
|
||||
src/osmo-nitb/osmo-nitb
|
||||
src/osmo-bsc_mgcp/osmo-bsc_mgcp
|
||||
src/osmo-bsc/osmo-bsc
|
||||
src/utils/meas_vis
|
||||
src/utils/meas_json
|
||||
src/utils/osmo-meas-pcap2db
|
||||
src/utils/osmo-meas-udp2db
|
||||
src/utils/smpp_mirror
|
||||
src/osmo-mgw/osmo-mgw
|
||||
*.*~
|
||||
*.sw?
|
||||
.libs
|
||||
@@ -46,32 +39,9 @@ m4/*.m4
|
||||
.version
|
||||
|
||||
|
||||
# apps and app data
|
||||
hlr.sqlite3
|
||||
src/utils/bs11_config
|
||||
src/ipaccess/ipaccess-config
|
||||
src/ipaccess/abisip-find
|
||||
src/ipaccess/ipaccess-firmware
|
||||
src/ipaccess/ipaccess-proxy
|
||||
src/utils/isdnsync
|
||||
src/nat/bsc_nat
|
||||
src/gprs/osmo-sgsn
|
||||
src/gprs/osmo-gbproxy
|
||||
src/gprs/osmo-gtphub
|
||||
src/osmo-bsc_nat/osmo-bsc_nat
|
||||
src/libcommon/gsup_test_client
|
||||
src/osmo-msc/osmo-msc
|
||||
|
||||
#tests
|
||||
tests/testsuite.dir
|
||||
tests/*/*_test
|
||||
# ignore compiled binaries like msc_vlr_test_foo; do not ignore
|
||||
# msc_vlr_test_foo.{c,ok,err}, but do still ignore the corresponding .o object
|
||||
# files:
|
||||
tests/msc_vlr/msc_vlr_test_*
|
||||
!tests/msc_vlr/msc_vlr_test_*.*
|
||||
tests/msc_vlr/msc_vlr_test_*.o
|
||||
|
||||
|
||||
tests/atconfig
|
||||
tests/atlocal
|
||||
@@ -79,7 +49,15 @@ tests/package.m4
|
||||
tests/testsuite
|
||||
tests/testsuite.log
|
||||
|
||||
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
|
||||
|
@@ -24,7 +24,7 @@ pkgconfig_DATA = \
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = git-version-gen osmoappdesc.py .version
|
||||
|
||||
DISTCHECK_CONFIGURE_FLAGS = \
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
||||
@RELMAKE@
|
||||
|
64
configure.ac
64
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.6.0)
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
@@ -76,21 +76,10 @@ fi
|
||||
|
||||
dnl Checks for typedefs, structures and compiler characteristics
|
||||
|
||||
# The following test is taken from WebKit's webkit.m4
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS -fvisibility=hidden "
|
||||
AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])],
|
||||
[ AC_MSG_RESULT([yes])
|
||||
SYMBOL_VISIBILITY="-fvisibility=hidden"],
|
||||
AC_MSG_RESULT([no]))
|
||||
CFLAGS="$saved_CFLAGS"
|
||||
AC_SUBST(SYMBOL_VISIBILITY)
|
||||
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=implicit], [CFLAGS="$CFLAGS -Werror=implicit"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=maybe-uninitialized], [CFLAGS="$CFLAGS -Werror=maybe-uninitialized"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=memset-transposed-args], [CFLAGS="$CFLAGS -Werror=memset-transposed-args"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=null-dereference], [CFLAGS="$CFLAGS -Werror=null-dereference"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wnull-dereference], [CFLAGS="$CFLAGS -Wnull-dereference"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=sizeof-array-argument], [CFLAGS="$CFLAGS -Werror=sizeof-array-argument"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=sizeof-pointer-memaccess], [CFLAGS="$CFLAGS -Werror=sizeof-pointer-memaccess"])
|
||||
|
||||
@@ -128,6 +117,48 @@ 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])],,
|
||||
@@ -166,6 +197,7 @@ AC_OUTPUT(
|
||||
tests/mgcp/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
doc/manuals/Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
Makefile)
|
||||
|
@@ -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,18 @@ 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
|
||||
|
||||
$MAKE maintainer-clean
|
||||
osmo-clean-workspace.sh
|
||||
|
@@ -1,5 +1,6 @@
|
||||
if HAVE_SYSTEMD
|
||||
EXTRA_DIST = osmo-mgw.service
|
||||
|
||||
if HAVE_SYSTEMD
|
||||
systemdsystemunit_DATA = \
|
||||
osmo-mgw.service
|
||||
endif
|
||||
|
213
debian/changelog
vendored
213
debian/changelog
vendored
@@ -1,3 +1,216 @@
|
||||
osmo-mgw (1.6.0) unstable; urgency=medium
|
||||
|
||||
[ Oliver Smith ]
|
||||
* Cosmetic: fix spaces/tabs in mgcp_requests[]
|
||||
* Inactive connection cleanup (disabled by default)
|
||||
* debian: create -doc subpackage with pdf manuals
|
||||
* vty: allow 0 as conn-timeout to disable it
|
||||
* vty: update desc of conn-timeout
|
||||
* manuals: update VTY documentation
|
||||
* "make dist" fix for: no rule to make mgcp_common.h
|
||||
* contrib/jenkins.sh: run "make maintainer-clean"
|
||||
|
||||
[ Philipp Maier ]
|
||||
* Add option to GSM HR frames to RFC5593 representation
|
||||
* mgcp_sdp: cosmetic: correct typo in comment
|
||||
* mgco_client: cosmetic: remove excess whitespace
|
||||
* mgcp_sdp: mgcp_sdp.c does not include own header file
|
||||
* mgcp_sdp: untangle parsing of a= parameters
|
||||
* mgcp_network: use mgcp_rtp_codec in downlink format callback
|
||||
* AMR: Add function to convert between bw-effient and octet aligned mode
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* log: don't spam with "can not patch PT" messages
|
||||
* mgcp client: allow setting conn mode for CRCX and MDCX
|
||||
* make codec_table public as osmo_mgcpc_codec_names
|
||||
* move MGW endpoint FSM from osmo-bsc to here
|
||||
* fix: multiple initial CRCX
|
||||
* constify map_codec_to_pt() ptmap arg
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* mgcp-client: whitespace cleanup in mgcp_client.c
|
||||
* mgcp-client: Sanitize implementation of mgcp_client_rtpbridge_wildcard
|
||||
* libosmo-mgcp: Use trunk type during endpoint allocation
|
||||
* cosmetic: Fix typos in comment
|
||||
* cosmetic: tests: mgcp_client_test: clean trailing whitespace
|
||||
* mgcp_msg: Log faulty line on Osmux parsing error
|
||||
* cosmetic: handle_modify_con: Fix indentation level
|
||||
* Introduce log fmt helpers LOGPENDP and LOGPCONN
|
||||
* create_response_with_sdp: Fix inclusion of X-Osmux
|
||||
* osmux: Cleanup of CID alloc pool APIs
|
||||
* mgcp_osmux.c: osmux_enable_endpoint: Fix incorrect return check
|
||||
* osmux: Log osmux socket during osmux_init
|
||||
* mgcp-cli: Change osmo_mgcpc_ep_fsm name to avoid collision with old osmo-bsc
|
||||
* osmux: Document func and return different rc upon osmux init failure
|
||||
* osmux: Fix reception of legacy dummy payloads
|
||||
* osmux: Use LOGPCONN in several log calls
|
||||
* cosmetic: mgcp_udp_send: Document port param is in network byte order
|
||||
* cosmetic: osmux: Document network byte order in port variable
|
||||
* vty: Allow enabling Osmux
|
||||
* osmux: Allocate CID during CRCX
|
||||
* osmux: Introduce mgcp_conn_rtp_is_osmux() helper
|
||||
* osmux: Mark conn_rtp->type as osmux during CRCX
|
||||
* mgcp-cli: Allow submitting X-Osmux on CRCX request
|
||||
* mgcp-cli: Parse X-Osmux on CRCX response
|
||||
* osmux: Handle Osmux MGCP extension in MDCX messages
|
||||
* mgcp-cli: Allow submitting X-Osmux on MDCX request
|
||||
* mgcp-cli: Parse X-Osmux on MDCX response
|
||||
* osmux: Drop unneeded OSMUX_STATE_NEGOTIATING
|
||||
* mgcp-cli: endpoint_fsm: Add API to retrieve Osmux CID from MGW
|
||||
* osmux: Provide correct local port during mgcp resp
|
||||
* osmux: Use remote port to send osmux frames
|
||||
* osmux: Improve logging around osmux enabling events
|
||||
* osmux: Delay osmux enable of conn until remote addr is configured by MDCX
|
||||
* osmux: Redo read/write osmux glue code to have data routed correctly
|
||||
* mgw, mgcp-li: Handle X-Osmux param name as case insensitive
|
||||
* osmux: Use DUMMY ft msg as per Osmux spec
|
||||
* osmux: Fix loopback for Osmux connections
|
||||
* osmux: Fix CID release for non-enabled connections
|
||||
* osmux: Fix hardcoded rtp payload_type 98 in osmux conn
|
||||
* mgcp-cli: Validate osmux cid value during mgcp_msg_gen
|
||||
* mgw: Support uppercase LCO options
|
||||
* mgw: Support lowercase header parameters
|
||||
* mgcp-cli: Support lowercase header parameters
|
||||
* mgw: Support receiving uppercase connection mode
|
||||
* mgw: Support receiving lowercase LCO codec
|
||||
* mgw: Make check of duplicated LCO fields case insensitive
|
||||
* mgw: Allow receiving lowercase MGCP header keyword
|
||||
* mgw: Allow receiving lowercase X-Osmo-Ign Callid field
|
||||
* mgw: Allow receiving uppercase noanswer keyword
|
||||
* doc: X-Osmo-IGN: small formatting and typo fixes
|
||||
* doc: Add Osmux documentation to OsmoMGW User Manual
|
||||
* Catch unsigned integer MGCP parsing errors with strtoul
|
||||
* Fix return variable of strtoul()
|
||||
* Remove undefined param passed to {logging,osmo_stats}_vty_add_cmds
|
||||
* configure.ac: Require libosmo-netif 0.6.0
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* mgcp_internal: LOGPENDP: ensure *endp is not NULL
|
||||
|
||||
[ Harald Welte ]
|
||||
* handle NULL return of rate_ctr_group_alloc()
|
||||
* update .gitignore
|
||||
* mgcp_sdp: Don't check if an unsigned int is below 0
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* configure.ac: drop useless check for -fvisibility=hidden
|
||||
|
||||
[ Daniel Willmann ]
|
||||
* manuals: Add script to regenerate vty/counter documentation
|
||||
* manuals: Update vty/counter documentation
|
||||
|
||||
[ Hoernchen ]
|
||||
* turn -Werror=null-dereference into a warning
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 07 Aug 2019 16:52:58 +0200
|
||||
|
||||
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 ]
|
||||
|
16
debian/control
vendored
16
debian/control
vendored
@@ -7,7 +7,8 @@ Build-Depends: debhelper (>=9),
|
||||
pkg-config,
|
||||
autotools-dev,
|
||||
libosmocore-dev,
|
||||
libosmo-netif-dev
|
||||
libosmo-netif-dev,
|
||||
osmo-gsm-manuals-dev
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: git://git.osmocom.org/osmo-mgw.git
|
||||
Vcs-Browser: https://git.osmocom.org/osmo-mgw/
|
||||
@@ -19,7 +20,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-client6
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
@@ -31,5 +32,14 @@ Package: libosmo-mgcp-client-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-mgcp-client3 (= ${binary:Version}), ${misc:Depends}
|
||||
Depends: libosmo-mgcp-client6 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
|
||||
|
||||
Package: osmo-mgw-doc
|
||||
Architecture: all
|
||||
Section: doc
|
||||
Priority: optional
|
||||
Depends: ${misc:Depends}
|
||||
Description: ${misc:Package} PDF documentation
|
||||
Various manuals: user manual, VTY reference manual and/or
|
||||
protocol/interface manuals.
|
||||
|
1
debian/osmo-mgw-doc.install
vendored
Normal file
1
debian/osmo-mgw-doc.install
vendored
Normal file
@@ -0,0 +1 @@
|
||||
usr/share/doc/osmo-mgw-doc/*.pdf
|
6
debian/rules
vendored
6
debian/rules
vendored
@@ -30,6 +30,10 @@ override_dh_auto_test:
|
||||
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
|
||||
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system
|
||||
dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
|
||||
|
||||
# Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers)
|
||||
override_dh_compress:
|
||||
dh_compress -X.pdf
|
||||
|
||||
# See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg
|
||||
|
@@ -1,3 +1,4 @@
|
||||
SUBDIRS = \
|
||||
examples \
|
||||
manuals \
|
||||
$(NULL)
|
||||
|
18
doc/manuals/Makefile.am
Normal file
18
doc/manuals/Makefile.am
Normal file
@@ -0,0 +1,18 @@
|
||||
EXTRA_DIST = osmomgw-usermanual.adoc \
|
||||
osmomgw-usermanual-docinfo.xml \
|
||||
osmomgw-vty-reference.xml \
|
||||
regen_doc.sh \
|
||||
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
|
||||
|
||||
OSMO_REPOSITORY = osmo-mgw
|
||||
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[]
|
83
doc/manuals/chapters/counters_generated.adoc
Normal file
83
doc/manuals/chapters/counters_generated.adoc
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
// autogenerated by show asciidoc counters
|
||||
These counters and their description based on OsmoMGW 1.5.0.64-189f (OsmoMGW).
|
||||
|
||||
=== Rate Counters
|
||||
|
||||
// generating tables for rate_ctr_group
|
||||
// rate_ctr_group table aggregated statistics for all rtp connections
|
||||
.all_rtp_conn - aggregated statistics for all rtp connections
|
||||
[options="header"]
|
||||
|===
|
||||
| Name | Reference | Description
|
||||
| all_rtp:err_tstmp_in | <<all_rtp_conn_all_rtp:err_tstmp_in>> | Total inbound rtp-stream timestamp errors.
|
||||
| all_rtp:err_tstmp_out | <<all_rtp_conn_all_rtp:err_tstmp_out>> | Total outbound rtp-stream timestamp errors.
|
||||
| all_rtp:packets_rx | <<all_rtp_conn_all_rtp:packets_rx>> | Total inbound rtp packets.
|
||||
| all_rtp:octets_rx | <<all_rtp_conn_all_rtp:octets_rx>> | Total inbound rtp octets.
|
||||
| all_rtp:packets_tx | <<all_rtp_conn_all_rtp:packets_tx>> | Total outbound rtp packets.
|
||||
| all_rtp:octets_tx | <<all_rtp_conn_all_rtp:octets_tx>> | Total outbound rtp octets.
|
||||
| all_rtp:dropped | <<all_rtp_conn_all_rtp:dropped>> | Total dropped rtp packets.
|
||||
| all_rtp:num_closed_conns | <<all_rtp_conn_all_rtp:num_closed_conns>> | Total number of rtp connections closed.
|
||||
|===
|
||||
// rate_ctr_group table dlcx statistics
|
||||
.dlcx - dlcx statistics
|
||||
[options="header"]
|
||||
|===
|
||||
| Name | Reference | Description
|
||||
| dlcx:success | <<dlcx_dlcx:success>> | DLCX command processed successfully.
|
||||
| dlcx:wildcard | <<dlcx_dlcx:wildcard>> | wildcard names in DLCX commands are unsupported.
|
||||
| dlcx:no_conn | <<dlcx_dlcx:no_conn>> | endpoint specified in DLCX command has no active connections.
|
||||
| dlcx:callid | <<dlcx_dlcx:callid>> | CallId specified in DLCX command mismatches endpoint's CallId .
|
||||
| dlcx:connid | <<dlcx_dlcx:connid>> | connection ID specified in DLCX command does not exist on endpoint.
|
||||
| dlcx:unhandled_param | <<dlcx_dlcx:unhandled_param>> | unhandled parameter in DLCX command.
|
||||
| dlcx:rejected | <<dlcx_dlcx:rejected>> | connection deletion rejected by policy.
|
||||
| dlcx:deferred | <<dlcx_dlcx:deferred>> | connection deletion deferred by policy.
|
||||
|===
|
||||
// rate_ctr_group table mdcx statistics
|
||||
.mdcx - mdcx statistics
|
||||
[options="header"]
|
||||
|===
|
||||
| Name | Reference | Description
|
||||
| mdcx:success | <<mdcx_mdcx:success>> | MDCX command processed successfully.
|
||||
| mdcx:wildcard | <<mdcx_mdcx:wildcard>> | wildcard endpoint names in MDCX commands are unsupported.
|
||||
| mdcx:no_conn | <<mdcx_mdcx:no_conn>> | endpoint specified in MDCX command has no active connections.
|
||||
| mdcx:callid | <<mdcx_mdcx:callid>> | invalid CallId specified in MDCX command.
|
||||
| mdcx:connid | <<mdcx_mdcx:connid>> | invalid connection ID specified in MDCX command.
|
||||
| crcx:unhandled_param | <<mdcx_crcx:unhandled_param>> | unhandled parameter in MDCX command.
|
||||
| mdcx:no_connid | <<mdcx_mdcx:no_connid>> | no connection ID specified in MDCX command.
|
||||
| mdcx:conn_not_found | <<mdcx_mdcx:conn_not_found>> | connection specified in MDCX command does not exist.
|
||||
| mdcx:invalid_mode | <<mdcx_mdcx:invalid_mode>> | invalid connection mode in MDCX command.
|
||||
| mdcx:conn_opt | <<mdcx_mdcx:conn_opt>> | connection options invalid.
|
||||
| mdcx:no_remote_conn_desc | <<mdcx_mdcx:no_remote_conn_desc>> | no opposite end specified for connection.
|
||||
| mdcx:start_rtp_failure | <<mdcx_mdcx:start_rtp_failure>> | failure to start RTP processing.
|
||||
| mdcx:conn_rejected | <<mdcx_mdcx:conn_rejected>> | connection rejected by policy.
|
||||
| mdcx:conn_deferred | <<mdcx_mdcx:conn_deferred>> | connection deferred by policy.
|
||||
|===
|
||||
// rate_ctr_group table crxc statistics
|
||||
.crcx - crxc statistics
|
||||
[options="header"]
|
||||
|===
|
||||
| Name | Reference | Description
|
||||
| crcx:success | <<crcx_crcx:success>> | CRCX command processed successfully.
|
||||
| crcx:bad_action | <<crcx_crcx:bad_action>> | bad action in CRCX command.
|
||||
| crcx:unhandled_param | <<crcx_crcx:unhandled_param>> | unhandled parameter in CRCX command.
|
||||
| crcx:missing_callid | <<crcx_crcx:missing_callid>> | missing CallId in CRCX command.
|
||||
| crcx:invalid_mode | <<crcx_crcx:invalid_mode>> | invalid connection mode in CRCX command.
|
||||
| crcx:limit_exceeded | <<crcx_crcx:limit_exceeded>> | limit of concurrent connections was reached.
|
||||
| crcx:unkown_callid | <<crcx_crcx:unkown_callid>> | unknown CallId in CRCX command.
|
||||
| crcx:alloc_conn_fail | <<crcx_crcx:alloc_conn_fail>> | connection allocation failure.
|
||||
| crcx:no_remote_conn_desc | <<crcx_crcx:no_remote_conn_desc>> | no opposite end specified for connection.
|
||||
| crcx:start_rtp_failure | <<crcx_crcx:start_rtp_failure>> | failure to start RTP processing.
|
||||
| crcx:conn_rejected | <<crcx_crcx:conn_rejected>> | connection rejected by policy.
|
||||
| crcx:no_osmux | <<crcx_crcx:no_osmux>> | no osmux offered by peer.
|
||||
| crcx:conn_opt | <<crcx_crcx:conn_opt>> | connection options invalid.
|
||||
| crcx:codec_nego | <<crcx_crcx:codec_nego>> | codec negotiation failure.
|
||||
| crcx:bind_port | <<crcx_crcx:bind_port>> | port bind failure.
|
||||
|===
|
||||
== Osmo Stat Items
|
||||
|
||||
// generating tables for osmo_stat_items
|
||||
== Osmo Counters
|
||||
|
||||
// generating tables for osmo_counters
|
||||
// there are no ungrouped osmo_counters
|
72
doc/manuals/chapters/mgcp_extensions.adoc
Normal file
72
doc/manuals/chapters/mgcp_extensions.adoc
Normal file
@@ -0,0 +1,72 @@
|
||||
== 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 header 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
|
||||
----
|
||||
|
||||
=== `X-Osmux`
|
||||
|
||||
See <<mgcp-extension-osmux>>
|
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::./common/chapters/osmux/osmux.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>
|
||||
|
17
doc/manuals/regen_doc.sh
Executable file
17
doc/manuals/regen_doc.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh -x
|
||||
|
||||
if [ -z "$DOCKER_PLAYGROUND" ]; then
|
||||
echo "You need to set DOCKER_PLAYGROUND"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SCRIPT=$(realpath "$0")
|
||||
MANUAL_DIR=$(dirname "$SCRIPT")
|
||||
|
||||
COMMIT=${COMMIT:-$(git log -1 --format=format:%H)}
|
||||
|
||||
cd "$DOCKER_PLAYGROUND/scripts" || exit 1
|
||||
|
||||
OSMO_MGW_BRANCH=$COMMIT ./regen_doc.sh osmo-mgw 4243 \
|
||||
"$MANUAL_DIR/chapters/counters_generated.adoc" \
|
||||
"$MANUAL_DIR/vty/mgw_vty_reference.xml"
|
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>
|
1677
doc/manuals/vty/mgw_vty_reference.xml
Normal file
1677
doc/manuals/vty/mgw_vty_reference.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,10 +4,16 @@ SUBDIRS = \
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
osmocom/mgcp_client/mgcp_client.h \
|
||||
osmocom/mgcp_client/mgcp_client_endpoint_fsm.h \
|
||||
osmocom/mgcp_client/mgcp_client_fsm.h \
|
||||
osmocom/mgcp_client/mgcp_common.h \
|
||||
osmocom/mgcp/mgcp.h \
|
||||
osmocom/mgcp/mgcp_common.h \
|
||||
osmocom/mgcp/mgcp_internal.h \
|
||||
osmocom/mgcp/osmux.h \
|
||||
$(NULL)
|
||||
|
||||
# This gets copied during make from osmocom/mgcp/mgcp_common.h. Therefore it is not included in the source tree and we
|
||||
# don't need to distribute it (OS#4084).
|
||||
nobase_nodist_include_HEADERS = \
|
||||
osmocom/mgcp_client/mgcp_common.h \
|
||||
$(NULL)
|
||||
|
@@ -81,10 +81,11 @@ typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst,
|
||||
struct mgcp_conn_rtp *conn_src);
|
||||
|
||||
struct mgcp_rtp_codec;
|
||||
|
||||
typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
|
||||
int *payload_type,
|
||||
const char**subtype_name,
|
||||
const char**fmtp_extra,
|
||||
const struct mgcp_rtp_codec **codec,
|
||||
const char **fmtp_extra,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
|
||||
/**
|
||||
@@ -152,9 +153,20 @@ enum {
|
||||
MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
|
||||
MGCP_MDCX_FAIL_START_RTP,
|
||||
MGCP_MDCX_FAIL_REJECTED_BY_POLICY,
|
||||
MGCP_MDCX_FAIL_DEFERRED_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;
|
||||
@@ -179,6 +191,7 @@ struct mgcp_trunk_config {
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* 0: don't, 1: once */
|
||||
int force_aligned_timing;
|
||||
bool rfc5993_hr_convert;
|
||||
|
||||
/* spec handling */
|
||||
int force_realloc;
|
||||
@@ -198,6 +211,8 @@ struct mgcp_trunk_config {
|
||||
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;
|
||||
};
|
||||
@@ -261,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 */
|
||||
|
@@ -2,6 +2,6 @@
|
||||
|
||||
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_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, struct mgcp_codec_param *param);
|
||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
|
||||
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
|
||||
|
@@ -50,6 +50,7 @@ enum mgcp_connection_mode {
|
||||
};
|
||||
|
||||
#define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:"
|
||||
#define MGCP_X_OSMO_OSMUX_HEADER "X-Osmux:"
|
||||
|
||||
/* Values should be bitwise-OR-able */
|
||||
enum mgcp_x_osmo_ign {
|
||||
@@ -57,6 +58,12 @@ enum mgcp_x_osmo_ign {
|
||||
MGCP_X_OSMO_IGN_CALLID = 1,
|
||||
};
|
||||
|
||||
/* Codec parameters (communicated via SDP/fmtp) */
|
||||
struct mgcp_codec_param {
|
||||
bool amr_octet_aligned_present;
|
||||
bool amr_octet_aligned;
|
||||
};
|
||||
|
||||
/* Ensure that the msg->l2h is NUL terminated. */
|
||||
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
||||
{
|
||||
|
@@ -99,4 +99,3 @@ struct mgcp_endpoint {
|
||||
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
|
||||
|
||||
void mgcp_endp_release(struct mgcp_endpoint *endp);
|
||||
|
||||
|
@@ -95,6 +95,9 @@ struct mgcp_rtp_codec {
|
||||
int payload_type;
|
||||
char *audio_name;
|
||||
char *subtype_name;
|
||||
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
};
|
||||
|
||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
|
||||
@@ -128,6 +131,7 @@ 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;
|
||||
|
||||
/* Each end has a separate socket for RTP and RTCP */
|
||||
struct osmo_fd rtp;
|
||||
@@ -184,9 +188,9 @@ struct mgcp_conn_rtp {
|
||||
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 */
|
||||
/* Is cid holding valid data? is it allocated from pool? */
|
||||
bool cid_allocated;
|
||||
/* Allocated Osmux circuit ID for this conn */
|
||||
uint8_t cid;
|
||||
/* handle to batch messages */
|
||||
struct osmux_in_handle *in;
|
||||
@@ -231,6 +235,9 @@ struct mgcp_conn {
|
||||
/*! 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;
|
||||
@@ -294,9 +301,8 @@ int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_src);
|
||||
|
||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
int *payload_type,
|
||||
const char**audio_name,
|
||||
const char**fmtp_extra,
|
||||
const struct mgcp_rtp_codec **codec,
|
||||
const char **fmtp_extra,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
|
||||
/* internal RTP Annex A counting */
|
||||
@@ -306,6 +312,11 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta
|
||||
|
||||
int mgcp_set_ip_tos(int fd, int tos);
|
||||
|
||||
/* Was conn configured to handle Osmux? */
|
||||
static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) {
|
||||
return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT;
|
||||
}
|
||||
|
||||
enum {
|
||||
MGCP_DEST_NET = 0,
|
||||
MGCP_DEST_BTS,
|
||||
@@ -328,3 +339,14 @@ 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);
|
||||
|
||||
#define LOGPENDP(endp, cat, level, fmt, args...) \
|
||||
LOGP(cat, level, "endpoint:0x%x " fmt, \
|
||||
endp ? ENDPOINT_NUMBER(endp) : -1, \
|
||||
## args)
|
||||
|
||||
#define LOGPCONN(conn, cat, level, fmt, args...) \
|
||||
LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
|
||||
(conn)->id, \
|
||||
## args)
|
||||
|
@@ -14,18 +14,20 @@ enum {
|
||||
int osmux_init(int role, struct mgcp_config *cfg);
|
||||
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||
struct in_addr *addr, uint16_t port);
|
||||
void osmux_disable_conn(struct mgcp_conn_rtp *conn);
|
||||
void osmux_allocate_cid(struct mgcp_conn_rtp *conn);
|
||||
void osmux_release_cid(struct mgcp_conn_rtp *conn);
|
||||
void conn_osmux_disable(struct mgcp_conn_rtp *conn);
|
||||
int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid);
|
||||
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn);
|
||||
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
|
||||
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
|
||||
int osmux_get_cid(void);
|
||||
void osmux_put_cid(uint8_t osmux_cid);
|
||||
int osmux_used_cid(void);
|
||||
|
||||
void osmux_cid_pool_get(uint8_t osmux_cid);
|
||||
int osmux_cid_pool_get_next(void);
|
||||
void osmux_cid_pool_put(uint8_t osmux_cid);
|
||||
bool osmux_cid_pool_allocated(uint8_t osmux_cid);
|
||||
int osmux_cid_pool_count_used(void);
|
||||
|
||||
enum osmux_state {
|
||||
OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */
|
||||
OSMUX_STATE_NEGOTIATING, /* Osmux was locally requested in MGCP CRCX */
|
||||
OSMUX_STATE_ACTIVATING, /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref osmux_enable_endpoint. */
|
||||
OSMUX_STATE_ENABLED, /* Osmux was initialized by \ref osmux_enable_endpoint and can process frames */
|
||||
};
|
||||
|
@@ -22,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;
|
||||
@@ -36,7 +37,7 @@ enum mgcp_codecs {
|
||||
CODEC_PCMA_8000_1 = 8,
|
||||
CODEC_G729_8000_1 = 18,
|
||||
CODEC_GSMEFR_8000_1 = 110,
|
||||
CODEC_GSMHR_8000_1 = 111,
|
||||
CODEC_GSMHR_8000_1 = 111,
|
||||
CODEC_AMR_8000_1 = 112,
|
||||
CODEC_AMRWB_16000_1 = 113,
|
||||
};
|
||||
@@ -46,6 +47,10 @@ enum mgcp_codecs {
|
||||
* this is an internal assumption that is made to avoid lookup tables.
|
||||
* The API-User should not rely on this coincidence! */
|
||||
|
||||
extern const struct value_string osmo_mgcpc_codec_names[];
|
||||
static inline const char *osmo_mgcpc_codec_name(enum mgcp_codecs val)
|
||||
{ return get_value_string(osmo_mgcpc_codec_names, val); }
|
||||
|
||||
/*! Structure to build a payload type map to allow the defiition custom payload
|
||||
* types. */
|
||||
struct ptmap {
|
||||
@@ -62,6 +67,8 @@ struct mgcp_response_head {
|
||||
char comment[MGCP_COMMENT_MAXLEN];
|
||||
char conn_id[MGCP_CONN_ID_MAXLEN];
|
||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
bool x_osmo_osmux_use;
|
||||
uint8_t x_osmo_osmux_cid;
|
||||
};
|
||||
|
||||
struct mgcp_response {
|
||||
@@ -73,7 +80,7 @@ struct mgcp_response {
|
||||
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_len;
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
unsigned int ptmap_len;
|
||||
};
|
||||
|
||||
enum mgcp_verb {
|
||||
@@ -90,6 +97,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_OSMUX_CID 0x4000
|
||||
#define MGCP_MSG_PRESENCE_X_OSMO_IGN 0x8000
|
||||
|
||||
struct mgcp_msg {
|
||||
@@ -108,6 +116,10 @@ struct mgcp_msg {
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
uint32_t x_osmo_ign;
|
||||
bool x_osmo_osmux_use;
|
||||
int x_osmo_osmux_cid; /* -1 is wildcard */
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
};
|
||||
|
||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
|
||||
@@ -123,8 +135,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. */
|
||||
@@ -137,20 +149,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);
|
||||
|
||||
@@ -161,7 +159,7 @@ static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
|
||||
}
|
||||
|
||||
enum mgcp_codecs map_str_to_codec(const char *str);
|
||||
unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
enum mgcp_codecs codec);
|
||||
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
unsigned int pt);
|
||||
|
48
include/osmocom/mgcp_client/mgcp_client_endpoint_fsm.h
Normal file
48
include/osmocom/mgcp_client/mgcp_client_endpoint_fsm.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* FSM to manage multiple connections of an MGW endpoint */
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client_fsm.h>
|
||||
|
||||
#define LOG_MGCPC_EP(ep, level, fmt, args...) do { \
|
||||
LOGPFSML(ep->fi, level, "%s " fmt, \
|
||||
osmo_mgcpc_ep_name(ep), ## args); \
|
||||
} while(0)
|
||||
|
||||
struct osmo_mgcpc_ep;
|
||||
struct osmo_mgcpc_ep_ci;
|
||||
struct osmo_tdef;
|
||||
|
||||
struct osmo_mgcpc_ep *osmo_mgcpc_ep_alloc(struct osmo_fsm_inst *parent, uint32_t parent_term_event,
|
||||
struct mgcp_client *mgcp_client,
|
||||
const struct osmo_tdef *T_defs,
|
||||
const char *fsm_id,
|
||||
const char *endpoint_str_fmt, ...);
|
||||
|
||||
struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_add(struct osmo_mgcpc_ep *ep, const char *label_fmt, ...);
|
||||
const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci);
|
||||
bool osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(const struct osmo_mgcpc_ep_ci *ci, struct sockaddr_storage *dest);
|
||||
bool osmo_mgcpc_ep_ci_get_crcx_info_to_osmux_cid(const struct osmo_mgcpc_ep_ci *ci, uint8_t* cid);
|
||||
|
||||
void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
|
||||
enum mgcp_verb verb, const struct mgcp_conn_peer *verb_info,
|
||||
struct osmo_fsm_inst *notify,
|
||||
uint32_t event_success, uint32_t event_failure,
|
||||
void *notify_data);
|
||||
|
||||
/*! Dispatch a DLCX for the given connection.
|
||||
* \param ci Connection identifier as obtained from osmo_mgcpc_ep_ci_add().
|
||||
*/
|
||||
static inline void osmo_mgcpc_ep_ci_dlcx(struct osmo_mgcpc_ep_ci *ci)
|
||||
{
|
||||
osmo_mgcpc_ep_ci_request(ci, MGCP_VERB_DLCX, NULL, NULL, 0, 0, NULL);
|
||||
}
|
||||
|
||||
void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep);
|
||||
|
||||
const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep);
|
||||
const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci);
|
||||
const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci);
|
||||
|
||||
extern const struct value_string osmo_mgcp_verb_names[];
|
||||
static inline const char *osmo_mgcp_verb_name(enum mgcp_verb val)
|
||||
{ return get_value_string(osmo_mgcp_verb_names, val); }
|
@@ -47,6 +47,20 @@ struct mgcp_conn_peer {
|
||||
* 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;
|
||||
|
||||
/*! send 'X-Osmux: %d' header (or "*" as wildcard). */
|
||||
bool x_osmo_osmux_use;
|
||||
/*! -1 means send wildcard. */
|
||||
int x_osmo_osmux_cid;
|
||||
|
||||
/*! If left MGCP_CONN_NONE, use MGCP_CONN_RECV_ONLY or MGCP_CONN_RECV_SEND, depending on whether an audio RTP
|
||||
* address is set. If != MGCP_CONN_NONE, force this conn mode. */
|
||||
enum mgcp_connection_mode conn_mode;
|
||||
|
||||
/*! If the codec requires additional format parameters (fmtp), those cann be set here, see also
|
||||
* mgcp_common.h */
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
};
|
||||
|
||||
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
|
||||
@@ -55,3 +69,5 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
|
||||
void mgcp_conn_delete(struct osmo_fsm_inst *fi);
|
||||
|
||||
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
|
||||
|
||||
const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info);
|
||||
|
@@ -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=6:0:0
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp-client.la \
|
||||
@@ -30,6 +30,7 @@ libosmo_mgcp_client_la_SOURCES = \
|
||||
mgcp_client.c \
|
||||
mgcp_client_vty.c \
|
||||
mgcp_client_fsm.c \
|
||||
mgcp_client_endpoint_fsm.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)
|
||||
|
@@ -35,9 +35,16 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifndef OSMUX_CID_MAX
|
||||
#define OSMUX_CID_MAX 255 /* FIXME: use OSMUX_CID_MAX from libosmo-netif? */
|
||||
#endif
|
||||
|
||||
/* Codec descripton for dynamic payload types (SDP) */
|
||||
static const struct value_string codec_table[] = {
|
||||
const struct value_string osmo_mgcpc_codec_names[] = {
|
||||
{ CODEC_PCMU_8000_1, "PCMU/8000/1" },
|
||||
{ CODEC_GSM_8000_1, "GSM/8000/1" },
|
||||
{ CODEC_PCMA_8000_1, "PCMA/8000/1" },
|
||||
@@ -81,19 +88,19 @@ enum mgcp_codecs map_str_to_codec(const char *str)
|
||||
|
||||
osmo_strlcpy(str_buf, extract_codec_name(str), sizeof(str_buf));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(codec_table); i++) {
|
||||
codec_name = extract_codec_name(codec_table[i].str);
|
||||
for (i = 0; i < ARRAY_SIZE(osmo_mgcpc_codec_names); i++) {
|
||||
codec_name = extract_codec_name(osmo_mgcpc_codec_names[i].str);
|
||||
if (!codec_name)
|
||||
continue;
|
||||
if (strcmp(codec_name, str_buf) == 0)
|
||||
return codec_table[i].value;
|
||||
return osmo_mgcpc_codec_names[i].value;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check the ptmap for illegal mappings */
|
||||
static int check_ptmap(struct ptmap *ptmap)
|
||||
static int check_ptmap(const struct ptmap *ptmap)
|
||||
{
|
||||
/* Check if there are mappings that leave the IANA assigned dynamic
|
||||
* payload type range. Under normal conditions such mappings should
|
||||
@@ -122,7 +129,7 @@ error:
|
||||
* \ptmap[in] ptmap_len length of the payload type map.
|
||||
* \ptmap[in] codec the codec for which the payload type should be looked up.
|
||||
* \returns assigned payload type */
|
||||
unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
enum mgcp_codecs codec)
|
||||
{
|
||||
unsigned int i;
|
||||
@@ -190,75 +197,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 +212,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);
|
||||
}
|
||||
|
||||
@@ -326,7 +267,8 @@ static bool mgcp_line_is_valid(const char *line)
|
||||
static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
|
||||
{
|
||||
char *pt_str;
|
||||
unsigned int pt;
|
||||
char *pt_end;
|
||||
unsigned long int pt;
|
||||
unsigned int count = 0;
|
||||
unsigned int i;
|
||||
|
||||
@@ -350,7 +292,14 @@ static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
|
||||
pt_str = strtok(NULL, " ");
|
||||
if (!pt_str)
|
||||
break;
|
||||
pt = atoi(pt_str);
|
||||
errno = 0;
|
||||
pt = strtoul(pt_str, &pt_end, 0);
|
||||
if ((errno == ERANGE && pt == ULONG_MAX) || (errno && !pt) ||
|
||||
pt_str == pt_end)
|
||||
goto response_parse_failure_pt;
|
||||
|
||||
if (pt >> 7) /* PT is 7 bit field, higher values not allowed */
|
||||
goto response_parse_failure_pt;
|
||||
|
||||
/* Do not allow duplicate payload types */
|
||||
for (i = 0; i < count; i++)
|
||||
@@ -389,8 +338,8 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
|
||||
unsigned int pt;
|
||||
char codec_resp[64];
|
||||
unsigned int codec;
|
||||
|
||||
|
||||
|
||||
|
||||
if (strstr(line, "ptime")) {
|
||||
if (sscanf(line, "a=ptime:%u", &r->ptime) != 1)
|
||||
goto response_parse_failure_ptime;
|
||||
@@ -419,17 +368,17 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
|
||||
} else
|
||||
goto response_parse_failure_rtpmap;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
|
||||
response_parse_failure_ptime:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse SDP parameter, invalid ptime (%s)\n", line);
|
||||
return -EINVAL;
|
||||
return -EINVAL;
|
||||
response_parse_failure_rtpmap:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse SDP parameter, invalid rtpmap (%s)\n", line);
|
||||
return -EINVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Parse a line like "c=IN IP4 10.11.12.13" */
|
||||
@@ -459,6 +408,37 @@ response_parse_failure:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! Extract OSMUX CID from an MGCP parameter line (string).
|
||||
* \param[in] line single parameter line from the MGCP message
|
||||
* \returns OSMUX CID, -1 wildcard, -2 on error
|
||||
* FIXME: This is a copy of function in mgcp_msg.c. Have some common.c file between both libs?
|
||||
*/
|
||||
static int mgcp_parse_osmux_cid(const char *line)
|
||||
{
|
||||
int osmux_cid;
|
||||
|
||||
|
||||
if (strcasecmp(line + 2, "Osmux: *") == 0) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sscanf(line + 2 + 7, "%u", &osmux_cid) != 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed parsing Osmux in MGCP msg line: %s\n",
|
||||
line);
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (osmux_cid > OSMUX_CID_MAX) { /* OSMUX_CID_MAX from libosmo-netif */
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
|
||||
osmux_cid, OSMUX_CID_MAX);
|
||||
return -2;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
|
||||
|
||||
return osmux_cid;
|
||||
}
|
||||
|
||||
/* A new section is marked by a double line break, check a few more
|
||||
* patterns as there may be variants */
|
||||
static char *mgcp_find_section_end(char *string)
|
||||
@@ -599,12 +579,12 @@ static int parse_head_params(struct mgcp_response *r)
|
||||
/* If there is an SDP body attached, prevent for_each_non_empty_line()
|
||||
* into running in there, we are not yet interested in the parameters
|
||||
* stored there. */
|
||||
data_end = mgcp_find_section_end(data);
|
||||
data_end = mgcp_find_section_end(data);
|
||||
if (data_end)
|
||||
*data_end = '\0';
|
||||
|
||||
for_each_non_empty_line(line, data_ptr) {
|
||||
switch (line[0]) {
|
||||
switch (toupper(line[0])) {
|
||||
case 'Z':
|
||||
rc = mgcp_parse_head_param(r->head.endpoint,
|
||||
sizeof(r->head.endpoint),
|
||||
@@ -633,6 +613,21 @@ static int parse_head_params(struct mgcp_response *r)
|
||||
if (rc)
|
||||
goto exit;
|
||||
break;
|
||||
case 'X':
|
||||
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
|
||||
rc = mgcp_parse_osmux_cid(line);
|
||||
if (rc < 0) {
|
||||
/* -1: we don't want wildcards in response. -2: error */
|
||||
rc = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
r->head.x_osmo_osmux_use = true;
|
||||
r->head.x_osmo_osmux_cid = (uint8_t) rc;
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
/* Ignore unknown X-headers */
|
||||
break;
|
||||
default:
|
||||
/* skip unhandled parameters */
|
||||
break;
|
||||
@@ -717,11 +712,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;
|
||||
}
|
||||
@@ -735,26 +732,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;
|
||||
}
|
||||
|
||||
@@ -780,9 +766,15 @@ 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;
|
||||
}
|
||||
@@ -814,12 +806,12 @@ static int init_socket(struct mgcp_client *mgcp)
|
||||
|
||||
/* Choose a new port number to try next */
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"MGCPGW faild to bind to port %u, retrying with port %u -- check configuration!\n",
|
||||
mgcp->actual.local_port, mgcp->actual.local_port + 1);
|
||||
"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 faild to find a port to bind on %i times.\n", i);
|
||||
LOGP(DLMGCP, LOGL_FATAL, "MGCPGW failed to find a port to bind on %i times.\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -857,9 +849,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:
|
||||
@@ -892,6 +882,36 @@ 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";
|
||||
}
|
||||
|
||||
static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, char *name)
|
||||
{
|
||||
static char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
int rc;
|
||||
|
||||
rc = snprintf(endpoint, sizeof(endpoint), "%s@%s", name, mgcp_client_endpoint_domain(mgcp));
|
||||
if (rc > sizeof(endpoint) - 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '%s@%s'\n",
|
||||
sizeof(endpoint) - 1, name, mgcp_client_endpoint_domain(mgcp));
|
||||
return NULL;
|
||||
}
|
||||
if (rc < 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP endpoint name\n");
|
||||
return NULL;
|
||||
}
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
|
||||
{
|
||||
return _mgcp_client_name_append_domain(mgcp, "rtpbridge/*");
|
||||
}
|
||||
|
||||
struct mgcp_response_pending * mgcp_client_pending_add(
|
||||
struct mgcp_client *mgcp,
|
||||
mgcp_trans_id_t trans_id,
|
||||
@@ -949,7 +969,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;
|
||||
|
||||
@@ -973,10 +993,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
|
||||
@@ -988,54 +1008,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 */
|
||||
@@ -1044,52 +1016,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)
|
||||
@@ -1117,8 +1043,8 @@ static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
|
||||
rc += msgb_printf(msg, " a:");
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
pt = mgcp_msg->codecs[i];
|
||||
codec = get_value_string_or_null(codec_table, pt);
|
||||
|
||||
codec = get_value_string_or_null(osmo_mgcpc_codec_names, pt);
|
||||
|
||||
/* Note: Use codec descriptors from enum mgcp_codecs
|
||||
* in mgcp_client only! */
|
||||
OSMO_ASSERT(codec);
|
||||
@@ -1190,23 +1116,37 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
|
||||
}
|
||||
rc += msgb_printf(msg, "\r\n");
|
||||
|
||||
/* Add optional codec parameters (fmtp) */
|
||||
if (mgcp_msg->param_present) {
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
/* The following is only applicable for AMR */
|
||||
if (mgcp_msg->codecs[i] != CODEC_AMR_8000_1 && mgcp_msg->codecs[i] != CODEC_AMRWB_16000_1)
|
||||
continue;
|
||||
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
||||
if (mgcp_msg->param.amr_octet_aligned_present && mgcp_msg->param.amr_octet_aligned)
|
||||
rc += msgb_printf(msg, "a=fmtp:%u octet-align=1\r\n", pt);
|
||||
else if (mgcp_msg->param.amr_octet_aligned_present && !mgcp_msg->param.amr_octet_aligned)
|
||||
rc += msgb_printf(msg, "a=fmtp:%u octet-align=0\r\n", pt);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
||||
|
||||
|
||||
/* Note: Only dynamic payload type from the range 96-127
|
||||
* require to be explained further via rtpmap. All others
|
||||
* are implcitly definedby the number in m=audio */
|
||||
if (pt >= 96 && pt <= 127) {
|
||||
codec = get_value_string_or_null(codec_table, mgcp_msg->codecs[i]);
|
||||
codec = get_value_string_or_null(osmo_mgcpc_codec_names, mgcp_msg->codecs[i]);
|
||||
|
||||
/* Note: Use codec descriptors from enum mgcp_codecs
|
||||
* in mgcp_client only! */
|
||||
OSMO_ASSERT(codec);
|
||||
|
||||
|
||||
rc += msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (mgcp_msg->ptime)
|
||||
rc += msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime);
|
||||
|
||||
@@ -1225,6 +1165,7 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
int rc = 0;
|
||||
int rc_sdp;
|
||||
bool use_sdp = false;
|
||||
char buf[32];
|
||||
|
||||
msg->l2h = msg->data;
|
||||
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
|
||||
@@ -1328,6 +1269,22 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
msgb_printf(msg, MGCP_X_OSMO_IGN_HEADER "%s\r\n",
|
||||
mgcp_msg->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID ? " C": "");
|
||||
|
||||
/* Add X-Osmo-Osmux */
|
||||
if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID)) {
|
||||
if (mgcp_msg->x_osmo_osmux_cid < -1 || mgcp_msg->x_osmo_osmux_cid > OSMUX_CID_MAX) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Wrong Osmux CID %d, can not generate MGCP message\n",
|
||||
mgcp_msg->x_osmo_osmux_cid);
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), " %d", mgcp_msg->x_osmo_osmux_cid);
|
||||
rc +=
|
||||
msgb_printf(msg, MGCP_X_OSMO_OSMUX_HEADER "%s\r\n",
|
||||
mgcp_msg->x_osmo_osmux_cid == -1 ? " *": buf);
|
||||
}
|
||||
|
||||
|
||||
/* Add session description protocol (SDP) */
|
||||
if (use_sdp
|
||||
&& (mgcp_msg->verb == MGCP_VERB_CRCX
|
||||
|
886
src/libosmo-mgcp-client/mgcp_client_endpoint_fsm.c
Normal file
886
src/libosmo-mgcp-client/mgcp_client_endpoint_fsm.c
Normal file
@@ -0,0 +1,886 @@
|
||||
/* FSM to manage multiple connections of an MGW endpoint
|
||||
*
|
||||
* (C) 2018-2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Neels Hofmeyr <neels@hofmeyr.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/byteswap.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
|
||||
|
||||
#define LOG_CI(ci, level, fmt, args...) do { \
|
||||
if (!ci || !ci->ep) \
|
||||
LOGP(DLGLOBAL, level, "(unknown MGW endpoint) " fmt, ## args); \
|
||||
else \
|
||||
LOG_MGCPC_EP(ci->ep, level, "CI[%d] %s%s%s: " fmt, \
|
||||
(int)(ci - ci->ep->ci), \
|
||||
ci->label ? : "-", \
|
||||
ci->mgcp_ci_str[0] ? " CI=" : "", \
|
||||
ci->mgcp_ci_str[0] ? ci->mgcp_ci_str : "", \
|
||||
## args); \
|
||||
} while(0)
|
||||
|
||||
#define LOG_CI_VERB(ci, level, fmt, args...) do { \
|
||||
if (ci->verb_info.addr[0]) \
|
||||
LOG_CI(ci, level, "%s %s:%u: " fmt, \
|
||||
osmo_mgcp_verb_name(ci->verb), ci->verb_info.addr, ci->verb_info.port, \
|
||||
## args); \
|
||||
else \
|
||||
LOG_CI(ci, level, "%s: " fmt, \
|
||||
osmo_mgcp_verb_name(ci->verb), \
|
||||
## args); \
|
||||
} while(0)
|
||||
|
||||
enum osmo_mgcpc_ep_fsm_state {
|
||||
OSMO_MGCPC_EP_ST_UNUSED = 0,
|
||||
OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE,
|
||||
OSMO_MGCPC_EP_ST_IN_USE,
|
||||
};
|
||||
|
||||
enum osmo_mgcpc_ep_fsm_event {
|
||||
_OSMO_MGCPC_EP_EV_LAST = 0,
|
||||
/* and MGW response events are allocated dynamically */
|
||||
};
|
||||
|
||||
#define FIRST_CI_EVENT (_OSMO_MGCPC_EP_EV_LAST + (_OSMO_MGCPC_EP_EV_LAST & 1)) /* rounded up to even nr */
|
||||
#define USABLE_CI ((32 - FIRST_CI_EVENT)/2)
|
||||
#define EV_TO_CI_IDX(event) ((event - FIRST_CI_EVENT) / 2)
|
||||
|
||||
#define CI_EV_SUCCESS(ci) (FIRST_CI_EVENT + (((ci) - ci->ep->ci) * 2))
|
||||
#define CI_EV_FAILURE(ci) (CI_EV_SUCCESS(ci) + 1)
|
||||
|
||||
static struct osmo_fsm osmo_mgcpc_ep_fsm;
|
||||
|
||||
/*! One connection on an endpoint, corresponding to a connection identifier (CI) as returned by the MGW.
|
||||
* An endpoint has a fixed number of slots of these, which may or may not be in use.
|
||||
*/
|
||||
struct osmo_mgcpc_ep_ci {
|
||||
struct osmo_mgcpc_ep *ep;
|
||||
|
||||
bool occupied;
|
||||
char label[64];
|
||||
struct osmo_fsm_inst *mgcp_client_fi;
|
||||
|
||||
bool pending;
|
||||
bool sent;
|
||||
enum mgcp_verb verb;
|
||||
struct mgcp_conn_peer verb_info;
|
||||
struct osmo_fsm_inst *notify;
|
||||
uint32_t notify_success;
|
||||
uint32_t notify_failure;
|
||||
void *notify_data;
|
||||
|
||||
bool got_port_info;
|
||||
struct mgcp_conn_peer rtp_info;
|
||||
char mgcp_ci_str[MGCP_CONN_ID_LENGTH];
|
||||
};
|
||||
|
||||
/*! An MGW endpoint with N connections, like "rtpbridge/23@mgw". */
|
||||
struct osmo_mgcpc_ep {
|
||||
/*! MGCP client connection to the MGW. */
|
||||
struct mgcp_client *mgcp_client;
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
/*! Endpoint string; at first this might be a wildcard, and upon the first CRCX OK response, this will reflect
|
||||
* the endpoint name returned by the MGW. */
|
||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
|
||||
/*! Timeout definitions used for this endpoint, see osmo_mgcpc_ep_fsm_timeouts. */
|
||||
const struct osmo_tdef *T_defs;
|
||||
|
||||
/*! True as soon as the first CRCX OK is received. The endpoint name may be determined by the first CRCX
|
||||
* response, so to dispatch any other messages, the FSM instance *must* wait for the first CRCX OK to arrive
|
||||
* first. Once the endpoint name is pinpointed, any amount of operations may be dispatched concurrently. */
|
||||
bool first_crcx_complete;
|
||||
|
||||
/*! Endpoint connection slots. Note that each connection has its own set of FSM event numbers to signal success
|
||||
* and failure, depending on its index within this array. See CI_EV_SUCCESS and CI_EV_FAILURE. */
|
||||
struct osmo_mgcpc_ep_ci ci[USABLE_CI];
|
||||
};
|
||||
|
||||
const struct value_string osmo_mgcp_verb_names[] = {
|
||||
{ MGCP_VERB_CRCX, "CRCX" },
|
||||
{ MGCP_VERB_MDCX, "MDCX" },
|
||||
{ MGCP_VERB_DLCX, "DLCX" },
|
||||
{ MGCP_VERB_AUEP, "AUEP" },
|
||||
{ MGCP_VERB_RSIP, "RSIP" },
|
||||
{}
|
||||
};
|
||||
|
||||
static void osmo_mgcpc_ep_count(struct osmo_mgcpc_ep *ep, int *occupied, int *pending_not_sent,
|
||||
int *waiting_for_response);
|
||||
|
||||
static struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_check_ci(struct osmo_mgcpc_ep_ci *ci)
|
||||
{
|
||||
if (!ci)
|
||||
return NULL;
|
||||
if (!ci->ep)
|
||||
return NULL;
|
||||
if (ci < ci->ep->ci || ci >= &ci->ep->ci[USABLE_CI])
|
||||
return NULL;
|
||||
return ci;
|
||||
}
|
||||
|
||||
static struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_for_event(struct osmo_mgcpc_ep *ep, uint32_t event)
|
||||
{
|
||||
int idx;
|
||||
if (event < FIRST_CI_EVENT)
|
||||
return NULL;
|
||||
idx = EV_TO_CI_IDX(event);
|
||||
if (idx >= sizeof(ep->ci))
|
||||
return NULL;
|
||||
return osmo_mgcpc_ep_check_ci(&ep->ci[idx]);
|
||||
}
|
||||
|
||||
const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep)
|
||||
{
|
||||
if (!ep)
|
||||
return "NULL";
|
||||
if (ep->endpoint[0])
|
||||
return ep->endpoint;
|
||||
return osmo_fsm_inst_name(ep->fi);
|
||||
}
|
||||
|
||||
const char *mgcp_conn_peer_name(const struct mgcp_conn_peer *info)
|
||||
{
|
||||
/* I'd be fine with a smaller buffer and accept truncation, but gcc possibly refuses to build if
|
||||
* this buffer is too small. */
|
||||
static char buf[1024];
|
||||
|
||||
if (!info)
|
||||
return "NULL";
|
||||
|
||||
if (info->endpoint[0]
|
||||
&& info->addr[0])
|
||||
snprintf(buf, sizeof(buf), "%s:%s:%u",
|
||||
info->endpoint, info->addr, info->port);
|
||||
else if (info->endpoint[0])
|
||||
snprintf(buf, sizeof(buf), "%s", info->endpoint);
|
||||
else if (info->addr[0])
|
||||
snprintf(buf, sizeof(buf), "%s:%u", info->addr, info->port);
|
||||
else
|
||||
return "empty";
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci)
|
||||
{
|
||||
const struct mgcp_conn_peer *rtp_info;
|
||||
|
||||
if (!ci)
|
||||
return "NULL";
|
||||
|
||||
rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci);
|
||||
|
||||
if (rtp_info)
|
||||
return mgcp_conn_peer_name(rtp_info);
|
||||
return osmo_mgcpc_ep_name(ci->ep);
|
||||
}
|
||||
|
||||
const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci)
|
||||
{
|
||||
if (!ci || !ci->mgcp_ci_str[0])
|
||||
return NULL;
|
||||
return ci->mgcp_ci_str;
|
||||
}
|
||||
|
||||
static struct value_string osmo_mgcpc_ep_fsm_event_names[33] = {};
|
||||
|
||||
static char osmo_mgcpc_ep_fsm_event_name_bufs[32][32] = {};
|
||||
|
||||
static void fill_event_names()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < (ARRAY_SIZE(osmo_mgcpc_ep_fsm_event_names) - 1); i++) {
|
||||
if (i < _OSMO_MGCPC_EP_EV_LAST)
|
||||
continue;
|
||||
if (i < FIRST_CI_EVENT || EV_TO_CI_IDX(i) > USABLE_CI) {
|
||||
osmo_mgcpc_ep_fsm_event_names[i] = (struct value_string){i, "Unused"};
|
||||
continue;
|
||||
}
|
||||
snprintf(osmo_mgcpc_ep_fsm_event_name_bufs[i], sizeof(osmo_mgcpc_ep_fsm_event_name_bufs[i]),
|
||||
"MGW Response for CI #%d", EV_TO_CI_IDX(i));
|
||||
osmo_mgcpc_ep_fsm_event_names[i] = (struct value_string){i, osmo_mgcpc_ep_fsm_event_name_bufs[i]};
|
||||
}
|
||||
}
|
||||
|
||||
/* T_defs is used to obtain an (Osmocom specific) T2427001: timeout for an MGCP response (note, 2427 corresponds to the
|
||||
* default MGCP port in osmo-mgw). */
|
||||
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init()
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&osmo_mgcpc_ep_fsm) == 0);
|
||||
fill_event_names();
|
||||
}
|
||||
|
||||
struct osmo_mgcpc_ep *osmo_mgcpc_ep_fi_mgwep(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
OSMO_ASSERT(fi);
|
||||
OSMO_ASSERT(fi->fsm == &osmo_mgcpc_ep_fsm);
|
||||
OSMO_ASSERT(fi->priv);
|
||||
return fi->priv;
|
||||
}
|
||||
|
||||
/*! Allocate an osmo_mgcpc_ep FSM.
|
||||
* MGCP messages to set up the endpoint will be sent on the given mgcp_client, as soon as the first
|
||||
* osmo_mgcpc_ep_ci_request() is invoked.
|
||||
*
|
||||
* A typical sequence of events would be:
|
||||
*
|
||||
* ep = osmo_mgcpc_ep_alloc(..., mgcp_client_rtpbridge_wildcard(client));
|
||||
* ci_to_ran = osmo_mgcpc_ep_ci_add(ep);
|
||||
* osmo_mgcpc_ep_ci_request(ci_to_ran, MGCP_VERB_CRCX, verb_info,
|
||||
* my_call_fsm, MY_EVENT_MGCP_OK, MY_EVENT_MGCP_FAIL);
|
||||
* ci_to_cn = osmo_mgcpc_ep_ci_add(ep);
|
||||
* osmo_mgcpc_ep_ci_request(ci_to_cn, MGCP_VERB_CRCX, verb_info,
|
||||
* my_call_fsm, MY_EVENT_MGCP_OK, MY_EVENT_MGCP_FAIL);
|
||||
* ...
|
||||
* osmo_mgcpc_ep_ci_request(ci_to_ran, MGCP_VERB_MDCX, ...);
|
||||
* ...
|
||||
* osmo_mgcpc_ep_clear(ep);
|
||||
* ep = NULL;
|
||||
*
|
||||
* \param parent Parent FSM.
|
||||
* \param parent_term_event Event to dispatch to the parent on termination of this FSM instance.
|
||||
* \param mgcp_client Connection to the MGW.
|
||||
* \param T_defs Timeout definitions to be used for FSM states, see osmo_mgcpc_ep_fsm_timeouts.
|
||||
* \param fsm_id FSM instance ID.
|
||||
* \param endpoint_str_fmt The endpoint string format to send to the MGW upon the first CRCX.
|
||||
* See mgcp_client_rtpbridge_wildcard() for "rtpbridge" endpoints.
|
||||
*/
|
||||
struct osmo_mgcpc_ep *osmo_mgcpc_ep_alloc(struct osmo_fsm_inst *parent, uint32_t parent_term_event,
|
||||
struct mgcp_client *mgcp_client,
|
||||
const struct osmo_tdef *T_defs,
|
||||
const char *fsm_id,
|
||||
const char *endpoint_str_fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct osmo_mgcpc_ep *ep;
|
||||
int rc;
|
||||
|
||||
if (!mgcp_client)
|
||||
return NULL;
|
||||
|
||||
fi = osmo_fsm_inst_alloc_child(&osmo_mgcpc_ep_fsm, parent, parent_term_event);
|
||||
OSMO_ASSERT(fi);
|
||||
|
||||
osmo_fsm_inst_update_id(fi, fsm_id);
|
||||
|
||||
ep = talloc_zero(fi, struct osmo_mgcpc_ep);
|
||||
OSMO_ASSERT(ep);
|
||||
|
||||
*ep = (struct osmo_mgcpc_ep){
|
||||
.mgcp_client = mgcp_client,
|
||||
.fi = fi,
|
||||
.T_defs = T_defs,
|
||||
};
|
||||
fi->priv = ep;
|
||||
|
||||
va_start(ap, endpoint_str_fmt);
|
||||
rc = vsnprintf(ep->endpoint, sizeof(ep->endpoint), endpoint_str_fmt ? : "", ap);
|
||||
va_end(ap);
|
||||
|
||||
if (rc <= 0 || rc >= sizeof(ep->endpoint)) {
|
||||
LOG_MGCPC_EP(ep, LOGL_ERROR, "Endpoint name too long or too short: %s\n",
|
||||
ep->endpoint);
|
||||
osmo_fsm_inst_term(ep->fi, OSMO_FSM_TERM_ERROR, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ep;
|
||||
}
|
||||
|
||||
/*! Add a connection to an endpoint.
|
||||
* Allocate a connection identifier slot in the osmo_mgcpc_ep instance, do not yet dispatch a CRCX.
|
||||
* The CRCX is dispatched only upon the first osmo_mgcpc_ep_ci_request().
|
||||
* \param ep Parent endpoint instance.
|
||||
* \param label_fmt Label for logging.
|
||||
*/
|
||||
struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_add(struct osmo_mgcpc_ep *ep,
|
||||
const char *label_fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i;
|
||||
struct osmo_mgcpc_ep_ci *ci;
|
||||
|
||||
for (i = 0; i < USABLE_CI; i++) {
|
||||
ci = &ep->ci[i];
|
||||
|
||||
if (ci->occupied || ci->mgcp_client_fi)
|
||||
continue;
|
||||
|
||||
*ci = (struct osmo_mgcpc_ep_ci){
|
||||
.ep = ep,
|
||||
.occupied = true,
|
||||
};
|
||||
if (label_fmt) {
|
||||
va_start(ap, label_fmt);
|
||||
vsnprintf(ci->label, sizeof(ci->label), label_fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
return ci;
|
||||
}
|
||||
|
||||
LOG_MGCPC_EP(ep, LOGL_ERROR,
|
||||
"Cannot allocate another endpoint, all "
|
||||
OSMO_STRINGIFY_VAL(USABLE_CI) " are in use\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool osmo_mgcpc_ep_fsm_check_state_chg_after_response(struct osmo_fsm_inst *fi);
|
||||
|
||||
static void on_failure(struct osmo_mgcpc_ep_ci *ci)
|
||||
{
|
||||
struct osmo_fsm_inst *notify = ci->notify;
|
||||
uint32_t notify_failure = ci->notify_failure;
|
||||
void *notify_data = ci->notify_data;
|
||||
|
||||
if (!ci->occupied)
|
||||
return;
|
||||
|
||||
*ci = (struct osmo_mgcpc_ep_ci){
|
||||
.ep = ci->ep,
|
||||
};
|
||||
|
||||
/* If this check has terminated the FSM instance, don't fire any more events to prevent use-after-free problems.
|
||||
* The endpoint FSM does dispatch a term event to its parent, and everything should be cleaned like that. */
|
||||
if (!osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi))
|
||||
return;
|
||||
|
||||
if (notify)
|
||||
osmo_fsm_inst_dispatch(notify, notify_failure, notify_data);
|
||||
}
|
||||
|
||||
static int update_endpoint_name(struct osmo_mgcpc_ep_ci *ci, const char *new_endpoint_name)
|
||||
{
|
||||
struct osmo_mgcpc_ep *ep = ci->ep;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
if (!strcmp(ep->endpoint, new_endpoint_name)) {
|
||||
/* Same endpoint name, nothing to do. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The endpoint name should only change on the very first CRCX response. */
|
||||
if (ep->first_crcx_complete) {
|
||||
LOG_CI(ci, LOGL_ERROR, "Reponse returned mismatching endpoint name."
|
||||
" This is endpoint %s, instead received %s\n",
|
||||
ep->endpoint, new_endpoint_name);
|
||||
on_failure(ci);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* This is the first CRCX response, update endpoint name. */
|
||||
rc = OSMO_STRLCPY_ARRAY(ep->endpoint, new_endpoint_name);
|
||||
if (rc <= 0 || rc >= sizeof(ep->endpoint)) {
|
||||
LOG_CI(ci, LOGL_ERROR, "Unable to copy endpoint name %s\n", osmo_quote_str(new_endpoint_name, -1));
|
||||
osmo_mgcpc_ep_ci_dlcx(ci);
|
||||
on_failure(ci);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/* Make sure already pending requests use this updated endpoint name. */
|
||||
for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
|
||||
struct osmo_mgcpc_ep_ci *other_ci = &ep->ci[i];
|
||||
if (!other_ci->occupied)
|
||||
continue;
|
||||
if (!other_ci->pending)
|
||||
continue;
|
||||
if (other_ci->sent)
|
||||
continue;
|
||||
OSMO_STRLCPY_ARRAY(other_ci->verb_info.endpoint, ep->endpoint);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data)
|
||||
{
|
||||
struct mgcp_conn_peer *rtp_info;
|
||||
|
||||
if (!ci->occupied)
|
||||
return;
|
||||
|
||||
ci->pending = false;
|
||||
|
||||
switch (ci->verb) {
|
||||
case MGCP_VERB_CRCX:
|
||||
/* If we sent a wildcarded endpoint name on CRCX, we need to store the resulting endpoint
|
||||
* name here. Also, we receive the MGW's RTP port information. */
|
||||
rtp_info = data;
|
||||
OSMO_ASSERT(rtp_info);
|
||||
ci->got_port_info = true;
|
||||
ci->rtp_info = *rtp_info;
|
||||
osmo_strlcpy(ci->mgcp_ci_str, mgcp_conn_get_ci(ci->mgcp_client_fi),
|
||||
sizeof(ci->mgcp_ci_str));
|
||||
if (rtp_info->endpoint[0]) {
|
||||
/* On errors, this instance might already be deallocated. Make sure to not access anything after
|
||||
* error. */
|
||||
if (update_endpoint_name(ci, rtp_info->endpoint))
|
||||
return;
|
||||
}
|
||||
ci->ep->first_crcx_complete = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_CI(ci, LOGL_DEBUG, "received successful response to %s: RTP=%s%s\n",
|
||||
osmo_mgcp_verb_name(ci->verb),
|
||||
mgcp_conn_peer_name(ci->got_port_info? &ci->rtp_info : NULL),
|
||||
ci->notify ? "" : " (not sending a notification)");
|
||||
|
||||
if (ci->notify)
|
||||
osmo_fsm_inst_dispatch(ci->notify, ci->notify_success, ci->notify_data);
|
||||
|
||||
osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi);
|
||||
}
|
||||
|
||||
/*! Return the MGW's RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */
|
||||
const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci)
|
||||
{
|
||||
ci = osmo_mgcpc_ep_check_ci((struct osmo_mgcpc_ep_ci*)ci);
|
||||
if (!ci)
|
||||
return NULL;
|
||||
if (!ci->got_port_info)
|
||||
return NULL;
|
||||
return &ci->rtp_info;
|
||||
}
|
||||
|
||||
/*! Return the MGW's RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */
|
||||
bool osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(const struct osmo_mgcpc_ep_ci *ci, struct sockaddr_storage *dest)
|
||||
{
|
||||
const struct mgcp_conn_peer *rtp_info;
|
||||
struct sockaddr_in *sin;
|
||||
|
||||
rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci);
|
||||
if (!rtp_info)
|
||||
return false;
|
||||
|
||||
sin = (struct sockaddr_in *)dest;
|
||||
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_addr.s_addr = inet_addr(rtp_info->addr);
|
||||
sin->sin_port = osmo_ntohs(rtp_info->port);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool osmo_mgcpc_ep_ci_get_crcx_info_to_osmux_cid(const struct osmo_mgcpc_ep_ci *ci, uint8_t* cid)
|
||||
{
|
||||
const struct mgcp_conn_peer *rtp_info;
|
||||
|
||||
rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci);
|
||||
if (!rtp_info)
|
||||
return false;
|
||||
|
||||
if (!rtp_info->x_osmo_osmux_use)
|
||||
return false;
|
||||
|
||||
*cid = rtp_info->x_osmo_osmux_cid;
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct osmo_tdef_state_timeout osmo_mgcpc_ep_fsm_timeouts[32] = {
|
||||
[OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE] = { .T=2427001 },
|
||||
};
|
||||
|
||||
/* Transition to a state, using the T timer defined in assignment_fsm_timeouts.
|
||||
* The actual timeout value is in turn obtained from osmo_mgcpc_ep.T_defs.
|
||||
* Assumes local variable fi exists. */
|
||||
#define osmo_mgcpc_ep_fsm_state_chg(state) \
|
||||
osmo_tdef_fsm_inst_state_chg(fi, state, osmo_mgcpc_ep_fsm_timeouts, \
|
||||
((struct osmo_mgcpc_ep*)fi->priv)->T_defs, 5)
|
||||
|
||||
/*! Dispatch an actual CRCX/MDCX/DLCX message for this connection.
|
||||
* \param ci Connection identifier as obtained from osmo_mgcpc_ep_ci_add().
|
||||
* \param verb MGCP operation to dispatch.
|
||||
* \param verb_info Parameters for the MGCP operation.
|
||||
* \param notify Peer FSM instance to notify of completed/failed operation.
|
||||
* \param event_success Which event to dispatch to 'notify' upon OK response.
|
||||
* \param event_failure Which event to dispatch to 'notify' upon failure response.
|
||||
* \param notify_data Data pointer to pass to the event dispatch for both success and failure.
|
||||
*/
|
||||
void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
|
||||
enum mgcp_verb verb, const struct mgcp_conn_peer *verb_info,
|
||||
struct osmo_fsm_inst *notify,
|
||||
uint32_t event_success, uint32_t event_failure,
|
||||
void *notify_data)
|
||||
{
|
||||
struct osmo_mgcpc_ep *ep;
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct osmo_mgcpc_ep_ci cleared_ci;
|
||||
ci = osmo_mgcpc_ep_check_ci(ci);
|
||||
|
||||
if (!ci) {
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "Invalid MGW endpoint request: no ci\n");
|
||||
goto dispatch_error;
|
||||
}
|
||||
if (!verb_info && verb != MGCP_VERB_DLCX) {
|
||||
LOG_CI(ci, LOGL_ERROR, "Invalid MGW endpoint request: missing verb details for %s\n",
|
||||
osmo_mgcp_verb_name(verb));
|
||||
goto dispatch_error;
|
||||
}
|
||||
if ((verb < 0) || (verb > MGCP_VERB_RSIP)) {
|
||||
LOG_CI(ci, LOGL_ERROR, "Invalid MGW endpoint request: unknown verb: %s\n",
|
||||
osmo_mgcp_verb_name(verb));
|
||||
goto dispatch_error;
|
||||
}
|
||||
|
||||
ep = ci->ep;
|
||||
fi = ep->fi;
|
||||
|
||||
/* Clear volatile state by explicitly keeping those that should remain. Because we can't assign
|
||||
* the char[] directly, dance through cleared_ci and copy back. */
|
||||
cleared_ci = (struct osmo_mgcpc_ep_ci){
|
||||
.ep = ep,
|
||||
.mgcp_client_fi = ci->mgcp_client_fi,
|
||||
.got_port_info = ci->got_port_info,
|
||||
.rtp_info = ci->rtp_info,
|
||||
|
||||
.occupied = true,
|
||||
/* .pending = true follows below */
|
||||
.verb = verb,
|
||||
.notify = notify,
|
||||
.notify_success = event_success,
|
||||
.notify_failure = event_failure,
|
||||
.notify_data = notify_data,
|
||||
};
|
||||
osmo_strlcpy(cleared_ci.label, ci->label, sizeof(cleared_ci.label));
|
||||
osmo_strlcpy(cleared_ci.mgcp_ci_str, ci->mgcp_ci_str, sizeof(cleared_ci.mgcp_ci_str));
|
||||
*ci = cleared_ci;
|
||||
|
||||
LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify));
|
||||
|
||||
if (verb_info)
|
||||
ci->verb_info = *verb_info;
|
||||
|
||||
if (ep->endpoint[0]) {
|
||||
if (ci->verb_info.endpoint[0] && strcmp(ci->verb_info.endpoint, ep->endpoint))
|
||||
LOG_CI(ci, LOGL_ERROR,
|
||||
"Warning: Requested %s on endpoint %s, but this CI is on endpoint %s."
|
||||
" Using the proper endpoint instead.\n",
|
||||
osmo_mgcp_verb_name(verb), ci->verb_info.endpoint, ep->endpoint);
|
||||
osmo_strlcpy(ci->verb_info.endpoint, ep->endpoint, sizeof(ci->verb_info.endpoint));
|
||||
}
|
||||
|
||||
switch (ci->verb) {
|
||||
case MGCP_VERB_CRCX:
|
||||
if (ci->mgcp_client_fi) {
|
||||
LOG_CI(ci, LOGL_ERROR, "CRCX can be called only once per MGW endpoint CI\n");
|
||||
on_failure(ci);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case MGCP_VERB_MDCX:
|
||||
if (!ci->mgcp_client_fi) {
|
||||
LOG_CI_VERB(ci, LOGL_ERROR, "The first verb on an unused MGW endpoint CI must be CRCX, not %s\n",
|
||||
osmo_mgcp_verb_name(ci->verb));
|
||||
on_failure(ci);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case MGCP_VERB_DLCX:
|
||||
if (!ci->mgcp_client_fi) {
|
||||
LOG_CI_VERB(ci, LOGL_DEBUG, "Ignoring DLCX on unused MGW endpoint CI\n");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_CI(ci, LOGL_ERROR, "This verb is not supported: %s\n", osmo_mgcp_verb_name(ci->verb));
|
||||
on_failure(ci);
|
||||
return;
|
||||
}
|
||||
|
||||
ci->pending = true;
|
||||
|
||||
LOG_CI_VERB(ci, LOGL_DEBUG, "Scheduling\n");
|
||||
|
||||
if (ep->fi->state != OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE)
|
||||
osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE);
|
||||
|
||||
return;
|
||||
dispatch_error:
|
||||
if (notify)
|
||||
osmo_fsm_inst_dispatch(notify, event_failure, notify_data);
|
||||
}
|
||||
|
||||
static int send_verb(struct osmo_mgcpc_ep_ci *ci)
|
||||
{
|
||||
int rc;
|
||||
struct osmo_mgcpc_ep *ep = ci->ep;
|
||||
|
||||
if (!ci->occupied || !ci->pending || ci->sent)
|
||||
return 0;
|
||||
|
||||
switch (ci->verb) {
|
||||
|
||||
case MGCP_VERB_CRCX:
|
||||
OSMO_ASSERT(!ci->mgcp_client_fi);
|
||||
LOG_CI_VERB(ci, LOGL_DEBUG, "Sending\n");
|
||||
ci->mgcp_client_fi = mgcp_conn_create(ep->mgcp_client, ep->fi,
|
||||
CI_EV_FAILURE(ci), CI_EV_SUCCESS(ci),
|
||||
&ci->verb_info);
|
||||
ci->sent = true;
|
||||
if (!ci->mgcp_client_fi){
|
||||
LOG_CI_VERB(ci, LOGL_ERROR, "Cannot send\n");
|
||||
on_failure(ci);
|
||||
return -EINVAL;
|
||||
}
|
||||
osmo_fsm_inst_update_id(ci->mgcp_client_fi, ci->label);
|
||||
break;
|
||||
|
||||
case MGCP_VERB_MDCX:
|
||||
OSMO_ASSERT(ci->mgcp_client_fi);
|
||||
LOG_CI_VERB(ci, LOGL_DEBUG, "Sending\n");
|
||||
rc = mgcp_conn_modify(ci->mgcp_client_fi, CI_EV_SUCCESS(ci), &ci->verb_info);
|
||||
ci->sent = true;
|
||||
if (rc) {
|
||||
LOG_CI_VERB(ci, LOGL_ERROR, "Cannot send (rc=%d %s)\n", rc, strerror(-rc));
|
||||
on_failure(ci);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case MGCP_VERB_DLCX:
|
||||
LOG_CI(ci, LOGL_DEBUG, "Sending MGCP: %s %s\n",
|
||||
osmo_mgcp_verb_name(ci->verb), ci->mgcp_ci_str);
|
||||
/* The way this is designed, we actually need to forget all about the ci right away. */
|
||||
mgcp_conn_delete(ci->mgcp_client_fi);
|
||||
if (ci->notify)
|
||||
osmo_fsm_inst_dispatch(ci->notify, ci->notify_success, ci->notify_data);
|
||||
*ci = (struct osmo_mgcpc_ep_ci){
|
||||
.ep = ep,
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*! DLCX all connections, terminate the endpoint FSM and free. */
|
||||
void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep)
|
||||
{
|
||||
if (!ep)
|
||||
return;
|
||||
osmo_fsm_inst_term(ep->fi, OSMO_FSM_TERM_REGULAR, 0);
|
||||
}
|
||||
|
||||
static void osmo_mgcpc_ep_count(struct osmo_mgcpc_ep *ep, int *occupied, int *pending_not_sent,
|
||||
int *waiting_for_response)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (occupied)
|
||||
*occupied = 0;
|
||||
|
||||
if (pending_not_sent)
|
||||
*pending_not_sent = 0;
|
||||
|
||||
if (waiting_for_response)
|
||||
*waiting_for_response = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
|
||||
struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
|
||||
if (ci->occupied) {
|
||||
if (occupied)
|
||||
(*occupied)++;
|
||||
} else
|
||||
continue;
|
||||
|
||||
if (ci->pending)
|
||||
LOG_CI_VERB(ci, LOGL_DEBUG, "%s\n",
|
||||
ci->sent ? "waiting for response" : "waiting to be sent");
|
||||
else
|
||||
LOG_CI_VERB(ci, LOGL_DEBUG, "done (%s)\n", mgcp_conn_peer_name(osmo_mgcpc_ep_ci_get_rtp_info(ci)));
|
||||
|
||||
if (ci->pending && ci->sent)
|
||||
if (waiting_for_response)
|
||||
(*waiting_for_response)++;
|
||||
if (ci->pending && !ci->sent)
|
||||
if (pending_not_sent)
|
||||
(*pending_not_sent)++;
|
||||
}
|
||||
}
|
||||
|
||||
static bool osmo_mgcpc_ep_fsm_check_state_chg_after_response(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
int waiting_for_response;
|
||||
int occupied;
|
||||
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
|
||||
|
||||
osmo_mgcpc_ep_count(ep, &occupied, NULL, &waiting_for_response);
|
||||
LOG_MGCPC_EP(ep, LOGL_DEBUG, "CI in use: %d, waiting for response: %d\n", occupied, waiting_for_response);
|
||||
|
||||
if (!occupied) {
|
||||
/* All CI have been released. The endpoint no longer exists. Notify the parent FSM, by
|
||||
* terminating. */
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!waiting_for_response) {
|
||||
if (fi->state != OSMO_MGCPC_EP_ST_IN_USE)
|
||||
osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_IN_USE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void osmo_mgcpc_ep_fsm_wait_mgw_response_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
static int re_enter = 0;
|
||||
int rc;
|
||||
int count = 0;
|
||||
int i;
|
||||
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
|
||||
|
||||
re_enter++;
|
||||
OSMO_ASSERT(re_enter < 10);
|
||||
|
||||
/* The first CRCX gives us the endpoint name in the CRCX response. So we must wait for the first CRCX endpoint
|
||||
* response to come in before sending any other MGCP requests -- otherwise we might end up creating new
|
||||
* endpoints instead of acting on the same. This FSM always sends out N requests and waits for all of them to
|
||||
* complete before sending out new requests. Hence we're safe when the very first time at most one request is
|
||||
* sent (which needs to be a CRCX). */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
|
||||
struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
|
||||
|
||||
/* Make sure that only CRCX get dispatched if no CRCX were sent yet. */
|
||||
if (!ep->first_crcx_complete) {
|
||||
if (ci->occupied && ci->verb != MGCP_VERB_CRCX)
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = send_verb(&ep->ci[i]);
|
||||
/* Need to be careful not to access the instance after failure. Event chains may already have
|
||||
* deallocated this memory. */
|
||||
if (rc < 0)
|
||||
return;
|
||||
if (!rc)
|
||||
continue;
|
||||
count++;
|
||||
/* Make sure that we wait for the first CRCX response before dispatching more requests. */
|
||||
if (!ep->first_crcx_complete)
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_MGCPC_EP(ep, LOGL_DEBUG, "Sent messages: %d\n", count);
|
||||
if (ep->first_crcx_complete)
|
||||
osmo_mgcpc_ep_fsm_check_state_chg_after_response(fi);
|
||||
re_enter--;
|
||||
}
|
||||
|
||||
static void osmo_mgcpc_ep_fsm_handle_ci_events(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct osmo_mgcpc_ep_ci *ci;
|
||||
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
|
||||
ci = osmo_mgcpc_ep_ci_for_event(ep, event);
|
||||
if (ci) {
|
||||
if (event == CI_EV_SUCCESS(ci))
|
||||
on_success(ci, data);
|
||||
else
|
||||
on_failure(ci);
|
||||
}
|
||||
}
|
||||
|
||||
static void osmo_mgcpc_ep_fsm_in_use_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
int pending_not_sent;
|
||||
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
|
||||
|
||||
osmo_mgcpc_ep_count(ep, NULL, &pending_not_sent, NULL);
|
||||
if (pending_not_sent)
|
||||
osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE);
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
static const struct osmo_fsm_state osmo_mgcpc_ep_fsm_states[] = {
|
||||
[OSMO_MGCPC_EP_ST_UNUSED] = {
|
||||
.name = "UNUSED",
|
||||
.in_event_mask = 0,
|
||||
.out_state_mask = 0
|
||||
| S(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE)
|
||||
,
|
||||
},
|
||||
[OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE] = {
|
||||
.name = "WAIT_MGW_RESPONSE",
|
||||
.onenter = osmo_mgcpc_ep_fsm_wait_mgw_response_onenter,
|
||||
.action = osmo_mgcpc_ep_fsm_handle_ci_events,
|
||||
.in_event_mask = 0xffffffff,
|
||||
.out_state_mask = 0
|
||||
| S(OSMO_MGCPC_EP_ST_IN_USE)
|
||||
,
|
||||
},
|
||||
[OSMO_MGCPC_EP_ST_IN_USE] = {
|
||||
.name = "IN_USE",
|
||||
.onenter = osmo_mgcpc_ep_fsm_in_use_onenter,
|
||||
.action = osmo_mgcpc_ep_fsm_handle_ci_events,
|
||||
.in_event_mask = 0xffffffff, /* mgcp_client_fsm may send parent term anytime */
|
||||
.out_state_mask = 0
|
||||
| S(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE)
|
||||
,
|
||||
},
|
||||
};
|
||||
|
||||
static int osmo_mgcpc_ep_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
int i;
|
||||
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
|
||||
|
||||
switch (fi->T) {
|
||||
default:
|
||||
for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
|
||||
struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
|
||||
if (!ci->occupied)
|
||||
continue;
|
||||
if (!(ci->pending && ci->sent))
|
||||
continue;
|
||||
on_failure(ci);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct osmo_fsm osmo_mgcpc_ep_fsm = {
|
||||
.name = "mgw-endp",
|
||||
.states = osmo_mgcpc_ep_fsm_states,
|
||||
.num_states = ARRAY_SIZE(osmo_mgcpc_ep_fsm_states),
|
||||
.log_subsys = DLMGCP,
|
||||
.event_names = osmo_mgcpc_ep_fsm_event_names,
|
||||
.timer_cb = osmo_mgcpc_ep_fsm_timer_cb,
|
||||
/* The FSM termination will automatically trigger any mgcp_client_fsm instances to DLCX. */
|
||||
};
|
@@ -114,16 +114,23 @@ static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info
|
||||
.conn_mode = MGCP_CONN_RECV_ONLY,
|
||||
.ptime = info->ptime,
|
||||
.codecs_len = info->codecs_len,
|
||||
.ptmap_len = info->ptmap_len
|
||||
.ptmap_len = info->ptmap_len,
|
||||
.param_present = info->param_present
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg->endpoint, info->endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
memcpy(mgcp_msg->codecs, info->codecs, sizeof(mgcp_msg->codecs));
|
||||
memcpy(mgcp_msg->ptmap, info->ptmap, sizeof(mgcp_msg->ptmap));
|
||||
memcpy(&mgcp_msg->param, &info->param, sizeof(mgcp_msg->param));
|
||||
|
||||
if (info->x_osmo_ign) {
|
||||
mgcp_msg->x_osmo_ign = info->x_osmo_ign;
|
||||
mgcp_msg->presence |= MGCP_MSG_PRESENCE_X_OSMO_IGN;
|
||||
}
|
||||
|
||||
if (info->x_osmo_osmux_use) {
|
||||
mgcp_msg->x_osmo_osmux_cid = info->x_osmo_osmux_cid;
|
||||
mgcp_msg->presence |= MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID;
|
||||
}
|
||||
}
|
||||
|
||||
static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
|
||||
@@ -134,6 +141,13 @@ static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
|
||||
mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
|
||||
}
|
||||
|
||||
static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer)
|
||||
{
|
||||
enum mgcp_connection_mode conn_mode = peer->conn_mode;
|
||||
if (conn_mode != MGCP_CONN_NONE)
|
||||
mgcp_msg->conn_mode = conn_mode;
|
||||
}
|
||||
|
||||
static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
{
|
||||
struct mgcp_msg mgcp_msg;
|
||||
@@ -149,11 +163,20 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
.audio_port = mgcp_ctx->conn_peer_local.port,
|
||||
.ptime = mgcp_ctx->conn_peer_local.ptime,
|
||||
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len,
|
||||
.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len
|
||||
.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len,
|
||||
.param_present = mgcp_ctx->conn_peer_local.param_present
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
|
||||
memcpy(mgcp_msg.ptmap, mgcp_ctx->conn_peer_local.ptmap, sizeof(mgcp_msg.ptmap));
|
||||
memcpy(&mgcp_msg.param, &mgcp_ctx->conn_peer_local.param, sizeof(mgcp_ctx->conn_peer_local.param));
|
||||
|
||||
set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
|
||||
if (mgcp_ctx->conn_peer_local.x_osmo_osmux_use) {
|
||||
mgcp_msg.x_osmo_osmux_cid = mgcp_ctx->conn_peer_local.x_osmo_osmux_cid;
|
||||
mgcp_msg.presence |= MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID;
|
||||
}
|
||||
|
||||
/* Note: We take the endpoint and the call_id from the remote
|
||||
* connection info, because we can be confident that the
|
||||
@@ -199,6 +222,8 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
make_crcx_msg(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
if (mgcp_ctx->conn_peer_local.port)
|
||||
add_audio(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
|
||||
msg = mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
OSMO_ASSERT(msg);
|
||||
|
||||
@@ -255,6 +280,11 @@ static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv)
|
||||
return;
|
||||
}
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
|
||||
if (r->head.x_osmo_osmux_use) {
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded using Osmux %u\n", r->head.x_osmo_osmux_cid);
|
||||
mgcp_ctx->conn_peer_remote.x_osmo_osmux_use = true;
|
||||
mgcp_ctx->conn_peer_remote.x_osmo_osmux_cid = r->head.x_osmo_osmux_cid;
|
||||
}
|
||||
|
||||
osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr));
|
||||
mgcp_ctx->conn_peer_remote.port = r->audio_port;
|
||||
@@ -374,6 +404,12 @@ static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv)
|
||||
}
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/MDCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
|
||||
|
||||
if (r->head.x_osmo_osmux_use) {
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded using Osmux %u\n", r->head.x_osmo_osmux_cid);
|
||||
mgcp_ctx->conn_peer_remote.x_osmo_osmux_use = true;
|
||||
mgcp_ctx->conn_peer_remote.x_osmo_osmux_cid = r->head.x_osmo_osmux_cid;
|
||||
}
|
||||
|
||||
osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr));
|
||||
mgcp_ctx->conn_peer_remote.port = r->audio_port;
|
||||
|
||||
@@ -685,3 +721,25 @@ void mgcp_conn_delete(struct osmo_fsm_inst *fi)
|
||||
}
|
||||
osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx);
|
||||
}
|
||||
|
||||
const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info)
|
||||
{
|
||||
/* I'd be fine with a smaller buffer and accept truncation, but gcc possibly refuses to build if
|
||||
* this buffer is too small. */
|
||||
static char buf[1024];
|
||||
|
||||
if (!info)
|
||||
return "NULL";
|
||||
|
||||
if (info->endpoint[0]
|
||||
&& info->addr[0])
|
||||
snprintf(buf, sizeof(buf), "%s:%s:%u",
|
||||
info->endpoint, info->addr, info->port);
|
||||
else if (info->endpoint[0])
|
||||
snprintf(buf, sizeof(buf), "%s", info->endpoint);
|
||||
else if (info->addr[0])
|
||||
snprintf(buf, sizeof(buf), "%s:%u", info->addr, info->port);
|
||||
else
|
||||
return "empty";
|
||||
return buf;
|
||||
}
|
||||
|
@@ -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,23 +101,14 @@ ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
|
||||
MGW_STR "remote bind to connect to MGCP gateway with\n"
|
||||
"remote bind port\n")
|
||||
|
||||
DEFUN(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
|
||||
DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
|
||||
"mgw endpoint-range <1-65534> <1-65534>",
|
||||
MGW_STR "usable range of endpoint identifiers\n"
|
||||
"set first usable endpoint identifier\n"
|
||||
"set last usable endpoint identifier\n")
|
||||
MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
|
||||
"-\n" "-\n")
|
||||
{
|
||||
uint16_t first_endpoint = atoi(argv[0]);
|
||||
uint16_t last_endpoint = atoi(argv[1]);
|
||||
|
||||
if (last_endpoint < first_endpoint) {
|
||||
vty_out(vty, "last endpoint must be greater than first endpoint!%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
global_mgcp_client_conf->first_endpoint = first_endpoint;
|
||||
global_mgcp_client_conf->last_endpoint = last_endpoint;
|
||||
vty_out(vty, "Please do not use legacy config 'mgw endpoint-range'"
|
||||
" (the range can no longer be defined by the MGCP client)%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
||||
@@ -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);
|
||||
|
@@ -56,8 +56,8 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
|
||||
endp = conn->conn->endp;
|
||||
|
||||
if (rtp->codecs_assigned == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x conn:%s no codecs available\n", ENDPOINT_NUMBER(endp),
|
||||
mgcp_conn_dump(conn->conn));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "conn:%s no codecs available\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -65,8 +65,8 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
|
||||
for (i = 0; i < rtp->codecs_assigned; i++) {
|
||||
codec = &rtp->codecs[i];
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x conn:%s codecs[%u]:%s", ENDPOINT_NUMBER(endp),
|
||||
mgcp_conn_dump(conn->conn), i, dump_codec(codec));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s codecs[%u]:%s",
|
||||
mgcp_conn_dump(conn->conn), i, dump_codec(codec));
|
||||
|
||||
if (codec == rtp->codec)
|
||||
LOGPC(DLMGCP, LOGL_DEBUG, " [selected]");
|
||||
@@ -99,9 +99,9 @@ void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
|
||||
conn->end.codec = NULL;
|
||||
}
|
||||
|
||||
/* Set members of struct mgcp_rtp_codec, extrapolate in missing information */
|
||||
static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
int payload_type, const char *audio_name, unsigned int pt_offset)
|
||||
/* Set members of struct mgcp_rtp_codec, extrapolate in missing information. Param audio_name is expected in uppercase. */
|
||||
static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type, const char *audio_name,
|
||||
unsigned int pt_offset, struct mgcp_codec_param *param)
|
||||
{
|
||||
int rate;
|
||||
int channels;
|
||||
@@ -219,6 +219,13 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy over optional codec parameters */
|
||||
if (param) {
|
||||
codec->param = *param;
|
||||
codec->param_present = true;
|
||||
} else
|
||||
codec->param_present = false;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
/* Make sure we leave a clean codec entry on error. */
|
||||
@@ -232,9 +239,10 @@ error:
|
||||
* \param[out] codec configuration (caller provided memory).
|
||||
* \param[out] conn related rtp-connection.
|
||||
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
|
||||
* \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
|
||||
* \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
|
||||
* \param[in] param optional codec parameters (set to NULL when unused).
|
||||
* \returns 0 on success, -EINVAL on failure. */
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name)
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, struct mgcp_codec_param *param)
|
||||
{
|
||||
int rc;
|
||||
|
||||
@@ -244,7 +252,7 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
|
||||
return -EINVAL;
|
||||
|
||||
rc = codec_set(conn->conn, &conn->end.codecs[conn->end.codecs_assigned], payload_type, audio_name,
|
||||
conn->end.codecs_assigned);
|
||||
conn->end.codecs_assigned, param);
|
||||
if (rc != 0)
|
||||
return -EINVAL;
|
||||
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#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>
|
||||
|
||||
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
|
||||
@@ -74,14 +75,13 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x, unable to generate a unique connectionIdentifier\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "unable to generate a unique connectionIdentifier\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize rtp connection struct with default values */
|
||||
static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
|
||||
static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
|
||||
{
|
||||
struct mgcp_rtp_end *end = &conn_rtp->end;
|
||||
/* FIXME: Each new rate counter group requires an unique index. At the
|
||||
@@ -90,7 +90,8 @@ static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn
|
||||
static unsigned int rate_ctr_index = 0;
|
||||
|
||||
conn_rtp->type = MGCP_RTP_DEFAULT;
|
||||
conn_rtp->osmux.allocated_cid = -1;
|
||||
conn_rtp->osmux.cid_allocated = false;
|
||||
conn_rtp->osmux.cid = 0;
|
||||
|
||||
/* backpointer to the generic part of the connection */
|
||||
conn->u.rtp.conn = conn;
|
||||
@@ -108,23 +109,45 @@ static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn
|
||||
end->maximum_packet_time = -1;
|
||||
|
||||
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index);
|
||||
if (!conn_rtp->rate_ctr_group)
|
||||
return -1;
|
||||
|
||||
conn_rtp->state.in_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[IN_STREAM_ERR_TSTMP_CTR];
|
||||
conn_rtp->state.out_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[OUT_STREAM_ERR_TSTMP_CTR];
|
||||
rate_ctr_index++;
|
||||
|
||||
/* Make sure codec table is reset */
|
||||
mgcp_codec_reset_all(conn_rtp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cleanup rtp connection struct */
|
||||
static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
osmux_disable_conn(conn_rtp);
|
||||
osmux_release_cid(conn_rtp);
|
||||
if (mgcp_conn_rtp_is_osmux(conn_rtp))
|
||||
conn_osmux_disable(conn_rtp);
|
||||
mgcp_free_rtp_port(&conn_rtp->end);
|
||||
rate_ctr_group_free(conn_rtp->rate_ctr_group);
|
||||
}
|
||||
|
||||
void mgcp_conn_watchdog_cb(void *data)
|
||||
{
|
||||
struct mgcp_conn *conn = data;
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR, "connection timed out!\n");
|
||||
mgcp_conn_free(conn->endp, conn->id);
|
||||
}
|
||||
|
||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn)
|
||||
{
|
||||
int timeout = conn->endp->cfg->conn_timeout;
|
||||
if (!timeout)
|
||||
return;
|
||||
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "watchdog kicked\n");
|
||||
osmo_timer_schedule(&conn->watchdog, timeout, 0);
|
||||
}
|
||||
|
||||
/*! allocate a new connection list entry.
|
||||
* \param[in] ctx talloc context
|
||||
* \param[in] endp associated endpoint
|
||||
@@ -158,7 +181,10 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
|
||||
switch (type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
mgcp_rtp_conn_init(&conn->u.rtp, conn);
|
||||
if (mgcp_rtp_conn_init(&conn->u.rtp, conn) < 0) {
|
||||
talloc_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* NOTE: This should never be called with an
|
||||
@@ -167,6 +193,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;
|
||||
@@ -274,6 +303,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);
|
||||
}
|
||||
|
@@ -36,8 +36,7 @@ const struct mgcp_endpoint_typeset ep_typeset = {
|
||||
* \param[in] endp endpoint to release */
|
||||
void mgcp_endp_release(struct mgcp_endpoint *endp)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint:0x%x\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
|
||||
|
||||
/* Normally this function should only be called when
|
||||
* all connections have been removed already. In case
|
||||
|
@@ -82,9 +82,8 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
int ret = 0;
|
||||
|
||||
if (!mode) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x missing connection mode\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"missing connection mode\n");
|
||||
return -1;
|
||||
}
|
||||
if (!conn)
|
||||
@@ -92,18 +91,17 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
if (!endp)
|
||||
return -1;
|
||||
|
||||
if (strcmp(mode, "recvonly") == 0)
|
||||
if (strcasecmp(mode, "recvonly") == 0)
|
||||
conn->mode = MGCP_CONN_RECV_ONLY;
|
||||
else if (strcmp(mode, "sendrecv") == 0)
|
||||
else if (strcasecmp(mode, "sendrecv") == 0)
|
||||
conn->mode = MGCP_CONN_RECV_SEND;
|
||||
else if (strcmp(mode, "sendonly") == 0)
|
||||
else if (strcasecmp(mode, "sendonly") == 0)
|
||||
conn->mode = MGCP_CONN_SEND_ONLY;
|
||||
else if (strcmp(mode, "loopback") == 0)
|
||||
else if (strcasecmp(mode, "loopback") == 0)
|
||||
conn->mode = MGCP_CONN_LOOPBACK;
|
||||
else {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x unknown connection mode: '%s'\n",
|
||||
ENDPOINT_NUMBER(endp), mode);
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"unknown connection mode: '%s'\n", mode);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
@@ -113,18 +111,15 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint:0x%x conn:%s\n",
|
||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint:0x%x connection mode '%s' %d\n",
|
||||
ENDPOINT_NUMBER(endp), mode, conn->mode);
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
|
||||
mode, conn->mode);
|
||||
|
||||
/* Special handling für RTP connections */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x output_enabled %d\n",
|
||||
ENDPOINT_NUMBER(endp), conn->u.rtp.end.output_enabled);
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %d\n",
|
||||
conn->u.rtp.end.output_enabled);
|
||||
}
|
||||
|
||||
/* The VTY might change the connection mode at any time, so we have
|
||||
@@ -197,9 +192,8 @@ static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
|
||||
for (i = 0; i < number_endpoints; i++) {
|
||||
if (endpoints[i].callid == NULL) {
|
||||
endp = &endpoints[i];
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint:0x%x found free endpoint\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
|
||||
"found free endpoint\n");
|
||||
endp->wildcarded_req = true;
|
||||
return endp;
|
||||
}
|
||||
@@ -223,8 +217,10 @@ static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
|
||||
if (!strcmp(cfg->domain, "*"))
|
||||
return 0;
|
||||
|
||||
if (strcmp(domain_to_check+1, cfg->domain) != 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;
|
||||
}
|
||||
@@ -244,7 +240,6 @@ 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;
|
||||
}
|
||||
@@ -330,7 +325,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (strcmp("MGCP", elem)) {
|
||||
if (strcasecmp("MGCP", elem)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MGCP header parsing error\n");
|
||||
return -510;
|
||||
@@ -359,18 +354,27 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
|
||||
/*! Extract OSMUX CID from an MGCP parameter line (string).
|
||||
* \param[in] line single parameter line from the MGCP message
|
||||
* \returns OSMUX CID, -1 on error */
|
||||
* \returns OSMUX CID, -1 wildcard, -2 on error */
|
||||
int mgcp_parse_osmux_cid(const char *line)
|
||||
{
|
||||
int osmux_cid;
|
||||
|
||||
if (sscanf(line + 2, "Osmux: %u", &osmux_cid) != 1)
|
||||
|
||||
if (strcasecmp(line + 2, "Osmux: *") == 0) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sscanf(line + 2 + 7, "%u", &osmux_cid) != 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed parsing Osmux in MGCP msg line: %s\n",
|
||||
line);
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (osmux_cid > OSMUX_CID_MAX) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
|
||||
osmux_cid, OSMUX_CID_MAX);
|
||||
return -1;
|
||||
return -2;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
|
||||
|
||||
@@ -418,9 +422,9 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
|
||||
return -1;
|
||||
|
||||
if (strcmp(endp->callid, callid) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x CallIDs mismatch: '%s' != '%s'\n",
|
||||
ENDPOINT_NUMBER(endp), endp->callid, callid);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CallIDs mismatch: '%s' != '%s'\n",
|
||||
endp->callid, callid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -439,25 +443,23 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
|
||||
|
||||
/* Check for null identifiers */
|
||||
if (!conn_id) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid ConnectionIdentifier (missing)\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"invalid ConnectionIdentifier (missing)\n");
|
||||
return 510;
|
||||
}
|
||||
|
||||
/* Check for empty connection identifiers */
|
||||
if (strlen(conn_id) == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid ConnectionIdentifier (empty)\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"invalid ConnectionIdentifier (empty)\n");
|
||||
return 510;
|
||||
}
|
||||
|
||||
/* Check for over long connection identifiers */
|
||||
if (strlen(conn_id) > (MGCP_CONN_ID_MAXLEN-1)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid ConnectionIdentifier (too long: %zu > %d) 0x%s\n",
|
||||
ENDPOINT_NUMBER(endp), strlen(conn_id), MGCP_CONN_ID_MAXLEN-1, conn_id);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"invalid ConnectionIdentifier (too long: %zu > %d) 0x%s\n",
|
||||
strlen(conn_id), MGCP_CONN_ID_MAXLEN-1, conn_id);
|
||||
return 510;
|
||||
}
|
||||
|
||||
@@ -465,9 +467,8 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
|
||||
if (mgcp_conn_get(endp, conn_id))
|
||||
return 0;
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"endpoint:0x%x no connection found under ConnectionIdentifier 0x%s\n",
|
||||
ENDPOINT_NUMBER(endp), conn_id);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"no connection found under ConnectionIdentifier 0x%s\n", conn_id);
|
||||
|
||||
/* When the conn_id was not found, return error code 515 "The transaction refers to an incorrect
|
||||
* connection-id (may have been already deleted)." */
|
||||
|
@@ -34,6 +34,7 @@
|
||||
#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>
|
||||
@@ -43,6 +44,7 @@
|
||||
#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)
|
||||
@@ -74,34 +76,31 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
|
||||
if (endp->cfg->net_ports.bind_addr_probe && conn->end.addr.s_addr != 0) {
|
||||
rc = osmo_sock_local_ip(addr, inet_ntoa(conn->end.addr));
|
||||
if (rc < 0)
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x CI:%s local interface auto detection failed, using configured addresses...\n",
|
||||
ENDPOINT_NUMBER(endp), conn->conn->id);
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"local interface auto detection failed, using configured addresses...\n");
|
||||
else {
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x CI:%s selected local rtp bind ip %s by probing using remote ip %s\n",
|
||||
ENDPOINT_NUMBER(endp), conn->conn->id, addr,
|
||||
inet_ntoa(conn->end.addr));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
|
||||
"selected local rtp bind ip %s by probing using remote ip %s\n",
|
||||
addr, inet_ntoa(conn->end.addr));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Select from preconfigured IP-Addresses */
|
||||
/* Select from preconfigured IP-Addresses. We don't have bind_addr for Osmux (yet?). */
|
||||
if (endp->cfg->net_ports.bind_addr) {
|
||||
/* Check there is a bind IP for the RTP traffic configured,
|
||||
* if so, use that IP-Address */
|
||||
osmo_strlcpy(addr, endp->cfg->net_ports.bind_addr, INET_ADDRSTRLEN);
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x CI:%s using configured rtp bind ip as local bind ip %s\n",
|
||||
ENDPOINT_NUMBER(endp), conn->conn->id, addr);
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
|
||||
"using configured rtp bind ip as local bind ip %s\n",
|
||||
addr);
|
||||
} else {
|
||||
/* No specific bind IP is configured for the RTP traffic, so
|
||||
* assume the IP where we listen for incoming MGCP messages
|
||||
* as bind IP */
|
||||
osmo_strlcpy(addr, endp->cfg->source_addr, INET_ADDRSTRLEN);
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x CI:%s using mgcp bind ip as local rtp bind ip: %s\n",
|
||||
ENDPOINT_NUMBER(endp), conn->conn->id, addr);
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
|
||||
"using mgcp bind ip as local rtp bind ip: %s\n", addr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +130,7 @@ static uint32_t get_current_ts(unsigned codec_rate)
|
||||
/*! send udp packet.
|
||||
* \param[in] fd associated file descriptor
|
||||
* \param[in] addr destination ip-address
|
||||
* \param[in] port destination UDP port
|
||||
* \param[in] port destination UDP port (network byte order)
|
||||
* \param[in] buf buffer that holds the data to be send
|
||||
* \param[in] len length of the data to be sent
|
||||
* \returns bytes sent, -1 on error */
|
||||
@@ -163,10 +162,8 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x sending dummy packet...\n", ENDPOINT_NUMBER(endp));
|
||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x conn:%s\n",
|
||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,"sending dummy packet... %s\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
|
||||
rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
|
||||
conn->end.rtp_port, buf, 1);
|
||||
@@ -185,9 +182,9 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
return rc;
|
||||
|
||||
failed:
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x Failed to send dummy %s packet.\n",
|
||||
ENDPOINT_NUMBER(endp), was_rtcp ? "RTCP" : "RTP");
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"Failed to send dummy %s packet.\n",
|
||||
was_rtcp ? "RTCP" : "RTP");
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -226,16 +223,16 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
|
||||
if (seq == sstate->last_seq) {
|
||||
if (timestamp != sstate->last_timestamp) {
|
||||
rate_ctr_inc(sstate->err_ts_ctr);
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"The %s timestamp delta is != 0 but the sequence "
|
||||
"number %d is the same, "
|
||||
"TS offset: %d, SeqNo offset: %d "
|
||||
"on 0x%x SSRC: %u timestamp: %u "
|
||||
"from %s:%d\n",
|
||||
text, seq,
|
||||
state->patch.timestamp_offset, state->patch.seq_offset,
|
||||
ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"The %s timestamp delta is != 0 but the sequence "
|
||||
"number %d is the same, "
|
||||
"TS offset: %d, SeqNo offset: %d "
|
||||
"on SSRC: %u timestamp: %u "
|
||||
"from %s:%d\n",
|
||||
text, seq,
|
||||
state->patch.timestamp_offset, state->patch.seq_offset,
|
||||
sstate->ssrc, timestamp, inet_ntoa(addr->sin_addr),
|
||||
ntohs(addr->sin_port));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -246,25 +243,25 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
|
||||
|
||||
if (tsdelta == 0) {
|
||||
/* Don't update *tsdelta_out */
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"The %s timestamp delta is %d "
|
||||
"on 0x%x SSRC: %u timestamp: %u "
|
||||
"from %s:%d\n",
|
||||
text, tsdelta,
|
||||
ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
LOGPENDP(endp, DRTP, LOGL_NOTICE,
|
||||
"The %s timestamp delta is %d "
|
||||
"on SSRC: %u timestamp: %u "
|
||||
"from %s:%d\n",
|
||||
text, tsdelta,
|
||||
sstate->ssrc, timestamp,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sstate->last_tsdelta != tsdelta) {
|
||||
if (sstate->last_tsdelta) {
|
||||
LOGP(DRTP, LOGL_INFO,
|
||||
"The %s timestamp delta changes from %d to %d "
|
||||
"on 0x%x SSRC: %u timestamp: %u from %s:%d\n",
|
||||
text, sstate->last_tsdelta, tsdelta,
|
||||
ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
LOGPENDP(endp, DRTP, LOGL_INFO,
|
||||
"The %s timestamp delta changes from %d to %d "
|
||||
"on SSRC: %u timestamp: %u from %s:%d\n",
|
||||
text, sstate->last_tsdelta, tsdelta,
|
||||
sstate->ssrc, timestamp,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,18 +273,18 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
|
||||
|
||||
if (timestamp_error) {
|
||||
rate_ctr_inc(sstate->err_ts_ctr);
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"The %s timestamp has an alignment error of %d "
|
||||
"on 0x%x SSRC: %u "
|
||||
"SeqNo delta: %d, TS delta: %d, dTS/dSeq: %d "
|
||||
"from %s:%d. ptime: %d\n",
|
||||
text, timestamp_error,
|
||||
ENDPOINT_NUMBER(endp), sstate->ssrc,
|
||||
(int16_t)(seq - sstate->last_seq),
|
||||
(int32_t)(timestamp - sstate->last_timestamp),
|
||||
tsdelta,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
|
||||
state->packet_duration);
|
||||
LOGPENDP(endp, DRTP, LOGL_NOTICE,
|
||||
"The %s timestamp has an alignment error of %d "
|
||||
"on SSRC: %u "
|
||||
"SeqNo delta: %d, TS delta: %d, dTS/dSeq: %d "
|
||||
"from %s:%d. ptime: %d\n",
|
||||
text, timestamp_error,
|
||||
sstate->ssrc,
|
||||
(int16_t)(seq - sstate->last_seq),
|
||||
(int32_t)(timestamp - sstate->last_timestamp),
|
||||
tsdelta,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
|
||||
state->packet_duration);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -306,21 +303,19 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
|
||||
if (tsdelta == 0) {
|
||||
tsdelta = state->out_stream.last_tsdelta;
|
||||
if (tsdelta != 0) {
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"A fixed packet duration is not available on 0x%x, "
|
||||
"using last output timestamp delta instead: %d "
|
||||
"from %s:%d\n",
|
||||
ENDPOINT_NUMBER(endp), tsdelta,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
LOGPENDP(endp, DRTP, LOGL_NOTICE,
|
||||
"A fixed packet duration is not available, "
|
||||
"using last output timestamp delta instead: %d "
|
||||
"from %s:%d\n", tsdelta,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
} else {
|
||||
tsdelta = rtp_end->codec->rate * 20 / 1000;
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"Fixed packet duration and last timestamp delta "
|
||||
"are not available on 0x%x, "
|
||||
"using fixed 20ms instead: %d "
|
||||
"from %s:%d\n",
|
||||
ENDPOINT_NUMBER(endp), tsdelta,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
LOGPENDP(endp, DRTP, LOGL_NOTICE,
|
||||
"Fixed packet duration and last timestamp delta "
|
||||
"are not available, "
|
||||
"using fixed 20ms instead: %d "
|
||||
"from %s:%d\n", tsdelta,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,13 +325,12 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
|
||||
if (state->patch.timestamp_offset != timestamp_offset) {
|
||||
state->patch.timestamp_offset = timestamp_offset;
|
||||
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"Timestamp offset change on 0x%x SSRC: %u "
|
||||
"SeqNo delta: %d, TS offset: %d, "
|
||||
"from %s:%d\n",
|
||||
ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
|
||||
delta_seq, state->patch.timestamp_offset,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
LOGPENDP(endp, DRTP, LOGL_NOTICE,
|
||||
"Timestamp offset change on SSRC: %u "
|
||||
"SeqNo delta: %d, TS offset: %d, "
|
||||
"from %s:%d\n", state->in_stream.ssrc,
|
||||
delta_seq, state->patch.timestamp_offset,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
}
|
||||
|
||||
return timestamp_offset;
|
||||
@@ -362,14 +356,13 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
|
||||
if (ts_error) {
|
||||
state->patch.timestamp_offset += ptime - ts_error;
|
||||
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"Corrected timestamp alignment error of %d on 0x%x SSRC: %u "
|
||||
"new TS offset: %d, "
|
||||
"from %s:%d\n",
|
||||
ts_error,
|
||||
ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
|
||||
state->patch.timestamp_offset, inet_ntoa(addr->sin_addr),
|
||||
ntohs(addr->sin_port));
|
||||
LOGPENDP(endp, DRTP, LOGL_NOTICE,
|
||||
"Corrected timestamp alignment error of %d on SSRC: %u "
|
||||
"new TS offset: %d, "
|
||||
"from %s:%d\n",
|
||||
ts_error, state->in_stream.ssrc,
|
||||
state->patch.timestamp_offset, inet_ntoa(addr->sin_addr),
|
||||
ntohs(addr->sin_port));
|
||||
}
|
||||
|
||||
/* Check we really managed to compensate the timestamp
|
||||
@@ -395,8 +388,7 @@ int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size)
|
||||
{
|
||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x transcoding disabled\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "transcoding disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -409,23 +401,19 @@ int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst,
|
||||
struct mgcp_conn_rtp *conn_src)
|
||||
{
|
||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x transcoding disabled\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "transcoding disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
int *payload_type,
|
||||
const char **audio_name,
|
||||
const struct mgcp_rtp_codec **codec,
|
||||
const char **fmtp_extra,
|
||||
struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x conn:%s using format defaults\n",
|
||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "conn:%s using format defaults\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
|
||||
*payload_type = conn->end.codec->payload_type;
|
||||
*audio_name = conn->end.codec->audio_name;
|
||||
*codec = conn->end.codec;
|
||||
*fmtp_extra = conn->end.fmtp_extra;
|
||||
}
|
||||
|
||||
@@ -459,9 +447,9 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
|
||||
if (seq < state->stats.max_seq)
|
||||
state->stats.cycles += RTP_SEQ_MOD;
|
||||
} else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"RTP seqno made a very large jump on 0x%x delta: %u\n",
|
||||
ENDPOINT_NUMBER(endp), udelta);
|
||||
LOGPENDP(endp, DRTP, LOGL_NOTICE,
|
||||
"RTP seqno made a very large jump on delta: %u\n",
|
||||
udelta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,28 +529,27 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
state->out_stream.last_tsdelta = 0;
|
||||
state->out_stream.last_timestamp = timestamp;
|
||||
state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */
|
||||
LOGP(DRTP, LOGL_INFO,
|
||||
"endpoint:0x%x initializing stream, SSRC: %u timestamp: %u "
|
||||
"pkt-duration: %d, from %s:%d\n",
|
||||
ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
|
||||
state->patch.seq_offset, state->packet_duration,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
LOGPENDP(endp, DRTP, LOGL_INFO,
|
||||
"initializing stream, SSRC: %u timestamp: %u "
|
||||
"pkt-duration: %d, from %s:%d\n",
|
||||
state->in_stream.ssrc,
|
||||
state->patch.seq_offset, state->packet_duration,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
if (state->packet_duration == 0) {
|
||||
state->packet_duration =
|
||||
rtp_end->codec->rate * 20 / 1000;
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"endpoint:0x%x fixed packet duration is not available, "
|
||||
"using fixed 20ms instead: %d from %s:%d\n",
|
||||
ENDPOINT_NUMBER(endp), state->packet_duration,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
LOGPENDP(endp, DRTP, LOGL_NOTICE,
|
||||
"fixed packet duration is not available, "
|
||||
"using fixed 20ms instead: %d from %s:%d\n",
|
||||
state->packet_duration,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
}
|
||||
} else if (state->in_stream.ssrc != ssrc) {
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"endpoint:0x%x SSRC changed: %u -> %u "
|
||||
"from %s:%d\n",
|
||||
ENDPOINT_NUMBER(endp),
|
||||
state->in_stream.ssrc, rtp_hdr->ssrc,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
LOGPENDP(endp, DRTP, LOGL_NOTICE,
|
||||
"SSRC changed: %u -> %u "
|
||||
"from %s:%d\n",
|
||||
state->in_stream.ssrc, rtp_hdr->ssrc,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
|
||||
state->in_stream.ssrc = ssrc;
|
||||
if (rtp_end->force_constant_ssrc) {
|
||||
@@ -586,13 +573,12 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
if (rtp_end->force_constant_ssrc != -1)
|
||||
rtp_end->force_constant_ssrc -= 1;
|
||||
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"endpoint:0x%x SSRC patching enabled, SSRC: %u "
|
||||
"SeqNo offset: %d, TS offset: %d "
|
||||
"from %s:%d\n",
|
||||
ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
|
||||
state->patch.seq_offset, state->patch.timestamp_offset,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
LOGPENDP(endp, DRTP, LOGL_NOTICE,
|
||||
"SSRC patching enabled, SSRC: %u "
|
||||
"SeqNo offset: %d, TS offset: %d "
|
||||
"from %s:%d\n", state->in_stream.ssrc,
|
||||
state->patch.seq_offset, state->patch.timestamp_offset,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
}
|
||||
|
||||
state->in_stream.last_tsdelta = 0;
|
||||
@@ -651,6 +637,118 @@ 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. */
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"cannot figure out how to convert RTP packet\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 int amr_oa_bwe_convert(struct mgcp_endpoint *endp, char *data, int *len,
|
||||
bool target_is_oa)
|
||||
{
|
||||
/* 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)) {
|
||||
if (!target_is_oa)
|
||||
/* Input data is oa an target format is bwe
|
||||
* ==> convert */
|
||||
rc = osmo_amr_oa_to_bwe(rtp_hdr->data, payload_len);
|
||||
else
|
||||
/* Input data is already bew, but we accept it anyway
|
||||
* ==> no conversion needed */
|
||||
rc = payload_len;
|
||||
} else {
|
||||
if (target_is_oa)
|
||||
/* Input data is bwe an target format is oa
|
||||
* ==> convert */
|
||||
rc = osmo_amr_bwe_to_oa(rtp_hdr->data, payload_len,
|
||||
RTP_BUF_SIZE);
|
||||
else
|
||||
/* Input data is already oa, but we accept it anyway
|
||||
* ==> no conversion needed */
|
||||
rc = payload_len;
|
||||
}
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"AMR RTP packet conversion failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*len = rc + sizeof(struct rtp_hdr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if a conversion between octet-aligned and bandwith-efficient mode is
|
||||
* indicated. */
|
||||
static bool amr_oa_bwe_convert_indicated(struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
if (codec->param_present == false)
|
||||
return false;
|
||||
if (!codec->param.amr_octet_aligned_present)
|
||||
return false;
|
||||
if (strcmp(codec->subtype_name, "AMR") != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Check if a given RTP with AMR payload for octet-aligned mode */
|
||||
static bool amr_oa_check(char *data, int len)
|
||||
{
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
unsigned int payload_len;
|
||||
|
||||
OSMO_ASSERT(len >= sizeof(struct rtp_hdr));
|
||||
rtp_hdr = (struct rtp_hdr *)data;
|
||||
|
||||
payload_len = len - sizeof(struct rtp_hdr);
|
||||
|
||||
return osmo_amr_is_oa(rtp_hdr->data, payload_len);
|
||||
}
|
||||
|
||||
/* 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,
|
||||
@@ -696,19 +794,14 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
OSMO_ASSERT(conn_dst);
|
||||
|
||||
if (is_rtp) {
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x delivering RTP packet...\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering RTP packet...\n");
|
||||
} else {
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x delivering RTCP packet...\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering RTCP packet...\n");
|
||||
}
|
||||
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x loop:%d, mode:%d%s\n",
|
||||
ENDPOINT_NUMBER(endp), tcfg->audio_loop, conn_src->conn->mode,
|
||||
conn_src->conn->mode == MGCP_CONN_LOOPBACK ? " (loopback)" : "");
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "loop:%d, mode:%d%s\n",
|
||||
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
|
||||
@@ -721,9 +814,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
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));
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"can not patch PT because no suitable egress codec was found.\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -735,13 +827,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
|
||||
if (!rtp_end->output_enabled) {
|
||||
rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_DROPPED_PACKETS_CTR]);
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x output disabled, drop to %s %s "
|
||||
"rtp_port:%u rtcp_port:%u\n",
|
||||
ENDPOINT_NUMBER(endp),
|
||||
dest_name,
|
||||
inet_ntoa(rtp_end->addr),
|
||||
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"output disabled, drop to %s %s "
|
||||
"rtp_port:%u rtcp_port:%u\n",
|
||||
dest_name,
|
||||
inet_ntoa(rtp_end->addr),
|
||||
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
|
||||
);
|
||||
} else if (is_rtp) {
|
||||
int cont;
|
||||
@@ -758,13 +849,22 @@ 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);
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x process/send to %s %s "
|
||||
"rtp_port:%u rtcp_port:%u\n",
|
||||
ENDPOINT_NUMBER(endp), dest_name,
|
||||
inet_ntoa(rtp_end->addr), ntohs(rtp_end->rtp_port),
|
||||
ntohs(rtp_end->rtcp_port)
|
||||
);
|
||||
|
||||
if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
|
||||
amr_oa_bwe_convert(endp, buf, &buflen,
|
||||
conn_dst->end.codec->param.amr_octet_aligned);
|
||||
}
|
||||
else if (rtp_end->rfc5993_hr_convert
|
||||
&& strcmp(conn_src->end.codec->subtype_name,
|
||||
"GSM-HR-08") == 0)
|
||||
rfc5993_hr_convert(endp, buf, &buflen);
|
||||
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"process/send to %s %s "
|
||||
"rtp_port:%u rtcp_port:%u\n",
|
||||
dest_name, inet_ntoa(rtp_end->addr),
|
||||
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
|
||||
);
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
|
||||
@@ -782,10 +882,9 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
data[0] = 0xe4;
|
||||
data[1] = 0x00;
|
||||
rtp_state->patched_first_rtp_payload = true;
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x Patching over first two bytes"
|
||||
" to fake an IuUP Initialization Ack\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"Patching over first two bytes"
|
||||
" to fake an IuUP Initialization Ack\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -804,13 +903,11 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
} while (buflen > 0);
|
||||
return nbytes;
|
||||
} else if (!tcfg->omit_rtcp) {
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x send to %s %s rtp_port:%u rtcp_port:%u\n",
|
||||
ENDPOINT_NUMBER(endp),
|
||||
dest_name,
|
||||
inet_ntoa(rtp_end->addr),
|
||||
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
|
||||
);
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"send to %s %s rtp_port:%u rtcp_port:%u\n",
|
||||
dest_name, inet_ntoa(rtp_end->addr),
|
||||
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
|
||||
);
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtcp.fd,
|
||||
&rtp_end->addr,
|
||||
@@ -846,20 +943,19 @@ static int receive_from(struct mgcp_endpoint *endp, int fd,
|
||||
|
||||
rc = recvfrom(fd, buf, bufsize, 0, (struct sockaddr *)addr, &slen);
|
||||
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"receiving %u bytes length packet from %s:%u ...\n",
|
||||
rc, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
|
||||
if (rc < 0) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x failed to receive packet, errno: %d/%s\n",
|
||||
ENDPOINT_NUMBER(endp), errno, strerror(errno));
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to receive packet, errno: %d/%s\n",
|
||||
errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tossed) {
|
||||
LOGP(DRTP, LOGL_ERROR, "endpoint:0x%x packet tossed\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "packet tossed\n");
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -870,9 +966,6 @@ static int receive_from(struct mgcp_endpoint *endp, int fd,
|
||||
static int check_rtp_origin(struct mgcp_conn_rtp *conn,
|
||||
struct sockaddr_in *addr)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
endp = conn->conn->endp;
|
||||
|
||||
if (conn->end.addr.s_addr == 0) {
|
||||
switch (conn->conn->mode) {
|
||||
case MGCP_CONN_LOOPBACK:
|
||||
@@ -883,20 +976,18 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn,
|
||||
* 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));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"In loopback mode and remote address not set:"
|
||||
" allowing data from address: %s\n", 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));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_INFO,
|
||||
"Rx RTP from %s, but remote address not set:"
|
||||
" dropping early media\n", inet_ntoa(addr->sin_addr));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -904,13 +995,11 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn,
|
||||
/* Note: Check if the inbound RTP data comes from the same host to
|
||||
* which we send our outgoing RTP traffic. */
|
||||
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));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"data from wrong address: %s, ", inet_ntoa(addr->sin_addr));
|
||||
LOGPC(DRTP, LOGL_ERROR, "expected: %s\n",
|
||||
inet_ntoa(conn->end.addr));
|
||||
LOGP(DRTP, LOGL_ERROR, "endpoint:0x%x packet tossed\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR, "packet tossed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -920,14 +1009,12 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn,
|
||||
* plausibility. */
|
||||
if (conn->end.rtp_port != addr->sin_port &&
|
||||
conn->end.rtcp_port != addr->sin_port) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x data from wrong source port: %d, ",
|
||||
ENDPOINT_NUMBER(endp), ntohs(addr->sin_port));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"data from wrong source port: %d, ", ntohs(addr->sin_port));
|
||||
LOGPC(DRTP, LOGL_ERROR,
|
||||
"expected: %d for RTP or %d for RTCP\n",
|
||||
ntohs(conn->end.rtp_port), ntohs(conn->end.rtcp_port));
|
||||
LOGP(DRTP, LOGL_ERROR, "endpoint:0x%x packet tossed\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR, "packet tossed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -938,29 +1025,26 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn,
|
||||
* makes sense */
|
||||
static int check_rtp_destin(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
endp = conn->conn->endp;
|
||||
|
||||
/* Note: it is legal to create a connection but never setting a port
|
||||
* and IP-address for outgoing data. */
|
||||
if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0 && conn->end.rtp_port == 0) {
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x destination IP-address and rtp port is (not yet) known\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
|
||||
"destination IP-address and rtp port is (not yet) known (%s:%u)\n",
|
||||
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));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"destination IP-address is invalid (%s:%u)\n",
|
||||
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));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"destination rtp port is invalid (%s:%u)\n",
|
||||
inet_ntoa(conn->end.addr), conn->end.rtp_port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1028,8 +1112,7 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
|
||||
endp = conn->conn->endp;
|
||||
tcfg = endp->tcfg;
|
||||
|
||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x receiving RTP/RTCP packet...\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "receiving RTP/RTCP packet...\n");
|
||||
|
||||
rc = receive_from(endp, fd->fd, addr, buf, buf_size);
|
||||
if (rc <= 0)
|
||||
@@ -1043,26 +1126,23 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
|
||||
|
||||
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));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"invalid RTP packet received -- packet tossed\n");
|
||||
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));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"invalid RTCP packet received -- packet tossed\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x ", ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "");
|
||||
LOGPC(DRTP, LOGL_DEBUG, "receiving from %s %s %d\n",
|
||||
conn->conn->name, inet_ntoa(addr->sin_addr),
|
||||
ntohs(addr->sin_port));
|
||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x conn:%s\n", ENDPOINT_NUMBER(endp),
|
||||
mgcp_conn_dump(conn->conn));
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn->conn));
|
||||
|
||||
/* Check if the origin of the RTP packet seems plausible */
|
||||
if (tcfg->rtp_accept_all == 0) {
|
||||
@@ -1072,11 +1152,10 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
|
||||
|
||||
/* Filter out dummy message */
|
||||
if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"endpoint:0x%x dummy message received\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x packet tossed\n", ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_NOTICE,
|
||||
"dummy message received\n");
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"packet tossed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1100,8 +1179,8 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
|
||||
struct mgcp_endpoint *endp;
|
||||
endp = conn_src->conn->endp;
|
||||
|
||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x destin conn:%s\n",
|
||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn_dst->conn));
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "destin conn:%s\n",
|
||||
mgcp_conn_dump(conn_dst->conn));
|
||||
|
||||
/* Before we try to deliver the packet, we check if the destination
|
||||
* port and IP-Address make sense at all. If not, we will be unable
|
||||
@@ -1113,27 +1192,23 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
|
||||
* destination connection. */
|
||||
switch (conn_dst->type) {
|
||||
case MGCP_RTP_DEFAULT:
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x endpoint type is MGCP_RTP_DEFAULT, "
|
||||
"using mgcp_send() to forward data directly\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"endpoint type is MGCP_RTP_DEFAULT, "
|
||||
"using mgcp_send() to forward data directly\n");
|
||||
return mgcp_send(endp, proto == MGCP_PROTO_RTP,
|
||||
addr, buf, buf_size, conn_src, conn_dst);
|
||||
case MGCP_OSMUX_BSC_NAT:
|
||||
case MGCP_OSMUX_BSC:
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x endpoint type is MGCP_OSMUX_BSC_NAT, "
|
||||
"using osmux_xfrm_to_osmux() to forward data through OSMUX\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"endpoint type is MGCP_OSMUX_BSC_NAT, "
|
||||
"using osmux_xfrm_to_osmux() to forward data through OSMUX\n");
|
||||
return osmux_xfrm_to_osmux(buf, buf_size, conn_dst);
|
||||
}
|
||||
|
||||
/* If the data has not been handled/forwarded until here, it will
|
||||
* be discarded, this should not happen, normally the MGCP type
|
||||
* should be properly set */
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x bad MGCP type -- data discarded!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "bad MGCP type -- data discarded!\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -1149,8 +1224,6 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
unsigned int buf_size, struct mgcp_conn *conn)
|
||||
{
|
||||
struct mgcp_conn *conn_dst;
|
||||
struct mgcp_endpoint *endp;
|
||||
endp = conn->endp;
|
||||
|
||||
/*! NOTE: This callback function implements the endpoint specific
|
||||
* dispatch bahviour of an rtp bridge/proxy endpoint. It is assumed
|
||||
@@ -1160,6 +1233,22 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
* destination connection is known the RTP packet is sent via
|
||||
* the destination connection. */
|
||||
|
||||
|
||||
/* Check if the connection is in loopback mode, if yes, just send the
|
||||
* incoming data back to the origin */
|
||||
if (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
|
||||
* address data from the UDP packet header to patch the
|
||||
* outgoing address in connection on the fly */
|
||||
if (conn->u.rtp.end.rtp_port == 0) {
|
||||
conn->u.rtp.end.addr = addr->sin_addr;
|
||||
conn->u.rtp.end.rtp_port = addr->sin_port;
|
||||
}
|
||||
return mgcp_send_rtp(proto, addr, buf,
|
||||
buf_size, &conn->u.rtp, &conn->u.rtp);
|
||||
}
|
||||
|
||||
/* Find a destination connection. */
|
||||
/* NOTE: This code path runs every time an RTP packet is received. The
|
||||
* function mgcp_find_dst_conn() we use to determine the detination
|
||||
@@ -1176,17 +1265,15 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
|
||||
/* There is no destination conn, stop here */
|
||||
if (!conn_dst) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x unable to find destination conn\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn, DRTP, LOGL_ERROR,
|
||||
"unable to find destination conn\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* The destination conn is not an RTP connection */
|
||||
if (conn_dst->type != MGCP_CONN_TYPE_RTP) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x unable to find suitable destination conn\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn, DRTP, LOGL_ERROR,
|
||||
"unable to find suitable destination conn\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1238,27 +1325,23 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
endp = conn_src->conn->endp;
|
||||
OSMO_ASSERT(endp);
|
||||
|
||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x source conn:%s\n",
|
||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn_src->conn));
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "source conn:%s\n",
|
||||
mgcp_conn_dump(conn_src->conn));
|
||||
|
||||
/* Receive packet */
|
||||
len = mgcp_recv(&proto, &addr, buf, sizeof(buf), fd);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
/* 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
|
||||
* address data from the UDP packet header to patch the
|
||||
* outgoing address in connection on the fly */
|
||||
if (conn_src->end.rtp_port == 0) {
|
||||
conn_src->end.addr = addr.sin_addr;
|
||||
conn_src->end.rtp_port = addr.sin_port;
|
||||
}
|
||||
return mgcp_send_rtp(proto, &addr, buf,
|
||||
len, conn_src, conn_src);
|
||||
mgcp_conn_watchdog_kick(conn_src->conn);
|
||||
|
||||
/* If AMR is configured for the ingress connection a conversion of the
|
||||
* framing mode (octet-aligned vs. bandwith-efficient is explicitly
|
||||
* define, then we check if the incoming payload matches that
|
||||
* expectation. */
|
||||
if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
|
||||
if (amr_oa_check(buf, len) != conn_src->end.codec->param.amr_octet_aligned)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Execute endpoint specific implementation that handles the
|
||||
@@ -1309,20 +1392,21 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
|
||||
{
|
||||
/* NOTE: The port that is used for RTCP is the RTP port incremented by one
|
||||
* (e.g. RTP-Port = 16000 ==> RTCP-Port = 16001) */
|
||||
struct mgcp_endpoint *endp = &cfg->trunk.endpoints[endpno];
|
||||
|
||||
if (mgcp_create_bind(source_addr, &rtp_end->rtp,
|
||||
rtp_end->local_port) != 0) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x failed to create RTP port: %s:%d\n", endpno,
|
||||
source_addr, rtp_end->local_port);
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to create RTP port: %s:%d\n",
|
||||
source_addr, rtp_end->local_port);
|
||||
goto cleanup0;
|
||||
}
|
||||
|
||||
if (mgcp_create_bind(source_addr, &rtp_end->rtcp,
|
||||
rtp_end->local_port + 1) != 0) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x failed to create RTCP port: %s:%d\n", endpno,
|
||||
source_addr, rtp_end->local_port + 1);
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to create RTCP port: %s:%d\n",
|
||||
source_addr, rtp_end->local_port + 1);
|
||||
goto cleanup1;
|
||||
}
|
||||
|
||||
@@ -1332,17 +1416,17 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
|
||||
|
||||
rtp_end->rtp.when = BSC_FD_READ;
|
||||
if (osmo_fd_register(&rtp_end->rtp) != 0) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x failed to register RTP port %d\n", endpno,
|
||||
rtp_end->local_port);
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to register RTP port %d\n",
|
||||
rtp_end->local_port);
|
||||
goto cleanup2;
|
||||
}
|
||||
|
||||
rtp_end->rtcp.when = BSC_FD_READ;
|
||||
if (osmo_fd_register(&rtp_end->rtcp) != 0) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x failed to register RTCP port %d\n", endpno,
|
||||
rtp_end->local_port + 1);
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to register RTCP port %d\n",
|
||||
rtp_end->local_port + 1);
|
||||
goto cleanup3;
|
||||
}
|
||||
|
||||
@@ -1376,10 +1460,8 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
end = &conn->end;
|
||||
|
||||
if (end->rtp.fd != -1 || end->rtcp.fd != -1) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x %u was already bound on conn:%s\n",
|
||||
ENDPOINT_NUMBER(endp), rtp_port,
|
||||
mgcp_conn_dump(conn->conn));
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "%u was already bound on conn:%s\n",
|
||||
rtp_port, mgcp_conn_dump(conn->conn));
|
||||
|
||||
/* Double bindings should never occour! Since we always allocate
|
||||
* connections dynamically and free them when they are not
|
||||
|
@@ -15,10 +15,12 @@
|
||||
#include <inttypes.h> /* for PRIu64 */
|
||||
#include <netinet/in.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
#include <osmocom/netif/osmux.h>
|
||||
#include <osmocom/netif/rtp.h>
|
||||
#include <osmocom/netif/amr.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
@@ -34,7 +36,7 @@ struct osmux_handle {
|
||||
struct llist_head head;
|
||||
struct osmux_in_handle *in;
|
||||
struct in_addr rem_addr;
|
||||
int rem_port;
|
||||
int rem_port; /* network byte order */
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
@@ -177,6 +179,12 @@ int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn)
|
||||
memcpy(msg->data, buf, buf_len);
|
||||
msgb_put(msg, buf_len);
|
||||
|
||||
if (conn->osmux.state != OSMUX_STATE_ENABLED) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_INFO, "forwarding RTP to Osmux conn not yet enabled, dropping (cid=%d)\n",
|
||||
conn->osmux.cid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.cid)) > 0) {
|
||||
/* batch full, build and deliver it */
|
||||
osmux_xfrm_input_deliver(conn->osmux.in);
|
||||
@@ -185,111 +193,54 @@ int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn)
|
||||
}
|
||||
|
||||
/* Lookup the endpoint that corresponds to the specified address (port) */
|
||||
static struct mgcp_endpoint *
|
||||
endpoint_lookup(struct mgcp_config *cfg, int cid,
|
||||
struct in_addr *from_addr, int type)
|
||||
static struct mgcp_conn_rtp*
|
||||
osmux_conn_lookup(struct mgcp_config *cfg, uint8_t cid,
|
||||
struct in_addr *from_addr)
|
||||
{
|
||||
struct mgcp_endpoint *endp = NULL;
|
||||
struct mgcp_endpoint *endp;
|
||||
struct mgcp_conn *conn = NULL;
|
||||
struct mgcp_conn_rtp * conn_rtp;
|
||||
int i;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
struct mgcp_conn_rtp *conn_bts = NULL;
|
||||
|
||||
for (i=0; i<cfg->trunk.number_endpoints; i++) {
|
||||
struct in_addr *this;
|
||||
|
||||
endp = &cfg->trunk.endpoints[i];
|
||||
|
||||
#if 0
|
||||
if (!tmp->allocated)
|
||||
continue;
|
||||
#endif
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
if (conn->type != MGCP_CONN_TYPE_RTP)
|
||||
continue;
|
||||
|
||||
switch(type) {
|
||||
case MGCP_DEST_NET:
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (conn_net)
|
||||
this = &conn_net->end.addr;
|
||||
else
|
||||
this = NULL;
|
||||
break;
|
||||
case MGCP_DEST_BTS:
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||
if (conn_bts)
|
||||
this = &conn_bts->end.addr;
|
||||
else
|
||||
this = NULL;
|
||||
break;
|
||||
default:
|
||||
/* Should not ever happen */
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type);
|
||||
return NULL;
|
||||
conn_rtp = &conn->u.rtp;
|
||||
if (!mgcp_conn_rtp_is_osmux(conn_rtp))
|
||||
continue;
|
||||
|
||||
if (conn_rtp->osmux.cid == cid)
|
||||
return conn_rtp;
|
||||
}
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (conn_net && this && conn_net->osmux.cid == cid
|
||||
&& this->s_addr == from_addr->s_addr)
|
||||
return endp;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid);
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot find osmux conn with cid=%d\n", cid);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void scheduled_tx_net_cb(struct msgb *msg, void *data)
|
||||
/* FIXME: this is declared and used in mgcp_network.c, but documentation of mgcp_dispatch_rtp_bridge_cb() states another enum is to be used */
|
||||
enum {
|
||||
MGCP_PROTO_RTP,
|
||||
MGCP_PROTO_RTCP,
|
||||
};
|
||||
|
||||
static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data)
|
||||
{
|
||||
struct mgcp_endpoint *endp = data;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
struct mgcp_conn_rtp *conn_bts = NULL;
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_bts || !conn_net)
|
||||
return;
|
||||
|
||||
struct mgcp_conn_rtp *conn = data;
|
||||
struct mgcp_endpoint *endp = conn->conn->endp;
|
||||
struct sockaddr_in addr = {
|
||||
.sin_addr = conn_net->end.addr,
|
||||
.sin_port = conn_net->end.rtp_port,
|
||||
};
|
||||
.sin_addr = conn->end.addr,
|
||||
.sin_port = conn->end.rtp_port,
|
||||
}; /* FIXME: not set/used in cb */
|
||||
|
||||
rate_ctr_inc(&conn_bts->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
|
||||
rate_ctr_add(&conn_bts->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);
|
||||
|
||||
/* Send RTP data to NET */
|
||||
/* FIXME: Get rid of conn_bts and conn_net! */
|
||||
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
|
||||
conn_bts, conn_net);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
|
||||
{
|
||||
struct mgcp_endpoint *endp = data;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
struct mgcp_conn_rtp *conn_bts = NULL;
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_bts || !conn_net)
|
||||
return;
|
||||
|
||||
struct sockaddr_in addr = {
|
||||
.sin_addr = conn_bts->end.addr,
|
||||
.sin_port = conn_bts->end.rtp_port,
|
||||
};
|
||||
|
||||
rate_ctr_inc(&conn_net->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
|
||||
rate_ctr_add(&conn_net->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);
|
||||
|
||||
/* Send RTP data to BTS */
|
||||
/* FIXME: Get rid of conn_bts and conn_net! */
|
||||
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
|
||||
conn_net, conn_bts);
|
||||
endp->type->dispatch_rtp_cb(MGCP_PROTO_RTP, &addr, (char *)msg->data, msg->len, conn->conn);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
@@ -322,24 +273,26 @@ static int endp_osmux_state_check(struct mgcp_endpoint *endp, struct mgcp_conn_r
|
||||
{
|
||||
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));
|
||||
if (osmux_enable_conn(endp, conn, &conn->end.addr, conn->end.rtp_port) < 0) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"Could not enable osmux for conn on %s: %s\n",
|
||||
sending ? "sent" : "received",
|
||||
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);
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"Osmux %s CID %u towards %s:%u is now enabled\n",
|
||||
sending ? "sent" : "received",
|
||||
conn->osmux.cid, inet_ntoa(conn->end.addr),
|
||||
ntohs(conn->end.rtp_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);
|
||||
LOGPCONN(conn->conn, 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;
|
||||
}
|
||||
}
|
||||
@@ -347,9 +300,9 @@ static int endp_osmux_state_check(struct mgcp_endpoint *endp, struct mgcp_conn_r
|
||||
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)) {
|
||||
if (msg->len < 1 + sizeof(*osmux_cid)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Discarding truncated Osmux dummy load\n");
|
||||
"Discarding truncated Osmux dummy load: %s\n", osmo_hexdump(msg->data, msg->len));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -358,16 +311,40 @@ static int osmux_legacy_dummy_parse_cid(struct sockaddr_in *addr, struct msgb *m
|
||||
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)
|
||||
{
|
||||
uint8_t osmux_cid;
|
||||
struct mgcp_conn_rtp *conn;
|
||||
|
||||
if (osmux_legacy_dummy_parse_cid(addr, msg, &osmux_cid) < 0)
|
||||
goto out;
|
||||
|
||||
conn = osmux_conn_lookup(cfg, osmux_cid, &addr->sin_addr);
|
||||
if (!conn) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find conn for Osmux CID %d\n", osmux_cid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
endp_osmux_state_check(conn->conn->endp, conn, false);
|
||||
/* Only needed to punch hole in firewall, it can be dropped */
|
||||
out:
|
||||
msgb_free(msg);
|
||||
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)
|
||||
static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmux_hdr *osmuxh;
|
||||
struct sockaddr_in addr;
|
||||
struct mgcp_config *cfg = ofd->data;
|
||||
uint32_t rem;
|
||||
struct mgcp_conn_rtp *conn_bts = NULL;
|
||||
struct mgcp_conn_rtp *conn_src;
|
||||
|
||||
msg = osmux_recv(ofd, &addr);
|
||||
if (!msg)
|
||||
@@ -381,115 +358,32 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
|
||||
/* not any further processing dummy messages */
|
||||
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
||||
goto out;
|
||||
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_NET to locate the origin */
|
||||
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
|
||||
&addr.sin_addr, MGCP_DEST_NET);
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||
if (!conn_bts)
|
||||
continue;
|
||||
|
||||
if (!endp) {
|
||||
conn_src = osmux_conn_lookup(cfg, osmuxh->circuit_id,
|
||||
&addr.sin_addr);
|
||||
if (!conn_src) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find an endpoint for circuit_id=%d\n",
|
||||
"Cannot find a src conn for circuit_id=%d\n",
|
||||
osmuxh->circuit_id);
|
||||
goto out;
|
||||
}
|
||||
if (endp_osmux_state_check(endp, conn_bts, false) == 0) {
|
||||
conn_bts->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||
conn_bts->osmux.stats.chunks++;
|
||||
osmux_xfrm_output_sched(&conn_bts->osmux.out, osmuxh);
|
||||
}
|
||||
rem = msg->len;
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is called from the bsc-nat */
|
||||
static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
|
||||
struct msgb *msg, int endp_type)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
uint8_t osmux_cid;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
|
||||
if (osmux_legacy_dummy_parse_cid(addr, msg, &osmux_cid) < 0)
|
||||
goto out;
|
||||
|
||||
endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, endp_type);
|
||||
if (!endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find endpoint for Osmux CID %d\n", osmux_cid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn = mgcp_conn_get_rtp(endp, endp_type == MGCP_DEST_BTS ? CONN_ID_NET : CONN_ID_BTS);
|
||||
if (!conn)
|
||||
goto out;
|
||||
|
||||
endp_osmux_state_check(endp, conn, false);
|
||||
/* Only needed to punch hole in firewall, it can be dropped */
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmux_hdr *osmuxh;
|
||||
struct sockaddr_in addr;
|
||||
struct mgcp_config *cfg = ofd->data;
|
||||
uint32_t rem;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
|
||||
msg = osmux_recv(ofd, &addr);
|
||||
if (!msg)
|
||||
return -1;
|
||||
|
||||
if (!cfg->osmux) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"bsc wants to use Osmux but bsc-nat did not request it\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* not any further processing dummy messages */
|
||||
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
||||
return osmux_handle_dummy(cfg, &addr, msg, MGCP_DEST_BTS);
|
||||
|
||||
rem = msg->len;
|
||||
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
/* Yes, we use MGCP_DEST_BTS to locate the origin */
|
||||
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
|
||||
&addr.sin_addr, MGCP_DEST_BTS);
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_net)
|
||||
continue;
|
||||
|
||||
if (!endp) {
|
||||
/*conn_dst = mgcp_find_dst_conn(conn_src->conn);
|
||||
if (!conn_dst) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find an endpoint for circuit_id=%d\n",
|
||||
"Cannot find a dst conn for circuit_id=%d\n",
|
||||
osmuxh->circuit_id);
|
||||
goto out;
|
||||
}
|
||||
if (endp_osmux_state_check(endp, conn_net, false) == 0) {
|
||||
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||
conn_net->osmux.stats.chunks++;
|
||||
osmux_xfrm_output_sched(&conn_net->osmux.out, osmuxh);
|
||||
}*/
|
||||
|
||||
if (endp_osmux_state_check(conn_src->conn->endp, conn_src, false) == 0) {
|
||||
conn_src->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||
conn_src->osmux.stats.chunks++;
|
||||
osmux_xfrm_output_sched(&conn_src->osmux.out, osmuxh);
|
||||
}
|
||||
rem = msg->len;
|
||||
}
|
||||
@@ -502,22 +396,13 @@ 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.cb = osmux_read_fd_cb;
|
||||
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");
|
||||
LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket to %s:%u\n",
|
||||
cfg->osmux_addr, cfg->osmux_port);
|
||||
return ret;
|
||||
}
|
||||
mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp);
|
||||
@@ -525,11 +410,15 @@ int osmux_init(int role, struct mgcp_config *cfg)
|
||||
|
||||
ret = osmo_fd_register(&osmux_fd);
|
||||
if (ret < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket\n");
|
||||
LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket %s\n",
|
||||
osmo_sock_get_name2(osmux_fd.fd));
|
||||
return ret;
|
||||
}
|
||||
cfg->osmux_init = 1;
|
||||
|
||||
LOGP(DLMGCP, LOGL_INFO, "OSMUX socket listening on %s\n",
|
||||
osmo_sock_get_name2(osmux_fd.fd));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -537,7 +426,7 @@ int osmux_init(int role, struct mgcp_config *cfg)
|
||||
* \param[in] endp mgcp endpoint (configuration)
|
||||
* \param[in] conn connection to disable
|
||||
* \param[in] addr IP address of remote OSMUX endpoint
|
||||
* \param[in] port portnumber of the remote OSMUX endpoint
|
||||
* \param[in] port portnumber of the remote OSMUX endpoint (in network byte order)
|
||||
* \returns 0 on success, -1 on ERROR */
|
||||
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||
struct in_addr *addr, uint16_t port)
|
||||
@@ -551,44 +440,46 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||
* overlapping RTP SSRC traveling to the BTSes behind the BSC,
|
||||
* similarly, for flows traveling to the MSC.
|
||||
*/
|
||||
struct in_addr addr_unset = {};
|
||||
static const uint32_t rtp_ssrc_winlen = UINT32_MAX / (OSMUX_CID_MAX + 1);
|
||||
uint16_t osmux_dummy = endp->cfg->osmux_dummy;
|
||||
|
||||
/* Check if osmux is enabled for the specified connection */
|
||||
if (conn->osmux.state != OSMUX_STATE_ACTIVATING) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "conn:%s didn't negotiate Osmux, state %d\n",
|
||||
mgcp_conn_dump(conn->conn), conn->osmux.state);
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"conn:%s didn't negotiate Osmux, state %d\n",
|
||||
mgcp_conn_dump(conn->conn), conn->osmux.state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wait until we have the connection information from MDCX */
|
||||
if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
|
||||
"Osmux remote address/port still unknown\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
|
||||
if (!conn->osmux.in) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle for conn:%s\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"Cannot allocate input osmux handle for conn:%s\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
return -1;
|
||||
}
|
||||
if (!osmux_xfrm_input_open_circuit(conn->osmux.in, conn->osmux.cid, osmux_dummy)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot open osmux circuit %u for conn:%s\n",
|
||||
if (osmux_xfrm_input_open_circuit(conn->osmux.in, conn->osmux.cid, osmux_dummy) < 0) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"Cannot open osmux circuit %u for conn:%s\n",
|
||||
conn->osmux.cid, mgcp_conn_dump(conn->conn));
|
||||
return -1;
|
||||
}
|
||||
|
||||
osmux_xfrm_output_init(&conn->osmux.out,
|
||||
osmux_xfrm_output_init2(&conn->osmux.out,
|
||||
(conn->osmux.cid * rtp_ssrc_winlen) +
|
||||
(random() % rtp_ssrc_winlen));
|
||||
(random() % rtp_ssrc_winlen),
|
||||
conn->end.codec->payload_type);
|
||||
|
||||
switch (endp->cfg->role) {
|
||||
case MGCP_BSC_NAT:
|
||||
conn->type = MGCP_OSMUX_BSC_NAT;
|
||||
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
|
||||
scheduled_tx_net_cb, endp);
|
||||
break;
|
||||
case MGCP_BSC:
|
||||
conn->type = MGCP_OSMUX_BSC;
|
||||
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
|
||||
scheduled_tx_bts_cb, endp);
|
||||
break;
|
||||
}
|
||||
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
|
||||
scheduled_from_osmux_tx_rtp_cb, conn);
|
||||
|
||||
conn->osmux.state = OSMUX_STATE_ENABLED;
|
||||
|
||||
@@ -597,48 +488,65 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||
|
||||
/*! disable OSXMUX circuit for a specified connection.
|
||||
* \param[in] conn connection to disable */
|
||||
void osmux_disable_conn(struct mgcp_conn_rtp *conn)
|
||||
void conn_osmux_disable(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
if (conn->osmux.state != OSMUX_STATE_ENABLED)
|
||||
return;
|
||||
OSMO_ASSERT(conn->osmux.state != OSMUX_STATE_DISABLED);
|
||||
|
||||
LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
|
||||
conn->conn->id, conn->osmux.cid);
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
|
||||
"Releasing connection using Osmux CID %u\n", conn->osmux.cid);
|
||||
|
||||
/* We are closing, we don't need pending RTP packets to be transmitted */
|
||||
osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL);
|
||||
osmux_xfrm_output_flush(&conn->osmux.out);
|
||||
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
|
||||
/* We are closing, we don't need pending RTP packets to be transmitted */
|
||||
osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL);
|
||||
osmux_xfrm_output_flush(&conn->osmux.out);
|
||||
|
||||
osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
|
||||
conn->osmux.state = OSMUX_STATE_DISABLED;
|
||||
conn->osmux.cid = -1;
|
||||
osmux_handle_put(conn->osmux.in);
|
||||
osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
|
||||
conn->osmux.state = OSMUX_STATE_DISABLED;
|
||||
conn_osmux_release_cid(conn);
|
||||
osmux_handle_put(conn->osmux.in);
|
||||
}
|
||||
conn_osmux_release_cid(conn);
|
||||
}
|
||||
|
||||
/*! relase OSXMUX cid, that had been allocated to this connection.
|
||||
* \param[in] conn connection with OSMUX cid to release */
|
||||
void osmux_release_cid(struct mgcp_conn_rtp *conn)
|
||||
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
if (conn->osmux.state != OSMUX_STATE_ENABLED)
|
||||
return;
|
||||
|
||||
if (conn->osmux.allocated_cid >= 0)
|
||||
osmux_put_cid(conn->osmux.allocated_cid);
|
||||
conn->osmux.allocated_cid = -1;
|
||||
if (conn->osmux.cid_allocated)
|
||||
osmux_cid_pool_put(conn->osmux.cid);
|
||||
conn->osmux.cid = 0;
|
||||
conn->osmux.cid_allocated = false;
|
||||
}
|
||||
|
||||
/*! allocate OSXMUX cid to connection.
|
||||
* \param[in] conn connection for which we allocate the OSMUX cid*/
|
||||
void osmux_allocate_cid(struct mgcp_conn_rtp *conn)
|
||||
* \param[in] conn connection for which we allocate the OSMUX cid
|
||||
* \param[in] osmux_cid OSMUX cid to allocate. -1 Means take next available one.
|
||||
* \returns Allocated OSMUX cid, -1 on error (no free cids avail, or selected one is already taken).
|
||||
*/
|
||||
int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid)
|
||||
{
|
||||
osmux_release_cid(conn);
|
||||
conn->osmux.allocated_cid = osmux_get_cid();
|
||||
if (osmux_cid != -1 && osmux_cid_pool_allocated((uint8_t) osmux_cid)) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
|
||||
"Osmux CID %d already allocated!\n",
|
||||
osmux_cid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (osmux_cid == -1) {
|
||||
osmux_cid = osmux_cid_pool_get_next();
|
||||
if (osmux_cid == -1) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
|
||||
"no available Osmux CID to allocate!\n");
|
||||
return -1;
|
||||
}
|
||||
} else
|
||||
osmux_cid_pool_get(osmux_cid);
|
||||
|
||||
conn->osmux.cid = (uint8_t) osmux_cid;
|
||||
conn->osmux.cid_allocated = true;
|
||||
conn->type = MGCP_OSMUX_BSC;
|
||||
return osmux_cid;
|
||||
}
|
||||
|
||||
/*! send RTP dummy packet to OSMUX connection port.
|
||||
@@ -647,7 +555,8 @@ void osmux_allocate_cid(struct mgcp_conn_rtp *conn)
|
||||
* \returns bytes sent, -1 on error */
|
||||
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
char buf[1 + sizeof(uint8_t)];
|
||||
struct osmux_hdr *osmuxh;
|
||||
int buf_len;
|
||||
struct in_addr addr_unset = {};
|
||||
|
||||
/*! The dummy packet will not be sent via the actual OSMUX connection,
|
||||
@@ -659,9 +568,6 @@ int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
* endpoint may have already punched the hole in the firewall. This
|
||||
* approach is simple though. */
|
||||
|
||||
buf[0] = MGCP_DUMMY_LOAD;
|
||||
memcpy(&buf[1], &conn->osmux.cid, sizeof(conn->osmux.cid));
|
||||
|
||||
/* Wait until we have the connection information from MDCX */
|
||||
if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0)
|
||||
return 0;
|
||||
@@ -669,12 +575,19 @@ int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
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);
|
||||
buf_len = sizeof(struct osmux_hdr) + osmo_amr_bytes(AMR_FT_0);
|
||||
osmuxh = (struct osmux_hdr *) alloca(buf_len);
|
||||
memset(osmuxh, 0, buf_len);
|
||||
osmuxh->ft = OSMUX_FT_DUMMY;
|
||||
osmuxh->amr_ft = AMR_FT_0;
|
||||
osmuxh->circuit_id = conn->osmux.cid;
|
||||
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
|
||||
"sending OSMUX dummy load to %s:%u CID %u\n",
|
||||
inet_ntoa(conn->end.addr), ntohs(conn->end.rtp_port), conn->osmux.cid);
|
||||
|
||||
return mgcp_udp_send(osmux_fd.fd, &conn->end.addr,
|
||||
htons(endp->cfg->osmux_port), buf, sizeof(buf));
|
||||
conn->end.rtp_port, (char*)osmuxh, buf_len);
|
||||
}
|
||||
|
||||
/* bsc-nat allocates/releases the Osmux circuit ID. +7 to round up to 8 bit boundary. */
|
||||
@@ -682,7 +595,7 @@ static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1 + 7) / 8];
|
||||
|
||||
/*! count the number of taken OSMUX cids.
|
||||
* \returns number of OSMUX cids in use */
|
||||
int osmux_used_cid(void)
|
||||
int osmux_cid_pool_count_used(void)
|
||||
{
|
||||
int i, j, used = 0;
|
||||
|
||||
@@ -698,7 +611,7 @@ int osmux_used_cid(void)
|
||||
|
||||
/*! take a free OSMUX cid.
|
||||
* \returns OSMUX cid */
|
||||
int osmux_get_cid(void)
|
||||
int osmux_cid_pool_get_next(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
@@ -718,10 +631,24 @@ int osmux_get_cid(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! take a specific OSMUX cid.
|
||||
* \param[in] osmux_cid OSMUX cid */
|
||||
void osmux_cid_pool_get(uint8_t osmux_cid)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Allocating Osmux CID %u from pool\n", osmux_cid);
|
||||
osmux_cid_bitmap[osmux_cid / 8] |= (1 << (osmux_cid % 8));
|
||||
}
|
||||
|
||||
/*! put back a no longer used OSMUX cid.
|
||||
* \param[in] osmux_cid OSMUX cid */
|
||||
void osmux_put_cid(uint8_t osmux_cid)
|
||||
void osmux_cid_pool_put(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));
|
||||
}
|
||||
|
||||
/*! check if OSMUX cid is already taken */
|
||||
bool osmux_cid_pool_allocated(uint8_t osmux_cid)
|
||||
{
|
||||
return !!(osmux_cid_bitmap[osmux_cid / 8] & (1 << (osmux_cid % 8)));
|
||||
}
|
||||
|
@@ -93,7 +93,7 @@ static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
|
||||
[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_FAIL_DEFERRED_BY_POLICY] = {"mdcx:conn_deferred", "connection deferred 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 = {
|
||||
@@ -104,6 +104,25 @@ const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_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",
|
||||
@@ -128,17 +147,15 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst = conn;
|
||||
struct mgcp_conn *_conn;
|
||||
|
||||
if (conn->type != MGCP_RTP_DEFAULT) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"endpoint:%x RTP-setup: Endpoint is not configured as RTP default, stopping here!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
if (conn->type != MGCP_RTP_DEFAULT && !mgcp_conn_rtp_is_osmux(conn)) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (conn->conn->mode == MGCP_CONN_LOOPBACK) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"endpoint:%x RTP-setup: Endpoint is in loopback mode, stopping here!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"RTP-setup: Endpoint is in loopback mode, stopping here!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -158,13 +175,13 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
|
||||
* for performance reasons. */
|
||||
static const struct mgcp_request mgcp_requests[] = {
|
||||
MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint")
|
||||
MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
|
||||
MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
|
||||
MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
|
||||
MGCP_REQUEST("RQNT", handle_noti_req, "NotificationRequest")
|
||||
MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
|
||||
MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
|
||||
MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
|
||||
MGCP_REQUEST("RQNT", handle_noti_req, "NotificationRequest")
|
||||
|
||||
/* SPEC extension */
|
||||
MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress")
|
||||
/* SPEC extension */
|
||||
MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress")
|
||||
};
|
||||
|
||||
/* Helper function to allocate some memory for responses and retransmissions */
|
||||
@@ -206,13 +223,13 @@ static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
|
||||
len = snprintf((char *)res->data, 2048, "%d %s%s%s\r\n%s",
|
||||
code, trans, txt, param ? param : "", sdp ? sdp : "");
|
||||
if (len < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
|
||||
msgb_free(res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res->l2h = msgb_put(res, len);
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
|
||||
mgcp_disp_msg(res->l2h, msgb_l2len(res), "Generated response");
|
||||
|
||||
/*
|
||||
@@ -283,11 +300,13 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
|
||||
const char *trans_id,
|
||||
bool add_conn_params)
|
||||
{
|
||||
/* TODO: we may want to define another local_ip_osmux var to us for
|
||||
OSMUX connections. Perhaps adding a new internal API to get it based
|
||||
on conn type */
|
||||
const char *addr = endp->cfg->local_ip;
|
||||
struct msgb *sdp;
|
||||
int rc;
|
||||
struct msgb *result;
|
||||
char osmux_extension[strlen("X-Osmux: 255") + 1];
|
||||
char local_ip_addr[INET_ADDRSTRLEN];
|
||||
|
||||
sdp = msgb_alloc_headroom(4096, 128, "sdp record");
|
||||
@@ -299,13 +318,6 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
|
||||
addr = local_ip_addr;
|
||||
}
|
||||
|
||||
if (conn->osmux.state == OSMUX_STATE_NEGOTIATING) {
|
||||
sprintf(osmux_extension, "X-Osmux: %u", conn->osmux.cid);
|
||||
conn->osmux.state = OSMUX_STATE_ACTIVATING;
|
||||
} else {
|
||||
osmux_extension[0] = '\0';
|
||||
}
|
||||
|
||||
/* Attach optional connection parameters */
|
||||
if (add_conn_params) {
|
||||
rc = add_params(sdp, endp, conn);
|
||||
@@ -314,8 +326,8 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
|
||||
}
|
||||
|
||||
/* Attach optional OSMUX parameters */
|
||||
if (conn->osmux.state == OSMUX_STATE_NEGOTIATING) {
|
||||
rc = msgb_printf(sdp, "%s\r\n", osmux_extension);
|
||||
if (mgcp_conn_rtp_is_osmux(conn)) {
|
||||
rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
|
||||
if (rc < 0)
|
||||
goto error;
|
||||
}
|
||||
@@ -411,15 +423,15 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
/* AUEP command handler, processes the received command */
|
||||
static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
|
||||
LOGPENDP(p->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
|
||||
return create_ok_response(p->endp, 200, "AUEP", p->trans);
|
||||
}
|
||||
|
||||
/* Try to find a free port by attempting to bind on it. Also handle the
|
||||
* counter that points on the next free port. Since we have a pointer
|
||||
* to the next free port, binding should in work on the first attempt in
|
||||
* general. In case of failure the next port is tryed until the whole port
|
||||
* range is tryed once. */
|
||||
* general. In case of failure the next port is tried until the whole port
|
||||
* range is tried once. */
|
||||
static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
int i;
|
||||
@@ -450,9 +462,9 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Allocating a RTP/RTCP port failed %u times 0x%x.\n",
|
||||
tries, ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"Allocating a RTP/RTCP port failed %u times.\n",
|
||||
tries);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -547,7 +559,7 @@ int check_local_cx_options(void *ctx, const char *options)
|
||||
* before. If yes, we must bail, an LCO must only appear once
|
||||
* in the LCO string */
|
||||
for (i = 0; i < lco_seen_n; i++) {
|
||||
if (strcmp(lco_seen[i], lco_identifier) == 0)
|
||||
if (strcasecmp(lco_seen[i], lco_identifier) == 0)
|
||||
goto error;
|
||||
}
|
||||
lco_seen[lco_seen_n] = lco_identifier;
|
||||
@@ -578,8 +590,9 @@ error:
|
||||
static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
|
||||
const char *options)
|
||||
{
|
||||
char *p_opt, *a_opt;
|
||||
char *lco_id;
|
||||
char codec[17];
|
||||
int len;
|
||||
|
||||
if (!options)
|
||||
return 0;
|
||||
@@ -596,18 +609,37 @@ static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
|
||||
talloc_free(lco->string);
|
||||
lco->string = talloc_strdup(ctx, options);
|
||||
|
||||
p_opt = strstr(lco->string, "p:");
|
||||
if (p_opt && sscanf(p_opt, "p:%d-%d",
|
||||
&lco->pkt_period_min, &lco->pkt_period_max) == 1)
|
||||
lco->pkt_period_max = lco->pkt_period_min;
|
||||
lco_id = lco->string;
|
||||
while ((lco_id = get_lco_identifier(lco_id))) {
|
||||
switch (tolower(lco_id[0])) {
|
||||
case 'p':
|
||||
if (sscanf(lco_id + 1, ":%d-%d",
|
||||
&lco->pkt_period_min, &lco->pkt_period_max) == 1)
|
||||
lco->pkt_period_max = lco->pkt_period_min;
|
||||
break;
|
||||
case 'a':
|
||||
/* FIXME: LCO also supports the negotiation of more then one codec.
|
||||
* (e.g. a:PCMU;G726-32) But this implementation only supports a single
|
||||
* codec only. */
|
||||
if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
|
||||
talloc_free(lco->codec);
|
||||
/* MGCP header is case insensive, and we'll need
|
||||
codec in uppercase when using it later: */
|
||||
len = strlen(codec);
|
||||
lco->codec = talloc_size(ctx, len + 1);
|
||||
osmo_str_toupper_buf(lco->codec, len + 1, codec);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"LCO: unhandled option: '%c'/%d in \"%s\"\n",
|
||||
*lco_id, *lco_id, lco->string);
|
||||
break;
|
||||
}
|
||||
|
||||
/* FIXME: LCO also supports the negotiation of more then one codec.
|
||||
* (e.g. a:PCMU;G726-32) But this implementation only supports a single
|
||||
* codec only. */
|
||||
a_opt = strstr(lco->string, "a:");
|
||||
if (a_opt && sscanf(a_opt, "a:%16[^,]", codec) == 1) {
|
||||
talloc_free(lco->codec);
|
||||
lco->codec = talloc_strdup(ctx, codec);
|
||||
lco_id = strchr(lco_id, ',');
|
||||
if (!lco_id)
|
||||
break;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
@@ -633,12 +665,13 @@ 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;
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"Configuring RTP endpoint: local port %d%s%s\n",
|
||||
ntohs(rtp->rtp_port),
|
||||
rtp->force_aligned_timing ? ", force constant timing" : "",
|
||||
rtp->force_constant_ssrc ? ", force constant ssrc" : "");
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
|
||||
"Configuring RTP endpoint: local port %d%s%s\n",
|
||||
ntohs(rtp->rtp_port),
|
||||
rtp->force_aligned_timing ? ", force constant timing" : "",
|
||||
rtp->force_constant_ssrc ? ", force constant ssrc" : "");
|
||||
}
|
||||
|
||||
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
|
||||
@@ -660,14 +693,19 @@ uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
|
||||
rtp->codec->frame_duration_den;
|
||||
}
|
||||
|
||||
/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
|
||||
* \param[in] endp Endpoint willing to initialize osmux
|
||||
* \param[in] line Line X-Osmux from MGCP header msg to parse
|
||||
* \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
|
||||
*/
|
||||
static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
|
||||
{
|
||||
if (!endp->cfg->osmux_init) {
|
||||
if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
|
||||
return -1;
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
|
||||
return -3;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
|
||||
}
|
||||
|
||||
return mgcp_parse_osmux_cid(line);
|
||||
@@ -693,9 +731,8 @@ static int handle_codec_info(struct mgcp_conn_rtp *conn,
|
||||
mgcp_codec_reset_all(conn);
|
||||
rc = mgcp_parse_sdp_data(endp, conn, p);
|
||||
if (rc != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"%s: endpoint:%x sdp not parseable\n", cmd,
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"%s: sdp not parseable\n", cmd);
|
||||
|
||||
/* See also RFC 3661: Protocol error */
|
||||
return 510;
|
||||
@@ -704,7 +741,7 @@ static int handle_codec_info(struct mgcp_conn_rtp *conn,
|
||||
/* When no SDP is available, we use the codec information from
|
||||
* the local connection options (if present) */
|
||||
mgcp_codec_reset_all(conn);
|
||||
rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec);
|
||||
rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
|
||||
if (rc != 0)
|
||||
goto error;
|
||||
}
|
||||
@@ -715,7 +752,7 @@ static int handle_codec_info(struct mgcp_conn_rtp *conn,
|
||||
* than it makes sense to pick a sane default: (payload-type 0,
|
||||
* PCMU), see also: OS#2658 */
|
||||
mgcp_codec_reset_all(conn);
|
||||
rc = mgcp_codec_add(conn, 0, NULL);
|
||||
rc = mgcp_codec_add(conn, 0, NULL, NULL);
|
||||
if (rc != 0)
|
||||
goto error;
|
||||
}
|
||||
@@ -727,9 +764,8 @@ static int handle_codec_info(struct mgcp_conn_rtp *conn,
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"%s: endpoint:0x%x codec negotiation failure\n", cmd,
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"%s: codec negotiation failure\n", cmd);
|
||||
|
||||
/* See also RFC 3661: Codec negotiation failure */
|
||||
return 534;
|
||||
@@ -749,11 +785,10 @@ static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
|
||||
if (!token)
|
||||
break;
|
||||
|
||||
if (!strcmp(token, "C"))
|
||||
if (!strcasecmp(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);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -770,20 +805,20 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
|
||||
const char *callid = NULL;
|
||||
const char *mode = NULL;
|
||||
char *line;
|
||||
int have_sdp = 0, osmux_cid = -1;
|
||||
int have_sdp = 0, osmux_cid = -2;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
struct mgcp_conn *_conn = NULL;
|
||||
char conn_name[512];
|
||||
int rc;
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
|
||||
|
||||
/* parse CallID C: and LocalParameters L: */
|
||||
for_each_line(line, p->save) {
|
||||
if (!mgcp_check_param(endp, line))
|
||||
continue;
|
||||
|
||||
switch (line[0]) {
|
||||
switch (toupper(line[0])) {
|
||||
case 'L':
|
||||
local_options = (const char *)line + 3;
|
||||
break;
|
||||
@@ -801,7 +836,7 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
|
||||
mode = (const char *)line + 3;
|
||||
break;
|
||||
case 'X':
|
||||
if (strncmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
|
||||
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
|
||||
/* If osmux is disabled, just skip setting it up */
|
||||
if (!p->endp->cfg->osmux)
|
||||
break;
|
||||
@@ -818,9 +853,8 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
|
||||
have_sdp = 1;
|
||||
goto mgcp_header_done;
|
||||
default:
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: endpoint:%x unhandled option: '%c'/%d\n",
|
||||
ENDPOINT_NUMBER(endp), *line, *line);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: unhandled option: '%c'/%d\n", *line, *line);
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_UNHANDLED_PARAM]);
|
||||
return create_err_response(NULL, 539, "CRCX", p->trans);
|
||||
break;
|
||||
@@ -830,26 +864,24 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
|
||||
mgcp_header_done:
|
||||
/* Check parameters */
|
||||
if (!callid) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:%x insufficient parameters, missing callid\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: insufficient parameters, missing callid\n");
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_MISSING_CALLID]);
|
||||
return create_err_response(endp, 516, "CRCX", p->trans);
|
||||
}
|
||||
|
||||
if (!mode) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:%x insufficient parameters, missing mode\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: insufficient parameters, missing mode\n");
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_INVALID_MODE]);
|
||||
return create_err_response(endp, 517, "CRCX", p->trans);
|
||||
}
|
||||
|
||||
/* Check if we are able to accept the creation of another connection */
|
||||
if (llist_count(&endp->conns) >= endp->type->max_conns) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:%x endpoint full, max. %i connections allowed!\n",
|
||||
endp->type->max_conns, ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint full, max. %i connections allowed!\n",
|
||||
endp->type->max_conns);
|
||||
if (tcfg->force_realloc) {
|
||||
/* There is no more room for a connection, make some
|
||||
* room by blindly tossing the oldest of the two two
|
||||
@@ -866,9 +898,9 @@ mgcp_header_done:
|
||||
/* Check if this endpoint already serves a call, if so, check if the
|
||||
* callids match up so that we are sure that this is our call */
|
||||
if (endp->callid && mgcp_verify_call_id(endp, callid)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:0x%x allready seized by other call (%s)\n",
|
||||
ENDPOINT_NUMBER(endp), endp->callid);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: already seized by other call (%s)\n",
|
||||
endp->callid);
|
||||
if (tcfg->force_realloc)
|
||||
/* This is not our call, toss everything by releasing
|
||||
* the entire endpoint. (rude!) */
|
||||
@@ -889,9 +921,8 @@ mgcp_header_done:
|
||||
snprintf(conn_name, sizeof(conn_name), "%s", callid);
|
||||
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, conn_name);
|
||||
if (!_conn) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:0x%x unable to allocate RTP connection\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: unable to allocate RTP connection\n");
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_ALLOC_CONN]);
|
||||
goto error2;
|
||||
|
||||
@@ -908,13 +939,15 @@ mgcp_header_done:
|
||||
/* Annotate Osmux circuit ID and set it to negotiating state until this
|
||||
* is fully set up from the dummy load. */
|
||||
conn->osmux.state = OSMUX_STATE_DISABLED;
|
||||
if (osmux_cid >= 0) {
|
||||
conn->osmux.cid = osmux_cid;
|
||||
conn->osmux.state = OSMUX_STATE_NEGOTIATING;
|
||||
if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
|
||||
conn->osmux.state = OSMUX_STATE_ACTIVATING;
|
||||
if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_NO_OSMUX]);
|
||||
goto error2;
|
||||
}
|
||||
} else if (endp->cfg->osmux == OSMUX_USAGE_ONLY) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:0x%x osmux only and no osmux offered\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: osmux only and no osmux offered\n");
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_NO_OSMUX]);
|
||||
goto error2;
|
||||
}
|
||||
@@ -924,9 +957,8 @@ mgcp_header_done:
|
||||
rc = set_local_cx_options(endp->tcfg->endpoints,
|
||||
&endp->local_options, local_options);
|
||||
if (rc != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:%x inavlid local connection options!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: inavlid local connection options!\n");
|
||||
error_code = rc;
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS]);
|
||||
goto error2;
|
||||
@@ -956,9 +988,8 @@ mgcp_header_done:
|
||||
if (conn->conn->mode != MGCP_CONN_LOOPBACK
|
||||
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
|
||||
&& conn->end.rtp_port == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:%x selected connection mode type requires an opposite end!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: selected connection mode type requires an opposite end!\n");
|
||||
error_code = 527;
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC]);
|
||||
goto error2;
|
||||
@@ -970,9 +1001,8 @@ mgcp_header_done:
|
||||
}
|
||||
|
||||
if (setup_rtp_processing(endp, conn) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:0x%x could not start RTP processing!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: could not start RTP processing!\n");
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_START_RTP]);
|
||||
goto error2;
|
||||
}
|
||||
@@ -984,9 +1014,8 @@ mgcp_header_done:
|
||||
MGCP_ENDP_CRCX, p->trans);
|
||||
switch (rc) {
|
||||
case MGCP_POLICY_REJECT:
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: endpoint:0x%x CRCX rejected by policy\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: CRCX rejected by policy\n");
|
||||
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);
|
||||
@@ -1001,9 +1030,8 @@ mgcp_header_done:
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"CRCX: endpoint:0x%x Creating connection: CI: %s port: %u\n",
|
||||
ENDPOINT_NUMBER(endp), conn->conn->id, conn->end.local_port);
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
|
||||
"CRCX: Creating connection: port: %u\n", conn->end.local_port);
|
||||
if (p->cfg->change_cb)
|
||||
p->cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX);
|
||||
|
||||
@@ -1013,16 +1041,14 @@ mgcp_header_done:
|
||||
&& tcfg->keepalive_interval != MGCP_KEEPALIVE_NEVER)
|
||||
send_dummy(endp, conn);
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: endpoint:0x%x connection successfully created\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: connection successfully created\n");
|
||||
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);
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: endpoint:0x%x unable to create connection\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: unable to create connection\n");
|
||||
return create_err_response(endp, error_code, "CRCX", p->trans);
|
||||
}
|
||||
|
||||
@@ -1044,23 +1070,22 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
||||
const char *mode = NULL;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
const char *conn_id = NULL;
|
||||
int osmux_cid = -2;
|
||||
int rc;
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
|
||||
|
||||
/* Prohibit wildcarded requests */
|
||||
if (endp->wildcarded_req) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint:0x%x wildcarded endpoint names not supported.\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: wildcarded endpoint names not supported.\n");
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_WILDCARD]);
|
||||
return create_err_response(endp, 507, "MDCX", p->trans);
|
||||
}
|
||||
|
||||
if (llist_count(&endp->conns) <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint:0x%x endpoint is not holding a connection.\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint is not holding a connection.\n");
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_NO_CONN]);
|
||||
return create_err_response(endp, 400, "MDCX", p->trans);
|
||||
}
|
||||
@@ -1069,7 +1094,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
||||
if (!mgcp_check_param(endp, line))
|
||||
continue;
|
||||
|
||||
switch (line[0]) {
|
||||
switch (toupper(line[0])) {
|
||||
case 'C':
|
||||
if (mgcp_verify_call_id(endp, line + 3) != 0) {
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_CALLID]);
|
||||
@@ -1091,16 +1116,26 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
||||
mode = (const char *)line + 3;
|
||||
break;
|
||||
case 'Z':
|
||||
silent = strcmp("noanswer", line + 3) == 0;
|
||||
silent = strcasecmp("noanswer", line + 3) == 0;
|
||||
break;
|
||||
case 'X':
|
||||
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
|
||||
/* If osmux is disabled, just skip setting it up */
|
||||
if (!p->endp->cfg->osmux)
|
||||
break;
|
||||
osmux_cid = mgcp_osmux_setup(endp, line);
|
||||
break;
|
||||
}
|
||||
/* Ignore unknown X-headers */
|
||||
break;
|
||||
case '\0':
|
||||
have_sdp = 1;
|
||||
goto mgcp_header_done;
|
||||
break;
|
||||
default:
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"MDCX: endpoint:0x%x Unhandled MGCP option: '%c'/%d\n",
|
||||
ENDPOINT_NUMBER(endp), line[0], line[0]);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"MDCX: Unhandled MGCP option: '%c'/%d\n",
|
||||
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;
|
||||
@@ -1109,9 +1144,8 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
||||
|
||||
mgcp_header_done:
|
||||
if (!conn_id) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint:0x%x insufficient parameters, missing ci (connectionIdentifier)\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_NO_CONNID]);
|
||||
return create_err_response(endp, 515, "MDCX", p->trans);
|
||||
}
|
||||
@@ -1122,6 +1156,8 @@ mgcp_header_done:
|
||||
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]);
|
||||
@@ -1129,16 +1165,15 @@ mgcp_header_done:
|
||||
goto error3;
|
||||
}
|
||||
} else
|
||||
conn->conn->mode = conn->conn->mode_orig;
|
||||
conn->conn->mode = conn->conn->mode_orig;
|
||||
|
||||
/* Set local connection options, if present */
|
||||
if (local_options) {
|
||||
rc = set_local_cx_options(endp->tcfg->endpoints,
|
||||
&endp->local_options, local_options);
|
||||
if (rc != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint:%x invalid local connection options!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: invalid local connection options!\n");
|
||||
error_code = rc;
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS]);
|
||||
goto error3;
|
||||
@@ -1157,14 +1192,32 @@ mgcp_header_done:
|
||||
if (conn->conn->mode != MGCP_CONN_LOOPBACK
|
||||
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
|
||||
&& conn->end.rtp_port == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint:%x selected connection mode type requires an opposite end!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: selected connection mode type requires an opposite end!\n");
|
||||
error_code = 527;
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC]);
|
||||
goto error3;
|
||||
}
|
||||
|
||||
if (mgcp_conn_rtp_is_osmux(conn)) {
|
||||
OSMO_ASSERT(conn->osmux.cid_allocated);
|
||||
if (osmux_cid < -1) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: Failed to parse Osmux CID!\n");
|
||||
goto error3;
|
||||
} else if (osmux_cid == -1) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: wilcard in MDCX is not supported!\n");
|
||||
goto error3;
|
||||
} else if (osmux_cid != (int) conn->osmux.cid) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
"MDCX: changing already allocated CID is not supported!\n");
|
||||
goto error3;
|
||||
}
|
||||
/* TODO: In the future (when we have recvCID!=sendCID), we need to
|
||||
tell Osmux code that osmux_cid is to be used as sendCID for
|
||||
that conn. */
|
||||
}
|
||||
|
||||
if (setup_rtp_processing(endp, conn) != 0) {
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_START_RTP]);
|
||||
@@ -1179,9 +1232,8 @@ mgcp_header_done:
|
||||
MGCP_ENDP_MDCX, p->trans);
|
||||
switch (rc) {
|
||||
case MGCP_POLICY_REJECT:
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"MDCX: endpoint:0x%x rejected by policy\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
|
||||
"MDCX: rejected by policy\n");
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_REJECTED_BY_POLICY]);
|
||||
if (silent)
|
||||
goto out_silent;
|
||||
@@ -1189,10 +1241,9 @@ mgcp_header_done:
|
||||
break;
|
||||
case MGCP_POLICY_DEFER:
|
||||
/* stop processing */
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"MDCX: endpoint:0x%x deferred by policy\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_DEFERRED_BY_POLICY]);
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
|
||||
"MDCX: deferred by policy\n");
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_DEFERRED_BY_POLICY]);
|
||||
return NULL;
|
||||
break;
|
||||
case MGCP_POLICY_CONT:
|
||||
@@ -1204,9 +1255,8 @@ mgcp_header_done:
|
||||
mgcp_rtp_end_config(endp, 1, &conn->end);
|
||||
|
||||
/* modify */
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"MDCX: endpoint:0x%x modified conn:%s\n",
|
||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
|
||||
"MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
|
||||
if (p->cfg->change_cb)
|
||||
p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
|
||||
MGCP_ENDP_MDCX);
|
||||
@@ -1221,23 +1271,23 @@ mgcp_header_done:
|
||||
if (silent)
|
||||
goto out_silent;
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"MDCX: endpoint:0x%x connection successfully modified\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
|
||||
"MDCX: connection successfully modified\n");
|
||||
return create_response_with_sdp(endp, conn, "MDCX", p->trans, false);
|
||||
error3:
|
||||
return create_err_response(endp, error_code, "MDCX", p->trans);
|
||||
|
||||
out_silent:
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "MDCX: endpoint:0x%x silent exit\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
@@ -1245,22 +1295,21 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
const char *conn_id = NULL;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: endpoint:0x%x deleting connection ...\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: deleting connection ...\n");
|
||||
|
||||
/* Prohibit wildcarded requests */
|
||||
if (endp->wildcarded_req) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"DLCX: endpoint:0x%x wildcarded endpoint names not supported.\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"DLCX: wildcarded endpoint names not supported.\n");
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_WILDCARD]);
|
||||
return create_err_response(endp, 507, "DLCX", p->trans);
|
||||
}
|
||||
|
||||
if (llist_count(&endp->conns) <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"DLCX: endpoint:0x%x endpoint is not holding a connection.\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"DLCX: endpoint is not holding a connection.\n");
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_NO_CONN]);
|
||||
return create_err_response(endp, 515, "DLCX", p->trans);
|
||||
}
|
||||
|
||||
@@ -1268,25 +1317,29 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
if (!mgcp_check_param(endp, line))
|
||||
continue;
|
||||
|
||||
switch (line[0]) {
|
||||
switch (toupper(line[0])) {
|
||||
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 ((error_code = mgcp_verify_ci(endp, conn_id)))
|
||||
if ((error_code = mgcp_verify_ci(endp, conn_id))) {
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_INVALID_CONNID]);
|
||||
goto error3;
|
||||
}
|
||||
break;
|
||||
case 'Z':
|
||||
silent = strcmp("noanswer", line + 3) == 0;
|
||||
silent = strcasecmp("noanswer", line + 3) == 0;
|
||||
break;
|
||||
default:
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: endpoint:0x%x Unhandled MGCP option: '%c'/%d\n",
|
||||
ENDPOINT_NUMBER(endp), line[0], line[0]);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: Unhandled MGCP option: '%c'/%d\n",
|
||||
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;
|
||||
}
|
||||
@@ -1299,15 +1352,15 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
MGCP_ENDP_DLCX, p->trans);
|
||||
switch (rc) {
|
||||
case MGCP_POLICY_REJECT:
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: endpoint:0x%x rejected by policy\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "DLCX: rejected by policy\n");
|
||||
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:
|
||||
@@ -1320,9 +1373,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) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: endpoint:0x%x missing ci (connectionIdentifier), will remove all connections at once\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
int num_conns = llist_count(&endp->conns);
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
|
||||
num_conns);
|
||||
|
||||
if (num_conns > 0)
|
||||
rate_ctr_add(&rate_ctrs->ctr[MGCP_DLCX_SUCCESS], num_conns);
|
||||
|
||||
mgcp_endp_release(endp);
|
||||
|
||||
@@ -1334,33 +1391,32 @@ 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);
|
||||
|
||||
/* delete connection */
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "DLCX: endpoint:0x%x deleting conn:%s\n",
|
||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
mgcp_conn_free(endp, conn_id);
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: endpoint:0x%x connection successfully deleted\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: connection successfully deleted\n");
|
||||
|
||||
/* When all connections are closed, the endpoint will be released
|
||||
* in order to be ready to be used by another call. */
|
||||
if (llist_count(&endp->conns) <= 0) {
|
||||
mgcp_endp_release(endp);
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"DLCX: endpoint:0x%x endpoint released\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
|
||||
}
|
||||
|
||||
if (p->cfg->change_cb)
|
||||
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);
|
||||
@@ -1369,8 +1425,7 @@ error3:
|
||||
return create_err_response(endp, error_code, "DLCX", p->trans);
|
||||
|
||||
out_silent:
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "DLCX: endpoint:0x%x silent exit\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1413,7 +1468,7 @@ static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
|
||||
|
||||
for_each_line(line, p->save) {
|
||||
switch (line[0]) {
|
||||
switch (toupper(line[0])) {
|
||||
case 'S':
|
||||
tone = extract_tone(line);
|
||||
break;
|
||||
@@ -1496,34 +1551,46 @@ static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alloc_mgcp_rate_counters(struct mgcp_trunk_config *trunk, void *ctx)
|
||||
static int 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);
|
||||
OSMO_ASSERT(trunk->mgcp_crcx_ctr_group);
|
||||
if (!trunk->mgcp_crcx_ctr_group)
|
||||
return -1;
|
||||
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);
|
||||
OSMO_ASSERT(trunk->mgcp_mdcx_ctr_group);
|
||||
if (!trunk->mgcp_mdcx_ctr_group)
|
||||
return -1;
|
||||
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);
|
||||
if (!trunk->mgcp_dlcx_ctr_group)
|
||||
return -1;
|
||||
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);
|
||||
OSMO_ASSERT(trunk->all_rtp_conn_stats);
|
||||
if (!trunk->all_rtp_conn_stats)
|
||||
return -1;
|
||||
talloc_set_destructor(trunk->all_rtp_conn_stats, free_rate_counter_group);
|
||||
all_rtp_conn_rate_ctr_index++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! allocate configuration with default values.
|
||||
@@ -1563,7 +1630,10 @@ 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);
|
||||
if (alloc_mgcp_rate_counters(&cfg->trunk, cfg) < 0) {
|
||||
talloc_free(cfg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_LLIST_HEAD(&cfg->trunks);
|
||||
|
||||
@@ -1636,9 +1706,18 @@ int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
|
||||
tcfg->endpoints[i].cfg = tcfg->cfg;
|
||||
tcfg->endpoints[i].tcfg = tcfg;
|
||||
|
||||
/* NOTE: Currently all endpoints are of type RTP, this will
|
||||
* change when new variations are implemented */
|
||||
tcfg->endpoints[i].type = &ep_typeset.rtp;
|
||||
switch (tcfg->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
tcfg->endpoints[i].type = &ep_typeset.rtp;
|
||||
break;
|
||||
case MGCP_TRUNK_E1:
|
||||
/* FIXME: Implement E1 allocation */
|
||||
LOGP(DLMGCP, LOGL_FATAL, "E1 trunks not implemented!\n");
|
||||
break;
|
||||
default:
|
||||
osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
|
||||
tcfg->trunk_type, __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
tcfg->number_endpoints = tcfg->vty_number_endpoints;
|
||||
|
@@ -26,12 +26,15 @@
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* A struct to store intermediate parsing results. The function
|
||||
* mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP
|
||||
* codec information. */
|
||||
/* Two structs to store intermediate parsing results. The function
|
||||
* mgcp_parse_sdp_data() is using the following two structs as temporary
|
||||
* storage for parsing the SDP codec information. */
|
||||
struct sdp_rtp_map {
|
||||
/* the type */
|
||||
int payload_type;
|
||||
@@ -43,6 +46,11 @@ struct sdp_rtp_map {
|
||||
int rate;
|
||||
int channels;
|
||||
};
|
||||
struct sdp_fmtp_param {
|
||||
int payload_type;
|
||||
struct mgcp_codec_param param;
|
||||
};
|
||||
|
||||
|
||||
/* Helper function to extrapolate missing codec parameters in a codec mao from
|
||||
* an already filled in payload_type, called from: mgcp_parse_sdp_data() */
|
||||
@@ -123,7 +131,8 @@ static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
|
||||
char *str;
|
||||
char *str_ptr;
|
||||
char *pt_str;
|
||||
unsigned int pt;
|
||||
char *pt_end;
|
||||
unsigned long int pt;
|
||||
unsigned int count = 0;
|
||||
unsigned int i;
|
||||
|
||||
@@ -148,7 +157,14 @@ static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
|
||||
if (!pt_str)
|
||||
break;
|
||||
|
||||
pt = atoi(pt_str);
|
||||
errno = 0;
|
||||
pt = strtoul(pt_str, &pt_end, 0);
|
||||
if ((errno == ERANGE && pt == ULONG_MAX) || (errno && !pt) ||
|
||||
pt_str == pt_end)
|
||||
goto error;
|
||||
|
||||
if (pt >> 7) /* PT is 7 bit field, higher values not allowed */
|
||||
goto error;
|
||||
|
||||
/* Do not allow duplicate payload types */
|
||||
for (i = 0; i < count; i++)
|
||||
@@ -167,6 +183,92 @@ error:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Extract fmtp parameters from SDP, called from: mgcp_parse_sdp_data() */
|
||||
static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp)
|
||||
{
|
||||
char *str;
|
||||
char *str_ptr;
|
||||
char *param_str;
|
||||
unsigned int pt;
|
||||
unsigned int count = 0;
|
||||
char delimiter;
|
||||
unsigned int amr_octet_aligned;
|
||||
|
||||
memset(fmtp_param, 0, sizeof(*fmtp_param));
|
||||
|
||||
str = talloc_zero_size(ctx, strlen(sdp) + 1);
|
||||
str_ptr = str;
|
||||
strcpy(str_ptr, sdp);
|
||||
|
||||
/* Check if the input string begins with an fmtp token */
|
||||
str_ptr = strstr(str_ptr, "fmtp:");
|
||||
if (!str_ptr)
|
||||
goto exit;
|
||||
str_ptr += 5;
|
||||
|
||||
/* Extract payload type */
|
||||
if (sscanf(str_ptr, "%u ", &pt) != 1)
|
||||
goto error;
|
||||
fmtp_param->payload_type = pt;
|
||||
|
||||
/* Advance pointer to the beginning of the parameter section and
|
||||
* tokenize string */
|
||||
str_ptr = strstr(str_ptr, " ");
|
||||
if (!str_ptr)
|
||||
goto error;
|
||||
str_ptr++;
|
||||
|
||||
param_str = strtok(str_ptr, " ");
|
||||
if (!param_str)
|
||||
goto exit;
|
||||
|
||||
while (1) {
|
||||
/* Make sure that we don't get trapped in an endless loop */
|
||||
if (count > 256)
|
||||
goto error;
|
||||
|
||||
/* Chop off delimiters ';' at the end */
|
||||
delimiter = str_ptr[strlen(str_ptr) - 1];
|
||||
if (delimiter == ';' || delimiter == ',')
|
||||
str_ptr[strlen(str_ptr) - 1] = '\0';
|
||||
|
||||
/* AMR octet aligned parameter */
|
||||
if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
|
||||
fmtp_param->param.amr_octet_aligned_present = true;
|
||||
fmtp_param->param.amr_octet_aligned = false;
|
||||
if (amr_octet_aligned == 1)
|
||||
fmtp_param->param.amr_octet_aligned = true;
|
||||
|
||||
}
|
||||
|
||||
param_str = strtok(NULL, " ");
|
||||
if (!param_str)
|
||||
break;
|
||||
count++;
|
||||
}
|
||||
|
||||
exit:
|
||||
talloc_free(str);
|
||||
return 0;
|
||||
error:
|
||||
talloc_free(str);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Pick optional fmtp parameters by payload type, if there are no fmtp
|
||||
* parameters, a nullpointer is returned */
|
||||
static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < fmtp_params_len; i++) {
|
||||
if (fmtp_params[i].payload_type == pt)
|
||||
return &fmtp_params[i].param;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Analyze SDP input string.
|
||||
* \param[in] endp trunk endpoint.
|
||||
* \param[out] conn associated rtp connection.
|
||||
@@ -180,6 +282,9 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
{
|
||||
struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_used = 0;
|
||||
struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
|
||||
unsigned int fmtp_used = 0;
|
||||
struct mgcp_codec_param *codec_param;
|
||||
char *line;
|
||||
unsigned int i;
|
||||
void *tmp_ctx = talloc_new(NULL);
|
||||
@@ -207,20 +312,31 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
/* skip these SDP attributes */
|
||||
break;
|
||||
case 'a':
|
||||
if (sscanf(line, "a=rtpmap:%d %63s",
|
||||
&payload, audio_name) == 2) {
|
||||
codecs_update(tmp_ctx, codecs,
|
||||
codecs_used, payload, audio_name);
|
||||
} else
|
||||
if (sscanf
|
||||
(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
|
||||
if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) {
|
||||
codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
|
||||
if (ptime2 > 0 && ptime2 != ptime)
|
||||
rtp->packet_duration_ms = 0;
|
||||
else
|
||||
rtp->packet_duration_ms = ptime;
|
||||
} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
|
||||
rtp->maximum_packet_time = ptime2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
|
||||
rtp->maximum_packet_time = ptime2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (strncmp("a=fmtp:", line, 6) == 0) {
|
||||
rc = fmtp_from_sdp(conn->conn, &fmtp_params[fmtp_used], line);
|
||||
if (rc >= 0)
|
||||
fmtp_used++;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'm':
|
||||
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
|
||||
@@ -262,14 +378,15 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
|
||||
/* Store parsed codec information */
|
||||
for (i = 0; i < codecs_used; i++) {
|
||||
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
|
||||
codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
|
||||
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
|
||||
if (rc < 0)
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
|
||||
}
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
|
||||
"Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
|
||||
ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
|
||||
rtp->packet_duration_ms);
|
||||
@@ -304,15 +421,12 @@ static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add audio string to sdp payload */
|
||||
/* Add audio strings 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;
|
||||
@@ -330,6 +444,64 @@ static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add fmtp strings to sdp payload */
|
||||
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len,
|
||||
const char *fmtp_extra)
|
||||
{
|
||||
unsigned int i;
|
||||
int rc;
|
||||
int fmtp_extra_pt = -1;
|
||||
char *fmtp_extra_pars = "";
|
||||
|
||||
/* When no fmtp parameters ara available but an fmtp extra string
|
||||
* is configured, just add the fmtp extra string */
|
||||
if (fmtp_params_len == 0 && fmtp_extra) {
|
||||
return msgb_printf(sdp, "%s\r\n", fmtp_extra);
|
||||
}
|
||||
|
||||
/* When there is fmtp extra configured we dissect it in order to drop
|
||||
* in the configured extra parameters at the right place when
|
||||
* generating the fmtp strings. */
|
||||
if (fmtp_extra) {
|
||||
if (sscanf(fmtp_extra, "a=fmtp:%d ", &fmtp_extra_pt) != 1)
|
||||
fmtp_extra_pt = -1;
|
||||
|
||||
fmtp_extra_pars = strstr(fmtp_extra, " ");
|
||||
|
||||
if (!fmtp_extra_pars)
|
||||
fmtp_extra_pars = "";
|
||||
else
|
||||
fmtp_extra_pars++;
|
||||
}
|
||||
|
||||
for (i = 0; i < fmtp_params_len; i++) {
|
||||
rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
|
||||
|
||||
/* Add amr octet align parameter */
|
||||
if (fmtp_params[i].param.amr_octet_aligned_present) {
|
||||
if (fmtp_params[i].param.amr_octet_aligned)
|
||||
rc = msgb_printf(sdp, " octet-align=1");
|
||||
else
|
||||
rc = msgb_printf(sdp, " octet-align=0");
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Append extra parameters from fmtp extra */
|
||||
if (fmtp_params[i].payload_type == fmtp_extra_pt) {
|
||||
rc = msgb_printf(sdp, " %s", fmtp_extra_pars);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = msgb_printf(sdp, "\r\n", fmtp_params[i].payload_type);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Generate SDP response string.
|
||||
* \param[in] endp trunk endpoint.
|
||||
* \param[in] conn associated rtp connection.
|
||||
@@ -340,11 +512,16 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
|
||||
const char *addr)
|
||||
{
|
||||
const struct mgcp_rtp_codec *codec;
|
||||
const char *fmtp_extra;
|
||||
const char *audio_name;
|
||||
int payload_type;
|
||||
struct sdp_fmtp_param fmtp_param;
|
||||
int rc;
|
||||
int payload_types[1];
|
||||
int local_port;
|
||||
struct sdp_fmtp_param fmtp_params[1];
|
||||
unsigned int fmtp_params_len = 0;
|
||||
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
@@ -353,10 +530,12 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
|
||||
/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
|
||||
endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
|
||||
&payload_type, &audio_name,
|
||||
&fmtp_extra,
|
||||
&codec, &fmtp_extra,
|
||||
(struct mgcp_conn_rtp *)conn);
|
||||
|
||||
audio_name = codec->audio_name;
|
||||
payload_type = codec->payload_type;
|
||||
|
||||
rc = msgb_printf(sdp,
|
||||
"v=0\r\n"
|
||||
"o=- %s 23 IN IP4 %s\r\n"
|
||||
@@ -370,7 +549,11 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
if (payload_type >= 0) {
|
||||
|
||||
payload_types[0] = payload_type;
|
||||
rc = add_audio(sdp, payload_types, 1, conn->end.local_port);
|
||||
if (mgcp_conn_rtp_is_osmux(conn))
|
||||
local_port = endp->cfg->osmux_port;
|
||||
else
|
||||
local_port = conn->end.local_port;
|
||||
rc = add_audio(sdp, payload_types, 1, local_port);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
|
||||
@@ -380,12 +563,15 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
goto buffer_too_small;
|
||||
}
|
||||
|
||||
if (fmtp_extra) {
|
||||
rc = msgb_printf(sdp, "%s\r\n", fmtp_extra);
|
||||
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
if (codec->param_present) {
|
||||
fmtp_param.payload_type = payload_type;
|
||||
fmtp_param.param = codec->param;
|
||||
fmtp_params[0] = fmtp_param;
|
||||
fmtp_params_len = 1;
|
||||
}
|
||||
rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
}
|
||||
if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
|
||||
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
|
||||
@@ -397,6 +583,6 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
return 0;
|
||||
|
||||
buffer_too_small:
|
||||
LOGP(DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
|
||||
return -1;
|
||||
}
|
||||
|
@@ -37,6 +37,8 @@
|
||||
#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"
|
||||
|
||||
|
||||
static struct mgcp_config *g_cfg = NULL;
|
||||
|
||||
@@ -96,13 +98,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)
|
||||
@@ -154,6 +160,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;
|
||||
}
|
||||
|
||||
@@ -215,6 +225,13 @@ static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, int epidx
|
||||
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 */
|
||||
@@ -249,6 +266,10 @@ static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg, int show_
|
||||
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);
|
||||
@@ -276,7 +297,7 @@ DEFUN(show_mcgp, show_mgcp_cmd,
|
||||
dump_trunk(vty, trunk, show_stats);
|
||||
|
||||
if (g_cfg->osmux)
|
||||
vty_out(vty, "Osmux used CID: %d%s", osmux_used_cid(),
|
||||
vty_out(vty, "Osmux used CID: %d%s", osmux_cid_pool_count_used(),
|
||||
VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
@@ -707,11 +728,28 @@ 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_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;
|
||||
}
|
||||
|
||||
@@ -808,13 +846,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)
|
||||
@@ -980,12 +1022,31 @@ 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_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;
|
||||
}
|
||||
|
||||
@@ -1244,14 +1305,7 @@ DEFUN(cfg_mgcp_osmux,
|
||||
if (strcmp(argv[0], "off") == 0) {
|
||||
g_cfg->osmux = OSMUX_USAGE_OFF;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* Since OSMUX support is not finished, we do not
|
||||
* allow to turn it on yet. */
|
||||
vty_out(vty, "OSMUX currently unavailable in this software version.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
#if 0
|
||||
if (strcmp(argv[0], "on") == 0)
|
||||
} else if (strcmp(argv[0], "on") == 0)
|
||||
g_cfg->osmux = OSMUX_USAGE_ON;
|
||||
else if (strcmp(argv[0], "only") == 0)
|
||||
g_cfg->osmux = OSMUX_USAGE_ONLY;
|
||||
@@ -1262,7 +1316,7 @@ DEFUN(cfg_mgcp_osmux,
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_osmux_ip,
|
||||
@@ -1323,6 +1377,19 @@ DEFUN(cfg_mgcp_domain,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_conn_timeout,
|
||||
cfg_mgcp_conn_timeout_cmd,
|
||||
"conn-timeout <0-65534>",
|
||||
"Set a time after which inactive connections (CIs) are closed. Set to 0 to disable timeout. This can be used to"
|
||||
" work around interoperability problems causing connections to stay open forever, and slowly exhausting all"
|
||||
" available ports. Enable keep-alive packets in MGW clients when using this option together with LCLS (OsmoBSC,"
|
||||
" OsmoMSC: 'rtp keep-alive')!\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);
|
||||
@@ -1373,6 +1440,8 @@ 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_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);
|
||||
@@ -1387,6 +1456,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);
|
||||
@@ -1403,6 +1473,8 @@ 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_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);
|
||||
|
@@ -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;
|
||||
@@ -270,9 +270,9 @@ int main(int argc, char **argv)
|
||||
|
||||
vty_info.copyright = osmomgw_copyright;
|
||||
vty_init(&vty_info);
|
||||
logging_vty_add_cmds(NULL);
|
||||
logging_vty_add_cmds();
|
||||
osmo_talloc_vty_add_cmds();
|
||||
osmo_stats_vty_add_cmds(&log_info);
|
||||
osmo_stats_vty_add_cmds();
|
||||
mgcp_vty_init();
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
@@ -165,7 +165,7 @@ static void test_strline(void)
|
||||
|
||||
#define MDCX4_PT1 \
|
||||
"MDCX 18983217 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendrecv\r" \
|
||||
"M: SENDRECV\r" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"L: p:20-40, a:AMR, nt:IN\r\n" \
|
||||
@@ -208,8 +208,24 @@ static void test_strline(void)
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define MDCX4_SO \
|
||||
/* Test different upper/lower case in options */
|
||||
#define MDCX4_PT4 \
|
||||
"MDCX 18983220 1@mgw MGCP 1.0\r\n" \
|
||||
"m: sendrecv\r" \
|
||||
"c: 2\r\n" \
|
||||
"i: %s\r\n" \
|
||||
"l: A:amr, NT:IN\r\n" \
|
||||
"\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 4441 RTP/AVP 99\r\n" \
|
||||
"a=rtpmap:99 AMR/8000\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define MDCX4_SO \
|
||||
"MDCX 18983221 1@mgw MGCP 1.0\r\n" \
|
||||
"M: sendonly\r" \
|
||||
"C: 2\r\n" \
|
||||
"I: %s\r\n" \
|
||||
@@ -224,17 +240,17 @@ static void test_strline(void)
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define MDCX4_RO \
|
||||
"MDCX 18983221 1@mgw MGCP 1.0\r\n" \
|
||||
"MDCX 18983222 1@mgw MGCP 1.0\r\n" \
|
||||
"M: recvonly\r" \
|
||||
"C: 2\r\n" \
|
||||
"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" \
|
||||
"MDCX 18983223 1@mgw MGCP 1.0\r\n" \
|
||||
"I: 123456789012345678901234567890123\n"
|
||||
|
||||
#define MDCX_TOO_LONG_CI_RET "510 18983222 FAIL\r\n"
|
||||
#define MDCX_TOO_LONG_CI_RET "510 18983223 FAIL\r\n"
|
||||
|
||||
#define SHORT2 "CRCX 1"
|
||||
#define SHORT2_RET "510 000000 FAIL\r\n"
|
||||
@@ -244,7 +260,7 @@ static void test_strline(void)
|
||||
|
||||
#define CRCX \
|
||||
"CRCX 2 1@mgw MGCP 1.0\r\n" \
|
||||
"M: recvonly\r\n" \
|
||||
"m: recvonly\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"L: p:20\r\n" \
|
||||
"\r\n" \
|
||||
@@ -467,6 +483,34 @@ static void test_strline(void)
|
||||
"M: recvonly\r\n" \
|
||||
"C: 2\r\n"
|
||||
|
||||
#define CRCX_AMR_WITH_FMTP \
|
||||
"CRCX 2 7@mgw MGCP 1.0\r\n" \
|
||||
"M: recvonly\r\n" \
|
||||
"C: 2\r\n" \
|
||||
"X\r\n" \
|
||||
"L: p:20\r\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"c=IN IP4 123.12.12.123\r\n" \
|
||||
"m=audio 5904 RTP/AVP 111\r\n" \
|
||||
"a=rtpmap:111 AMR/8000/1\r\n" \
|
||||
"a=ptime:20\r\n" \
|
||||
"a=fmtp:111 mode-change-capability=2; octet-align=1\r\n" \
|
||||
|
||||
#define CRCX_AMR_WITH_FMTP_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 16012 RTP/AVP 111\r\n" \
|
||||
"a=rtpmap:111 AMR/8000/1\r\n" \
|
||||
"a=fmtp:111 octet-align=1\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
#define CRCX_NO_LCO_NO_SDP_RET \
|
||||
"200 2 OK\r\n" \
|
||||
"I: %s\r\n" \
|
||||
@@ -498,8 +542,9 @@ static const struct mgcp_test tests[] = {
|
||||
{"MDCX4_PT1", MDCX4_PT1, MDCX4_RET("18983217"), 99},
|
||||
{"MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983218"), 99},
|
||||
{"MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983219"), 99},
|
||||
{"MDCX4_SO", MDCX4_SO, MDCX4_RET("18983220"), 99},
|
||||
{"MDCX4_RO", MDCX4_RO, MDCX4_RO_RET("18983221"), PTYPE_IGNORE},
|
||||
{"MDCX4_PT4", MDCX4_PT4, MDCX4_RET("18983220"), 99},
|
||||
{"MDCX4_SO", MDCX4_SO, MDCX4_RET("18983221"), 99},
|
||||
{"MDCX4_RO", MDCX4_RO, MDCX4_RO_RET("18983222"), PTYPE_IGNORE},
|
||||
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
|
||||
{"CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET, 97},
|
||||
{"EMPTY", EMPTY, EMPTY_RET},
|
||||
@@ -517,6 +562,7 @@ static const struct mgcp_test tests[] = {
|
||||
{"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},
|
||||
{"CRCX", CRCX_AMR_WITH_FMTP, CRCX_AMR_WITH_FMTP_RET},
|
||||
};
|
||||
|
||||
static const struct mgcp_test retransmit[] = {
|
||||
@@ -970,6 +1016,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");
|
||||
@@ -977,6 +1024,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;
|
||||
@@ -1197,6 +1245,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 };
|
||||
@@ -1224,6 +1273,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;
|
||||
@@ -1242,7 +1292,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
|
||||
rtp = &conn->end;
|
||||
|
||||
OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1") == 0);
|
||||
OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1", NULL) == 0);
|
||||
rtp->codec = &rtp->codecs[0];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
|
||||
@@ -1521,25 +1571,34 @@ static void test_osmux_cid(void)
|
||||
{
|
||||
int id, i;
|
||||
|
||||
OSMO_ASSERT(osmux_used_cid() == 0);
|
||||
id = osmux_get_cid();
|
||||
OSMO_ASSERT(osmux_cid_pool_count_used() == 0);
|
||||
|
||||
id = osmux_cid_pool_get_next();
|
||||
OSMO_ASSERT(id == 0);
|
||||
OSMO_ASSERT(osmux_used_cid() == 1);
|
||||
osmux_put_cid(id);
|
||||
OSMO_ASSERT(osmux_used_cid() == 0);
|
||||
OSMO_ASSERT(osmux_cid_pool_count_used() == 1);
|
||||
|
||||
osmux_cid_pool_get(30);
|
||||
OSMO_ASSERT(osmux_cid_pool_count_used() == 2);
|
||||
osmux_cid_pool_get(30);
|
||||
OSMO_ASSERT(osmux_cid_pool_count_used() == 2);
|
||||
|
||||
osmux_cid_pool_put(id);
|
||||
OSMO_ASSERT(osmux_cid_pool_count_used() == 1);
|
||||
osmux_cid_pool_put(30);
|
||||
OSMO_ASSERT(osmux_cid_pool_count_used() == 0);
|
||||
|
||||
for (i = 0; i < 256; ++i) {
|
||||
id = osmux_get_cid();
|
||||
id = osmux_cid_pool_get_next();
|
||||
OSMO_ASSERT(id == i);
|
||||
OSMO_ASSERT(osmux_used_cid() == i + 1);
|
||||
OSMO_ASSERT(osmux_cid_pool_count_used() == i + 1);
|
||||
}
|
||||
|
||||
id = osmux_get_cid();
|
||||
id = osmux_cid_pool_get_next();
|
||||
OSMO_ASSERT(id == -1);
|
||||
|
||||
for (i = 0; i < 256; ++i)
|
||||
osmux_put_cid(i);
|
||||
OSMO_ASSERT(osmux_used_cid() == 0);
|
||||
osmux_cid_pool_put(i);
|
||||
OSMO_ASSERT(osmux_cid_pool_count_used() == 0);
|
||||
}
|
||||
|
||||
static const struct log_info_cat log_categories[] = {
|
||||
|
@@ -65,7 +65,7 @@ Testing CRCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 2 1@mgw MGCP 1.0
|
||||
M: recvonly
|
||||
m: recvonly
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
@@ -125,7 +125,7 @@ Testing MDCX4_PT1
|
||||
Testing MDCX4_PT1
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983217 1@mgw MGCP 1.0
|
||||
MDCX 18983217 1@mgw MGCP 1.0
|
||||
M: SENDRECV
|
||||
C: 2
|
||||
I: %s
|
||||
@@ -193,10 +193,34 @@ Response matches our expectations.
|
||||
checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
(response contains a connection id)
|
||||
Dummy packets: 2
|
||||
|
||||
================================================
|
||||
Testing MDCX4_PT4
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983220 1@mgw MGCP 1.0
|
||||
m: sendrecv
|
||||
c: 2
|
||||
i: %s
|
||||
l: A:amr, NT:IN
|
||||
|
||||
v=0
|
||||
o=- %s 23 IN IP4 0.0.0.0
|
||||
c=IN IP4 0.0.0.0
|
||||
t=0 0
|
||||
m=audio 4441 RTP/AVP 99
|
||||
a=rtpmap:99 AMR/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 MDCX4_SO
|
||||
creating message from statically defined input:
|
||||
@@ -219,7 +243,7 @@ Response matches our expectations.
|
||||
checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
|
||||
(response contains a connection id)
|
||||
|
||||
================================================
|
||||
Testing MDCX4_RO
|
||||
@@ -359,7 +383,7 @@ Testing CRCX
|
||||
DLCX 7 1@mgw MGCP 1.0
|
||||
I: %s
|
||||
C: 2
|
||||
---------8<---------
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message as statically defined for comparison
|
||||
@@ -446,7 +470,7 @@ Dummy packets: 2
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
a=ptime:40
|
||||
---------8<---------
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message with patched conn_id for comparison
|
||||
@@ -459,9 +483,33 @@ Response matches our expectations.
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983223 1@mgw MGCP 1.0
|
||||
|
||||
I: 123456789012345678901234567890123
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message as statically defined for comparison
|
||||
Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
Testing CRCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 2 7@mgw MGCP 1.0
|
||||
M: recvonly
|
||||
C: 2
|
||||
X
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 111
|
||||
a=rtpmap:111 AMR/8000/1
|
||||
a=ptime:20
|
||||
a=fmtp:111 mode-change-capability=2; octet-align=1
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
@@ -478,7 +526,7 @@ Re-transmitting CRCX
|
||||
|
||||
v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
a=ptime:40
|
||||
|
||||
@@ -590,7 +638,7 @@ Testing packet loss calculation.
|
||||
Re-transmitting DLCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
I: %s
|
||||
DLCX 7 1@mgw MGCP 1.0
|
||||
I: %s
|
||||
C: 2
|
||||
|
||||
@@ -1141,7 +1189,7 @@ Testing no rtpmap name
|
||||
a=rtpmap:4 G723/8000
|
||||
a=rtpmap:96 iLBC/8000
|
||||
a=fmtp:96 mode=20
|
||||
a=fmtp:97 mode=30
|
||||
a=rtpmap:97 iLBC/8000
|
||||
a=fmtp:97 mode=30
|
||||
a=rtpmap:101 telephone-event/8000
|
||||
a=fmtp:101 0-15
|
||||
|
@@ -113,9 +113,9 @@ void test_response_cb(struct mgcp_response *response, void *priv)
|
||||
printf(" ptmap_len = %u\n", response->ptmap_len);
|
||||
for(i=0;i<response->ptmap_len;i++) {
|
||||
printf(" ptmap[%u].codec = %u\n", i, response->ptmap[i].codec);
|
||||
printf(" ptmap[%u].pt = %u\n", i, response->ptmap[i].pt);
|
||||
printf(" ptmap[%u].pt = %u\n", i, response->ptmap[i].pt);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
|
||||
@@ -135,87 +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",
|
||||
"I: 1\r\n\r\n"
|
||||
"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_crcx_long_conn_id(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",
|
||||
"I: 123456789abcdef0123456789ABCDEF0\r\n\r\n"
|
||||
"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_crcx_too_long_conn_id(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",
|
||||
"I: 123456789abcdef0123456789ABCDEF01001029\r\n\r\n"
|
||||
"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;
|
||||
@@ -238,6 +157,7 @@ void test_mgcp_msg(void)
|
||||
.ptmap[0].pt = 96,
|
||||
.ptmap_len = 1,
|
||||
.x_osmo_ign = MGCP_X_OSMO_IGN_CALLID,
|
||||
.x_osmo_osmux_cid = -1, /* wildcard */
|
||||
};
|
||||
|
||||
if (mgcp)
|
||||
@@ -261,7 +181,7 @@ void test_mgcp_msg(void)
|
||||
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
|
||||
mgcp_msg.codecs_len = 2;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
mgcp_msg.codecs_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated CRCX message (three codecs, one with custom pt):\n");
|
||||
@@ -271,8 +191,8 @@ void test_mgcp_msg(void)
|
||||
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
|
||||
mgcp_msg.codecs_len = 3;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated MDCX message:\n");
|
||||
mgcp_msg.verb = MGCP_VERB_MDCX;
|
||||
@@ -291,7 +211,7 @@ void test_mgcp_msg(void)
|
||||
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
|
||||
mgcp_msg.codecs_len = 2;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
mgcp_msg.codecs_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated MDCX message (three codecs, one with custom pt):\n");
|
||||
@@ -302,8 +222,8 @@ void test_mgcp_msg(void)
|
||||
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
|
||||
mgcp_msg.codecs_len = 3;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated DLCX message:\n");
|
||||
mgcp_msg.verb = MGCP_VERB_DLCX;
|
||||
@@ -335,6 +255,38 @@ void test_mgcp_msg(void)
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generate X-Osmo-Osmux 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_OSMUX_CID);
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generate X-Osmo-Osmux message (fixed CID 2):\n");
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.verb = MGCP_VERB_CRCX;
|
||||
mgcp_msg.x_osmo_osmux_cid = 2;
|
||||
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_OSMUX_CID);
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generate X-Osmo-Osmux message (MDCX):\n");
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.verb = MGCP_VERB_MDCX;
|
||||
mgcp_msg.x_osmo_osmux_cid = 2;
|
||||
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_OSMUX_CID);
|
||||
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 =
|
||||
@@ -367,7 +319,7 @@ void test_mgcp_client_cancel()
|
||||
| MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE),
|
||||
.ptime = 20,
|
||||
.codecs[0] = CODEC_AMR_8000_1,
|
||||
.codecs_len = 1
|
||||
.codecs_len = 1
|
||||
};
|
||||
|
||||
printf("\n%s():\n", __func__);
|
||||
@@ -618,14 +570,11 @@ int main(int argc, char **argv)
|
||||
|
||||
mgcp_client_conf_init(&conf);
|
||||
|
||||
test_crcx();
|
||||
test_mgcp_msg();
|
||||
test_mgcp_client_cancel();
|
||||
test_sdp_section_start();
|
||||
test_map_codec_to_pt_and_map_pt_to_codec();
|
||||
test_map_pt_to_codec();
|
||||
test_crcx_long_conn_id();
|
||||
test_crcx_too_long_conn_id();
|
||||
|
||||
printf("Done\n");
|
||||
fprintf(stderr, "Done\n");
|
||||
|
@@ -1,6 +1,8 @@
|
||||
DLMGCP MGCP client: using endpoint domain '@mgw'
|
||||
DLMGCP message buffer to small, can not generate MGCP message
|
||||
|
||||
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
|
||||
@@ -66,6 +68,4 @@ DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
|
||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
|
||||
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
|
||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
|
||||
DLMGCP Failed to parse MGCP response (parameter label: I): the received conn ID is too long: 39, maximum is 32 characters
|
||||
DLMGCP Cannot parse MGCP response (head parameters)
|
||||
Done
|
||||
|
@@ -1,46 +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.conn_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
|
||||
@@ -126,6 +84,30 @@ L: p:20, a:GSM, nt:IN
|
||||
M: sendrecv
|
||||
X-Osmo-IGN: C
|
||||
|
||||
Generate X-Osmo-Osmux message:
|
||||
CRCX 13 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
L: p:20, a:GSM, nt:IN
|
||||
M: sendrecv
|
||||
X-Osmux: *
|
||||
|
||||
Generate X-Osmo-Osmux message (fixed CID 2):
|
||||
CRCX 15 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
L: p:20, a:GSM, nt:IN
|
||||
M: sendrecv
|
||||
X-Osmux: 2
|
||||
|
||||
Generate X-Osmo-Osmux message (MDCX):
|
||||
MDCX 17 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
L: p:20, a:GSM, nt:IN
|
||||
M: sendrecv
|
||||
X-Osmux: 2
|
||||
|
||||
Overfolow test:
|
||||
|
||||
|
||||
@@ -196,72 +178,4 @@ test_sdp_section_start() test [9]:
|
||||
2 <= 2
|
||||
100 <= 100
|
||||
|
||||
|
||||
===== test_crcx_long_conn_id =====
|
||||
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: 123456789abcdef0123456789ABCDEF0
|
||||
|
||||
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.conn_id = 123456789abcdef0123456789ABCDEF0
|
||||
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
|
||||
|
||||
===== test_crcx_too_long_conn_id =====
|
||||
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: 123456789abcdef0123456789ABCDEF01001029
|
||||
|
||||
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
|
||||
|
||||
-----
|
||||
Done
|
||||
|
Reference in New Issue
Block a user