Compare commits

..

1 Commits

Author SHA1 Message Date
Philipp Maier
63ce8ce2e9 AMR: Add option to convert between bw-effient and octet aligned mode
RFC3267 specifies two framing modes for AMR packets. An octet aligned
mode is specified where all fields of the AMR packets are aligned to
octet boundaries. The second framing mode is the bandwith efficient mode
where the fields are directly packed one after another.

Which format is used is usually communicated by out of band means,
however it is still possible to distinguish the framing mode by looking
at the frame. Once the framing mode is distinguished, the packet can be
converted into the opposite framing mode.

- Add new VTY rtp-patch options
- Add conversion function

Change-Id: Id46a3622a6b1995d5fce4bc611d409bcb4aef13b
Depends: libosmo-netif I5b5a0fa644d8dbb1f04f9d7e35312683c7b3d196
Related: OS#3807
2019-02-28 17:18:10 +01:00
77 changed files with 4794 additions and 8516 deletions

40
.gitignore vendored
View File

@@ -2,13 +2,20 @@ debian/*.log
*.o
*.lo
*.a
*.la
.deps
Makefile
Makefile.in
bscconfig.h
bscconfig.h.in
src/osmo-mgw/osmo-mgw
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
*.*~
*.sw?
.libs
@@ -16,7 +23,6 @@ src/osmo-mgw/osmo-mgw
*.gcda
*.gcno
*.pc
*~
#configure
aclocal.m4
@@ -40,9 +46,32 @@ 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
@@ -50,7 +79,10 @@ tests/package.m4
tests/testsuite
tests/testsuite.log
gsn_restart
src/openbsc.cfg*
writtenconfig/
gtphub_restart_count
# manuals
doc/manuals/*.html
@@ -62,5 +94,3 @@ doc/manuals/generated/
doc/manuals/osmomsc-usermanual.xml
doc/manuals/common
doc/manuals/build
contrib/osmo-mgw.spec

View File

@@ -9,10 +9,10 @@ AM_CPPFLAGS = \
$(NULL)
SUBDIRS = \
doc \
include \
src \
tests \
doc \
contrib \
$(NULL)
@@ -22,13 +22,7 @@ pkgconfig_DATA = \
$(NULL)
BUILT_SOURCES = $(top_srcdir)/.version
EXTRA_DIST = \
.version \
contrib/osmo-mgw.spec.in \
debian \
git-version-gen \
osmoappdesc.py \
$(NULL)
EXTRA_DIST = git-version-gen osmoappdesc.py .version
AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)

View File

@@ -24,4 +24,3 @@
# If any interfaces have been removed or changed since the last public release, a=0.
#
#library what description / commit summary line
update dependency to libosmocore > 1.5.1 for our use of osmo_sock_set_dscp()

View File

@@ -9,8 +9,6 @@ AC_CONFIG_AUX_DIR([.])
AM_INIT_AUTOMAKE([dist-bzip2])
AC_CONFIG_TESTDIR(tests)
CFLAGS="$CFLAGS -std=gnu11"
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -24,11 +22,6 @@ AC_PROG_CC
AC_PROG_INSTALL
LT_INIT
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
AS_CASE(["$LD"],[*clang*],
[AS_CASE(["${host_os}"],
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
@@ -45,17 +38,11 @@ dnl checks for libraries
AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DL)
AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DLSYM)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.1.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.1.0)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.1.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.4.0)
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
@@ -89,10 +76,21 @@ 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([-Wnull-dereference], [CFLAGS="$CFLAGS -Wnull-dereference"])
AX_CHECK_COMPILE_FLAG([-Werror=null-dereference], [CFLAGS="$CFLAGS -Werror=null-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"])
@@ -213,5 +211,4 @@ AC_OUTPUT(
doc/manuals/Makefile
contrib/Makefile
contrib/systemd/Makefile
contrib/osmo-mgw.spec
Makefile)

View File

@@ -36,6 +36,7 @@ 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
@@ -55,12 +56,11 @@ LD_LIBRARY_PATH="$inst/lib" $MAKE check \
|| cat-testlogs.sh
LD_LIBRARY_PATH="$inst/lib" \
DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests $CONFIG" \
$MAKE $PARALLEL_MAKE distcheck \
$MAKE distcheck \
|| cat-testlogs.sh
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
make -C "$base/doc/manuals" publish
fi
$MAKE $PARALLEL_MAKE maintainer-clean
osmo-clean-workspace.sh

View File

@@ -1,137 +0,0 @@
#
# spec file for package osmo-mgw
#
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# Please submit bugfixes or comments via http://bugs.opensuse.org/
#
Name: osmo-mgw
Version: @VERSION@
Release: 0
Summary: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
License: AGPL-3.0-or-later AND GPL-2.0-or-later
Group: Hardware/Mobile
URL: https://osmocom.org/projects/osmo-mgw
Source: %{name}-%{version}.tar.xz
BuildRequires: automake >= 1.9
BuildRequires: libtool >= 2
BuildRequires: pkgconfig >= 0.20
%if 0%{?suse_version}
BuildRequires: systemd-rpm-macros
%endif
BuildRequires: pkgconfig(libosmo-netif) >= 1.1.0
BuildRequires: pkgconfig(libosmocore) >= 1.5.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.5.0
BuildRequires: pkgconfig(libosmogsm) >= 1.5.0
BuildRequires: pkgconfig(libosmovty) >= 1.5.0
BuildRequires: pkgconfig(libosmocoding) >= 1.5.0
BuildRequires: pkgconfig(libosmoabis) >= 1.1.0
BuildRequires: pkgconfig(libosmotrau) >= 1.1.0
%{?systemd_requires}
%description
OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks.
%package -n libosmo-mgcp-client8
Summary: Osmocom's Media Gateway Control Protocol client library
Group: System/Libraries
%description -n libosmo-mgcp-client8
Osmocom's Media Gateway Control Protocol client library.
%package -n libosmo-mgcp-client-devel
Summary: Development files for Osmocom's Media Gateway Control Protocol client library
Group: Development/Libraries/C and C++
Requires: libosmo-mgcp-client8 = %{version}
%description -n libosmo-mgcp-client-devel
Osmocom's Media Gateway Control Protocol client librarary.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmo-mgcp-client.
%package -n libosmo-mgcp-devel
Summary: Development files for Osmocom's Media Gateway server library
Group: Development/Libraries/C and C++
%description -n libosmo-mgcp-devel
Osmocom's Media Gateway Control Protocol server library.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmo-mgcp.
%prep
%setup -q
%build
echo "%{version}" >.tarball-version
autoreconf -fi
%configure \
--disable-static \
--docdir=%{_docdir}/%{name} \
--with-systemdsystemunitdir=%{_unitdir}
make %{?_smp_mflags}
%install
%make_install
find %{buildroot} -type f -name "*.la" -delete -print
%check
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%post -n libosmo-mgcp-client8 -p /sbin/ldconfig
%postun -n libosmo-mgcp-client8 -p /sbin/ldconfig
%if 0%{?suse_version}
%preun
%service_del_preun osmo-mgw.service
%postun
%service_del_postun osmo-mgw.service
%pre
%service_add_pre osmo-mgw.service
%post
%service_add_post osmo-mgw.service
%endif
%files
%license COPYING
%doc AUTHORS README
%dir %{_docdir}/%{name}/examples
%dir %{_docdir}/%{name}/examples/osmo-mgw
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw.cfg
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
%{_bindir}/osmo-mgw
%{_unitdir}/osmo-mgw.service
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg
%files -n libosmo-mgcp-client8
%{_libdir}/libosmo-mgcp-client.so.8*
%files -n libosmo-mgcp-client-devel
%{_libdir}/libosmo-mgcp-client.so
%{_libdir}/pkgconfig/libosmo-mgcp-client.pc
%dir %{_includedir}/osmocom
%dir %{_includedir}/osmocom/mgcp_client
%{_includedir}/osmocom/mgcp_client/*.h
%files -n libosmo-mgcp-devel
%dir %{_includedir}/osmocom
%dir %{_includedir}/osmocom/mgcp
%{_includedir}/osmocom/mgcp/*.h
%changelog

314
debian/changelog vendored
View File

@@ -1,317 +1,3 @@
osmo-mgw (1.8.1) unstable; urgency=medium
* attempt to fix RPM spec file after recent soversion bump
-- Harald Welte <laforge@osmocom.org> Wed, 24 Feb 2021 10:56:17 +0100
osmo-mgw (1.8.0) unstable; urgency=medium
[ Harald Welte ]
* Fix number of endpoints of default trunk
* Add CTRL interface to osmo-mgw
* Update per-trunk global packet/byte counters in real-time
* remove accidential TODO-RELEASE entry
* mgcp_protocol: Avoid code duplication between virtual + other trunks
* osmo-mgw.spec.in: Use %config(noreplace) to retain current config file
* mgcp_client_init(): consider "talloc returns NULL" case
* mgcp_client_pending_add(): Consider "talloc returns NULL" case
* libosmo-mgcp-client: fix memleak in case if no response is received
* debian/control + SPEC: Add missing build dependency to libosmo-abis
* osmo-mgw.spec.in: Fix dependency to libosmoabis
* osmo-mgw.spec.in: Add missing dependency to libosmotrau
* Add example osmo-mgw configuration file for Abis/E1
* use osmo_fd_setup() whenever applicable
[ Neels Hofmeyr ]
* fix vty dump_trunk: start from zero, do not omit first CONN
* allow larger MGCP client wqueue: 10 -> 1024
* add osmo-mgw --vty-ref-xml: dump VTY ref XML to stdout
* manuals: generate vty reference xml at build time
* refactor: use msgb to receive, pass and send RTP packets
* change timer T2427001 to X2427
[ Philipp Maier ]
* doc: do not bind osmo-mgw to random ip-address
* cosmetic: remove excess newlines
* mgcp: remove unused callback pointer
* mgcp: find better locations for LOGPCONN and LOGPENDP
* vty: fix unreachable code (error msg on trunk alloc fail)
* mgcp_vty: fix indentation
* osmo-mgw: rename struct mgcp_trunk_config and symbol tcfg
* mgcp_vty: fix indentation in VTY config write
* osmo-mgw: refactor endpoint and trunk handling
* mgcp_trunk: remove audio_name and audio_payload
* ratectr: move rate counter definitions into mgcp_ratectr.h
* cosmetic: fix doxygen
* cosmetic: fix doxygen
* cosmetic: fix doxygen for mgcp_cleanup_rtp_bridge_cb()
* trunk: get rid of virt_trunk pointer
* cosmetic: remove excess space
* endp: move endpoint name generation into mgcp_endp.c
* endp: add name generator function for E1 endpoints
* mgcp_client: add docstring for mgcp_client_rtpbridge_wildcard()
* mgcp_trunk: fix docstring for mgcp_trunk_alloc()
* mgcp_osmux: remove unused define constants
* mgcp_trunk: move enum mgcp_trunk_type to mgcp_trunk.h
* mgcp_trunk: use enum type for trunk type variable
* trunk: parse E1 trunk number
* endp: add typeset for e1-endpoints
* mgcp_conn: move struct mgcp_conn mgcp_conn.h
* mgcp_internal: remove forward declaration struct mgcp_endpoint_type
* endp: add E1 endpoint interlocking
* endp: require domain name also for E1 endpoints
* mgcp_trunk: pick trunk by number and type
* mgcp_client: add function to generate e1-endpoint names
* mgcp_endp: use define constant to define max number of E1 subslots
* mgcp_endp.c: cosmetic: fix sourcecode formatting
* mgcp_trunk: remove double check
* mgcp_test: do not access endpoint array elements directly
* mgcp_vty: fix endpoint number configuration
* mgcp_test: remove trunk2 from unit-test
* mgcp_trunk: use talloc_zero_array instead of _talloc_zero_array
* mgcp_protocol: remove unused variable
* mgcp_e1: finish E1 support, add E1 support from libosmoabis
* get rid of mgcp_internal.h
* mgcp_ratectr: fix comments in header file
* mgcp_e1: use return value of e1inp_line_update()
* mgcp_protocol: log when endpoint is unavailable
* cosmetic: add missing new-line
* mgcp_trunk: drop "trunk 0" limitation
* mgcp_e1: make E1 ts initalization more debugable
* mgcp_e1: remove unused struct member trunk->e1.line
* mgcp_endp: use NUM_E1_TS from e1_input.h
* mgcp_trunk: increase default number of virtual endpoints
* cosmetic: mgcp_client_fsm: change error message.
* mgcp_vty: remove remains of loopback functionality
* mgcp_vty: deprecate bind early command
* mgcp_e1: do not expose function mgcp_e1_init()
* mgcp_vty: add user attributes to configuration commands
* overview: update section limitations.
* overview: fix graph "OsmoMGW used with OsmoBSC"
* overview: fix graph "OsmoMGW used with OsmoMSC"
* overview: add graph to show E1 integration
* configuration: remove hint towards trunk 0 limit
* configuration: drop note about lackin E1 support
* configuration: add note that changes to trunks need a restart
* mgcp_vty: fix config write for trunk 0
* mgcp_vty: add missing VTY commands for E1 trunks
* mgcp_trunk: get rid of magic numbers for E1 slots
* configuration: add section about E1 trunks
* usermanual: add chapter about mgcp endpoints
* mgcp_client: get rid of magic numbers for E1 slots
[ Eric ]
* tests: dlopen does not imply availability of dlsym..
* configure.ac: fix libtool issue with clang and sanitizer
[ Alexander Chemeris ]
* vty: Prepend VTY output of counters for better visual separation.
* counters: Implement more useful counters.
* mgcp_network: Fix a typo in the comment bahviour -> behaviour
* rtp_bridge: Demote a chatty ERROR log message to DEBUG level.
[ Pau Espin Pedrol ]
* Use OSMO_FD_* instead of deprecated BSC_FD_*
* Support setting rt-prio and cpu-affinity mask through VTY
* cosmetic: Rename main talloc ctx
* mgcp-client: Support IPv6 in osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr() implementation
* mgcp-client: Fix trailing whitespace in mgcp_client_fsm.h
* mgcp_client: Use INET6_ADDRSTRLEN to store addresses in str format
* mgcp_client: Allow submitting and parsing IPv6 addr in SDP
* mgcp_client: Allow setting IPv6 addresses
* mgcp_client: Make MGCP_CLIENT_LOCAL_ADDR_DEFAULT IPv6 compatible
* mgcp_client: Support validating IPv6 addresses in CRCX and MDCX commands
* mgcp_client: Deprecate unused IPv4-only API
* mgcp_client: copy back Connection Information from MDCX ACK
* mgw: Fix mgcp_rtp_end field description comment
* mgw: Initial IPv6 support
* mgw: Introduce VTY cmd 'rtp bind-ip-v6' command
* mgw: Find and store RTP conn local_addr once during CRCX handling
* mgw: Announce and rebind new local address if change required during MDCX
* mgw: osmux: Avoid sending packets on recvonly connection
* mgw: Release endpoint after last conn times out
* mgw: osmux: Fix conn watchdog timeout not updated
* mgw: Don't be case-sensitive when parsing X-Osmo-IGN param
* cosmetic: Fix typo in comment
* mgw: Avoid logging notice message each time we receive nt param in LCO
* mgw: Fix return value documentation for API mgcp_verify_call_id
* cosmetic: Fix typo in comment
* contrib/jenkins: Enable parallel make in make distcheck
* .gitignore: Ignore new autofoo tmp files
* tests: Replace deprecated API log_set_print_filename
[ Oliver Smith ]
* contrib: import RPM spec
* contrib: integrate RPM spec
* Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
* contrib/jenkins: don't build osmo-gsm-manuals
* configure.ac: set -std=gnu11
[ Vadim Yanitskiy ]
* libosmo-mgcp-client: fix use-after-free in mgcp_client_tx()
* libosmo-mgcp-client: fix use-after-free in mgcp_msg_gen()
* libosmo-mgcp: fix unsigned compared against 0 in mgcp_trunk_by_name()
* libosmo-mgcp: fix unused extra argument to printf() in add_fmtp()
* libosmo-mgcp: always check result of msgb_printf() in add_fmtp()
* libosmo-mgcp-client: mgcp_client_tx(): return rc on error
* debian/control: change maintainer to the Osmocom team / mailing list
* vty: use install_lib_element() and install_lib_element_ve()
* main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
* fixup mgcp_trunk: increase default number of virtual endpoints
[ Alexander Couzens ]
* configure.ac: require libosmoabis + libosmotrau >= 1.0.0
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 18:28:45 +0100
osmo-mgw (1.7.0) unstable; urgency=medium
[ Neels Hofmeyr ]
* rename codecs_cmp() to codecs_same()
* mgcp_codec: constify 'param' arg
* fix crashes: don't assert on incoming RTP packet size
* mgcp_send(): stop looping on conversion error
* mgcp_codec: split codec_free() off of codec_init()
* fix memleak: actually free strings in mgcp_codec_reset_all()
* mgcp_test: extend / rewrite test_mgcp_codec_pt_translate()
* test_mgcp_codec_pt_translate(): more tests
* differentiate AMR octet-aligned=0 vs =1
* ptmap: implicitly match '/8000' and '/8000/1'
* mgcp_codec: codec_set(): log about all possible errors
* mgcp_codec_add: fix audio_name size check
* explicitly free codecs in mgcp_rtp_conn_cleanup()
* tweak mgcp_parse_audio_ptime_rtpmap()
* SDP: store all ptmap entries
* mgcp_client_fsm cleanup: Do not assert on DLCX failure
* clear pending requests on MGCP failure
* client: endp fsm: add notify struct, prep for cancel-notify
* client: endp fsm: clear ci[] before dispatching DLCX success
* client: endp fsm: allow cancelling a notify event
* client: endp fsm: add osmo_mgcpc_ep_ci_ep()
* accept MGCP without SDP
* fix use-after-free: require new fsm deferred dealloc, check for term
[ Pau Espin Pedrol ]
* mgcp_test: Correctly release all endpoints allocated
* mgw: Allocate mgcp_conn instance under tcfg->endpoints
[ Harald Welte ]
* manual: Fix copy+paste error
* mgcp_client: Check for osmo_fsm_register() error return value
* Move fsm_mgcp_client regstration to __attribute__((contructor))
* exit(2) on unsupported positional arguments on command line
[ Oliver Smith ]
* osmoappdesc.py: switch to python 3
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 03 Jan 2020 13:35:09 +0100
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 ]

21
debian/control vendored
View File

@@ -1,15 +1,13 @@
Source: osmo-mgw
Section: net
Priority: extra
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
Maintainer: Alexander Couzens <lynxis@fe80.eu>
Build-Depends: debhelper (>=9),
dh-autoreconf,
pkg-config,
autotools-dev,
libosmocore-dev (>= 1.5.0),
libosmo-netif-dev (>= 1.1.0),
libosmo-abis-dev (>= 1.1.0),
osmo-gsm-manuals-dev (>= 1.1.0)
libosmocore-dev,
libosmo-netif-dev
Standards-Version: 3.9.8
Vcs-Git: git://git.osmocom.org/osmo-mgw.git
Vcs-Browser: https://git.osmocom.org/osmo-mgw/
@@ -21,7 +19,7 @@ Multi-Arch: foreign
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
Package: libosmo-mgcp-client8
Package: libosmo-mgcp-client5
Section: libs
Architecture: any
Multi-Arch: same
@@ -33,14 +31,5 @@ Package: libosmo-mgcp-client-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-mgcp-client8 (= ${binary:Version}), ${misc:Depends}
Depends: libosmo-mgcp-client5 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
Package: osmo-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.

View File

@@ -1 +0,0 @@
usr/share/doc/osmo-mgw-doc/*.pdf

View File

@@ -2,4 +2,3 @@ etc/osmocom/osmo-mgw.cfg
lib/systemd/system/osmo-mgw.service
usr/bin/osmo-mgw
usr/share/doc/osmo-mgw/examples/osmo-mgw/osmo-mgw.cfg
usr/share/doc/osmo-mgw/examples/osmo-mgw/osmo-mgw-abis_e1.cfg

6
debian/rules vendored
View File

@@ -30,10 +30,6 @@ 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 --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
dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system
# See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg

View File

@@ -1,25 +0,0 @@
!
! MGCP configuration example
!
e1_input
e1_line 0 driver dahdi
e1_line 0 port 0
mgcp
bind ip 127.0.0.1
rtp port-range 4002 16000
rtp bind-ip 127.0.0.1
rtp ip-probing
rtp ip-dscp 46
bind port 2427
sdp audio payload number 98
sdp audio payload name GSM
number endpoints 512
loop 0
force-realloc 1
rtcp-omit
rtp-patch ssrc
rtp-patch timestamp
trunk 1
rtp keep-alive once
no rtp keep-alive
line 0

View File

@@ -4,13 +4,13 @@
mgcp
bind ip 127.0.0.1
rtp port-range 4002 16000
rtp bind-ip 127.0.0.1
rtp bind-ip 10.9.1.122
rtp ip-probing
rtp ip-dscp 46
rtp ip-tos 184
bind port 2427
sdp audio payload number 98
sdp audio payload name GSM
number endpoints 512
number endpoints 31
loop 0
force-realloc 1
rtcp-omit

View File

@@ -1,7 +1,6 @@
EXTRA_DIST = osmomgw-usermanual.adoc \
osmomgw-usermanual-docinfo.xml \
osmomgw-vty-reference.xml \
regen_doc.sh \
chapters \
vty
@@ -13,11 +12,5 @@ if BUILD_MANUALS
VTY_REFERENCE = osmomgw-vty-reference.xml
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
BUILT_REFERENCE_XML = $(builddir)/vty/mgw_vty_reference.xml
$(builddir)/vty/mgw_vty_reference.xml: $(top_builddir)/src/osmo-mgw/osmo-mgw
mkdir -p $(builddir)/vty
$(top_builddir)/src/osmo-mgw/osmo-mgw --vty-ref-xml > $@
OSMO_REPOSITORY = osmo-mgw
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
endif

View File

@@ -23,13 +23,14 @@ OsmoMGW(config-mgcp)# local ip 127.0.0.1
=== Configuring the trunk
The first trunk is considered a virtual trunk in OsmoMGW. All
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.
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
----
@@ -53,72 +54,4 @@ OsmoMGW(config-mgcp)# rtp-patch timestamp <3>
<2> Hide SSRC changes
<3> Ensure RTP timestamp is aligned with frame duration
NOTE: Changes to trunks that affect resource allocation, such as newly created
trunks or a change of the number of available endpoints, require a full restart
of osmo-mgw!
=== E1 trunk considerations
While the RTP bridge trunks are natively based on IP no special considerations
are required during setup. E1 trunks are mapped on a physical E1 line, which has
to be configured as shown below.
.Example: E1 line setup
----
OsmoMGW(config-e1_input)# e1_line 0 driver dahdi <1>
OsmoMGW(config-e1_input)# e1_line 0 port 2 <2>
----
<1> Name of the libosmo-abis driver implementation ("dahdi")
<2> Port number of the physical E1 port to use (2)
In osmo-mgw the e1_input node is used to configure the physical E1 line. The
line number will be used internally to identify the configured E1 line. The
port number is the physical E1 connector (sometimes called 'span') at the E1
hardware. Per trunk an individual E1 line will be needed. Beware that the E1
driver may also need configuration settings that are not discussed here.
.Example: E1 trunk setup
----
OsmoMGW(config-mgcp)# trunk 0 <1>
OsmoMGW(config-mgcp-trunk)# line 0 <2>
----
<1> Creation of a trunk (0)
<2> Reference to the E1 line to use (0)
The E1 trunk is created along with a number, typically starting at 0, but if
required any number from 0-64 is allowed. The E1 trunk configuration concerning
the IP related aspects is nearly identical to the configuration of the virtual
trunk. However, it is important that the user assigns one of the E1 line numbers
that were configured under the e1_input node.
.Example: A typical configuration with one E1 trunk
----
e1_input
e1_line 0 driver dahdi
e1_line 0 port 2
mgcp
bind ip 127.0.0.1
rtp net-range 6000 6011
rtp net-bind-ip 192.168.100.130
rtp ip-probing
rtp ip-dscp 46
no rtp keep-alive
bind port 2428
number endpoints 30
loop 0
force-realloc 1
osmux off
rtp-patch rfc5993hr
trunk 0
rtp keep-alive once
no rtp keep-alive
line 0
----
NOTE: One E1 trunk always covers a whole E1 line. All subslots (I.640) will be mapped
to individual MGCP endpoints. As long as the endpoints remain unused the
underlying E1 timeslot is not used.
NOTE: The E1 trunk implementation also works with T1 lines, however since T1 has
24 instead of 31 usable timeslots only the endpoints that fall into that 1-24 timeslot
range will be useable.

View File

@@ -1,79 +1,10 @@
// autogenerated by show asciidoc counters
These counters and their description based on OsmoMGW 1.5.0.64-189f (OsmoMGW).
These counters and their description based on OsmoMGW 1.3.0.34-9cd52 (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

View File

@@ -1,94 +0,0 @@
== MGCP Endpoints
MGCP organizes the switching resources in so called endpoints. Each endpoint is
referenced by its unique identifier. While RFC 3435 specifies a naming scheme, the
actual identifier naming is subject to the implementation and configuration.
=== RTP proxy / RTP bridge endpoints
OsmoMGW implements a freely configurable number of `rtpbridge` endpoints. Those
endpoints are able to host two connections at a time to model the functionality
of a tandem switch.
RTP bridge endpoint identifiers are referenced by the string `rtpbridge/`, a
hexadecimal number without leading zeros and a domain name (configurable).
----
rtpbridge/<number>@<domain>
----
.Example: List of virtual endpoints
----
rtpbridge/1@mgw
rtpbridge/2@mgw
rtpbridge/3@mgw
rtpbridge/4@mgw
rtpbridge/5@mgw
rtpbridge/6@mgw
rtpbridge/7@mgw
rtpbridge/8@mgw
rtpbridge/9@mgw
rtpbridge/a@mgw
rtpbridge/b@mgw
rtpbridge/c@mgw
rtpbridge/d@mgw
rtpbridge/e@mgw
rtpbridge/f@mgw
rtpbridge/10@mgw
----
=== E1/T1 endpoints
OsmoMGW supports E1 subslot multiplexing as specified by I.460. All possible
subslot combinations are mapped on individual endpoints. The endpoint names
are prefixed with `ds/e1-` followed by the trunk number and the E1 timeslot.
The subslot is defined by a bit rate and a bit offset.
----
ds/e1-<trunk>/s-<timeslot>/su<bitrate>-<bitoffset>@<domain>
----
.Example: List of endpoints on E1 trunk 0 at E1 timeslot 2
----
ds/e1-0/s-2/su64-0@mgw
ds/e1-0/s-2/su32-0@mgw
ds/e1-0/s-2/su32-4@mgw
ds/e1-0/s-2/su16-0@mgw
ds/e1-0/s-2/su16-2@mgw
ds/e1-0/s-2/su16-4@mgw
ds/e1-0/s-2/su16-6@mgw
ds/e1-0/s-2/su8-0@mgw
ds/e1-0/s-2/su8-1@mgw
ds/e1-0/s-2/su8-2@mgw
ds/e1-0/s-2/su8-3@mgw
ds/e1-0/s-2/su8-4@mgw
ds/e1-0/s-2/su8-5@mgw
ds/e1-0/s-2/su8-6@mgw
ds/e1-0/s-2/su8-7@mgw
----
When creating connections on endpoints that reside in one E1 timeslot the call
agent must make sure that no overlapping endpoints are used. It is for example
not possible to use `ds/e1-0/s-2/su16-2@mgw` and `ds/e1-0/s-2/su8-3@mgw` at the
same time because they overlap.
.Subslot overlapping
[options="header"]
|===
| Bit offset 4+| Subslots
| 0 | 8k .2+| 16k .4+| 32k .8+| 64k
| 1 | 8k
| 2 | 8k .2+| 16k
| 3 | 8k
| 4 | 8k .2+| 16k .4+| 32k
| 5 | 8k
| 6 | 8k .2+| 16k
| 7 | 8k
|===
NOTE: The current implementation (December 2020) only implements TRAU frame
encoding/decoding for 16K and 8K subslots. Endpoints with other bitrates are
not yet useable.
NOTE: the VTY command "show mgcp" can be used to get a list of all available
endpoints (including identifiers)

View File

@@ -11,7 +11,7 @@ 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
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".
@@ -19,7 +19,7 @@ spaces. Each item consists of one or more non-whitespace characters.
X-Osmo-IGN: X abc 123
----
`X-Osmo-IGN` must be issued in the MGCP header section (typically as its last item),
`X-Osmo-IGN` must be issued in the MGCP section (typically as its last item),
before the SDP section starts.
==== Supported `X-Osmo-IGN` Items
@@ -66,7 +66,3 @@ m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:40
----
=== `X-Osmux`
See <<mgcp-extension-osmux>>

View File

@@ -21,10 +21,10 @@ Protocol.
digraph G {
rankdir = LR;
OsmoBTS -> OsmoBSC [label="Abis/IP"];
OsmoBSC -> "core-network" [label="3GPP AoIP"];
OsmoBSC -> OsmoMSC [label="3GPP AoIP"];
OsmoBSC -> OsmoMGW [label="MGCP"];
OsmoBTS -> OsmoMGW [label="RTP",dir=both];
OsmoMGW -> "core-network" [label="RTP",dir=both];
OsmoMGW -> OsmoMSC [label="RTP",dir=both];
{rank=same OsmoBSC OsmoMGW}
OsmoMGW [color=red];
}
@@ -36,9 +36,10 @@ digraph G {
----
digraph G {
rankdir = LR;
"2G BSS" -> OsmoMSC [label="3GPP AoIP"];
BTS -> BSC [label="Abis"];
BSC -> OsmoMSC [label="3GPP AoIP"];
OsmoMSC -> OsmoMGW [label="MGCP"];
"2G BSS" -> OsmoMGW [label="RTP",dir=both];
BSC -> OsmoMGW [label="RTP",dir=both];
OsmoMSC -> OsmoSIP [label="MNCC"];
OsmoSIP -> PBX [label="SIP Trunk"];
OsmoMGW -> PBX [label="RTP",dir=both];
@@ -52,22 +53,6 @@ digraph G {
}
----
[[fig-bsc-e1]]
.Integration of legacy E1 BTS in AoIP network
[graphviz]
----
digraph G {
rankdir = LR;
BTS -> OsmoBSC [label="Abis/E1"];
OsmoBSC -> "core-network" [label="3GPP AoIP"];
OsmoBSC -> OsmoMGW [label="MGCP"];
BTS -> OsmoMGW [label="TRAU/E1",dir=both];
OsmoMGW -> "core-network" [label="RTP",dir=both];
{rank=same OsmoBSC OsmoMGW}
OsmoMGW [color=red];
}
----
=== Software Components
OsmoMGW contains a variety of different software components, which well
@@ -92,14 +77,14 @@ Transcoding is currently not supported in OsmoMGW.
=== Limitations
At the moment (November 2020), OsmoMGW implements RTP proxy / RTP bridge
type endpoints and E1/T1 16k/8k sub-slots with TRAU frames for classic BTS
support. To the RTP proxy / RTP bridge endpoints two RTP connections can
be established, which then work as a tandem. E1/T1 endpoints support one
RTP connection at a time that is associated with a sub-slot on an E1 line.
We are planning to add further endpoint types for:
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

View File

@@ -1,42 +0,0 @@
==== Full example of QoS for osmo-mgw
In the below example we will show the full set of configuration required
for both DSCP and PCP differentiation of RTP traffic by osmo-mgw.
What we want to achieve in this example is the following configuration:
.DSCP and PCP assignments for osmo-mgw Abis downlink traffic in this example
[options="header",width="30%",cols="2,1,1"]
|===
|Traffic |DSCP|PCP
|RTP | 46| 6
|===
. configure the osmo-mgw program to set the DSCP value
. configure an egrees QoS map to map from priority to PCP
.Example Step 1: add related VTY configuration to `osmo-mgw.cfg`
----
...
mgcp
rtp ip-dscp 46
rtp socket-priority 6
...
----
.Example Step 2: egress QoS map to map from socket priority to PCP values
----
$ sudo ip link set dev eth0.9<1> type vlan egress-qos-map 0:0 5:5 6:6 7:7 <2>
----
<1> make sure to specify your specific VLAN interface name here instead of `eth0.9`.
<2> create a egress QoS map that maps the priority value 1:1 to the PCP. We also include the
mapping 5:5 and 7:7 from the osmo-bsc example (see <<userman-osmobsc>>) here.
NOTE:: The settings of the `ip` command are volatile and only active until
the next reboot (or the network device or VLAN is removed). Please refer to
the documentation of your specific Linux distribution in order to find out how
to make such settings persistent by means of an `ifup` hook whenever the interface
comes up. For CentOS/RHEL 8 this can e.g. be achieved by means of an `/sbin/ifup-local
script` (when using `network-scripts` and not NetworkManager). For Debian or Ubuntu,
this typically involves adding `up` lines to `/etc/network/interfaces` or a `/etc/network/if-up.d`
script.

View File

@@ -12,7 +12,7 @@ arguments:
*-h, --help*::
Print a short help message about the supported options
*-V, --version*::
Print the compile-time version number of the program
Print the compile-time version number of the OsmoBTS program
*-D, --daemonize*::
Fork the process as a daemon into background.
*-c, --config-file 'CONFIGFILE'*::

View File

@@ -18,18 +18,10 @@ include::./common/chapters/logging.adoc[]
include::{srcdir}/chapters/configuration.adoc[]
include::{srcdir}/chapters/mgcp_endpoints.adoc[]
include::{srcdir}/chapters/mgcp_extensions.adoc[]
include::./common/chapters/osmux/osmux.adoc[]
include::./common/chapters/qos-dscp-pcp.adoc[]
//include::{srcdir}/chapters/counters.adoc[]
include::./common/chapters/vty_cpu_sched.adoc[]
include::./common/chapters/port_numbers.adoc[]
include::./common/chapters/bibliography.adoc[]
@@ -37,3 +29,5 @@ include::./common/chapters/bibliography.adoc[]
include::./common/chapters/glossary.adoc[]
include::./common/chapters/gfdl.adoc[]

View File

@@ -1,17 +0,0 @@
#!/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"

File diff suppressed because it is too large Load Diff

View File

@@ -4,15 +4,10 @@ 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)

View File

@@ -6,11 +6,5 @@ noinst_HEADERS = \
mgcp_endp.h \
mgcp_sdp.h \
mgcp_codec.h \
mgcp_ctrl.h \
mgcp_trunk.h \
debug.h \
mgcp_ratectr.h \
mgcp_e1.h \
mgcp_network.h \
mgcp_protocol.h \
$(NULL)

View File

@@ -29,7 +29,6 @@
/* Debug Areas of the code */
enum {
DRTP,
DE1,
Debug_LastEntry,
};

View File

@@ -23,7 +23,6 @@
#pragma once
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/logging.h>
@@ -35,8 +34,6 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include "mgcp_ratectr.h"
#define RTP_PORT_DEFAULT_RANGE_START 16002
#define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64
@@ -45,7 +42,7 @@
*/
struct mgcp_endpoint;
struct mgcp_config;
struct mgcp_trunk;
struct mgcp_trunk_config;
struct mgcp_rtp_end;
#define MGCP_ENDP_CRCX 1
@@ -62,9 +59,10 @@ struct mgcp_rtp_end;
#define MGCP_POLICY_REJECT 5
#define MGCP_POLICY_DEFER 6
typedef int (*mgcp_change)(struct mgcp_endpoint *endp, int state);
typedef int (*mgcp_policy)(struct mgcp_endpoint *endp, int state, const char *transaction_id);
typedef int (*mgcp_reset)(struct mgcp_trunk *cfg);
typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint);
typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state);
typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id);
typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
/**
@@ -83,11 +81,10 @@ 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,
const struct mgcp_rtp_codec **codec,
const char **fmtp_extra,
int *payload_type,
const char**subtype_name,
const char**fmtp_extra,
struct mgcp_conn_rtp *conn);
/**
@@ -95,8 +92,7 @@ typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
*/
struct mgcp_port_range {
/* addr or NULL to fall-back to default */
char *bind_addr_v4;
char *bind_addr_v6;
char *bind_addr;
/* dynamically allocated */
int range_start;
@@ -122,6 +118,105 @@ struct mgcp_port_range {
#define MGCP_KEEPALIVE_ONCE (-1)
#define MGCP_KEEPALIVE_NEVER 0
/* Global MCGP CRCX related rate counters */
enum {
MGCP_CRCX_SUCCESS,
MGCP_CRCX_FAIL_BAD_ACTION,
MGCP_CRCX_FAIL_UNHANDLED_PARAM,
MGCP_CRCX_FAIL_MISSING_CALLID,
MGCP_CRCX_FAIL_INVALID_MODE,
MGCP_CRCX_FAIL_LIMIT_EXCEEDED,
MGCP_CRCX_FAIL_UNKNOWN_CALLID,
MGCP_CRCX_FAIL_ALLOC_CONN,
MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC,
MGCP_CRCX_FAIL_START_RTP,
MGCP_CRCX_FAIL_REJECTED_BY_POLICY,
MGCP_CRCX_FAIL_NO_OSMUX,
MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS,
MGCP_CRCX_FAIL_CODEC_NEGOTIATION,
MGCP_CRCX_FAIL_BIND_PORT,
};
/* Global MCGP MDCX related rate counters */
enum {
MGCP_MDCX_SUCCESS,
MGCP_MDCX_FAIL_WILDCARD,
MGCP_MDCX_FAIL_NO_CONN,
MGCP_MDCX_FAIL_INVALID_CALLID,
MGCP_MDCX_FAIL_INVALID_CONNID,
MGCP_MDCX_FAIL_UNHANDLED_PARAM,
MGCP_MDCX_FAIL_NO_CONNID,
MGCP_MDCX_FAIL_CONN_NOT_FOUND,
MGCP_MDCX_FAIL_INVALID_MODE,
MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS,
MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
MGCP_MDCX_FAIL_START_RTP,
MGCP_MDCX_FAIL_REJECTED_BY_POLICY,
MGCP_MDCX_DEFERRED_BY_POLICY
};
/* Global MCGP DLCX related rate counters */
enum {
MGCP_DLCX_SUCCESS,
MGCP_DLCX_FAIL_WILDCARD,
MGCP_DLCX_FAIL_NO_CONN,
MGCP_DLCX_FAIL_INVALID_CALLID,
MGCP_DLCX_FAIL_INVALID_CONNID,
MGCP_DLCX_FAIL_UNHANDLED_PARAM,
MGCP_DLCX_FAIL_REJECTED_BY_POLICY,
MGCP_DLCX_DEFERRED_BY_POLICY,
};
struct mgcp_trunk_config {
struct llist_head entry;
struct mgcp_config *cfg;
int trunk_nr;
int trunk_type;
char *audio_fmtp_extra;
char *audio_name;
int audio_payload;
int audio_send_ptime;
int audio_send_name;
int audio_loop;
int no_audio_transcoding;
int omit_rtcp;
int keepalive_interval;
/* RTP patching */
int force_constant_ssrc; /* 0: don't, 1: once */
int force_aligned_timing;
bool rfc5993_hr_convert;
bool amr_oa_bwe_convert;
/* spec handling */
int force_realloc;
/* timer */
struct osmo_timer_list keepalive_timer;
/* When set, incoming RTP packets are not filtered
* when ports and ip-address do not match (debug) */
int rtp_accept_all;
unsigned int number_endpoints;
int vty_number_endpoints;
struct mgcp_endpoint *endpoints;
/* Rate counter group which contains stats for processed CRCX commands. */
struct rate_ctr_group *mgcp_crcx_ctr_group;
/* Rate counter group which contains stats for processed MDCX commands. */
struct rate_ctr_group *mgcp_mdcx_ctr_group;
/* Rate counter group which contains stats for processed DLCX commands. */
struct rate_ctr_group *mgcp_dlcx_ctr_group;
/* Rate counter group which aggregates stats of individual RTP connections. */
struct rate_ctr_group *all_rtp_conn_stats;
};
enum mgcp_role {
MGCP_BSC = 0,
MGCP_BSC_NAT,
@@ -143,19 +238,20 @@ struct mgcp_config {
struct mgcp_port_range net_ports;
int endp_dscp;
int endp_priority;
int force_ptime;
mgcp_change change_cb;
mgcp_policy policy_cb;
mgcp_reset reset_cb;
mgcp_realloc realloc_cb;
mgcp_rqnt rqnt_cb;
void *data;
uint32_t last_call_id;
/* list holding the trunks */
/* trunk handling */
struct mgcp_trunk_config trunk;
struct llist_head trunks;
enum mgcp_role role;
@@ -183,13 +279,6 @@ struct mgcp_config {
/* time after which inactive connections (CIs) get closed */
int conn_timeout;
/* osmocom CTRL interface */
struct ctrl_handle *ctrl;
/* global rate counters to measure the MGWs overall performance and
* health */
struct mgcp_ratectr_global ratectr;
};
/* config management */
@@ -197,7 +286,8 @@ struct mgcp_config *mgcp_config_alloc(void);
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
enum mgcp_role role);
int mgcp_vty_init(void);
void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval);
int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg);
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
/*
* format helper functions
@@ -205,10 +295,9 @@ void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval);
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
int mgcp_send_reset_ep(struct mgcp_endpoint *endp);
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint);
int mgcp_send_reset_all(struct mgcp_config *cfg);
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint8_t dscp,
uint8_t prio);
int mgcp_udp_send(int fd, struct osmo_sockaddr *addr, int port, char *buf, int len);
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port);
int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len);

View File

@@ -1,19 +1,7 @@
#pragma once
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
#define PTYPE_UNDEFINED (-1)
struct mgcp_conn_rtp;
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, const struct mgcp_codec_param *param);
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name);
int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
const char *subtype_name, unsigned int match_nr);

View File

@@ -50,7 +50,6 @@ 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 {
@@ -58,12 +57,6 @@ 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)
{
@@ -100,10 +93,6 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
/* A prefix to denote the virtual trunk (RTP on both ends) */
#define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/"
/* A prefix to denote the e1 trunk
* (see also RFC3435 section E.2) */
#define MGCP_ENDPOINT_PREFIX_E1_TRUNK "ds/e1-"
/* Maximal number of payload types / codecs that can be negotiated via SDP at
* at once. */
#define MGCP_MAX_CODECS 10

View File

@@ -23,114 +23,11 @@
#pragma once
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_network.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include <inttypes.h>
#define LOGPCONN(conn, cat, level, fmt, args...) \
LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
(conn)->id, \
## args)
#define LOG_CONN(conn, level, fmt, args...) \
LOGP(DRTP, level, "(%s I:%s) " fmt, \
(conn)->endp ? (conn)->endp->name : "none", (conn)->id, ## args)
#define LOG_CONN_RTP(conn_rtp, level, fmt, args...) \
LOG_CONN((conn_rtp)->conn, level, fmt, ## args)
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
enum mgcp_conn_rtp_type {
MGCP_RTP_DEFAULT = 0,
MGCP_OSMUX_BSC,
MGCP_OSMUX_BSC_NAT,
};
/*! Connection type, specifies which member of the union "u" in mgcp_conn
* contains a useful connection description (currently only RTP) */
enum mgcp_conn_type {
MGCP_CONN_TYPE_RTP,
};
/* MGCP connection (RTP) */
struct mgcp_conn_rtp {
/* Backpointer to conn struct */
struct mgcp_conn *conn;
/* Specific connection type */
enum mgcp_conn_rtp_type type;
/* Port status */
struct mgcp_rtp_end end;
/* Sequence bits */
struct mgcp_rtp_state state;
/* taps for the rtp connection; one per direction */
struct mgcp_rtp_tap tap_in;
struct mgcp_rtp_tap tap_out;
/* Osmux states (optional) */
struct {
/* Osmux state: disabled, activating, active */
enum osmux_state state;
/* 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;
/* handle to unbatch messages */
struct osmux_out_handle out;
/* statistics */
struct {
uint32_t chunks;
uint32_t octets;
} stats;
} osmux;
struct rate_ctr_group *rate_ctr_group;
};
/*! MGCP connection (untyped) */
struct mgcp_conn {
/*! list head */
struct llist_head entry;
/*! Backpointer to the endpoint where the conn belongs to */
struct mgcp_endpoint *endp;
/*! type of the connection (union) */
enum mgcp_conn_type type;
/*! mode of the connection */
enum mgcp_connection_mode mode;
/*! copy of the mode to restore the original setting (VTY) */
enum mgcp_connection_mode mode_orig;
/*! connection id to identify the connection */
char id[MGCP_CONN_ID_MAXLEN];
/*! human readable name (vty, logging) */
char name[256];
/*! activity tracker (for cleaning up inactive connections) */
struct osmo_timer_list watchdog;
/*! union with connection description */
union {
struct mgcp_conn_rtp rtp;
} u;
/*! pointer to optional private data */
void *priv;
};
/* RTP connection related counters */
enum {
IN_STREAM_ERR_TSTMP_CTR,
@@ -171,11 +68,6 @@ static const struct rate_ctr_desc all_rtp_conn_rate_ctr_desc[] = {
[RTP_NUM_CONNECTIONS] = {"all_rtp:num_closed_conns", "Total number of rtp connections closed."}
};
/* 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;
}
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
enum mgcp_conn_type type, char *name);
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
@@ -186,5 +78,3 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
char *mgcp_conn_dump(struct mgcp_conn *conn);
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp);
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);

View File

@@ -1,24 +0,0 @@
/*
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
const char *bind_addr, uint16_t port);

View File

@@ -1,27 +0,0 @@
#pragma once
/* A 64k timeslot on an E1 line can be subdevied into the following
* subslot combinations:
*
* subslot: offset:
* [ ][ ][ 16k ][8k_subslot] 0
* [ ][ 32k ][_subslot__][8k_subslot] 1
* [ ][ subslot ][ 16k ][8k_subslot] 2
* [ 64k ][__________][_subslot__][8k_subslot] 3
* [ timeslot ][ ][ 16k ][8k_subslot] 4
* [ ][ 32K ][_subslot__][8k_subslot] 5
* [ ][ subslot ][ 16k ][8k_subslot] 6
* [ ][ ][ subslot ][8k_subslot] 7
*
* Since overlapping assignment of subslots is not possible there is a limited
* set of subslot assignments possible. The e1_rates array lists the possible
* assignments as depicted above. Also each subslot assignment comes along with
* a bit offset in the E1 bitstream. The e1_offsets arrays lists the bit
* offsets. */
static const uint8_t e1_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
static const uint8_t e1_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs);
void mgcp_e1_endp_update(struct mgcp_endpoint *endp);
void mgcp_e1_endp_release(struct mgcp_endpoint *endp);
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg);

View File

@@ -23,35 +23,15 @@
#pragma once
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/i460_mux.h>
struct sockaddr;
struct sockaddr_in;
struct mgcp_conn;
struct mgcp_conn_rtp;
struct mgcp_endpoint;
/* Number of E1 subslots (different variants, not all useable at the same time) */
#define MGCP_ENDP_E1_SUBSLOTS 15
#define LOGPENDP(endp, cat, level, fmt, args...) \
LOGP(cat, level, "endpoint:%s " fmt, \
endp ? endp->name : "none", \
## args)
struct osmo_rtp_msg_ctx {
int proto;
struct mgcp_conn_rtp *conn_src;
struct osmo_sockaddr *from_addr;
};
#define OSMO_RTP_MSG_CTX(MSGB) ((struct osmo_rtp_msg_ctx*)(MSGB)->cb)
osmo_static_assert(sizeof(((struct msgb*)0)->cb) >= sizeof(struct osmo_rtp_msg_ctx), osmo_rtp_msg_ctx_fits_in_msgb_cb);
/* Callback type for RTP dispatcher functions (e.g mgcp_dispatch_rtp_bridge_cb, see below).
* The OSMO_RTP_MSG_CTX() should be set appropriately on the msg. */
typedef int (*mgcp_dispatch_rtp_cb) (struct msgb *msg);
/* Callback type for RTP dispatcher functions
(e.g mgcp_dispatch_rtp_bridge_cb, see below) */
typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in *addr,
char *buf, unsigned int buf_size,
struct mgcp_conn *conn);
/* Callback type for endpoint specific cleanup actions. This function
* is automatically executed when a connection is freed (see mgcp_conn_free()
@@ -75,7 +55,6 @@ struct mgcp_endpoint_type {
/*! MGCP endpoint typeset */
struct mgcp_endpoint_typeset {
struct mgcp_endpoint_type rtp;
struct mgcp_endpoint_type e1;
};
/*! static MGCP endpoint typeset (pre-initalized, read-only) */
@@ -84,9 +63,6 @@ extern const struct mgcp_endpoint_typeset ep_typeset;
/*! MGCP endpoint model */
struct mgcp_endpoint {
/*! Unique endpoint name, used for addressing via MGCP */
char *name;
/*! Call identifier string (as supplied by the call agant) */
char *callid;
@@ -99,8 +75,8 @@ struct mgcp_endpoint {
/*! Backpointer to the MGW configuration */
struct mgcp_config *cfg;
/*! Backpointer to the trunk this endpoint belongs to */
struct mgcp_trunk *trunk;
/*! Backpointer to the Trunk specific configuration */
struct mgcp_trunk_config *tcfg;
/*! Endpoint properties (see above) */
const struct mgcp_endpoint_type *type;
@@ -117,27 +93,10 @@ struct mgcp_endpoint {
/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */
uint32_t x_osmo_ign;
/* E1 specific */
struct {
struct osmo_i460_schan_desc scd;
struct osmo_i460_subchan *schan;
struct osmo_fsm_inst *trau_sync_fi;
struct osmo_trau2rtp_state *trau_rtp_st;
uint8_t last_amr_ft;
struct mgcp_rtp_codec *last_codec;
} e1;
};
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int index);
/*! Extract endpoint number for a given endpoint */
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
void mgcp_endp_release(struct mgcp_endpoint *endp);
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid);
void mgcp_endp_update(struct mgcp_endpoint *endp);
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
const struct mgcp_trunk *trunk);
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
struct mgcp_config *cfg);
bool mgcp_endp_avail(struct mgcp_endpoint *endp);
void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);

View File

@@ -0,0 +1,336 @@
/* MGCP Private Data */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <string.h>
#include <inttypes.h>
#include <osmocom/core/select.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/counter.h>
#include <osmocom/core/rate_ctr.h>
#define CI_UNUSED 0
/* FIXME: This this is only needed to compile the currently
* broken OSMUX support. Remove when fixed */
#define CONN_ID_BTS "0"
#define CONN_ID_NET "1"
enum mgcp_trunk_type {
MGCP_TRUNK_VIRTUAL,
MGCP_TRUNK_E1,
};
struct mgcp_rtp_stream_state {
uint32_t ssrc;
uint16_t last_seq;
uint32_t last_timestamp;
struct rate_ctr *err_ts_ctr;
int32_t last_tsdelta;
uint32_t last_arrival_time;
};
struct mgcp_rtp_state {
/* has this state structure been initialized? */
int initialized;
struct {
/* are we patching the SSRC value? */
int patch_ssrc;
/* original SSRC (to which we shall patch any different SSRC) */
uint32_t orig_ssrc;
/* offset to apply on the sequence number */
int seq_offset;
/* offset to apply on the timestamp number */
int32_t timestamp_offset;
} patch;
/* duration of a packet (FIXME: in which unit?) */
uint32_t packet_duration;
struct mgcp_rtp_stream_state in_stream;
struct mgcp_rtp_stream_state out_stream;
/* jitter and packet loss calculation */
struct {
int initialized;
uint16_t base_seq;
uint16_t max_seq;
uint32_t ssrc;
uint32_t jitter;
int32_t transit;
int cycles;
} stats;
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
};
struct mgcp_rtp_codec {
uint32_t rate;
int channels;
uint32_t frame_duration_num;
uint32_t frame_duration_den;
int payload_type;
char *audio_name;
char *subtype_name;
};
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
struct mgcp_rtp_end {
/* local IP address of the RTP socket */
struct in_addr addr;
/* in network byte order */
int rtp_port, rtcp_port;
/* currently selected audio codec */
struct mgcp_rtp_codec *codec;
/* array with assigned audio codecs to choose from (SDP) */
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
/* number of assigned audio codecs (SDP) */
unsigned int codecs_assigned;
/* per endpoint data */
int frames_per_packet;
uint32_t packet_duration_ms;
int maximum_packet_time; /* -1: not set */
char *fmtp_extra;
/* are we transmitting packets (1) or dropping (0) outbound packets */
int output_enabled;
/* FIXME: This parameter can be set + printed, but is nowhere used! */
int force_output_ptime;
/* RTP patching */
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
int force_aligned_timing;
bool rfc5993_hr_convert;
bool amr_oa_bwe_convert;
/* Each end has a separate socket for RTP and RTCP */
struct osmo_fd rtp;
struct osmo_fd rtcp;
/* local UDP port number of the RTP socket; RTCP is +1 */
int local_port;
};
struct mgcp_rtp_tap {
/* is this tap active (1) or not (0) */
int enabled;
/* IP/port to which we're forwarding the tapped data */
struct sockaddr_in forward;
};
struct mgcp_lco {
char *string;
char *codec;
int pkt_period_min; /* time in ms */
int pkt_period_max; /* time in ms */
};
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
enum mgcp_conn_rtp_type {
MGCP_RTP_DEFAULT = 0,
MGCP_OSMUX_BSC,
MGCP_OSMUX_BSC_NAT,
};
#include <osmocom/mgcp/osmux.h>
struct mgcp_conn;
/* MGCP connection (RTP) */
struct mgcp_conn_rtp {
/* Backpointer to conn struct */
struct mgcp_conn *conn;
/* Specific connection type */
enum mgcp_conn_rtp_type type;
/* Port status */
struct mgcp_rtp_end end;
/* Sequence bits */
struct mgcp_rtp_state state;
/* taps for the rtp connection; one per direction */
struct mgcp_rtp_tap tap_in;
struct mgcp_rtp_tap tap_out;
/* Osmux states (optional) */
struct {
/* Osmux state: disabled, activating, active */
enum osmux_state state;
/* Allocated Osmux circuit ID for this endpoint */
int allocated_cid;
/* Used Osmux circuit ID for this endpoint */
uint8_t cid;
/* handle to batch messages */
struct osmux_in_handle *in;
/* handle to unbatch messages */
struct osmux_out_handle out;
/* statistics */
struct {
uint32_t chunks;
uint32_t octets;
} stats;
} osmux;
struct rate_ctr_group *rate_ctr_group;
};
/*! Connection type, specifies which member of the union "u" in mgcp_conn
* contains a useful connection description (currently only RTP) */
enum mgcp_conn_type {
MGCP_CONN_TYPE_RTP,
};
/*! MGCP connection (untyped) */
struct mgcp_conn {
/*! list head */
struct llist_head entry;
/*! Backpointer to the endpoint where the conn belongs to */
struct mgcp_endpoint *endp;
/*! type of the connection (union) */
enum mgcp_conn_type type;
/*! mode of the connection */
enum mgcp_connection_mode mode;
/*! copy of the mode to restore the original setting (VTY) */
enum mgcp_connection_mode mode_orig;
/*! connection id to identify the connection */
char id[MGCP_CONN_ID_MAXLEN];
/*! human readable name (vty, logging) */
char name[256];
/*! activity tracker (for cleaning up inactive connections) */
struct osmo_timer_list watchdog;
/*! union with connection description */
union {
struct mgcp_conn_rtp rtp;
} u;
/*! pointer to optional private data */
void *priv;
};
#include <osmocom/mgcp/mgcp_conn.h>
struct mgcp_endpoint_type;
/**
* Internal structure while parsing a request
*/
struct mgcp_parse_data {
struct mgcp_config *cfg;
struct mgcp_endpoint *endp;
char *trans;
char *save;
};
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
char *buf, int rc, struct mgcp_conn_rtp *conn_src,
struct mgcp_conn_rtp *conn_dst);
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
unsigned int buf_size, struct mgcp_conn *conn);
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
struct mgcp_conn_rtp *conn);
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
/* For transcoding we need to manage an in and an output that are connected */
static inline int endp_back_channel(int endpoint)
{
return endpoint + 60;
}
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
char *get_lco_identifier(const char *options);
int check_local_cx_options(void *ctx, const char *options);
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
struct mgcp_rtp_end *rtp);
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *rtp);
/* payload processing default functions */
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn_dst,
struct mgcp_conn_rtp *conn_src);
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
int *payload_type,
const char**audio_name,
const char**fmtp_extra,
struct mgcp_conn_rtp *conn);
/* internal RTP Annex A counting */
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
const uint16_t seq, const int32_t transit,
const uint32_t ssrc);
int mgcp_set_ip_tos(int fd, int tos);
enum {
MGCP_DEST_NET = 0,
MGCP_DEST_BTS,
};
#define MGCP_DUMMY_LOAD 0x23
/**
* SDP related information
*/
/* Assume audio frame length of 20ms */
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
#define PTYPE_UNDEFINED (-1)
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);

View File

@@ -1,168 +0,0 @@
#pragma once
#include <inttypes.h>
#include <stdbool.h>
#include <osmocom/core/socket.h>
#include <osmocom/mgcp/mgcp.h>
#define MGCP_DUMMY_LOAD 0x23
#define RTP_BUF_SIZE 4096
struct mgcp_rtp_stream_state {
uint32_t ssrc;
uint16_t last_seq;
uint32_t last_timestamp;
struct rate_ctr *err_ts_ctr;
int32_t last_tsdelta;
uint32_t last_arrival_time;
};
struct mgcp_rtp_state {
/* has this state structure been initialized? */
int initialized;
struct {
/* are we patching the SSRC value? */
int patch_ssrc;
/* original SSRC (to which we shall patch any different SSRC) */
uint32_t orig_ssrc;
/* offset to apply on the sequence number */
int seq_offset;
/* offset to apply on the timestamp number */
int32_t timestamp_offset;
} patch;
/* duration of a packet (FIXME: in which unit?) */
uint32_t packet_duration;
/* Note: These states are not continuously updated, they serve as an
* information source to patch certain values in the RTP header. Do
* not use this state if constantly updated data about the RTP stream
* is needed. (see also mgcp_patch_and_count() */
struct mgcp_rtp_stream_state in_stream;
struct mgcp_rtp_stream_state out_stream;
/* jitter and packet loss calculation */
struct {
int initialized;
uint16_t base_seq;
uint16_t max_seq;
uint32_t ssrc;
uint32_t jitter;
int32_t transit;
int cycles;
} stats;
/* Alternative values for RTP tx, in case no sufficient header
* information is available so the header needs to be generated
* locally (when just forwarding packets, the header of incoming
* data is just re-used) */
uint16_t alt_rtp_tx_sequence;
uint32_t alt_rtp_tx_ssrc;
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
};
struct mgcp_rtp_codec {
uint32_t rate;
int channels;
uint32_t frame_duration_num;
uint32_t frame_duration_den;
int payload_type;
char *audio_name;
char *subtype_name;
bool param_present;
struct mgcp_codec_param param;
};
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
struct mgcp_rtp_end {
/* remote IP address of the RTP socket */
struct osmo_sockaddr addr;
/* in network byte order */
int rtp_port, rtcp_port;
/* currently selected audio codec */
struct mgcp_rtp_codec *codec;
/* array with assigned audio codecs to choose from (SDP) */
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
/* number of assigned audio codecs (SDP) */
unsigned int codecs_assigned;
/* per endpoint data */
int frames_per_packet;
uint32_t packet_duration_ms;
int maximum_packet_time; /* -1: not set */
char *fmtp_extra;
/* are we transmitting packets (1) or dropping (0) outbound packets */
int output_enabled;
/* FIXME: This parameter can be set + printed, but is nowhere used! */
int force_output_ptime;
/* RTP patching */
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
int force_aligned_timing;
bool rfc5993_hr_convert;
/* Each end has a separate socket for RTP and RTCP */
struct osmo_fd rtp;
struct osmo_fd rtcp;
/* local UDP port number of the RTP socket; RTCP is +1 */
int local_port;
/* where the endpoint RTP connection binds to, set during CRCX and
* possibly updated during MDCX */
char local_addr[INET6_ADDRSTRLEN];
};
struct mgcp_rtp_tap {
/* is this tap active (1) or not (0) */
int enabled;
/* IP/port to which we're forwarding the tapped data */
struct osmo_sockaddr forward;
};
struct mgcp_conn;
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr,
struct msgb *msg, struct mgcp_conn_rtp *conn_src,
struct mgcp_conn_rtp *conn_dst);
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
int mgcp_dispatch_e1_bridge_cb(struct msgb *msg);
void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
struct mgcp_conn_rtp *conn);
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
struct mgcp_rtp_state *state,
struct mgcp_rtp_end *rtp_end,
struct osmo_sockaddr *addr, struct msgb *msg);
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
/* payload processing default functions */
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn_dst,
struct mgcp_conn_rtp *conn_src);
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
const struct mgcp_rtp_codec **codec,
const char **fmtp_extra,
struct mgcp_conn_rtp *conn);
/* internal RTP Annex A counting */
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
const uint16_t seq, const int32_t transit,
const uint32_t ssrc);

View File

@@ -1,27 +0,0 @@
#pragma once
/* Internal structure while parsing a request */
struct mgcp_parse_data {
struct mgcp_config *cfg;
struct mgcp_endpoint *endp;
char *trans;
char *save;
};
/* Local connection options */
struct mgcp_lco {
char *string;
char *codec;
int pkt_period_min; /* time in ms */
int pkt_period_max; /* time in ms */
};
char *get_lco_identifier(const char *options);
int check_local_cx_options(void *ctx, const char *options);
struct mgcp_rtp_end;
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
struct mgcp_rtp_end *rtp);
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *rtp);

View File

@@ -1,95 +0,0 @@
#pragma once
/* Global MCGP general rate counters */
enum {
MGCP_GENERAL_RX_MSGS_TOTAL,
MGCP_GENERAL_RX_MSGS_RETRANSMITTED,
MGCP_GENERAL_RX_MSGS_HANDLED,
MGCP_GENERAL_RX_MSGS_UNHANDLED,
MGCP_GENERAL_RX_FAIL_MSG_PARSE,
MGCP_GENERAL_RX_FAIL_NO_ENDPOINT,
};
/* Trunk-global MCGP CRCX related rate counters */
enum {
MGCP_CRCX_SUCCESS,
MGCP_CRCX_FAIL_BAD_ACTION,
MGCP_CRCX_FAIL_UNHANDLED_PARAM,
MGCP_CRCX_FAIL_MISSING_CALLID,
MGCP_CRCX_FAIL_INVALID_MODE,
MGCP_CRCX_FAIL_LIMIT_EXCEEDED,
MGCP_CRCX_FAIL_UNKNOWN_CALLID,
MGCP_CRCX_FAIL_ALLOC_CONN,
MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC,
MGCP_CRCX_FAIL_START_RTP,
MGCP_CRCX_FAIL_REJECTED_BY_POLICY,
MGCP_CRCX_FAIL_NO_OSMUX,
MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS,
MGCP_CRCX_FAIL_CODEC_NEGOTIATION,
MGCP_CRCX_FAIL_BIND_PORT,
MGCP_CRCX_FAIL_AVAIL,
MGCP_CRCX_FAIL_CLAIM,
};
/* Trunk-global MCGP MDCX related rate counters */
enum {
MGCP_MDCX_SUCCESS,
MGCP_MDCX_FAIL_WILDCARD,
MGCP_MDCX_FAIL_NO_CONN,
MGCP_MDCX_FAIL_INVALID_CALLID,
MGCP_MDCX_FAIL_INVALID_CONNID,
MGCP_MDCX_FAIL_UNHANDLED_PARAM,
MGCP_MDCX_FAIL_NO_CONNID,
MGCP_MDCX_FAIL_CONN_NOT_FOUND,
MGCP_MDCX_FAIL_INVALID_MODE,
MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS,
MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
MGCP_MDCX_FAIL_START_RTP,
MGCP_MDCX_FAIL_REJECTED_BY_POLICY,
MGCP_MDCX_DEFERRED_BY_POLICY,
MGCP_MDCX_FAIL_AVAIL,
};
/* Trunk-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,
MGCP_DLCX_FAIL_AVAIL,
};
/* Trunk-global E1 related counters */
enum {
E1_I460_TRAU_RX_FAIL_CTR,
E1_I460_TRAU_TX_FAIL_CTR,
E1_I460_TRAU_MUX_EMPTY_CTR,
};
/* NOTE: When adding counters, also the dump_ratectr_* routines in vty.c must be updated. */
struct mgcp_ratectr_global {
/* Rate counter group which contains stats for generic MGCP events. */
struct rate_ctr_group *mgcp_general_ctr_group;
};
struct mgcp_ratectr_trunk {
/* Rate counter group which contains stats for processed CRCX commands. */
struct rate_ctr_group *mgcp_crcx_ctr_group;
/* Rate counter group which contains stats for processed MDCX commands. */
struct rate_ctr_group *mgcp_mdcx_ctr_group;
/* Rate counter group which contains stats for processed DLCX commands. */
struct rate_ctr_group *mgcp_dlcx_ctr_group;
/* Rate counter group which aggregates stats of individual RTP connections. */
struct rate_ctr_group *all_rtp_conn_stats;
/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */
struct rate_ctr_group *e1_stats;
};
int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr);
int mgcp_ratectr_trunk_alloc(void *ctx, struct mgcp_ratectr_trunk *ratectr);

View File

@@ -24,6 +24,7 @@
#pragma once
#include <osmocom/mgcp/mgcp_internal.h>
#include <inttypes.h>
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn);

View File

@@ -1,82 +0,0 @@
#pragma once
#include <osmocom/gsm/i460_mux.h>
#include <osmocom/abis/e1_input.h>
#define LOGPTRUNK(trunk, cat, level, fmt, args...) \
LOGP(cat, level, "trunk:%u " fmt, \
trunk ? trunk->trunk_nr : 0, \
## args)
enum mgcp_trunk_type {
MGCP_TRUNK_VIRTUAL,
MGCP_TRUNK_E1,
};
struct mgcp_trunk {
struct llist_head entry;
struct mgcp_config *cfg;
int trunk_nr;
enum mgcp_trunk_type trunk_type;
char *audio_fmtp_extra;
int audio_send_ptime;
int audio_send_name;
int no_audio_transcoding;
int omit_rtcp;
int keepalive_interval;
/* RTP patching */
int force_constant_ssrc; /* 0: don't, 1: once */
int force_aligned_timing;
bool rfc5993_hr_convert;
/* spec handling */
int force_realloc;
/* timer */
struct osmo_timer_list keepalive_timer;
/* When set, incoming RTP packets are not filtered
* when ports and ip-address do not match (debug) */
int rtp_accept_all;
unsigned int number_endpoints;
struct mgcp_endpoint **endpoints;
/* global rate counters to measure the trunks overall performance and health */
struct mgcp_ratectr_trunk ratectr;
union {
/* Virtual trunk specific */
struct {
unsigned int vty_number_endpoints;
} v;
/* E1 specific */
struct {
unsigned int vty_line_nr;
bool ts_in_use[NUM_E1_TS-1];
struct osmo_i460_timeslot i460_ts[NUM_E1_TS-1];
/* Note: on an E1 line TS 0 is devoted to framing and
* alignment and therefore only NUM_E1_TS-1 timeslots
* are available for traffic. */
} e1;
};
};
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
int mgcp_trunk_equip(struct mgcp_trunk *trunk);
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname);
int e1_trunk_nr_from_epname(const char *epname);
struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num);
/* The virtual trunk is always created on trunk id 0 for historical reasons,
* use this define constant as ID when allocating a virtual trunk. Other
* trunks may be assigned with arbritrary id numbers */
#define MGCP_VIRT_TRUNK_ID 0

View File

@@ -1,6 +1,5 @@
#pragma once
#include <osmocom/core/socket.h>
#include <osmocom/netif/osmux.h>
struct mgcp_conn_rtp;
@@ -14,21 +13,19 @@ enum {
int osmux_init(int role, struct mgcp_config *cfg);
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
struct osmo_sockaddr *addr, uint16_t port);
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);
struct in_addr *addr, uint16_t port);
void osmux_disable_conn(struct mgcp_conn_rtp *conn);
void osmux_allocate_cid(struct mgcp_conn_rtp *conn);
void osmux_release_cid(struct mgcp_conn_rtp *conn);
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
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);
int osmux_get_cid(void);
void osmux_put_cid(uint8_t osmux_cid);
int osmux_used_cid(void);
enum osmux_state {
OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */
OSMUX_STATE_NEGOTIATING, /* Osmux was locally requested in MGCP CRCX */
OSMUX_STATE_ACTIVATING, /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref osmux_enable_endpoint. */
OSMUX_STATE_ENABLED, /* Osmux was initialized by \ref osmux_enable_endpoint and can process frames */
};

View File

@@ -6,7 +6,3 @@ enum mgcp_vty_node {
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
TRUNK_NODE,
};
enum mgw_vty_cmd_attr {
MGW_CMD_ATTR_NEWCONN = 0,
};

View File

@@ -6,7 +6,7 @@
#include <osmocom/mgcp_client/mgcp_common.h>
/* See also: RFC 3435, chapter 3.5 Transmission over UDP */
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT NULL /* INADDR(6)_ANY */
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727
#define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
@@ -37,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,
};
@@ -47,10 +47,6 @@ 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 {
@@ -67,20 +63,18 @@ 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 {
char *body;
struct mgcp_response_head head;
uint16_t audio_port;
char audio_ip[INET6_ADDRSTRLEN];
char audio_ip[INET_ADDRSTRLEN];
unsigned int ptime;
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
unsigned int codecs_len;
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
unsigned int ptmap_len;
};
enum mgcp_verb {
@@ -97,7 +91,6 @@ 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 {
@@ -116,10 +109,6 @@ 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);
@@ -133,12 +122,10 @@ int mgcp_client_connect(struct mgcp_client *mgcp);
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) OSMO_DEPRECATED("deprecated, returns 0");
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp);
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp);
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp);
const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts,
uint8_t rate, uint8_t offset);
/* Invoked when an MGCP response is received or sending failed. When the
* response is passed as NULL, this indicates failure during transmission. */
@@ -161,7 +148,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(const struct ptmap *ptmap, unsigned int ptmap_len,
unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
enum mgcp_codecs codec);
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
unsigned int pt);

View File

@@ -1,51 +0,0 @@
/* 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);
void osmo_mgcpc_ep_cancel_notify(struct osmo_mgcpc_ep *ep, struct osmo_fsm_inst *notify);
struct osmo_mgcpc_ep *osmo_mgcpc_ep_ci_ep(struct osmo_mgcpc_ep_ci *ci);
/*! 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); }

View File

@@ -8,14 +8,14 @@
* (either remote or local). It is used to pass parameters (local) to the FSM
* and get responses (remote) from the FSM as pointer attached to the FSM
* event.
*
*
* When modifiying a connection, the endpoint and call_id members may be left
* unpopulated. The call_id field is ignored in this case. If an endpoint
* identifier is supplied it is checked against the internal state to make
* sure it is correct. */
struct mgcp_conn_peer {
/*! RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
char addr[INET6_ADDRSTRLEN];
char addr[INET_ADDRSTRLEN];
/*! RTP connection IP-Port (optional) */
uint16_t port;
@@ -47,20 +47,6 @@ 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,
@@ -69,5 +55,3 @@ 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);

View File

@@ -6,6 +6,7 @@
struct mgcp_client {
struct mgcp_client_conf actual;
uint32_t remote_addr;
struct osmo_wqueue wq;
mgcp_trans_id_t next_trans_id;
struct llist_head responses_pending;

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3
#!/usr/bin/env python
# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
# This program is free software: you can redistribute it and/or modify

View File

@@ -9,7 +9,6 @@ AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -21,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=8:0:0
MGCP_CLIENT_LIBVERSION=5:0:0
lib_LTLIBRARIES = \
libosmo-mgcp-client.la \
@@ -31,7 +30,6 @@ 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)

View File

@@ -25,29 +25,19 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/byteswap.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
#include <osmocom/abis/e1_input.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#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) */
const struct value_string osmo_mgcpc_codec_names[] = {
static const struct value_string codec_table[] = {
{ CODEC_PCMU_8000_1, "PCMU/8000/1" },
{ CODEC_GSM_8000_1, "GSM/8000/1" },
{ CODEC_PCMA_8000_1, "PCMA/8000/1" },
@@ -91,19 +81,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(osmo_mgcpc_codec_names); i++) {
codec_name = extract_codec_name(osmo_mgcpc_codec_names[i].str);
for (i = 0; i < ARRAY_SIZE(codec_table); i++) {
codec_name = extract_codec_name(codec_table[i].str);
if (!codec_name)
continue;
if (strcmp(codec_name, str_buf) == 0)
return osmo_mgcpc_codec_names[i].value;
return codec_table[i].value;
}
return -1;
}
/* Check the ptmap for illegal mappings */
static int check_ptmap(const struct ptmap *ptmap)
static int check_ptmap(struct ptmap *ptmap)
{
/* Check if there are mappings that leave the IANA assigned dynamic
* payload type range. Under normal conditions such mappings should
@@ -132,7 +122,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(const struct ptmap *ptmap, unsigned int ptmap_len,
unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
enum mgcp_codecs codec)
{
unsigned int i;
@@ -270,8 +260,7 @@ 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;
char *pt_end;
unsigned long int pt;
unsigned int pt;
unsigned int count = 0;
unsigned int i;
@@ -295,14 +284,7 @@ static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
pt_str = strtok(NULL, " ");
if (!pt_str)
break;
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;
pt = atoi(pt_str);
/* Do not allow duplicate payload types */
for (i = 0; i < count; i++)
@@ -340,70 +322,69 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
{
unsigned int pt;
char codec_resp[64];
enum mgcp_codecs codec;
unsigned int codec;
if (strstr(line, "ptime")) {
if (sscanf(line, "a=ptime:%u", &r->ptime) != 1)
goto response_parse_failure_ptime;
} else if (strstr(line, "rtpmap")) {
if (sscanf(line, "a=rtpmap:%d %63s", &pt, codec_resp) == 2) {
/* The MGW may assign an own payload type in the
* response if the choosen codec falls into the IANA
* assigned dynamic payload type range (96-127).
* Normally the MGW should obey the 3gpp payload type
* assignments, which are fixed, so we likely wont see
* anything unexpected here. In order to be sure that
* we will now check the codec string and if the result
* does not match to what is IANA / 3gpp assigned, we
* will create an entry in the ptmap table so we can
* lookup later what has been assigned. */
codec = map_str_to_codec(codec_resp);
if (codec != pt) {
if (r->ptmap_len < ARRAY_SIZE(r->ptmap)) {
r->ptmap[r->ptmap_len].pt = pt;
r->ptmap[r->ptmap_len].codec = codec;
r->ptmap_len++;
} else
goto response_parse_failure_rtpmap;
}
#define A_PTIME "a=ptime:"
#define A_RTPMAP "a=rtpmap:"
if (osmo_str_startswith(line, A_PTIME)) {
if (sscanf(line, A_PTIME "%u", &r->ptime) != 1) {
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse SDP parameter, invalid ptime (%s)\n", line);
return -EINVAL;
}
} else if (osmo_str_startswith(line, A_RTPMAP)) {
if (sscanf(line, A_RTPMAP "%d %63s", &pt, codec_resp) != 2) {
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse SDP parameter, invalid rtpmap: %s\n", osmo_quote_str(line, -1));
return -EINVAL;
}
if (r->ptmap_len >= ARRAY_SIZE(r->ptmap)) {
LOGP(DLMGCP, LOGL_ERROR, "No more space in ptmap array (len=%u)\n", r->ptmap_len);
return -ENOSPC;
}
codec = map_str_to_codec(codec_resp);
r->ptmap[r->ptmap_len].pt = pt;
r->ptmap[r->ptmap_len].codec = codec;
r->ptmap_len++;
} else
goto response_parse_failure_rtpmap;
}
return 0;
response_parse_failure_ptime:
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse SDP parameter, invalid ptime (%s)\n", line);
return -EINVAL;
response_parse_failure_rtpmap:
LOGP(DLMGCP, LOGL_ERROR,
"Failed to parse SDP parameter, invalid rtpmap (%s)\n", line);
return -EINVAL;
}
/* Parse a line like "c=IN IP4 10.11.12.13" */
static int mgcp_parse_audio_ip(struct mgcp_response *r, const char *line)
{
struct in6_addr ip_test;
bool is_ipv6;
struct in_addr ip_test;
if (strncmp("c=IN IP", line, 7) != 0)
if (strlen(line) < 16)
goto response_parse_failure;
/* The current implementation strictly supports IPV4 only ! */
if (memcmp("c=IN IP4 ", line, 9) != 0)
goto response_parse_failure;
/* Extract IP-Address */
osmo_strlcpy(r->audio_ip, line + 9, sizeof(r->audio_ip));
/* Check IP-Address */
if (inet_aton(r->audio_ip, &ip_test) == 0)
goto response_parse_failure;
line += 7;
if (*line == '6')
is_ipv6 = true;
else if (*line == '4')
is_ipv6 = false;
else
goto response_parse_failure;
line++;
if (*line != ' ')
goto response_parse_failure;
line++;
/* Extract and check IP-Address */
if (is_ipv6) {
/* 45 = INET6_ADDRSTRLEN -1 */
if (sscanf(line, "%45s", r->audio_ip) != 1)
goto response_parse_failure;
if (inet_pton(AF_INET6, r->audio_ip, &ip_test) != 1)
goto response_parse_failure;
} else {
/* 15 = INET_ADDRSTRLEN -1 */
if (sscanf(line, "%15s", r->audio_ip) != 1)
goto response_parse_failure;
if (inet_pton(AF_INET, r->audio_ip, &ip_test) != 1)
goto response_parse_failure;
}
return 0;
response_parse_failure:
@@ -412,37 +393,6 @@ 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)
@@ -484,8 +434,9 @@ int mgcp_response_parse_params(struct mgcp_response *r)
/* Find beginning of the parameter (SDP) section */
data_ptr = mgcp_find_section_end(data);
if (!data_ptr) {
LOGP(DLMGCP, LOGL_DEBUG, "MGCP response contains no SDP parameters\n");
rc = 0;
LOGP(DLMGCP, LOGL_ERROR,
"MGCP response: cannot find start of SDP parameters\n");
rc = -EINVAL;
goto exit;
}
@@ -582,12 +533,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 (toupper(line[0])) {
switch (line[0]) {
case 'Z':
rc = mgcp_parse_head_param(r->head.endpoint,
sizeof(r->head.endpoint),
@@ -616,21 +567,6 @@ 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;
@@ -753,8 +689,6 @@ struct mgcp_client *mgcp_client_init(void *ctx,
struct mgcp_client *mgcp;
mgcp = talloc_zero(ctx, struct mgcp_client);
if (!mgcp)
return NULL;
INIT_LLIST_HEAD(&mgcp->responses_pending);
INIT_LLIST_HEAD(&mgcp->inuse_endpoints);
@@ -796,7 +730,7 @@ static int init_socket(struct mgcp_client *mgcp)
/* Initalize socket with the currently configured port
* number */
rc = osmo_sock_init2_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
rc = osmo_sock_init2_ofd(&wq->bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (rc > 0)
@@ -825,6 +759,7 @@ static int init_socket(struct mgcp_client *mgcp)
* \returns 0 on success, -EINVAL on error. */
int mgcp_client_connect(struct mgcp_client *mgcp)
{
struct sockaddr_in addr;
struct osmo_wqueue *wq;
int rc;
@@ -834,11 +769,6 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
}
wq = &mgcp->wq;
osmo_wqueue_init(wq, 1024);
wq->read_cb = mgcp_do_read;
wq->write_cb = mgcp_do_write;
osmo_fd_setup(&wq->bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, mgcp, 0);
rc = init_socket(mgcp);
if (rc < 0) {
@@ -849,6 +779,14 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
goto error_close_fd;
}
inet_aton(mgcp->actual.remote_addr, &addr.sin_addr);
mgcp->remote_addr = htonl(addr.sin_addr.s_addr);
osmo_wqueue_init(wq, 10);
wq->bfd.when = BSC_FD_READ;
wq->bfd.data = mgcp;
wq->read_cb = mgcp_do_read;
wq->write_cb = mgcp_do_write;
LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s\n", osmo_sock_get_name2(wq->bfd.fd));
@@ -875,13 +813,12 @@ uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp)
return mgcp->actual.remote_port;
}
/*! Get the IP-Address of the associated MGW as its numeric representation.
* DEPRECATED, DON'T USE.
/*! Get the IP-Aaddress of the associated MGW as its numeric representation.
* \param[in] mgcp MGCP client descriptor.
* \returns IP-Address as 32 bit integer (network byte order) */
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
{
return 0;
return mgcp->remote_addr;
}
/* To compose endpoint names, usually for CRCX, use this as domain name.
@@ -891,15 +828,16 @@ 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)
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
{
static char endpoint[MGCP_ENDPOINT_MAXLEN];
int rc;
rc = snprintf(endpoint, sizeof(endpoint), "%s@%s", name, mgcp_client_endpoint_domain(mgcp));
#define RTPBRIDGE_WILDCARD_FMT "rtpbridge/*@%s"
rc = snprintf(endpoint, sizeof(endpoint), RTPBRIDGE_WILDCARD_FMT, mgcp_client_endpoint_domain(mgcp));
if (rc > sizeof(endpoint) - 1) {
LOGP(DLMGCP, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '%s@%s'\n",
sizeof(endpoint) - 1, name, mgcp_client_endpoint_domain(mgcp));
LOGP(DLMGCP, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '" RTPBRIDGE_WILDCARD_FMT "'\n",
sizeof(endpoint) - 1, mgcp_client_endpoint_domain(mgcp));
return NULL;
}
if (rc < 1) {
@@ -909,66 +847,6 @@ static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgc
return endpoint;
}
/*! Compose endpoint name for a wildcarded request to the virtual trunk
* \param[in] mgcp MGCP client descriptor.
* \returns string containing the endpoint name (e.g. rtpbridge\*@mgw) */
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
{
return _mgcp_client_name_append_domain(mgcp, "rtpbridge/*");
}
/*! Compose endpoint name for an E1 endpoint.
* \param[in] ctx talloc context.
* \param[in] mgcp MGCP client descriptor.
* \param[in] trunk_id id number of the E1 trunk (1-64).
* \param[in] ts timeslot on the E1 trunk (1-31).
* \param[in] rate bitrate used on the E1 trunk (e.g 16 for 16kbit).
* \param[in] offset bit offset of the E1 subslot (e.g. 4 for the third 16k subslot).
* \returns string containing the endpoint name (e.g. ds/e1-1/s-1/su16-4). */
const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts,
uint8_t rate, uint8_t offset)
{
/* See also comment in libosmo-mgcp, mgcp_client.c, gen_e1_epname() */
const uint8_t valid_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
const uint8_t valid_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
uint8_t i;
bool rate_offs_valid = false;
char *epname;
epname =
talloc_asprintf(ctx, "ds/e1-%u/s-%u/su%u-%u@%s", trunk_id, ts, rate, offset,
mgcp_client_endpoint_domain(mgcp));
if (!epname) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP e1-endpoint name!\n");
return NULL;
}
/* Check if the supplied rate/offset pair resembles a valid combination */
for (i = 0; i < sizeof(valid_rates); i++) {
if (valid_rates[i] == rate && valid_offsets[i] == offset)
rate_offs_valid = true;
}
if (!rate_offs_valid) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot compose MGCP e1-endpoint name (%s), rate(%u)/offset(%u) combination is invalid!\n", epname,
rate, offset);
talloc_free(epname);
return NULL;
}
/* An E1 line has a maximum of 32 timeslots, while the first (ts=0) is
* reserverd for framing and alignment, so we can not use it here. */
if (ts == 0 || ts > NUM_E1_TS-1) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot compose MGCP e1-endpoint name (%s), E1-timeslot number (%u) is invalid!\n", epname, ts);
talloc_free(epname);
return NULL;
}
return epname;
}
struct mgcp_response_pending * mgcp_client_pending_add(
struct mgcp_client *mgcp,
mgcp_trans_id_t trans_id,
@@ -978,9 +856,6 @@ struct mgcp_response_pending * mgcp_client_pending_add(
struct mgcp_response_pending *pending;
pending = talloc_zero(mgcp, struct mgcp_response_pending);
if (!pending)
return NULL;
pending->trans_id = trans_id;
pending->response_cb = response_cb;
pending->priv = priv;
@@ -1000,7 +875,7 @@ struct mgcp_response_pending * mgcp_client_pending_add(
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
mgcp_response_cb_t response_cb, void *priv)
{
struct mgcp_response_pending *pending = NULL;
struct mgcp_response_pending *pending;
mgcp_trans_id_t trans_id;
int rc;
@@ -1012,14 +887,7 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
return -EINVAL;
}
/* Do not allocate a dummy 'mgcp_response_pending' entry */
if (response_cb != NULL) {
pending = mgcp_client_pending_add(mgcp, trans_id, response_cb, priv);
if (!pending) {
talloc_free(msg);
return -ENOMEM;
}
}
pending = mgcp_client_pending_add(mgcp, trans_id, response_cb, priv);
if (msgb_l2len(msg) > 4096) {
LOGP(DLMGCP, LOGL_ERROR,
@@ -1041,13 +909,9 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
return 0;
mgcp_tx_error:
if (!pending)
return rc;
/* Dequeue pending response, it's going to be free()d */
llist_del(&pending->entry);
/* Pass NULL to response cb to indicate an error */
mgcp_client_handle_response(mgcp, pending, NULL);
return rc;
return -1;
}
/*! Cancel a pending transaction.
@@ -1114,8 +978,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(osmo_mgcpc_codec_names, pt);
codec = get_value_string_or_null(codec_table, pt);
/* Note: Use codec descriptors from enum mgcp_codecs
* in mgcp_client only! */
OSMO_ASSERT(codec);
@@ -1136,8 +1000,7 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
{
unsigned int i;
int rc = 0;
char local_ip[INET6_ADDRSTRLEN];
int local_ip_family, audio_ip_family;
char local_ip[INET_ADDRSTRLEN];
const char *codec;
unsigned int pt;
@@ -1154,21 +1017,10 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
msgb_free(msg);
return -2;
}
local_ip_family = osmo_ip_str_type(local_ip);
if (local_ip_family == AF_UNSPEC) {
msgb_free(msg);
return -2;
}
audio_ip_family = osmo_ip_str_type(mgcp_msg->audio_ip);
if (audio_ip_family == AF_UNSPEC) {
msgb_free(msg);
return -2;
}
/* Add owner/creator (SDP) */
rc += msgb_printf(msg, "o=- %x 23 IN IP%c %s\r\n", mgcp_msg->call_id,
local_ip_family == AF_INET6 ? '6' : '4',
local_ip);
rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
mgcp_msg->call_id, local_ip);
/* Add session name (none) */
rc += msgb_printf(msg, "s=-\r\n");
@@ -1186,9 +1038,7 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
msgb_free(msg);
return -2;
}
rc += msgb_printf(msg, "c=IN IP%c %s\r\n",
audio_ip_family == AF_INET6 ? '6' : '4',
mgcp_msg->audio_ip);
rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
/* Add time description, active time (SDP) */
rc += msgb_printf(msg, "t=0 0\r\n");
@@ -1201,37 +1051,23 @@ 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(osmo_mgcpc_codec_names, mgcp_msg->codecs[i]);
codec = get_value_string_or_null(codec_table, mgcp_msg->codecs[i]);
/* Note: Use codec descriptors from enum mgcp_codecs
* in mgcp_client only! */
OSMO_ASSERT(codec);
rc += msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec);
}
}
if (mgcp_msg->ptime)
rc += msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime);
@@ -1250,7 +1086,6 @@ 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;
@@ -1306,7 +1141,6 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
"Endpoint name (%s) lacks separator (@), can not generate MGCP message\n",
mgcp_msg->endpoint);
msgb_free(msg);
return NULL;
}
rc += msgb_printf(msg, " %s", mgcp_msg->endpoint);
@@ -1355,22 +1189,6 @@ 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

View File

@@ -1,990 +0,0 @@
/* 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/core/sockaddr_str.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;
struct fsm_notify {
struct llist_head entry;
struct osmo_fsm_inst *fi;
uint32_t success;
uint32_t failure;
void *data;
};
/*! 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 fsm_notify notify;
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];
/*! Internal use: if a function keeps an fsm_notify for later dispatch while already clearing or re-using the
* ci[], the fsm_notify should be kept here to also get canceled by osmo_mgcpc_ep_cancel_notify(). */
struct llist_head background_notify;
};
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]};
}
}
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.
*
* IMPORTANT: To avoid use-after-free problems, using this FSM requires use of deferred FSM deallocation using
* osmo_fsm_set_dealloc_ctx(), e.g. using osmo_select_main_ctx(OTC_SELECT) with osmo_select_main_ctx() as main loop.
*
* 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,
};
INIT_LLIST_HEAD(&ep->background_notify);
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_mgcpc_ep *ep = ci->ep;
struct fsm_notify notify;
int i;
if (!ci->occupied)
return;
/* When dispatching an event for this CI, the user may decide to trigger the next request for this conn right
* away. So we must be ready with a cleared *ci. Store the notify separately and clear before dispatching. */
notify = ci->notify;
/* Register the planned notification in ep->background_notify so we also catch any osmo_mgcpc_ep_cancel_notify()
* that might be triggered between clearing the ci and actually dispatching the event. */
llist_add(&notify.entry, &ep->background_notify);
*ci = (struct osmo_mgcpc_ep_ci){
.ep = ci->ep,
};
/* An MGCP failure typically means the endpoint becomes unusable, cancel all pending request (except DLCX).
* Particularly, if two CRCX were scheduled and the first fails, we must no longer dispatch the second CRCX. */
for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
struct osmo_mgcpc_ep_ci *other_ci = &ep->ci[i];
if (other_ci == ci)
continue;
if (!other_ci->occupied)
continue;
if (!other_ci->pending)
continue;
if (other_ci->sent)
continue;
if (other_ci->verb == MGCP_VERB_DLCX)
continue;
/* Just clear the pending request, don't fire more events than below. */
other_ci->pending = false;
}
/* 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(ep->fi)) {
/* The ep has deallocated, no need to llist_del(&notify.entry) here. */
return;
}
if (notify.fi)
osmo_fsm_inst_dispatch(notify.fi, notify.failure, notify.data);
llist_del(&notify.entry);
}
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;
rtp_info = data;
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. */
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;
OSMO_ASSERT(rtp_info);
/* fall through */
case MGCP_VERB_MDCX:
/* Always update the received RTP ip/port information, since MGW
* may provide new one after remote end params changed */
if (rtp_info) {
ci->got_port_info = true;
ci->rtp_info = *rtp_info;
}
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.fi ? "" : " (not sending a notification)");
if (ci->notify.fi)
osmo_fsm_inst_dispatch(ci->notify.fi, 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;
int family;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci);
if (!rtp_info)
return false;
family = osmo_ip_str_type(rtp_info->addr);
switch (family) {
case AF_INET:
sin = (struct sockaddr_in *)dest;
sin->sin_family = AF_INET;
sin->sin_port = osmo_ntohs(rtp_info->port);
if (inet_pton(AF_INET, rtp_info->addr, &sin->sin_addr) != 1)
return false;
break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)dest;
sin6->sin6_family = AF_INET6;
sin6->sin6_port = osmo_ntohs(rtp_info->port);
if (inet_pton(AF_INET6, rtp_info->addr, &sin6->sin6_addr) != 1)
return false;
break;
default:
return false;
}
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=-2427 },
};
/* 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.
*
* If the 'notify' instance deallocates before it received a notification of event_success or event_failure,
* osmo_mgcpc_ep_ci_cancel_notify() or osmo_mgcpc_ep_cancel_notify() must be called. It is not harmful to cancel
* notification after an event has been received.
*
* \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 = {
.fi = notify,
.success = event_success,
.failure = event_failure,
.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.fi));
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);
}
/*! No longer notify for any state changes for any conns of this endpoint.
* Useful if the notify instance passed to osmo_mgcpc_ep_ci_request() is about to deallocate.
* \param ep The endpoint FSM instance.
* \param notify Which target to cancel notification for, if NULL cancel all notifications. */
void osmo_mgcpc_ep_cancel_notify(struct osmo_mgcpc_ep *ep, struct osmo_fsm_inst *notify)
{
struct fsm_notify *n;
int i;
for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
if (!notify || ci->notify.fi == notify)
ci->notify.fi = NULL;
}
llist_for_each_entry(n, &ep->background_notify, entry) {
if (!notify || n->fi == notify)
n->fi = NULL;
}
}
/* Return the osmo_mgcpc_ep that this conn belongs to. */
struct osmo_mgcpc_ep *osmo_mgcpc_ep_ci_ep(struct osmo_mgcpc_ep_ci *conn)
{
if (!conn)
return NULL;
return conn->ep;
}
static int send_verb(struct osmo_mgcpc_ep_ci *ci)
{
int rc;
struct osmo_mgcpc_ep *ep = ci->ep;
struct fsm_notify notify;
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);
notify = ci->notify;
*ci = (struct osmo_mgcpc_ep_ci){
.ep = ep,
};
/* When dispatching an event for this CI, the user may decide to trigger the next request for this conn
* right away. So we must be ready with a cleared *ci. */
if (notify.fi)
osmo_fsm_inst_dispatch(notify.fi, notify.success, notify.data);
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_mgcpc_ep_cancel_notify(ep, NULL);
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. */
};

View File

@@ -25,7 +25,6 @@
#include <osmocom/core/byteswap.h>
#include <arpa/inet.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/sockaddr_str.h>
/* Context information, this is attached to the priv pointer of the FSM and
* is also handed back when dispatcheing events to the parent FSM. This is
@@ -115,23 +114,16 @@ 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,
.param_present = info->param_present
.ptmap_len = info->ptmap_len
};
osmo_strlcpy(mgcp_msg->endpoint, info->endpoint, MGCP_ENDPOINT_MAXLEN);
memcpy(mgcp_msg->codecs, info->codecs, sizeof(mgcp_msg->codecs));
memcpy(mgcp_msg->ptmap, info->ptmap, sizeof(mgcp_msg->ptmap));
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)
@@ -142,13 +134,6 @@ 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;
@@ -164,20 +149,11 @@ 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,
.param_present = mgcp_ctx->conn_peer_local.param_present
.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len
};
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
memcpy(mgcp_msg.ptmap, mgcp_ctx->conn_peer_local.ptmap, sizeof(mgcp_msg.ptmap));
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
@@ -223,8 +199,6 @@ 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);
@@ -281,11 +255,6 @@ 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;
@@ -405,12 +374,6 @@ 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;
@@ -523,13 +486,11 @@ static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca
* connection. This is not the normal case. The user should always use
* mgcp_conn_delete() to instruct the FSM to perform a graceful exit */
if (strlen(mgcp_ctx->conn_id)) {
LOGPFSML(fi, LOGL_NOTICE,
"MGW/DLCX: FSM termination with connections still present, sending unconditional DLCX...\n");
LOGPFSML(fi, LOGL_ERROR,
"MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n");
msg = make_dlcx_msg(mgcp_ctx);
if (!msg)
LOGPFSML(fi, LOGL_ERROR, "MGW/DLCX: Error composing DLCX message\n");
else
mgcp_client_tx(mgcp, msg, NULL, NULL);
OSMO_ASSERT(msg);
mgcp_client_tx(mgcp, msg, NULL, NULL);
}
talloc_free(mgcp_ctx);
@@ -607,19 +568,24 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm
uint32_t parent_term_evt, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
{
struct mgcp_ctx *mgcp_ctx;
static bool fsm_registered = false;
struct osmo_fsm_inst *fi;
struct in6_addr ip_test;
struct in_addr ip_test;
OSMO_ASSERT(parent_fi);
OSMO_ASSERT(mgcp);
OSMO_ASSERT(conn_peer);
/* Check if IP/Port information in conn info makes sense */
if (conn_peer->port && inet_pton(osmo_ip_str_type(conn_peer->addr),
conn_peer->addr, &ip_test) != 1)
if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0)
return NULL;
/* Register the fsm description (if not already done) */
if (fsm_registered == false) {
osmo_fsm_register(&fsm_mgcp_client);
fsm_registered = true;
}
/* Allocate and configure a new fsm instance */
fi = osmo_fsm_inst_alloc_child(&fsm_mgcp_client, parent_fi, parent_term_evt);
OSMO_ASSERT(fi);
@@ -647,7 +613,7 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
{
OSMO_ASSERT(fi);
struct mgcp_ctx *mgcp_ctx = fi->priv;
struct in6_addr ip_test;
struct in_addr ip_test;
OSMO_ASSERT(mgcp_ctx);
OSMO_ASSERT(conn_peer);
@@ -671,8 +637,8 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, port == 0\n");
return -EINVAL;
}
if (inet_pton(osmo_ip_str_type(conn_peer->addr), conn_peer->addr, &ip_test) != 1) {
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address %s\n", conn_peer->addr);
if (inet_aton(conn_peer->addr, &ip_test) == 0) {
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address == 0.0.0.0\n");
return -EINVAL;
}
@@ -704,9 +670,6 @@ void mgcp_conn_delete(struct osmo_fsm_inst *fi)
OSMO_ASSERT(mgcp_ctx);
if (fi->proc.terminating)
return;
/* Unlink FSM from parent */
osmo_fsm_inst_unlink_parent(fi, NULL);
@@ -722,30 +685,3 @@ 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;
}
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init()
{
OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0);
}

View File

@@ -36,10 +36,9 @@ void *global_mgcp_client_ctx = NULL;
struct mgcp_client_conf *global_mgcp_client_conf = NULL;
DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
"mgw local-ip " VTY_IPV46_CMD,
"mgw local-ip A.B.C.D",
MGW_STR "local bind to connect to MGW from\n"
"local bind IPv4 address\n"
"local bind IPv6 address\n")
"local bind IP address\n")
{
if (!global_mgcp_client_conf)
return CMD_ERR_NOTHING_TODO;
@@ -70,10 +69,9 @@ ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd,
"local bind port\n")
DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
"mgw remote-ip " VTY_IPV46_CMD,
"mgw remote-ip A.B.C.D",
MGW_STR "remote IP address to reach the MGW at\n"
"remote IPv4 address\n"
"remote IPv6 address\n")
"remote IP address\n")
{
if (!global_mgcp_client_conf)
return CMD_ERR_NOTHING_TODO;
@@ -190,21 +188,21 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
global_mgcp_client_ctx = talloc_ctx;
global_mgcp_client_conf = conf;
install_lib_element(node, &cfg_mgw_local_ip_cmd);
install_lib_element(node, &cfg_mgw_local_port_cmd);
install_lib_element(node, &cfg_mgw_remote_ip_cmd);
install_lib_element(node, &cfg_mgw_remote_port_cmd);
install_lib_element(node, &cfg_mgw_endpoint_range_cmd);
install_lib_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
install_lib_element(node, &cfg_mgw_endpoint_domain_name_cmd);
install_element(node, &cfg_mgw_local_ip_cmd);
install_element(node, &cfg_mgw_local_port_cmd);
install_element(node, &cfg_mgw_remote_ip_cmd);
install_element(node, &cfg_mgw_remote_port_cmd);
install_element(node, &cfg_mgw_endpoint_range_cmd);
install_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
install_element(node, &cfg_mgw_endpoint_domain_name_cmd);
/* deprecated 'mgcpgw' commands */
install_lib_element(node, &cfg_mgcpgw_local_ip_cmd);
install_lib_element(node, &cfg_mgcpgw_local_port_cmd);
install_lib_element(node, &cfg_mgcpgw_remote_ip_cmd);
install_lib_element(node, &cfg_mgcpgw_remote_port_cmd);
install_lib_element(node, &cfg_mgcpgw_endpoint_range_cmd);
install_lib_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
install_element(node, &cfg_mgcpgw_local_ip_cmd);
install_element(node, &cfg_mgcpgw_local_port_cmd);
install_element(node, &cfg_mgcpgw_remote_ip_cmd);
install_element(node, &cfg_mgcpgw_remote_port_cmd);
install_element(node, &cfg_mgcpgw_endpoint_range_cmd);
install_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
osmo_fsm_vty_add_cmds();
}

View File

@@ -10,8 +10,6 @@ AM_CFLAGS = \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(LIBOSMOTRAU_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -20,8 +18,6 @@ AM_LDFLAGS = \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
@@ -44,8 +40,4 @@ libosmo_mgcp_a_SOURCES = \
mgcp_conn.c \
mgcp_stat.c \
mgcp_endp.c \
mgcp_trunk.c \
mgcp_ctrl.c \
mgcp_ratectr.c \
mgcp_e1.c \
$(NULL)

View File

@@ -17,13 +17,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <errno.h>
/* Helper function to dump codec information of a specified codec to a printable
@@ -61,8 +56,8 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
endp = conn->conn->endp;
if (rtp->codecs_assigned == 0) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "conn:%s no codecs available\n",
mgcp_conn_dump(conn->conn));
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x conn:%s no codecs available\n", ENDPOINT_NUMBER(endp),
mgcp_conn_dump(conn->conn));
return;
}
@@ -70,8 +65,8 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
for (i = 0; i < rtp->codecs_assigned; i++) {
codec = &rtp->codecs[i];
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s codecs[%u]:%s",
mgcp_conn_dump(conn->conn), i, dump_codec(codec));
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x conn:%s codecs[%u]:%s", ENDPOINT_NUMBER(endp),
mgcp_conn_dump(conn->conn), i, dump_codec(codec));
if (codec == rtp->codec)
LOGPC(DLMGCP, LOGL_DEBUG, " [selected]");
@@ -81,61 +76,36 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
}
/* Initalize or reset codec information with default data. */
static void codec_init(struct mgcp_rtp_codec *codec)
{
*codec = (struct mgcp_rtp_codec){
.payload_type = -1,
.frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM,
.frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN,
.rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE,
.channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS,
};
}
static void codec_free(struct mgcp_rtp_codec *codec)
void codec_init(struct mgcp_rtp_codec *codec)
{
if (codec->subtype_name)
talloc_free(codec->subtype_name);
if (codec->audio_name)
talloc_free(codec->audio_name);
*codec = (struct mgcp_rtp_codec){};
memset(codec, 0, sizeof(*codec));
codec->payload_type = -1;
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
}
/*! Initalize or reset codec information with default data.
* \param[out] conn related rtp-connection. */
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
{
int i;
for (i = 0; i < conn->end.codecs_assigned; i++)
codec_free(&conn->end.codecs[i]);
memset(conn->end.codecs, 0, sizeof(conn->end.codecs));
conn->end.codecs_assigned = 0;
conn->end.codec = NULL;
}
/*! Add codec configuration depending on payload type and/or codec name. This
* function uses the input parameters to extrapolate the full codec information.
* \param[out] codec configuration (caller provided memory).
* \param[out] conn related rtp-connection.
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
* \param[in] audio_name audio codec name, 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, const struct mgcp_codec_param *param)
/* Set members of struct mgcp_rtp_codec, extrapolate in missing information */
static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
int payload_type, const char *audio_name, unsigned int pt_offset)
{
int rate;
int channels;
char audio_codec[64];
struct mgcp_rtp_codec *codec;
unsigned int pt_offset = conn->end.codecs_assigned;
void *ctx = conn->conn;
/* The amount of codecs we can store is limited, make sure we do not
* overrun this limit. */
if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
return -EINVAL;
/* First unused entry */
codec = &conn->end.codecs[conn->end.codecs_assigned];
/* Initalize the codec struct with some default data to begin with */
codec_init(codec);
@@ -143,13 +113,12 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
if (payload_type != PTYPE_UNDEFINED) {
/* Make sure we do not get any reserved or undefined type numbers */
/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
if ((payload_type == 1 || payload_type == 2 || payload_type == 19)
|| (payload_type >= 72 && payload_type <= 76)
|| (payload_type >= 127)) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot add codec, payload type number %d is reserved\n",
payload_type);
if (payload_type == 1 || payload_type == 2 || payload_type == 19)
goto error;
if (payload_type >= 72 && payload_type <= 76)
goto error;
if (payload_type >= 127)
goto error;
}
codec->payload_type = payload_type;
}
@@ -175,8 +144,6 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
/* The given payload type is not known to us, or it
* it is a dynamic payload type for which we do not
* know the audio name. We must give up here */
LOGP(DLMGCP, LOGL_ERROR, "No audio codec name given, and payload type %d unknown\n",
payload_type);
goto error;
}
}
@@ -184,23 +151,16 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
/* Now we extract the codec subtype name, rate and channels. The latter
* two are optional. If they are not present we use the safe defaults
* above. */
if (strlen(audio_name) >= sizeof(audio_codec)) {
LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(audio_name, -1));
if (strlen(audio_name) > sizeof(audio_codec))
goto error;
}
channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1) {
LOGP(DLMGCP, LOGL_ERROR, "Invalid audio codec: %s\n", osmo_quote_str(audio_name, -1));
if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1)
goto error;
}
/* Note: We only accept configurations with one audio channel! */
if (channels != 1) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot handle audio codec with more than one channel: %s\n",
osmo_quote_str(audio_name, -1));
if (channels != 1)
goto error;
}
codec->rate = rate;
codec->channels = channels;
@@ -218,7 +178,6 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
/* Derive the payload type if it is unknown */
if (codec->payload_type == PTYPE_UNDEFINED) {
/* TODO: This is semi dead code, see OS#4150 */
/* For the known codecs from the static range we restore
* the IANA or 3GPP assigned payload type number */
@@ -254,46 +213,80 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
* 110 onwards 3gpp defines prefered codec types, which are
* also fixed, see above) */
if (codec->payload_type < 0) {
/* FIXME: pt_offset is completely unrelated and useless here, any of those numbers may already
* have been added to the codecs. Instead, there should be an iterator checking for an actually
* unused dynamic payload type number. */
codec->payload_type = 96 + pt_offset;
if (codec->payload_type > 109) {
LOGP(DLMGCP, LOGL_ERROR, "Ran out of payload type numbers to assign dynamically\n");
if (codec->payload_type > 109)
goto error;
}
}
}
/* Copy over optional codec parameters */
if (param) {
codec->param = *param;
codec->param_present = true;
} else
codec->param_present = false;
conn->end.codecs_assigned++;
return 0;
error:
/* Make sure we leave a clean codec entry on error. */
codec_free(codec);
codec_init(codec);
memset(codec, 0, sizeof(*codec));
return -EINVAL;
}
/*! Add codec configuration depending on payload type and/or codec name. This
* function uses the input parameters to extrapolate the full codec information.
* \param[out] codec configuration (caller provided memory).
* \param[out] conn related rtp-connection.
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
* \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name)
{
int rc;
/* The amount of codecs we can store is limited, make sure we do not
* overrun this limit. */
if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
return -EINVAL;
rc = codec_set(conn->conn, &conn->end.codecs[conn->end.codecs_assigned], payload_type, audio_name,
conn->end.codecs_assigned);
if (rc != 0)
return -EINVAL;
conn->end.codecs_assigned++;
return 0;
}
/* Check if the given codec is applicable on the specified endpoint
* Helper function for mgcp_codec_decide() */
static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
{
char codec_name[64];
/* A codec name must be set, if not, this might mean that the codec
* (payload type) that was assigned is unknown to us so we must stop
* here. */
if (!codec->subtype_name)
return false;
/* FIXME: implement meaningful checks to make sure that the given codec
* is compatible with the given endpoint */
/* We now extract the codec_name (letters before the /, e.g. "GSM"
* from the audio name that is stored in the trunk configuration.
* We do not compare to the full audio_name because we expect that
* "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the
* audio name of the codec is set to "GSM" */
if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
return false;
return true;
/* Finally we check if the subtype_name we have generated from the
* audio_name in the trunc struct patches the codec_name of the
* given codec */
if (strcasecmp(codec_name, codec->subtype_name) == 0)
return true;
/* FIXME: It is questinable that the method to pick a compatible
* codec can work properly. Since this useses tcfg->audio_name, as
* a reference, which is set to "AMR/8000" permanently.
* tcfg->audio_name must be updated by the first connection that
* has been made on an endpoint, so that the second connection
* can make a meaningful decision here */
return false;
}
/*! Decide for one suitable codec
@@ -320,7 +313,7 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
for (i = 0; i < rtp->codecs_assigned; i++) {
/* When no transcoding is available, avoid codecs that would
* require transcoding. */
if (endp->trunk->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
continue;
@@ -349,28 +342,9 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
return -EINVAL;
}
/* Return true if octet-aligned is set in the given codec. Default to octet-aligned=0, i.e. bandwidth-efficient mode.
* See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB
* Media Type Registration":
*
* octet-align: Permissible values are 0 and 1. If 1, octet-aligned
* operation SHALL be used. If 0 or if not present,
* bandwidth-efficient operation is employed.
*
* https://tools.ietf.org/html/rfc4867
*/
static bool amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
{
if (!codec->param_present)
return false;
if (!codec->param.amr_octet_aligned_present)
return false;
return codec->param.amr_octet_aligned;
}
/* Compare two codecs, all parameters must match up, except for the payload type
* number. */
static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
static bool codecs_cmp(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
{
if (codec_a->rate != codec_b->rate)
return false;
@@ -380,12 +354,10 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c
return false;
if (codec_a->frame_duration_den != codec_b->frame_duration_den)
return false;
if (strcmp(codec_a->audio_name, codec_b->audio_name))
return false;
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
return false;
if (!strcmp(codec_a->subtype_name, "AMR")) {
if (amr_is_octet_aligned(codec_a) != amr_is_octet_aligned(codec_b))
return false;
}
return true;
}
@@ -426,7 +398,7 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
codecs_assigned = rtp_dst->codecs_assigned;
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
for (i = 0; i < codecs_assigned; i++) {
if (codecs_same(codec_src, &rtp_dst->codecs[i])) {
if (codecs_cmp(codec_src, &rtp_dst->codecs[i])) {
codec_dst = &rtp_dst->codecs[i];
break;
}
@@ -436,28 +408,3 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
return codec_dst->payload_type;
}
/* Find the payload type number configured for a specific codec by SDP.
* For example, IuUP gets assigned a payload type number, and the endpoint needs to translate that to the number
* assigned to "AMR" on the other conn (by a=rtpmap:N).
* \param conn The side of an endpoint to get the payload type number for (to translate the payload type number to).
* \param subtype_name SDP codec name without parameters (e.g. "AMR").
* \param match_nr Index for the match found, first being match_nr == 0. Iterate all matches by calling multiple times
* with incrementing match_nr.
* \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found.
*/
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
const char *subtype_name, unsigned int match_nr)
{
int i;
for (i = 0; i < conn->end.codecs_assigned; i++) {
if (!strcmp(conn->end.codecs[i].subtype_name, subtype_name)) {
if (match_nr) {
match_nr--;
continue;
}
return &conn->end.codecs[i];
}
}
return NULL;
}

View File

@@ -22,11 +22,9 @@
*/
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_network.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/gsm/gsm_utils.h>
@@ -77,13 +75,14 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
}
}
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "unable to generate a unique connectionIdentifier\n");
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x, unable to generate a unique connectionIdentifier\n",
ENDPOINT_NUMBER(endp));
return -1;
}
/* Initialize rtp connection struct with default values */
static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
{
struct mgcp_rtp_end *end = &conn_rtp->end;
/* FIXME: Each new rate counter group requires an unique index. At the
@@ -92,8 +91,7 @@ static int 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.cid_allocated = false;
conn_rtp->osmux.cid = 0;
conn_rtp->osmux.allocated_cid = -1;
/* backpointer to the generic part of the connection */
conn->u.rtp.conn = conn;
@@ -111,33 +109,27 @@ static int 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)
{
if (mgcp_conn_rtp_is_osmux(conn_rtp))
conn_osmux_disable(conn_rtp);
osmux_disable_conn(conn_rtp);
osmux_release_cid(conn_rtp);
mgcp_free_rtp_port(&conn_rtp->end);
rate_ctr_group_free(conn_rtp->rate_ctr_group);
mgcp_codec_reset_all(conn_rtp);
}
void mgcp_conn_watchdog_cb(void *data)
{
struct mgcp_conn *conn = data;
LOGPCONN(conn, DLMGCP, LOGL_ERROR, "connection timed out!\n");
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x CI:%s connection timed out!\n", ENDPOINT_NUMBER(conn->endp), conn->id);
mgcp_conn_free(conn->endp, conn->id);
}
@@ -147,7 +139,7 @@ void mgcp_conn_watchdog_kick(struct mgcp_conn *conn)
if (!timeout)
return;
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "watchdog kicked\n");
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x CI:%s watchdog kicked\n", ENDPOINT_NUMBER(conn->endp), conn->id);
osmo_timer_schedule(&conn->watchdog, timeout, 0);
}
@@ -184,10 +176,7 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
switch (type) {
case MGCP_CONN_TYPE_RTP:
if (mgcp_rtp_conn_init(&conn->u.rtp, conn) < 0) {
talloc_free(conn);
return NULL;
}
mgcp_rtp_conn_init(&conn->u.rtp, conn);
break;
default:
/* NOTE: This should never be called with an
@@ -199,7 +188,7 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
/* Initialize watchdog */
osmo_timer_setup(&conn->watchdog, mgcp_conn_watchdog_cb, conn);
mgcp_conn_watchdog_kick(conn);
mgcp_endp_add_conn(endp, conn);
llist_add(&conn->entry, &endp->conns);
return conn;
}
@@ -256,10 +245,12 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
return NULL;
}
static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
static void
aggregate_rtp_conn_stats(struct mgcp_trunk_config *trunk, struct mgcp_conn_rtp *conn_rtp)
{
struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
struct rate_ctr_group *all_stats = trunk->all_rtp_conn_stats;
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
int i;
if (all_stats == NULL || conn_stats == NULL)
return;
@@ -269,11 +260,8 @@ static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_con
* All other counters in both counter groups correspond to each other. */
OSMO_ASSERT(conn_stats->desc->num_ctr + 1 == all_stats->desc->num_ctr);
/* all other counters are [now] updated in real-time */
rate_ctr_add(&all_stats->ctr[IN_STREAM_ERR_TSTMP_CTR],
conn_stats->ctr[IN_STREAM_ERR_TSTMP_CTR].current);
rate_ctr_add(&all_stats->ctr[OUT_STREAM_ERR_TSTMP_CTR],
conn_stats->ctr[OUT_STREAM_ERR_TSTMP_CTR].current);
for (i = 0; i < conn_stats->desc->num_ctr; i++)
rate_ctr_add(&all_stats->ctr[i], conn_stats->ctr[i].current);
rate_ctr_inc(&all_stats->ctr[RTP_NUM_CONNECTIONS]);
}
@@ -289,9 +277,15 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
if (!conn)
return;
/* Run endpoint cleanup action. By this we inform the endpoint about
* the removal of the connection and allow it to clean up its inner
* state accordingly */
if (endp->type->cleanup_cb)
endp->type->cleanup_cb(endp, conn);
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
aggregate_rtp_conn_stats(endp, &conn->u.rtp);
aggregate_rtp_conn_stats(endp->tcfg, &conn->u.rtp);
mgcp_rtp_conn_cleanup(&conn->u.rtp);
break;
default:
@@ -302,8 +296,7 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
}
osmo_timer_del(&conn->watchdog);
mgcp_endp_remove_conn(endp, conn);
/* WARN: endp may have be freed after call to mgcp_endp_remove_conn */
llist_del(&conn->entry);
talloc_free(conn);
}
@@ -344,7 +337,6 @@ void mgcp_conn_free_all(struct mgcp_endpoint *endp)
char *mgcp_conn_dump(struct mgcp_conn *conn)
{
static char str[sizeof(conn->name)+sizeof(conn->id)+256];
char ipbuf[INET6_ADDRSTRLEN];
if (!conn) {
snprintf(str, sizeof(str), "(null connection)");
@@ -358,7 +350,7 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
"rtp:%u rtcp:%u)",
conn->name,
conn->id,
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
inet_ntoa(conn->u.rtp.end.addr),
ntohs(conn->u.rtp.end.rtp_port),
ntohs(conn->u.rtp.end.rtcp_port));
break;
@@ -394,13 +386,3 @@ struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
return NULL;
}
/*! get oldest connection in the list.
* \param[in] endp associated endpoint */
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp)
{
if (llist_empty(&endp->conns))
return NULL;
return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
}

View File

@@ -1,36 +0,0 @@
/*
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/ctrl/control_if.h>
#include <osmocom/mgcp/mgcp.h>
static int mgw_ctrl_node_lookup(void *data, vector vline, int *node_type,
void **node_data, int *i)
{
return 0;
}
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
const char *bind_addr, uint16_t port)
{
return ctrl_interface_setup_dynip2(cfg, bind_addr, port, mgw_ctrl_node_lookup,
_LAST_CTRL_NODE);
}

View File

@@ -1,685 +0,0 @@
/* E1 traffic handling */
/*
* (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <inttypes.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/core/msgb.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/abis/abis.h>
#include <osmocom/trau/trau_sync.h>
#include <osmocom/trau/trau_frame.h>
#include <osmocom/trau/trau_rtp.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/netif/rtp.h>
#include <osmocom/mgcp/debug.h>
#include <osmocom/mgcp/mgcp_e1.h>
#include <osmocom/codec/codec.h>
#define DEBUG_BITS_MAX 80
#define DEBUG_BYTES_MAX 40
#define DEBUG_E1_TS 0
#define E1_TS_BYTES 160
#define E1_TRAU_BITS 320
#define E1_TRAU_BITS_MSGB 2048
static struct mgcp_config *cfg;
static const struct e1inp_line_ops dummy_e1_line_ops = {
.sign_link_up = NULL,
.sign_link_down = NULL,
.sign_link = NULL,
};
/* EFR idle frame */
static const ubit_t idle_tf_efr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1,
};
/* FR idle frame */
static const ubit_t idle_tf_fr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1,
};
/* Idle speech frame, see also GSM 08.60, chapter 3.4 */
static const ubit_t idle_tf_spch[] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1,
};
/* If the RTP transmission has dropouts for some reason the I.460 TX-Queue may
* run empty. In order to make sure that the TRAU frame transmission continues
* we generate idle TRAU frames here. */
static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_data)
{
struct mgcp_endpoint *endp = user_data;
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
struct msgb *msg = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame");
uint8_t *ptr;
const uint8_t *ptr_ft;
enum osmo_trau_frame_type ft;
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_MUX_EMPTY_CTR]);
/* Choose an appropiate idle frame type */
ft = endp->e1.trau_rtp_st->type;
switch (ft) {
case OSMO_TRAU16_FT_FR:
ptr_ft = idle_tf_fr;
break;
case OSMO_TRAU16_FT_EFR:
ptr_ft = idle_tf_efr;
break;
default:
/* FIXME: What about 8k subslots and AMR frames? */
ptr_ft = idle_tf_spch;
}
/* Put the replacement into a message buffer and enqueue it into the
* I.460 multiplexer */
ptr = msgb_put(msg, E1_TRAU_BITS);
memcpy(ptr, ptr_ft, E1_TRAU_BITS);
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-IDLE-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg),
osmo_ubit_dump(msgb_data(msg), msgb_length(msg) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg)));
osmo_i460_mux_enqueue(endp->e1.schan, msg);
}
/* called by I.460 de-multeiplexer; feed output of I.460 demux into TRAU frame sync */
static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits,
unsigned int num_bits)
{
struct mgcp_endpoint *endp = user_data;
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u bits from subslot: %s...\n", num_bits,
osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
OSMO_ASSERT(endp->e1.trau_sync_fi);
osmo_trau_sync_rx_ubits(endp->e1.trau_sync_fi, bits, num_bits);
}
/* called for each synchronized TRAU frame received; decode frame + convert to RTP
* (the resulting frame will be prepended with an all-zero (12-byte) rtp header) */
static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
{
struct msgb *msg = msgb_alloc(RTP_BUF_SIZE, "RTP-rx-from-E1");
unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
struct mgcp_endpoint *endp = user_data;
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
struct mgcp_conn *conn_dst;
struct osmo_trau_frame fr;
int rc;
if (!bits || num_bits == 0)
goto skip;
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u TRAU frame bits from E1 subslot: %s...\n",
num_bits, osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
/* Decode TRAU frame */
switch (endp->e1.scd.rate) {
case OSMO_I460_RATE_8k:
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 8k trau frame...\n");
rc = osmo_trau_frame_decode_8k(&fr, bits, OSMO_TRAU_DIR_UL);
break;
case OSMO_I460_RATE_16k:
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 16k trau frame...\n");
rc = osmo_trau_frame_decode_16k(&fr, bits, OSMO_TRAU_DIR_UL);
break;
default:
/* TRAU frames only exist in 8K or 16K subslots. */
OSMO_ASSERT(false);
break;
}
if (rc != 0) {
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to decode trau frame\n");
goto skip;
}
/* Check if the payload type is supported and what the expected lenth
* of the RTP payload will be. */
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoded trau frame type: %s\n",
osmo_trau_frame_type_name(fr.type));
/* Convert decoded trau frame to RTP frame */
struct osmo_trau2rtp_state t2rs = {
.type = fr.type,
};
rc = osmo_trau2rtp(msgb_data(msg) + rtp_hdr_len, msg->data_len - rtp_hdr_len, &fr, &t2rs);
if (rc <= 0) {
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to convert trau frame to RTP audio\n");
goto skip;
}
msgb_put(msg, rtp_hdr_len + rc);
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: encoded %u bytes of RTP audio: %s\n", rc,
osmo_hexdump(msgb_data(msg) + rtp_hdr_len, msgb_length(msg) - rtp_hdr_len));
/* Forward RTP data to IP */
conn_dst = llist_first_entry(&endp->conns, struct mgcp_conn, entry);
if (!conn_dst) {
LOGPENDP(endp, DE1, LOGL_DEBUG,
"E1-I.460-RX: unable to forward RTP audio data from E1: no connection to forward an incoming RTP packet to\n");
goto skip;
}
OSMO_ASSERT(conn_dst->type == MGCP_CONN_TYPE_RTP);
mgcp_send(endp, 1, NULL, msg, &conn_dst->u.rtp, &conn_dst->u.rtp);
msgb_free(msg);
return;
skip:
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_RX_FAIL_CTR]);
msgb_free(msg);
return;
}
/* Function to handle outgoing E1 traffic */
static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
{
struct msgb *msg = msgb_alloc(E1_TS_BYTES, "E1-TX-timeslot-bytes");
uint8_t *ptr;
/* Get E1 frame from I.460 multiplexer */
ptr = msgb_put(msg, E1_TS_BYTES);
osmo_i460_mux_out(&trunk->e1.i460_ts[ts->num - 1], ptr, E1_TS_BYTES);
#if DEBUG_E1_TS == 1
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-TX: (ts:%u) sending %u bytes: %s...\n", ts->num, msgb_length(msg),
osmo_hexdump_nospc(msgb_data(msg),
msgb_length(msg) > DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
#endif
/* Hand data over to the E1 stack */
msgb_enqueue(&ts->raw.tx_queue, msg);
return;
}
/* Callback function to handle incoming E1 traffic */
static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
{
struct mgcp_trunk *trunk;
/* Find associated trunk */
trunk = mgcp_trunk_by_line_num(cfg, ts->line->num);
if (!trunk) {
LOGP(DE1, LOGL_DEBUG, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
return;
}
/* Check if the incoming data looks sane */
if (msgb_length(msg) != E1_TS_BYTES) {
LOGPTRUNK(trunk, DE1, LOGL_NOTICE,
"E1-RX: (ts:%u) expected length is %u, actual length is %u!\n", ts->num, E1_TS_BYTES,
msgb_length(msg));
}
#if DEBUG_E1_TS == 1
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-RX: (ts:%u) receiving %u bytes: %s...\n", ts->num,
msgb_length(msg), osmo_hexdump_nospc(msgb_data(msg),
msgb_length(msg) >
DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
#endif
/* Hand data over to the I.460 demultiplexer. */
osmo_i460_demux_in(&trunk->e1.i460_ts[ts->num - 1], msgb_data(msg), msgb_length(msg));
/* Trigger sending of pending E1 traffic */
e1_send(ts, trunk);
}
static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
{
/*! Each timeslot needs only to be configured once. The Timeslot then
* stays open and permanently receives data. It is then up to the
* I.460 demultiplexer to add/remove subchannels as needed. It is
* allowed to call this function multiple times since we check if the
* timeslot is already configured. */
struct e1inp_line *e1_line;
int rc;
OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
cfg = trunk->cfg;
if (trunk->e1.ts_in_use[ts_nr - 1]) {
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u already set up, skipping...\n", ts_nr);
return 0;
}
/* Get E1 line */
e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
if (!e1_line) {
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "no such E1 line %u - check VTY config!\n",
trunk->e1.vty_line_nr);
return -EINVAL;
}
e1inp_line_bind_ops(e1_line, &dummy_e1_line_ops);
/* Configure E1 timeslot */
rc = e1inp_ts_config_raw(&e1_line->ts[ts_nr - 1], e1_line, e1_recv_cb);
if (rc < 0) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to put E1 timeslot %u in raw mode.\n", ts_nr);
return -EINVAL;
}
rc = e1inp_line_update(e1_line);
if (rc < 0) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 timeslot %u.\n", ts_nr);
return -EINVAL;
}
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u set up successfully.\n", ts_nr);
trunk->e1.ts_in_use[ts_nr - 1] = true;
return 0;
}
/* Determine a suitable TRAU frame type for a given codec */
static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
uint8_t amr_ft, struct mgcp_endpoint *endp)
{
if (strcmp(sdp_subtype_name, "GSM") == 0)
return OSMO_TRAU16_FT_FR;
else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
return OSMO_TRAU16_FT_EFR;
else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
return OSMO_TRAU16_FT_HR;
else if (strcmp(sdp_subtype_name, "AMR") == 0) {
if (i460_rate == OSMO_I460_RATE_8k) {
switch (amr_ft) {
case AMR_4_75:
case AMR_5_15:
case AMR_5_90:
return OSMO_TRAU8_AMR_LOW;
case AMR_6_70:
return OSMO_TRAU8_AMR_6k7;
case AMR_7_40:
return OSMO_TRAU8_AMR_7k4;
default:
LOGPENDP(endp, DE1, LOGL_ERROR,
"E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
return OSMO_TRAU_FT_NONE;
}
}
return OSMO_TRAU16_FT_AMR;
} else {
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
sdp_subtype_name);
return OSMO_TRAU_FT_NONE;
}
}
/* Determine a suitable TRAU frame type for a given codec */
static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
uint8_t amr_ft, struct mgcp_endpoint *endp)
{
if (strcmp(sdp_subtype_name, "GSM") == 0)
return OSMO_TRAU_SYNCP_16_FR_EFR;
else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
return OSMO_TRAU_SYNCP_16_FR_EFR;
else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
return OSMO_TRAU_SYNCP_8_HR;
else if (strcmp(sdp_subtype_name, "AMR") == 0) {
if (i460_rate == OSMO_I460_RATE_8k) {
switch (amr_ft) {
case AMR_4_75:
case AMR_5_15:
case AMR_5_90:
return OSMO_TRAU_SYNCP_8_AMR_LOW;
case AMR_6_70:
return OSMO_TRAU_SYNCP_8_AMR_6K7;
case AMR_7_40:
return OSMO_TRAU_SYNCP_8_AMR_7K4;
default:
LOGPENDP(endp, DE1, LOGL_ERROR,
"E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
return OSMO_TRAU_SYNCP_16_FR_EFR;
}
}
return OSMO_TRAU_SYNCP_16_FR_EFR;
} else {
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
sdp_subtype_name);
return OSMO_TRAU_SYNCP_16_FR_EFR;
}
}
/* Find out if a given TRAU frame type is AMR */
static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
{
switch (ft) {
case OSMO_TRAU16_FT_AMR:
case OSMO_TRAU8_AMR_LOW:
case OSMO_TRAU8_AMR_6k7:
case OSMO_TRAU8_AMR_7k4:
return true;
default:
return false;
}
}
/* !Equip E1 endpoint with I.460 mux resources.
* \param[in] endp endpoint to equip
* \param[in] ts E1 timeslot number.
* \param[in] ss E1 subslot number.
* \param[in] offset E1 bit offset.
* \returns 0 on success, -EINVAL on error. */
int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs)
{
int rc;
enum osmo_tray_sync_pat_id sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
OSMO_ASSERT(ts != 0);
OSMO_ASSERT(ts != 0xFF);
OSMO_ASSERT(ss != 0xFF);
OSMO_ASSERT(offs != 0xFF);
memset(&endp->e1, 0, sizeof(endp->e1));
endp->e1.last_amr_ft = AMR_4_75;
/* Set up E1 line / timeslot */
rc = e1_init(endp->trunk, ts);
if (rc != 0)
return -EINVAL;
/* Set up I.460 mux */
switch (e1_rates[ss]) {
case 64:
endp->e1.scd.rate = OSMO_I460_RATE_64k;
endp->e1.scd.demux.num_bits = 160 * 8;
break;
case 32:
endp->e1.scd.rate = OSMO_I460_RATE_32k;
endp->e1.scd.demux.num_bits = 80 * 8;
break;
case 16:
endp->e1.scd.rate = OSMO_I460_RATE_16k;
endp->e1.scd.demux.num_bits = 40 * 8;
sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
break;
case 8:
endp->e1.scd.rate = OSMO_I460_RATE_8k;
endp->e1.scd.demux.num_bits = 20 * 8;
sync_pat_id = OSMO_TRAU_SYNCP_8_HR;
break;
}
endp->e1.scd.bit_offset = offs;
endp->e1.scd.demux.out_cb_bits = e1_i460_demux_bits_cb;
endp->e1.scd.demux.out_cb_bytes = NULL;
endp->e1.scd.demux.user_data = endp;
endp->e1.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb;
endp->e1.scd.mux.user_data = endp;
LOGPENDP(endp, DE1, LOGL_DEBUG, "adding I.460 subchannel: ts=%u, bit_offset=%u, rate=%uk, num_bits=%lu\n", ts,
offs, e1_rates[ss], endp->e1.scd.demux.num_bits);
endp->e1.schan = osmo_i460_subchan_add(endp, &endp->trunk->e1.i460_ts[ts - 1], &endp->e1.scd);
if (!endp->e1.schan) {
LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 subchannel: failed!\n");
return -EINVAL;
}
if (endp->e1.scd.rate == OSMO_I460_RATE_16k || endp->e1.scd.rate == OSMO_I460_RATE_8k) {
/* TRAU frames are only specified for 16k and 8k subslots. For all other subslot
* types the concept of TRAU frames does not apply. However, at the moment this
* is the only format we currently support in osmo-mgw */
endp->e1.trau_sync_fi = osmo_trau_sync_alloc(endp, "trau-sync", sync_frame_out_cb, sync_pat_id, endp);
if (!endp->e1.trau_sync_fi) {
LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 TRAU frame sync: failed!\n");
return -EINVAL;
}
endp->e1.trau_rtp_st = talloc_zero(endp->e1.trau_sync_fi, struct osmo_trau2rtp_state);
endp->e1.trau_rtp_st->type = OSMO_TRAU_FT_NONE;
} else {
LOGPENDP(endp, DE1, LOGL_ERROR,
"osmo-mgw currently only supports 16K and 8K subslots (TRAU frames)!\n");
return -EINVAL;
}
return 0;
}
/*! Update E1 related parameters (codec and sync pattern).
* \param[in] endp endpoint to update. */
void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
{
struct mgcp_conn *conn;
struct mgcp_rtp_codec *codec;
enum osmo_tray_sync_pat_id sync_pat_id;
/* In order to determine the codec, find the oldest connection on
* the endpoint and use its codec information. Normally on an E1
* endpoint no more than one connection should exist. */
conn = mgcp_conn_get_oldest(endp);
OSMO_ASSERT(conn);
codec = conn->u.rtp.end.codec;
OSMO_ASSERT(codec);
/* Update codec information */
endp->e1.trau_rtp_st->type =
determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
endp->e1.last_codec = codec;
/* Update sync pattern */
sync_pat_id = determine_trau_sync_pat(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
osmo_trau_sync_set_pat(endp->e1.trau_sync_fi, sync_pat_id);
}
/*! Remove E1 resources from endpoint
* \param[in] endp endpoint to release. */
void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
{
LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
if (endp->e1.schan)
osmo_i460_subchan_del(endp->e1.schan);
if (endp->e1.trau_rtp_st)
talloc_free(endp->e1.trau_rtp_st);
if (endp->e1.trau_sync_fi)
osmo_fsm_inst_term(endp->e1.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL);
memset(&endp->e1, 0, sizeof(endp->e1));
}
/*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit.
* \param[in] endp related endpoint (does not take ownership).
* \param[in] codec configuration.
* \param[in] msg RTP message buffer (including RTP header).
* \returns 0 on success, -1 on ERROR. */
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg)
{
struct msgb *msg_tf = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-TX-TRAU-frame");
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
struct osmo_trau_frame tf;
uint8_t amr_ft;
int rc;
/* Extract AMR frame type from AMR head (if AMR is used) */
if (tf_type_is_amr(endp->e1.trau_rtp_st->type))
amr_ft = (msgb_data(msg)[rtp_hdr_len + 1] >> 3) & 0xf;
else
amr_ft = 0xff;
/* Adapt TRAU frame type on codec changes */
OSMO_ASSERT(endp->e1.last_codec);
if (codec != endp->e1.last_codec || (amr_ft != 0xff && amr_ft != endp->e1.last_amr_ft)) {
endp->e1.trau_rtp_st->type =
determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, amr_ft, endp);
endp->e1.last_codec = codec;
endp->e1.last_amr_ft = amr_ft;
}
if (endp->e1.trau_rtp_st->type == OSMO_TRAU_FT_NONE)
goto skip;
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: using trau frame type for encoding: %s\n",
osmo_trau_frame_type_name(endp->e1.trau_rtp_st->type));
/* Convert from RTP to TRAU format */
msg->l2h = msgb_data(msg) + rtp_hdr_len;
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: decoding %u bytes of RTP audio to TRAU format: %s\n",
msgb_length(msg), osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
memset(&tf, 0, sizeof(tf));
tf.dir = OSMO_TRAU_DIR_DL;
rc = osmo_rtp2trau(&tf, msgb_l2(msg), msgb_l2len(msg), endp->e1.trau_rtp_st);
if (rc < 0) {
LOGPENDP(endp, DE1, LOGL_DEBUG,
"E1-I.460-TX: failed to decode from RTP payload format to TRAU format\n");
goto skip;
}
rc = osmo_trau_frame_encode(msgb_data(msg_tf), msg_tf->data_len, &tf);
if (rc < 0) {
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: failed to encode TRAU frame\n");
goto skip;
}
msgb_put(msg_tf, rc);
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg_tf),
osmo_ubit_dump(msgb_data(msg_tf),
msgb_length(msg_tf) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg_tf)));
/* Enqueue data to I.460 multiplexer */
OSMO_ASSERT(endp->e1.schan);
OSMO_ASSERT(endp->e1.trau_sync_fi);
osmo_i460_mux_enqueue(endp->e1.schan, msg_tf);
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: %u bits of audio enqued for E1 tx\n", msgb_length(msg_tf));
return 0;
skip:
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_TX_FAIL_CTR]);
msgb_free(msg_tf);
return -1;
}

View File

@@ -1,7 +1,7 @@
/* Endpoint types */
/*
* (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
@@ -21,100 +21,23 @@
*
*/
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/mgcp/mgcp_e1.h>
#define E1_RATE_MAX 64
#define E1_OFFS_MAX 8
/* Endpoint typeset definition */
const struct mgcp_endpoint_typeset ep_typeset = {
/* Specify endpoint properties for RTP endpoint */
.rtp = {
.max_conns = 2,
.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
.cleanup_cb = mgcp_cleanup_rtp_bridge_cb,
},
/* Specify endpoint properties for E1 endpoint */
.e1 = {
.max_conns = 1,
.dispatch_rtp_cb = mgcp_dispatch_e1_bridge_cb,
.cleanup_cb = mgcp_cleanup_e1_bridge_cb,
},
.rtp.max_conns = 2,
.rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
.rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb
};
/* Generate virtual endpoint name from given parameters */
static char *gen_virtual_epname(void *ctx, const char *domain,
unsigned int index)
{
return talloc_asprintf(ctx, "%s%x@%s",
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, index, domain);
}
/* Generate E1 endpoint name from given numeric parameters */
static char *gen_e1_epname(void *ctx, const char *domain, uint8_t trunk_nr,
uint8_t ts_nr, uint8_t ss_nr)
{
unsigned int rate;
unsigned int offset;
OSMO_ASSERT(ss_nr < sizeof(e1_rates));
rate = e1_rates[ss_nr];
offset = e1_offsets[ss_nr];
return talloc_asprintf(ctx, "%s%u/s-%u/su%u-%u@%s",
MGCP_ENDPOINT_PREFIX_E1_TRUNK, trunk_nr, ts_nr,
rate, offset, domain);
}
/*! allocate an endpoint and set default values.
* \param[in] trunk configuration.
* \param[in] name endpoint index.
* \returns endpoint on success, NULL on failure. */
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
unsigned int index)
{
struct mgcp_endpoint *endp;
endp = talloc_zero(trunk->endpoints, struct mgcp_endpoint);
if (!endp)
return NULL;
INIT_LLIST_HEAD(&endp->conns);
endp->cfg = trunk->cfg;
endp->trunk = trunk;
switch (trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
endp->type = &ep_typeset.rtp;
endp->name = gen_virtual_epname(endp, trunk->cfg->domain, index);
break;
case MGCP_TRUNK_E1:
endp->type = &ep_typeset.e1;
endp->name = gen_e1_epname(endp, trunk->cfg->domain,
trunk->trunk_nr,
index / MGCP_ENDP_E1_SUBSLOTS, index % MGCP_ENDP_E1_SUBSLOTS);
break;
default:
osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
trunk->trunk_type, __FILE__, __LINE__);
}
return endp;
}
/*! release endpoint, all open connections are closed.
* \param[in] endp endpoint to release */
void mgcp_endp_release(struct mgcp_endpoint *endp)
{
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint:0x%x\n",
ENDPOINT_NUMBER(endp));
/* Normally this function should only be called when
* all connections have been removed already. In case
@@ -130,555 +53,4 @@ void mgcp_endp_release(struct mgcp_endpoint *endp)
talloc_free(endp->local_options.codec);
endp->local_options.codec = NULL;
endp->wildcarded_req = false;
if (endp->trunk->trunk_type == MGCP_TRUNK_E1)
mgcp_e1_endp_release(endp);
}
/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
* "ds/e1-") and write the epname without the prefix back to the memory
* pointed at by epname. (per trunk the prefix is the same for all endpoints,
* so no ambiguity is introduced) */
static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)
{
size_t prefix_len;
switch (trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
if (strncmp
(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
prefix_len) == 0)
memmove(epname, epname + prefix_len,
strlen(epname) - prefix_len + 1);
return;
case MGCP_TRUNK_E1:
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
if (strncmp
(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
prefix_len) == 0)
memmove(epname, epname + prefix_len,
strlen(epname) - prefix_len + 1);
return;
default:
OSMO_ASSERT(false);
}
}
/* Check if the endpoint name contains a suffix (e.g. "@mgw") and truncate
* epname by writing a '\0' char where the suffix starts. */
static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
{
char *suffix_begin;
/* Endpoints on the virtual trunk may have a domain name that is
* followed after an @ character, this can be chopped off. All
* other supported trunk types do not have any suffixes that may
* be chopped off */
if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
suffix_begin = strchr(epname, '@');
if (!suffix_begin)
return;
*suffix_begin = '\0';
}
}
/* Convert all characters in epname to lowercase and strip trunk prefix and
* endpoint name suffix (domain name) from epname. The result is written to
* to the memory pointed at by epname_stripped. The expected size of the
* result is either equal or lower then the length of the input string
* (epname) */
static void strip_epname(char *epname_stripped, const char *epname,
const struct mgcp_trunk *trunk)
{
osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname);
chop_epname_prefix(epname_stripped, trunk);
chop_epname_suffix(epname_stripped, trunk);
}
/* Go through the trunk and find a random free (no active calls) endpoint,
* this function is called when a wildcarded request is carried out, which
* means that it is up to the MGW to choose a random free endpoint. */
static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
{
struct mgcp_endpoint *endp;
unsigned int i;
for (i = 0; i < trunk->number_endpoints; i++) {
endp = trunk->endpoints[i];
/* A free endpoint must not serve a call already and it must
* be available. */
if (endp->callid == NULL && mgcp_endp_avail(endp))
return endp;
}
return NULL;
}
/* Find an endpoint specified by its name. If the endpoint can not be found,
* return NULL */
static struct mgcp_endpoint *find_specific_endpoint(const char *epname,
const struct mgcp_trunk *trunk)
{
char epname_stripped[MGCP_ENDPOINT_MAXLEN];
char epname_stripped_endp[MGCP_ENDPOINT_MAXLEN];
struct mgcp_endpoint *endp;
unsigned int i;
/* Strip irrelevant information from the endpoint name */
strip_epname(epname_stripped, epname, trunk);
for (i = 0; i < trunk->number_endpoints; i++) {
endp = trunk->endpoints[i];
strip_epname(epname_stripped_endp, endp->name, trunk);
if (strcmp(epname_stripped_endp, epname_stripped) == 0)
return endp;
}
return NULL;
}
/*! Find an endpoint by its name on a specified trunk.
* \param[out] cause pointer to store cause code, can be NULL.
* \param[in] epname endpoint name to lookup.
* \param[in] trunk where the endpoint is located.
* \returns endpoint or NULL if endpoint was not found. */
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
const struct mgcp_trunk *trunk)
{
struct mgcp_endpoint *endp;
if (cause)
*cause = 0;
/* At the moment we only support a primitive ('*'-only) method of
* wildcarded endpoint searches that picks the next free endpoint on
* a trunk. */
if (strstr(epname, "*")) {
endp = find_free_endpoint(trunk);
if (endp) {
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
"(trunk:%d) found free endpoint: %s\n",
trunk->trunk_nr, endp->name);
endp->wildcarded_req = true;
return endp;
}
LOGP(DLMGCP, LOGL_ERROR,
"(trunk:%d) Not able to find a free endpoint\n",
trunk->trunk_nr);
if (cause)
*cause = -403;
return NULL;
}
/* Find an endpoint by its name (if wildcarded request is not
* applicable) */
endp = find_specific_endpoint(epname, trunk);
if (endp) {
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
"(trunk:%d) found endpoint: %s\n",
trunk->trunk_nr, endp->name);
endp->wildcarded_req = false;
return endp;
}
LOGP(DLMGCP, LOGL_ERROR,
"(trunk:%d) Not able to find specified endpoint: %s\n",
trunk->trunk_nr, epname);
if (cause)
*cause = -500;
return NULL;
}
/* Check if the domain name, which is supplied with the endpoint name
* matches the configuration. */
static int check_domain_name(const char *epname, struct mgcp_config *cfg)
{
char *domain_to_check;
domain_to_check = strstr(epname, "@");
if (!domain_to_check) {
LOGP(DLMGCP, LOGL_ERROR, "missing domain name in endpoint name \"%s\", expecting \"%s\"\n",
epname, cfg->domain);
return -EINVAL;
}
/* Accept any domain if configured as "*" */
if (!strcmp(cfg->domain, "*"))
return 0;
if (strcmp(domain_to_check+1, cfg->domain) != 0) {
LOGP(DLMGCP, LOGL_ERROR, "wrong domain name in endpoint name \"%s\", expecting \"%s\"\n",
epname, cfg->domain);
return -EINVAL;
}
return 0;
}
/*! Find an endpoint by its name, search at all trunks.
* \param[out] cause, pointer to store cause code, can be NULL.
* \param[in] epname, must contain trunk prefix.
* \param[in] cfg, mgcp configuration (trunks).
* \returns endpoint or NULL if endpoint was not found. */
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
struct mgcp_config *cfg)
{
struct mgcp_trunk *trunk;
struct mgcp_endpoint *endp;
char epname_lc[MGCP_ENDPOINT_MAXLEN];
osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
epname = epname_lc;
if (cause)
*cause = -500;
/* Identify the trunk where the endpoint is located */
trunk = mgcp_trunk_by_name(cfg, epname);
if (!trunk)
return NULL;
/* All endpoint names require a domain as suffix */
if (check_domain_name(epname, cfg))
return NULL;
/* Identify the endpoint on the trunk */
endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
if (!endp) {
return NULL;
}
if (cause)
*cause = 0;
return endp;
}
/* Get the E1 timeslot number from a given E1 endpoint name
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
static uint8_t e1_ts_nr_from_epname(const char *epname)
{
char buf[MGCP_ENDPOINT_MAXLEN + 1];
char *save_ptr = NULL;
char *buf_ptr = buf;
char *token;
unsigned long int res = 0;
strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
while (1) {
token = strtok_r(buf_ptr, "/", &save_ptr);
buf_ptr = NULL;
if (!token)
break;
if (strncmp(token, "s-", 2) == 0) {
errno = 0;
res = strtoul(token + 2, NULL, 10);
if (errno == ERANGE || res > NUM_E1_TS)
return 0xff;
return (uint8_t) res;
}
}
return 0xff;
}
/* Get the E1 timeslot number from a given E1 endpoint name
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
static uint8_t e1_rate_from_epname(const char *epname)
{
char buf[MGCP_ENDPOINT_MAXLEN + 1];
char *save_ptr = NULL;
char *buf_ptr = buf;
char *token;
unsigned long int res = 0;
unsigned int i;
strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
while (1) {
token = strtok_r(buf_ptr, "/", &save_ptr);
buf_ptr = NULL;
if (!token)
break;
if (strncmp(token, "su", 2) == 0) {
errno = 0;
res = strtoul(token + 2, NULL, 10);
if (errno == ERANGE || res > E1_RATE_MAX)
return 0xff;
/* Make sure the rate is a valid rate */
for (i = 0; i < sizeof(e1_rates); i++) {
if (res == e1_rates[i])
return (uint8_t) res;
}
return 0xff;
}
}
return 0xff;
}
/* Get the E1 bitstream offset from a given E1 endpoint name
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
static uint8_t e1_offs_from_epname(const char *epname)
{
char buf[MGCP_ENDPOINT_MAXLEN + 1];
char *save_ptr = NULL;
char *buf_ptr = buf;
char *token;
unsigned long int res = 0;
strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
while (1) {
token = strtok_r(buf_ptr, "/", &save_ptr);
buf_ptr = NULL;
if (!token)
break;
if (strncmp(token, "su", 2) == 0) {
token = strstr(token, "-");
if (!token)
return 0xff;
token += 1;
errno = 0;
res = strtoul(token, NULL, 10);
if (errno == ERANGE || res > E1_OFFS_MAX)
return 0xff;
return (uint8_t) res;
}
}
return 0xff;
}
/* Get the E1 subslot number (internal) from a given E1 endpoint name
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
static uint8_t e1_ss_nr_from_epname(const char *epname)
{
uint8_t rate;
uint8_t offs;
unsigned int i;
rate = e1_rate_from_epname(epname);
offs = e1_offs_from_epname(epname);
osmo_static_assert(sizeof(e1_rates) == sizeof(e1_offsets), e1_rates_e1_offsets_size);
for (i = 0; i < sizeof(e1_rates); i++) {
if ((e1_rates[i] == rate) && (e1_offsets[i] == offs))
return i;
}
return 0xff;
}
/* Check if the selected E1 endpoint is avalable, which means that none of
* the overlapping endpoints are currently serving a call. (if the system
* is properly configured such a situation should never ocurr!) */
static bool endp_avail_e1(struct mgcp_endpoint *endp)
{
/* The following map shows the overlapping of the subslots and their
* respective rates. The numbers on the right running from top to bottom
* are the bit offsets in the whole 64k timeslot. The numbers inside the
* boxes symbolize the internal subslot number (array index) and the
* rate in the form: i:r where i is the subslot number and r the
* respective rate.
*
* +--------+--------+--------+--------+ 0
* | | | | 7:8k |
* | | + 3:16k +--------+ 1
* | | | | 8:8k |
* | | 1:32k +--------+--------+ 2
* | | | | 9:8k |
* | | + 4:16k +--------+ 3
* | | | | 10:8k |
* | 0:64k +--------+--------+--------+ 4
* | | | | 11:8k |
* | | + 5:16k +--------+ 5
* | | | | 12:8k |
* | | 2:32k +--------+--------+ 6
* | | | | 13:8k |
* | | + 6:16k +--------+ 7
* | | | | 14:8k |
* +--------+--------+--------+--------+ 8
*
* The following array contains tables with the subslot numbers that must be
* unused for each subslot. During this test we do not have to check the
* endpoint we need to verify, only the overlaps need to be checked. This is
* also the reason why the related subslot number is missing from each each
* line. */
const int8_t interlock_tab[MGCP_ENDP_E1_SUBSLOTS][15] = {
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1 },
{ 0, 3, 4, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 5, 6, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 1, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 1, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 2, 11, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 2, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
{ 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } };
const int8_t *interlock;
unsigned int i;
uint8_t ts_nr = 0;
uint8_t ss_nr = 0;
char *epname_check;
struct mgcp_endpoint *endp_check;
bool available = true;
/* This function must only be used with E1 type endpoints! */
OSMO_ASSERT(endp->trunk->trunk_type == MGCP_TRUNK_E1);
ts_nr = e1_ts_nr_from_epname(endp->name);
ss_nr = e1_ss_nr_from_epname(endp->name);
if (ts_nr == 0xff || ss_nr == 0xff) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"cannot check endpoint availability, endpoint name not parseable!\n");
return false;
}
interlock = interlock_tab[ss_nr];
for (i = 0; i < sizeof(interlock_tab[0]); i++) {
/* Detect row end */
if (interlock[i] == -1)
break;
/* Pick overlapping endpoint to check */
epname_check = gen_e1_epname(endp, endp->trunk->cfg->domain,
endp->trunk->trunk_nr, ts_nr,
interlock[i]);
endp_check = find_specific_endpoint(epname_check, endp->trunk);
if (!endp_check) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"cannot check endpoint availability, overlapping endpoint:%s not found!\n",
epname_check);
talloc_free(epname_check);
continue;
}
talloc_free(epname_check);
/* Check if overlapping endpoint currently serves another call
* (This is an exceptional situation, that should not occur
* in a properly configured environment!) */
if (endp_check->callid) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"endpoint unavailable - overlapping endpoint:%s already serves a call!\n",
endp_check->name);
available = false;
}
}
return available;
}
/*! check if an endpoint is available for any kind of operation.
* \param[in] endp endpoint to check.
* \returns true if endpoint is avalable, false it is blocked for any reason. */
bool mgcp_endp_avail(struct mgcp_endpoint *endp)
{
switch (endp->trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
/* There are no obstacles that may render a virtual trunk
* endpoint unusable, so virtual trunk endpoints are always
* available */
return true;
case MGCP_TRUNK_E1:
return endp_avail_e1(endp);
default:
OSMO_ASSERT(false);
}
return false;
}
/*! claim endpoint, sets callid and activates endpoint, should be called at the
* beginning of the CRCX procedure when it is clear that a new call should be
* created.
* \param[in] endp endpoint to claim.
* \param[in] callid that is assingned to this endpoint. */
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid)
{
int rc = 0;
uint8_t ts;
uint8_t ss;
uint8_t offs;
/* TODO: Make this function more intelligent, it should run the
* call id checks we currently have in protocol.c directly here. */
/* Set the callid, creation of another connection will only be possible
* when the callid matches up. (Connections are distinguished by their
* connection ids) */
endp->callid = talloc_strdup(endp, callid);
OSMO_ASSERT(endp->callid);
/* Allocate resources */
switch (endp->trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
/* No additional initaliziation required here, virtual
* endpoints will open/close network sockets themselves
* on demand. */
break;
case MGCP_TRUNK_E1:
ts = e1_ts_nr_from_epname(endp->name);
ss = e1_ss_nr_from_epname(endp->name);
offs = e1_offs_from_epname(endp->name);
OSMO_ASSERT(ts != 0xFF);
OSMO_ASSERT(ts != 0);
OSMO_ASSERT(ss != 0xFF);
OSMO_ASSERT(offs != 0xFF);
rc = mgcp_e1_endp_equip(endp, ts, ss, offs);
break;
default:
OSMO_ASSERT(false);
}
/* Make sure the endpoint is released when claiming the endpoint fails. */
if (rc < 0)
mgcp_endp_release(endp);
return rc;
}
/*! update endpoint, updates internal endpoint specific data, should be
* after when MDCX or CRCX has been executed successuflly.
* \param[in] endp endpoint to update. */
void mgcp_endp_update(struct mgcp_endpoint *endp)
{
/* Allocate resources */
switch (endp->trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
/* No updating initaliziation required for virtual endpoints. */
break;
case MGCP_TRUNK_E1:
mgcp_e1_endp_update(endp);
break;
default:
OSMO_ASSERT(false);
}
}
void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
{
llist_add(&conn->entry, &endp->conns);
}
void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
{
/* Run endpoint cleanup action. By this we inform the endpoint about
* the removal of the connection and allow it to clean up its inner
* state accordingly */
if (endp->type->cleanup_cb)
endp->type->cleanup_cb(endp, conn);
llist_del(&conn->entry);
if (llist_empty(&endp->conns))
mgcp_endp_release(endp);
}

View File

@@ -24,9 +24,7 @@
#include <limits.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_conn.h>
@@ -84,8 +82,9 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
int ret = 0;
if (!mode) {
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"missing connection mode\n");
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:0x%x missing connection mode\n",
ENDPOINT_NUMBER(endp));
return -1;
}
if (!conn)
@@ -93,35 +92,39 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
if (!endp)
return -1;
if (strcasecmp(mode, "recvonly") == 0)
if (strcmp(mode, "recvonly") == 0)
conn->mode = MGCP_CONN_RECV_ONLY;
else if (strcasecmp(mode, "sendrecv") == 0)
else if (strcmp(mode, "sendrecv") == 0)
conn->mode = MGCP_CONN_RECV_SEND;
else if (strcasecmp(mode, "sendonly") == 0)
else if (strcmp(mode, "sendonly") == 0)
conn->mode = MGCP_CONN_SEND_ONLY;
else if (strcasecmp(mode, "loopback") == 0)
else if (strcmp(mode, "loopback") == 0)
conn->mode = MGCP_CONN_LOOPBACK;
else {
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
"unknown connection mode: '%s'\n", mode);
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:0x%x unknown connection mode: '%s'\n",
ENDPOINT_NUMBER(endp), mode);
ret = -1;
}
/* Special handling for RTP connections */
/* Special handling für RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
conn->u.rtp.end.output_enabled =
conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
}
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
LOGP(DLMGCP, LOGL_DEBUG,
"endpoint:0x%x conn:%s\n",
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn));
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
mode, conn->mode);
LOGP(DLMGCP, LOGL_DEBUG,
"endpoint:0x%x connection mode '%s' %d\n",
ENDPOINT_NUMBER(endp), mode, conn->mode);
/* Special handling für RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %d\n",
conn->u.rtp.end.output_enabled);
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x output_enabled %d\n",
ENDPOINT_NUMBER(endp), conn->u.rtp.end.output_enabled);
}
/* The VTY might change the connection mode at any time, so we have
@@ -131,6 +134,167 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
return ret;
}
/* We have a null terminated string with the endpoint name here. We only
* support two kinds. Simple ones as seen on the BSC level and the ones
* seen on the trunk side. (helper function for find_endpoint()) */
static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
const char *mgcp)
{
char *rest = NULL;
struct mgcp_trunk_config *tcfg;
int trunk, endp;
struct mgcp_endpoint *endp_ptr;
trunk = strtoul(mgcp + 6, &rest, 10);
if (rest == NULL || rest[0] != '/' || trunk < 1) {
LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
return NULL;
}
endp = strtoul(rest + 1, &rest, 10);
if (rest == NULL || rest[0] != '@') {
LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
return NULL;
}
/* signalling is on timeslot 1 */
if (endp == 1)
return NULL;
tcfg = mgcp_trunk_num(cfg, trunk);
if (!tcfg) {
LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n",
trunk);
return NULL;
}
if (!tcfg->endpoints) {
LOGP(DLMGCP, LOGL_ERROR,
"Endpoints of trunk %d not allocated.\n", trunk);
return NULL;
}
if (endp < 1 || endp >= tcfg->number_endpoints) {
LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n",
mgcp);
return NULL;
}
endp_ptr = &tcfg->endpoints[endp];
endp_ptr->wildcarded_req = false;
return endp_ptr;
}
/* Find an endpoint that is not in use. Do this by going through the endpoint
* array, check the callid. A callid nullpointer indicates that the endpoint
* is free */
static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
unsigned int number_endpoints)
{
struct mgcp_endpoint *endp;
unsigned int i;
for (i = 0; i < number_endpoints; i++) {
if (endpoints[i].callid == NULL) {
endp = &endpoints[i];
LOGP(DLMGCP, LOGL_DEBUG,
"endpoint:0x%x found free endpoint\n",
ENDPOINT_NUMBER(endp));
endp->wildcarded_req = true;
return endp;
}
}
LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n");
return NULL;
}
/* Check if the domain name, which is supplied with the endpoint name
* matches the configuration. */
static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
{
char *domain_to_check;
domain_to_check = strstr(mgcp, "@");
if (!domain_to_check)
return -EINVAL;
/* Accept any domain if configured as "*" */
if (!strcmp(cfg->domain, "*"))
return 0;
if (strcmp(domain_to_check+1, cfg->domain) != 0) {
LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s', expecting '%s'\n", mgcp, cfg->domain);
return -EINVAL;
}
return 0;
}
/* Search the endpoint pool for the endpoint that had been selected via the
* MGCP message (helper function for mgcp_analyze_header()) */
static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
const char *mgcp,
int *cause)
{
char *endptr = NULL;
unsigned int gw = INT_MAX;
const char *endpoint_number_str;
struct mgcp_endpoint *endp;
*cause = 0;
/* Check if the domainname in the request is correct */
if (check_domain_name(cfg, mgcp)) {
*cause = -500;
return NULL;
}
/* Check if the E1 trunk is requested */
if (strncmp(mgcp, "ds/e1", 5) == 0) {
endp = find_e1_endpoint(cfg, mgcp);
if (!endp)
*cause = -500;
return endp;
}
/* Check if the virtual trunk is addressed (new, correct way with prefix) */
if (strncmp
(mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) {
endpoint_number_str =
mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
if (endpoint_number_str[0] == '*') {
endp = find_free_endpoint(cfg->trunk.endpoints,
cfg->trunk.number_endpoints);
if (!endp)
*cause = -403;
return endp;
}
gw = strtoul(endpoint_number_str, &endptr, 16);
if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
endp = &cfg->trunk.endpoints[gw];
endp->wildcarded_req = false;
return endp;
}
}
/* Deprecated method without prefix */
LOGP(DLMGCP, LOGL_NOTICE,
"Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n",
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp);
gw = strtoul(mgcp, &endptr, 16);
if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
endp = &cfg->trunk.endpoints[gw];
endp->wildcarded_req = false;
return endp;
}
LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
*cause = -500;
return NULL;
}
/*! Analyze and parse the the hader of an MGCP messeage string.
* \param[out] pdata caller provided memory to store the parsing results
* \param[in] data mgcp message string
@@ -158,7 +322,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
pdata->trans = elem;
break;
case 1:
pdata->endp = mgcp_endp_by_name(&cause, elem, pdata->cfg);
pdata->endp = find_endpoint(pdata->cfg, elem, &cause);
if (!pdata->endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Unable to find Endpoint `%s'\n", elem);
@@ -167,7 +331,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
}
break;
case 2:
if (strcasecmp("MGCP", elem)) {
if (strcmp("MGCP", elem)) {
LOGP(DLMGCP, LOGL_ERROR,
"MGCP header parsing error\n");
return -510;
@@ -196,27 +360,18 @@ 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 wildcard, -2 on error */
* \returns OSMUX CID, -1 on error */
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");
if (sscanf(line + 2, "Osmux: %u", &osmux_cid) != 1)
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 -2;
return -1;
}
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
@@ -232,8 +387,8 @@ int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
const size_t line_len = strlen(line);
if (line[0] != '\0' && line_len < 2) {
LOGP(DLMGCP, LOGL_ERROR,
"Wrong MGCP option format: '%s' on %s\n",
line, endp->name);
"Wrong MGCP option format: '%s' on 0x%x\n",
line, ENDPOINT_NUMBER(endp));
return 0;
}
@@ -245,7 +400,7 @@ int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
/*! Check if the specified callid seems plausible.
* \param[in] endp pointer to endpoint
* \param{in] callid to verify
* \returns 0 when callid seems plausible, -1 on error */
* \returns 1 when callid seems plausible, 0 on error */
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
{
/*! This function compares the supplied callid with the called that is
@@ -264,9 +419,9 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
return -1;
if (strcmp(endp->callid, callid) != 0) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"CallIDs mismatch: '%s' != '%s'\n",
endp->callid, callid);
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:0x%x CallIDs mismatch: '%s' != '%s'\n",
ENDPOINT_NUMBER(endp), endp->callid, callid);
return -1;
}
@@ -285,23 +440,25 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
/* Check for null identifiers */
if (!conn_id) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"invalid ConnectionIdentifier (missing)\n");
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:0x%x invalid ConnectionIdentifier (missing)\n",
ENDPOINT_NUMBER(endp));
return 510;
}
/* Check for empty connection identifiers */
if (strlen(conn_id) == 0) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"invalid ConnectionIdentifier (empty)\n");
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:0x%x invalid ConnectionIdentifier (empty)\n",
ENDPOINT_NUMBER(endp));
return 510;
}
/* Check for over long connection identifiers */
if (strlen(conn_id) > (MGCP_CONN_ID_MAXLEN-1)) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"invalid ConnectionIdentifier (too long: %zu > %d) 0x%s\n",
strlen(conn_id), MGCP_CONN_ID_MAXLEN-1, conn_id);
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:0x%x invalid ConnectionIdentifier (too long: %zu > %d) 0x%s\n",
ENDPOINT_NUMBER(endp), strlen(conn_id), MGCP_CONN_ID_MAXLEN-1, conn_id);
return 510;
}
@@ -309,8 +466,9 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
if (mgcp_conn_get(endp, conn_id))
return 0;
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"no connection found under ConnectionIdentifier 0x%s\n", conn_id);
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:0x%x no connection found under ConnectionIdentifier 0x%s\n",
ENDPOINT_NUMBER(endp), conn_id);
/* When the conn_id was not found, return error code 515 "The transaction refers to an incorrect
* connection-id (may have been already deleted)." */

File diff suppressed because it is too large Load Diff

View File

@@ -15,19 +15,16 @@
#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_protocol.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
static struct osmo_fd osmux_fd;
@@ -35,10 +32,9 @@ static LLIST_HEAD(osmux_handle_list);
struct osmux_handle {
struct llist_head head;
struct mgcp_conn_rtp *conn;
struct osmux_in_handle *in;
struct in_addr rem_addr;
int rem_port; /* network byte order */
int rem_port;
int refcnt;
};
@@ -48,17 +44,14 @@ static void *osmux;
static void osmux_deliver_cb(struct msgb *batch_msg, void *data)
{
struct osmux_handle *handle = data;
struct mgcp_conn_rtp *conn = handle->conn;
struct sockaddr_in out = {
.sin_family = AF_INET,
.sin_port = handle->rem_port,
};
if (conn->end.output_enabled) {
struct sockaddr_in out = {
.sin_family = AF_INET,
.sin_port = handle->rem_port,
};
memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr));
sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0,
(struct sockaddr *)&out, sizeof(out));
}
memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr));
sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0,
(struct sockaddr *)&out, sizeof(out));
msgb_free(batch_msg);
}
@@ -113,15 +106,13 @@ static void osmux_handle_put(struct osmux_in_handle *in)
/* Allocate free OSMUX handle */
static struct osmux_handle *
osmux_handle_alloc(struct mgcp_conn_rtp *conn, struct in_addr *addr, int rem_port)
osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
{
struct osmux_handle *h;
struct mgcp_config *cfg = conn->conn->endp->cfg;
h = talloc_zero(osmux, struct osmux_handle);
if (!h)
return NULL;
h->conn = conn;
h->rem_addr = *addr;
h->rem_port = rem_port;
h->refcnt++;
@@ -154,20 +145,15 @@ osmux_handle_alloc(struct mgcp_conn_rtp *conn, struct in_addr *addr, int rem_por
/* Lookup existing handle for a specified address, if the handle can not be
* found, the function will automatically allocate one */
static struct osmux_in_handle *
osmux_handle_lookup(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *addr, int rem_port)
osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
{
struct osmux_handle *h;
if (addr->u.sa.sa_family != AF_INET) {
LOGP(DLMGCP, LOGL_DEBUG, "IPv6 not supported in osmux yet!\n");
return NULL;
}
h = osmux_handle_find_get(&addr->u.sin.sin_addr, rem_port);
h = osmux_handle_find_get(addr, rem_port);
if (h != NULL)
return h->in;
h = osmux_handle_alloc(conn, &addr->u.sin.sin_addr, rem_port);
h = osmux_handle_alloc(cfg, addr, rem_port);
if (h == NULL)
return NULL;
@@ -191,12 +177,6 @@ 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);
@@ -205,57 +185,111 @@ 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_conn_rtp*
osmux_conn_lookup(struct mgcp_config *cfg, uint8_t cid,
struct in_addr *from_addr)
static struct mgcp_endpoint *
endpoint_lookup(struct mgcp_config *cfg, int cid,
struct in_addr *from_addr, int type)
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
struct mgcp_endpoint *endp;
struct mgcp_conn *conn = NULL;
struct mgcp_conn_rtp * conn_rtp;
struct mgcp_endpoint *endp = NULL;
int i;
struct mgcp_conn_rtp *conn_net = NULL;
struct mgcp_conn_rtp *conn_bts = NULL;
for (i = 0; i < trunk->number_endpoints; i++) {
for (i=0; i<cfg->trunk.number_endpoints; i++) {
struct in_addr *this;
endp = trunk->endpoints[i];
endp = &cfg->trunk.endpoints[i];
llist_for_each_entry(conn, &endp->conns, entry) {
if (conn->type != MGCP_CONN_TYPE_RTP)
continue;
#if 0
if (!tmp->allocated)
continue;
#endif
conn_rtp = &conn->u.rtp;
if (!mgcp_conn_rtp_is_osmux(conn_rtp))
continue;
if (conn_rtp->osmux.cid == cid)
return conn_rtp;
switch(type) {
case MGCP_DEST_NET:
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (conn_net)
this = &conn_net->end.addr;
else
this = NULL;
break;
case MGCP_DEST_BTS:
/* FIXME: Get rid of CONN_ID_XXX! */
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
if (conn_bts)
this = &conn_bts->end.addr;
else
this = NULL;
break;
default:
/* Should not ever happen */
LOGP(DLMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type);
return NULL;
}
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (conn_net && this && conn_net->osmux.cid == cid
&& this->s_addr == from_addr->s_addr)
return endp;
}
LOGP(DLMGCP, LOGL_ERROR, "Cannot find osmux conn with cid=%d\n", cid);
LOGP(DLMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid);
return NULL;
}
/* 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)
static void scheduled_tx_net_cb(struct msgb *msg, void *data)
{
struct mgcp_conn_rtp *conn = data;
struct mgcp_endpoint *endp = conn->conn->endp;
struct osmo_sockaddr addr = { /* FIXME: do we know the source address?? */ };
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
*mc = (struct osmo_rtp_msg_ctx){
.proto = MGCP_PROTO_RTP,
.conn_src = conn,
.from_addr = &addr,
struct mgcp_endpoint *endp = data;
struct mgcp_conn_rtp *conn_net = NULL;
struct mgcp_conn_rtp *conn_bts = NULL;
/* FIXME: Get rid of CONN_ID_XXX! */
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_bts || !conn_net)
return;
struct sockaddr_in addr = {
.sin_addr = conn_net->end.addr,
.sin_port = conn_net->end.rtp_port,
};
endp->type->dispatch_rtp_cb(msg);
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);
msgb_free(msg);
}
@@ -286,31 +320,26 @@ static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr)
static int endp_osmux_state_check(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
bool sending)
{
char ipbuf[INET6_ADDRSTRLEN];
switch(conn->osmux.state) {
case OSMUX_STATE_ACTIVATING:
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));
if (osmux_enable_conn(endp, conn, &conn->end.addr, htons(endp->cfg->osmux_port)) < 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Could not enable osmux for conn:%s\n",
mgcp_conn_dump(conn->conn));
return -1;
}
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"Osmux %s CID %u towards %s:%u is now enabled\n",
sending ? "sent" : "received",
conn->osmux.cid,
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf),
ntohs(conn->end.rtp_port));
LOGP(DLMGCP, LOGL_ERROR,
"Osmux CID %u for %s:%u is now enabled\n",
conn->osmux.cid, inet_ntoa(conn->end.addr),
endp->cfg->osmux_port);
return 0;
case OSMUX_STATE_ENABLED:
return 0;
default:
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);
LOGP(DLMGCP, LOGL_ERROR,
"Osmux %s in conn %s without full negotiation, state %d\n",
sending ? "sent" : "received",
mgcp_conn_dump(conn->conn), conn->osmux.state);
return -1;
}
}
@@ -318,9 +347,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: %s\n", osmo_hexdump(msg->data, msg->len));
"Discarding truncated Osmux dummy load\n");
return -1;
}
@@ -329,40 +358,16 @@ 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);
static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
struct mgcp_conn_rtp *conn_src;
struct mgcp_conn_rtp *conn_bts = NULL;
msg = osmux_recv(ofd, &addr);
if (!msg)
@@ -376,34 +381,115 @@ static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
/* not any further processing dummy messages */
if (msg->data[0] == MGCP_DUMMY_LOAD)
return osmux_handle_dummy(cfg, &addr, msg);
goto out;
rem = msg->len;
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
struct mgcp_endpoint *endp;
conn_src = osmux_conn_lookup(cfg, osmuxh->circuit_id,
&addr.sin_addr);
if (!conn_src) {
/* Yes, we use MGCP_DEST_NET to locate the origin */
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
&addr.sin_addr, MGCP_DEST_NET);
/* FIXME: Get rid of CONN_ID_XXX! */
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
if (!conn_bts)
continue;
if (!endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot find a src conn for circuit_id=%d\n",
"Cannot find an endpoint for circuit_id=%d\n",
osmuxh->circuit_id);
goto out;
}
if (endp_osmux_state_check(endp, conn_bts, false) == 0) {
conn_bts->osmux.stats.octets += osmux_chunk_length(msg, rem);
conn_bts->osmux.stats.chunks++;
osmux_xfrm_output_sched(&conn_bts->osmux.out, osmuxh);
}
rem = msg->len;
}
out:
msgb_free(msg);
return 0;
}
mgcp_conn_watchdog_kick(conn_src->conn);
/* 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;
/*conn_dst = mgcp_find_dst_conn(conn_src->conn);
if (!conn_dst) {
if (osmux_legacy_dummy_parse_cid(addr, msg, &osmux_cid) < 0)
goto out;
endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, endp_type);
if (!endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot find endpoint for Osmux CID %d\n", osmux_cid);
goto out;
}
/* FIXME: Get rid of CONN_ID_XXX! */
conn = mgcp_conn_get_rtp(endp, endp_type == MGCP_DEST_BTS ? CONN_ID_NET : CONN_ID_BTS);
if (!conn)
goto out;
endp_osmux_state_check(endp, conn, false);
/* Only needed to punch hole in firewall, it can be dropped */
out:
msgb_free(msg);
return 0;
}
int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
struct mgcp_conn_rtp *conn_net = NULL;
msg = osmux_recv(ofd, &addr);
if (!msg)
return -1;
if (!cfg->osmux) {
LOGP(DLMGCP, LOGL_ERROR,
"bsc wants to use Osmux but bsc-nat did not request it\n");
goto out;
}
/* not any further processing dummy messages */
if (msg->data[0] == MGCP_DUMMY_LOAD)
return osmux_handle_dummy(cfg, &addr, msg, MGCP_DEST_BTS);
rem = msg->len;
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
struct mgcp_endpoint *endp;
/* Yes, we use MGCP_DEST_BTS to locate the origin */
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
&addr.sin_addr, MGCP_DEST_BTS);
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_net)
continue;
if (!endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot find a dst conn for circuit_id=%d\n",
"Cannot find an endpoint for circuit_id=%d\n",
osmuxh->circuit_id);
goto out;
}*/
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);
}
if (endp_osmux_state_check(endp, conn_net, false) == 0) {
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
conn_net->osmux.stats.chunks++;
osmux_xfrm_output_sched(&conn_net->osmux.out, osmuxh);
}
rem = msg->len;
}
@@ -416,27 +502,34 @@ int osmux_init(int role, struct mgcp_config *cfg)
{
int ret;
osmo_fd_setup(&osmux_fd, -1, OSMO_FD_READ, osmux_read_fd_cb, cfg, 0);
switch(role) {
case OSMUX_ROLE_BSC:
osmux_fd.cb = osmux_read_from_bsc_nat_cb;
break;
case OSMUX_ROLE_BSC_NAT:
osmux_fd.cb = osmux_read_from_bsc_cb;
break;
default:
LOGP(DLMGCP, LOGL_ERROR, "wrong role for OSMUX\n");
return -1;
}
osmux_fd.data = cfg;
ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port,
cfg->endp_dscp, cfg->endp_priority);
ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port);
if (ret < 0) {
LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket to %s:%u\n",
cfg->osmux_addr, cfg->osmux_port);
LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket\n");
return ret;
}
mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp);
osmux_fd.when |= BSC_FD_READ;
ret = osmo_fd_register(&osmux_fd);
if (ret < 0) {
LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket %s\n",
osmo_sock_get_name2(osmux_fd.fd));
LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket\n");
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;
}
@@ -444,10 +537,10 @@ 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 (in network byte order)
* \param[in] port portnumber of the remote OSMUX endpoint
* \returns 0 on success, -1 on ERROR */
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
struct osmo_sockaddr *addr, uint16_t port)
struct in_addr *addr, uint16_t port)
{
/*! If osmux is enabled, initialize the output handler. This handler is
* used to reconstruct the RTP flow from osmux. The RTP SSRC is
@@ -458,49 +551,44 @@ 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 in6_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) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"conn:%s didn't negotiate Osmux, state %d\n",
mgcp_conn_dump(conn->conn), conn->osmux.state);
LOGP(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,
conn->end.addr.u.sa.sa_family == AF_INET6 ?
sizeof(struct in6_addr) :
sizeof(struct in_addr)) == 0) {
LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
"Osmux remote address/port still unknown\n");
return -1;
}
conn->osmux.in = osmux_handle_lookup(conn, addr, port);
conn->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
if (!conn->osmux.in) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"Cannot allocate input osmux handle for conn:%s\n",
mgcp_conn_dump(conn->conn));
LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle for conn:%s\n",
mgcp_conn_dump(conn->conn));
return -1;
}
if (osmux_xfrm_input_open_circuit(conn->osmux.in, conn->osmux.cid, osmux_dummy) < 0) {
LOGPCONN(conn->conn, 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)) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot open osmux circuit %u for conn:%s\n",
conn->osmux.cid, mgcp_conn_dump(conn->conn));
return -1;
}
osmux_xfrm_output_init2(&conn->osmux.out,
osmux_xfrm_output_init(&conn->osmux.out,
(conn->osmux.cid * rtp_ssrc_winlen) +
(random() % rtp_ssrc_winlen),
conn->end.codec->payload_type);
(random() % rtp_ssrc_winlen));
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
scheduled_from_osmux_tx_rtp_cb, conn);
switch (endp->cfg->role) {
case MGCP_BSC_NAT:
conn->type = MGCP_OSMUX_BSC_NAT;
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
scheduled_tx_net_cb, endp);
break;
case MGCP_BSC:
conn->type = MGCP_OSMUX_BSC;
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
scheduled_tx_bts_cb, endp);
break;
}
conn->osmux.state = OSMUX_STATE_ENABLED;
@@ -509,65 +597,48 @@ 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 conn_osmux_disable(struct mgcp_conn_rtp *conn)
void osmux_disable_conn(struct mgcp_conn_rtp *conn)
{
if (!conn)
return;
OSMO_ASSERT(conn->osmux.state != OSMUX_STATE_DISABLED);
if (conn->osmux.state != OSMUX_STATE_ENABLED)
return;
LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
"Releasing connection using Osmux CID %u\n", conn->osmux.cid);
LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
conn->conn->id, conn->osmux.cid);
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);
/* 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_release_cid(conn);
osmux_handle_put(conn->osmux.in);
}
conn_osmux_release_cid(conn);
osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
conn->osmux.state = OSMUX_STATE_DISABLED;
conn->osmux.cid = -1;
osmux_handle_put(conn->osmux.in);
}
/*! relase OSXMUX cid, that had been allocated to this connection.
* \param[in] conn connection with OSMUX cid to release */
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn)
void osmux_release_cid(struct mgcp_conn_rtp *conn)
{
if (conn->osmux.cid_allocated)
osmux_cid_pool_put(conn->osmux.cid);
conn->osmux.cid = 0;
conn->osmux.cid_allocated = false;
if (!conn)
return;
if (conn->osmux.state != OSMUX_STATE_ENABLED)
return;
if (conn->osmux.allocated_cid >= 0)
osmux_put_cid(conn->osmux.allocated_cid);
conn->osmux.allocated_cid = -1;
}
/*! allocate OSXMUX cid to connection.
* \param[in] conn connection for which we allocate the OSMUX cid
* \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)
* \param[in] conn connection for which we allocate the OSMUX cid*/
void osmux_allocate_cid(struct mgcp_conn_rtp *conn)
{
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;
osmux_release_cid(conn);
conn->osmux.allocated_cid = osmux_get_cid();
}
/*! send RTP dummy packet to OSMUX connection port.
@@ -576,9 +647,7 @@ int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid)
* \returns bytes sent, -1 on error */
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
char ipbuf[INET6_ADDRSTRLEN];
struct osmux_hdr *osmuxh;
int buf_len;
char buf[1 + sizeof(uint8_t)];
struct in_addr addr_unset = {};
/*! The dummy packet will not be sent via the actual OSMUX connection,
@@ -590,6 +659,9 @@ 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;
@@ -597,20 +669,12 @@ int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
if (endp_osmux_state_check(endp, conn, true) < 0)
return 0;
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",
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf),
ntohs(conn->end.rtp_port), conn->osmux.cid);
LOGP(DLMGCP, LOGL_DEBUG,
"sending OSMUX dummy load to %s CID %u\n",
inet_ntoa(conn->end.addr), conn->osmux.cid);
return mgcp_udp_send(osmux_fd.fd, &conn->end.addr,
conn->end.rtp_port, (char*)osmuxh, buf_len);
htons(endp->cfg->osmux_port), buf, sizeof(buf));
}
/* bsc-nat allocates/releases the Osmux circuit ID. +7 to round up to 8 bit boundary. */
@@ -618,7 +682,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_cid_pool_count_used(void)
int osmux_used_cid(void)
{
int i, j, used = 0;
@@ -634,7 +698,7 @@ int osmux_cid_pool_count_used(void)
/*! take a free OSMUX cid.
* \returns OSMUX cid */
int osmux_cid_pool_get_next(void)
int osmux_get_cid(void)
{
int i, j;
@@ -654,24 +718,10 @@ int osmux_cid_pool_get_next(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_cid_pool_put(uint8_t osmux_cid)
void osmux_put_cid(uint8_t osmux_cid)
{
LOGP(DLMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid);
osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8));
}
/*! 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)));
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,229 +0,0 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* rate-counter implementation */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <errno.h>
#include <osmocom/core/stats.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_ratectr.h>
static const struct rate_ctr_desc mgcp_general_ctr_desc[] = {
/* rx_msgs = rx_msgs_retransmitted + rx_msgs_handled + rx_msgs_unhandled + err_rx_msg_parse + err_rx_no_endpoint */
[MGCP_GENERAL_RX_MSGS_TOTAL] = { "mgcp:rx_msgs", "total number of MGCP messages received." },
[MGCP_GENERAL_RX_MSGS_RETRANSMITTED] = { "mgcp:rx_msgs_retransmitted", "number of received retransmissions." },
[MGCP_GENERAL_RX_MSGS_HANDLED] = { "mgcp:rx_msgs_handled", "number of handled MGCP messages." },
[MGCP_GENERAL_RX_MSGS_UNHANDLED] = { "mgcp:rx_msgs_unhandled", "number of unhandled MGCP messages." },
[MGCP_GENERAL_RX_FAIL_MSG_PARSE] = { "mgcp:err_rx_msg_parse", "error parsing MGCP message." },
[MGCP_GENERAL_RX_FAIL_NO_ENDPOINT] =
{ "mgcp:err_rx_no_endpoint", "can't find MGCP endpoint, probably we've used all allocated endpoints." },
};
const static struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
.group_name_prefix = "mgcp",
.group_description = "mgcp general statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(mgcp_general_ctr_desc),
.ctr_desc = mgcp_general_ctr_desc
};
static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
[MGCP_CRCX_SUCCESS] = { "crcx:success", "CRCX command processed successfully." },
[MGCP_CRCX_FAIL_BAD_ACTION] = { "crcx:bad_action", "bad action in CRCX command." },
[MGCP_CRCX_FAIL_UNHANDLED_PARAM] = { "crcx:unhandled_param", "unhandled parameter in CRCX command." },
[MGCP_CRCX_FAIL_MISSING_CALLID] = { "crcx:missing_callid", "missing CallId in CRCX command." },
[MGCP_CRCX_FAIL_INVALID_MODE] = { "crcx:invalid_mode", "invalid connection mode in CRCX command." },
[MGCP_CRCX_FAIL_LIMIT_EXCEEDED] = { "crcx:limit_exceeded", "limit of concurrent connections was reached." },
[MGCP_CRCX_FAIL_UNKNOWN_CALLID] = { "crcx:unkown_callid", "unknown CallId in CRCX command." },
[MGCP_CRCX_FAIL_ALLOC_CONN] = { "crcx:alloc_conn_fail", "connection allocation failure." },
[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC] =
{ "crcx:no_remote_conn_desc", "no opposite end specified for connection." },
[MGCP_CRCX_FAIL_START_RTP] = { "crcx:start_rtp_failure", "failure to start RTP processing." },
[MGCP_CRCX_FAIL_REJECTED_BY_POLICY] = { "crcx:conn_rejected", "connection rejected by policy." },
[MGCP_CRCX_FAIL_NO_OSMUX] = { "crcx:no_osmux", "no osmux offered by peer." },
[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS] = { "crcx:conn_opt", "connection options invalid." },
[MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = { "crcx:codec_nego", "codec negotiation failure." },
[MGCP_CRCX_FAIL_BIND_PORT] = { "crcx:bind_port", "port bind failure." },
[MGCP_CRCX_FAIL_AVAIL] = { "crcx:unavailable", "endpoint unavailable." },
[MGCP_CRCX_FAIL_CLAIM] = { "crcx:claim", "endpoint can not be claimed." },
};
const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
.group_name_prefix = "crcx",
.group_description = "crxc statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(mgcp_crcx_ctr_desc),
.ctr_desc = mgcp_crcx_ctr_desc
};
static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
[MGCP_MDCX_SUCCESS] = { "mdcx:success", "MDCX command processed successfully." },
[MGCP_MDCX_FAIL_WILDCARD] = { "mdcx:wildcard", "wildcard endpoint names in MDCX commands are unsupported." },
[MGCP_MDCX_FAIL_NO_CONN] = { "mdcx:no_conn", "endpoint specified in MDCX command has no active connections." },
[MGCP_MDCX_FAIL_INVALID_CALLID] = { "mdcx:callid", "invalid CallId specified in MDCX command." },
[MGCP_MDCX_FAIL_INVALID_CONNID] = { "mdcx:connid", "invalid connection ID specified in MDCX command." },
[MGCP_MDCX_FAIL_UNHANDLED_PARAM] = { "crcx:unhandled_param", "unhandled parameter in MDCX command." },
[MGCP_MDCX_FAIL_NO_CONNID] = { "mdcx:no_connid", "no connection ID specified in MDCX command." },
[MGCP_MDCX_FAIL_CONN_NOT_FOUND] =
{ "mdcx:conn_not_found", "connection specified in MDCX command does not exist." },
[MGCP_MDCX_FAIL_INVALID_MODE] = { "mdcx:invalid_mode", "invalid connection mode in MDCX command." },
[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = { "mdcx:conn_opt", "connection options invalid." },
[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] =
{ "mdcx:no_remote_conn_desc", "no opposite end specified for connection." },
[MGCP_MDCX_FAIL_START_RTP] = { "mdcx:start_rtp_failure", "failure to start RTP processing." },
[MGCP_MDCX_FAIL_REJECTED_BY_POLICY] = { "mdcx:conn_rejected", "connection rejected by policy." },
[MGCP_MDCX_DEFERRED_BY_POLICY] = { "mdcx:conn_deferred", "connection deferred by policy." },
[MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." },
};
const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
.group_name_prefix = "mdcx",
.group_description = "mdcx statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(mgcp_mdcx_ctr_desc),
.ctr_desc = mgcp_mdcx_ctr_desc
};
static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
[MGCP_DLCX_SUCCESS] = { "dlcx:success", "DLCX command processed successfully." },
[MGCP_DLCX_FAIL_WILDCARD] = { "dlcx:wildcard", "wildcard names in DLCX commands are unsupported." },
[MGCP_DLCX_FAIL_NO_CONN] = { "dlcx:no_conn", "endpoint specified in DLCX command has no active connections." },
[MGCP_DLCX_FAIL_INVALID_CALLID] =
{ "dlcx:callid", "CallId specified in DLCX command mismatches endpoint's CallId ." },
[MGCP_DLCX_FAIL_INVALID_CONNID] =
{ "dlcx:connid", "connection ID specified in DLCX command does not exist on endpoint." },
[MGCP_DLCX_FAIL_UNHANDLED_PARAM] = { "dlcx:unhandled_param", "unhandled parameter in DLCX command." },
[MGCP_DLCX_FAIL_REJECTED_BY_POLICY] = { "dlcx:rejected", "connection deletion rejected by policy." },
[MGCP_DLCX_DEFERRED_BY_POLICY] = { "dlcx:deferred", "connection deletion deferred by policy." },
[MGCP_DLCX_FAIL_AVAIL] = { "dlcx:unavailable", "endpoint unavailable." },
};
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
};
static const struct rate_ctr_desc e1_rate_ctr_desc[] = {
[E1_I460_TRAU_RX_FAIL_CTR] = {"e1:rx_fail", "Inbound I.460 TRAU failures."},
[E1_I460_TRAU_TX_FAIL_CTR] = {"e1:tx_fail", "Outbound I.460 TRAU failures."},
[E1_I460_TRAU_MUX_EMPTY_CTR] = {"e1:i460", "Outbound I.460 MUX queue empty."}
};
const static struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
.group_name_prefix = "e1",
.group_description = "e1 statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
.num_ctr = ARRAY_SIZE(e1_rate_ctr_desc),
.ctr_desc = e1_rate_ctr_desc
};
const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
.group_name_prefix = "all_rtp_conn",
.group_description = "aggregated statistics for all rtp connections",
.class_id = 1,
.num_ctr = ARRAY_SIZE(all_rtp_conn_rate_ctr_desc),
.ctr_desc = all_rtp_conn_rate_ctr_desc
};
static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group)
{
rate_ctr_group_free(rate_ctr_group);
return 0;
}
/*! allocate global rate counters into a given rate counter struct
* (called once at startup)
* \param[in] ctx talloc context.
* \param[out] ratectr struct that holds the counters
* \returns 0 on success, -EINVAL on failure */
int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr)
{
/* 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 general_rate_ctr_index = 0;
if (ratectr->mgcp_general_ctr_group == NULL) {
ratectr->mgcp_general_ctr_group =
rate_ctr_group_alloc(ctx, &mgcp_general_ctr_group_desc, general_rate_ctr_index);
if (!ratectr->mgcp_general_ctr_group)
return -EINVAL;
talloc_set_destructor(ratectr->mgcp_general_ctr_group, free_rate_counter_group);
general_rate_ctr_index++;
}
return 0;
}
/*! allocate trunk specific rate counters into a given rate counter struct
* (called once on trunk initialization)
* \param[in] ctx talloc context.
* \param[out] ratectr struct that holds the counters
* \returns 0 on success, -EINVAL on failure */
int mgcp_ratectr_trunk_alloc(void *ctx, struct mgcp_ratectr_trunk *ratectr)
{
/* FIXME: see comment in mgcp_ratectr_global_alloc() */
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 (ratectr->mgcp_crcx_ctr_group == NULL) {
ratectr->mgcp_crcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index);
if (!ratectr->mgcp_crcx_ctr_group)
return -EINVAL;
talloc_set_destructor(ratectr->mgcp_crcx_ctr_group, free_rate_counter_group);
crcx_rate_ctr_index++;
}
if (ratectr->mgcp_mdcx_ctr_group == NULL) {
ratectr->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index);
if (!ratectr->mgcp_mdcx_ctr_group)
return -EINVAL;
talloc_set_destructor(ratectr->mgcp_mdcx_ctr_group, free_rate_counter_group);
mdcx_rate_ctr_index++;
}
if (ratectr->mgcp_dlcx_ctr_group == NULL) {
ratectr->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index);
if (!ratectr->mgcp_dlcx_ctr_group)
return -EINVAL;
talloc_set_destructor(ratectr->mgcp_dlcx_ctr_group, free_rate_counter_group);
dlcx_rate_ctr_index++;
}
if (ratectr->all_rtp_conn_stats == NULL) {
ratectr->all_rtp_conn_stats = rate_ctr_group_alloc(ctx, &all_rtp_conn_rate_ctr_group_desc,
all_rtp_conn_rate_ctr_index);
if (!ratectr->all_rtp_conn_stats)
return -EINVAL;
talloc_set_destructor(ratectr->all_rtp_conn_stats, free_rate_counter_group);
all_rtp_conn_rate_ctr_index++;
}
if (ratectr->e1_stats == NULL) {
ratectr->e1_stats = rate_ctr_group_alloc(ctx, &e1_rate_ctr_group_desc, mdcx_rate_ctr_index);
if (!ratectr->e1_stats)
return -EINVAL;
talloc_set_destructor(ratectr->e1_stats, free_rate_counter_group);
mdcx_rate_ctr_index++;
}
return 0;
}

View File

@@ -21,27 +21,17 @@
*/
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
/* 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. */
/* A struct to store intermediate parsing results. The function
* mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP
* codec information. */
struct sdp_rtp_map {
/* the type */
int payload_type;
@@ -53,11 +43,6 @@ 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() */
@@ -138,8 +123,7 @@ static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
char *str;
char *str_ptr;
char *pt_str;
char *pt_end;
unsigned long int pt;
unsigned int pt;
unsigned int count = 0;
unsigned int i;
@@ -164,14 +148,7 @@ static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
if (!pt_str)
break;
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;
pt = atoi(pt_str);
/* Do not allow duplicate payload types */
for (i = 0; i < count; i++)
@@ -190,128 +167,6 @@ 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;
}
static int audio_ip_from_sdp(struct osmo_sockaddr *dst_addr, char *sdp)
{
bool is_ipv6;
char ipbuf[INET6_ADDRSTRLEN];
if (strncmp("c=IN IP", sdp, 7) != 0)
return -1;
sdp += 7;
if (*sdp == '6')
is_ipv6 = true;
else if (*sdp == '4')
is_ipv6 = false;
else
return -1;
sdp++;
if (*sdp != ' ')
return -1;
sdp++;
if (is_ipv6) {
/* 45 = INET6_ADDRSTRLEN -1 */
if (sscanf(sdp, "%45s", ipbuf) != 1)
return -1;
if (inet_pton(AF_INET6, ipbuf, &dst_addr->u.sin6.sin6_addr) != 1)
return -1;
dst_addr->u.sa.sa_family = AF_INET6;
} else {
/* 15 = INET_ADDRSTRLEN -1 */
if (sscanf(sdp, "%15s", ipbuf) != 1)
return -1;
if (inet_pton(AF_INET, ipbuf, &dst_addr->u.sin.sin_addr) != 1)
return -1;
dst_addr->u.sa.sa_family = AF_INET;
}
return 0;
}
/* 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.
@@ -325,10 +180,6 @@ 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 ipbuf[INET6_ADDRSTRLEN];
char *line;
unsigned int i;
void *tmp_ctx = talloc_new(NULL);
@@ -338,6 +189,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
int ptime, ptime2 = 0;
char audio_name[64];
int port, rc;
char ipv4[16];
OSMO_ASSERT(endp);
OSMO_ASSERT(conn);
@@ -355,31 +207,20 @@ 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);
break;
}
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);
} else
if (sscanf
(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
if (ptime2 > 0 && ptime2 != ptime)
rtp->packet_duration_ms = 0;
else
rtp->packet_duration_ms = ptime;
break;
}
if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
} else 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);
@@ -394,16 +235,17 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
codecs_used = rc;
break;
case 'c':
if (audio_ip_from_sdp(&rtp->addr, line) < 0)
return -1;
if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
inet_aton(ipv4, &rtp->addr);
}
break;
default:
if (p->endp)
/* TODO: Check spec: We used the bare endpoint number before,
* now we use the endpoint name as a whole? Is this allowed? */
LOGP(DLMGCP, LOGL_NOTICE,
"Unhandled SDP option: '%c'/%d on %s\n",
line[0], line[0], endp->name);
"Unhandled SDP option: '%c'/%d on 0x%x\n",
line[0], line[0],
ENDPOINT_NUMBER(p->endp));
else
LOGP(DLMGCP, LOGL_NOTICE,
"Unhandled SDP option: '%c'/%d\n",
@@ -420,17 +262,16 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
/* Store parsed codec information */
for (i = 0; i < codecs_used; i++) {
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);
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
if (rc < 0)
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
}
talloc_free(tmp_ctx);
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
LOGP(DLMGCP, LOGL_NOTICE,
"Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
ntohs(rtp->rtp_port), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
rtp->packet_duration_ms);
if (codecs_used == 0)
LOGPC(DLMGCP, LOGL_NOTICE, "none");
@@ -463,12 +304,15 @@ static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name
return 0;
}
/* Add audio strings to sdp payload */
/* Add audio string to sdp payload */
static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
{
int rc;
unsigned int i;
if (payload_types_len < 0)
return -EINVAL;
rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
if (rc < 0)
return -EINVAL;
@@ -486,66 +330,6 @@ 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);
if (rc < 0)
return -EINVAL;
/* 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");
if (rc < 0)
return -EINVAL;
}
return 0;
}
/*! Generate SDP response string.
* \param[in] endp trunk endpoint.
* \param[in] conn associated rtp connection.
@@ -556,17 +340,11 @@ 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;
bool addr_is_v6;
OSMO_ASSERT(endp);
OSMO_ASSERT(conn);
@@ -575,22 +353,16 @@ 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,
&codec, &fmtp_extra,
&payload_type, &audio_name,
&fmtp_extra,
(struct mgcp_conn_rtp *)conn);
audio_name = codec->audio_name;
payload_type = codec->payload_type;
addr_is_v6 = osmo_ip_str_type(addr) == AF_INET6;
rc = msgb_printf(sdp,
"v=0\r\n"
"o=- %s 23 IN IP%c %s\r\n"
"o=- %s 23 IN IP4 %s\r\n"
"s=-\r\n"
"c=IN IP%c %s\r\n"
"t=0 0\r\n", conn->conn->id,
addr_is_v6 ? '6' : '4', addr,
addr_is_v6 ? '6' : '4', addr);
"c=IN IP4 %s\r\n"
"t=0 0\r\n", conn->conn->id, addr, addr);
if (rc < 0)
goto buffer_too_small;
@@ -598,31 +370,24 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
if (payload_type >= 0) {
payload_types[0] = payload_type;
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);
rc = add_audio(sdp, payload_types, 1, conn->end.local_port);
if (rc < 0)
goto buffer_too_small;
if (endp->trunk->audio_send_name) {
if (endp->tcfg->audio_send_name) {
rc = add_rtpmap(sdp, payload_type, audio_name);
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;
if (fmtp_extra) {
rc = msgb_printf(sdp, "%s\r\n", fmtp_extra);
if (rc < 0)
goto buffer_too_small;
}
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->trunk->audio_send_ptime) {
if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
conn->end.packet_duration_ms);
if (rc < 0)
@@ -632,6 +397,6 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
return 0;
buffer_too_small:
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
LOGP(DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
return -1;
}

View File

@@ -22,12 +22,10 @@
*
*/
#include <limits.h>
#include <inttypes.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <limits.h>
#include <inttypes.h>
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)

View File

@@ -1,255 +0,0 @@
/* Trunk handling */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_e1.h>
#include <osmocom/abis/e1_input.h>
/*! allocate trunk and add it (if required) to the trunk list.
* (called once at startup by VTY).
* \param[in] cfg mgcp configuration.
* \param[in] ttype trunk type.
* \param[in] nr trunk number.
* \returns pointer to allocated trunk, NULL on failure. */
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
{
struct mgcp_trunk *trunk;
trunk = talloc_zero(cfg, struct mgcp_trunk);
if (!trunk) {
LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate.\n");
return NULL;
}
trunk->cfg = cfg;
trunk->trunk_type = ttype;
trunk->trunk_nr = nr;
trunk->audio_send_ptime = 1;
trunk->audio_send_name = 1;
trunk->v.vty_number_endpoints = 512;
trunk->omit_rtcp = 0;
mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
llist_add_tail(&trunk->entry, &cfg->trunks);
mgcp_ratectr_trunk_alloc(cfg, &trunk->ratectr);
return trunk;
}
/*! allocate endpoints and set default values
* (called once at startup by VTY).
* \param[in] trunk trunk configuration.
* \returns 0 on success, -1 on failure. */
int mgcp_trunk_alloc_endpts(struct mgcp_trunk *trunk)
{
int i;
struct mgcp_endpoint *endp;
unsigned int number_endpoints;
unsigned int first_endpoint_nr;
/* This function is called once on startup by the VTY to allocate the
* endpoints. The number of endpoints must not change througout the
* runtime of the MGW */
OSMO_ASSERT(trunk->number_endpoints == 0);
OSMO_ASSERT(trunk->endpoints == NULL);
switch (trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
/* Due to historical reasons the endpoints on the virtual
* trunk start counting at 1. */
first_endpoint_nr = 1;
number_endpoints = trunk->v.vty_number_endpoints;
break;
case MGCP_TRUNK_E1:
/* The first timeslot on an E1 line is reserved for framing
* and alignment and can not be used for audio transport */
first_endpoint_nr = 1 * MGCP_ENDP_E1_SUBSLOTS;
number_endpoints = (NUM_E1_TS-1) * MGCP_ENDP_E1_SUBSLOTS;
break;
default:
OSMO_ASSERT(false);
}
/* Make sure the amount of requested endpoints does not execeed
* sane limits. The VTY already limits the possible amount,
* however miss-initialization of the struct or memory corruption
* could still lead to an excessive allocation of endpoints, so
* better stop early if that is the case. */
OSMO_ASSERT(number_endpoints < 65534);
/* allocate pointer array for the endpoints */
trunk->endpoints = talloc_zero_array(trunk->cfg, struct mgcp_endpoint*,
number_endpoints);
if (!trunk->endpoints)
return -1;
/* create endpoints */
for (i = 0; i < number_endpoints; i++) {
endp = mgcp_endp_alloc(trunk, i + first_endpoint_nr);
if (!endp) {
talloc_free(trunk->endpoints);
return -1;
}
trunk->endpoints[i] = endp;
}
/* make the endpoints we just created available to the MGW code */
trunk->number_endpoints = number_endpoints;
return 0;
}
/*! Equip trunk with endpoints and resources
* (called once at startup by VTY).
* \param[in] trunk trunk configuration.
* \returns 0 on success, -1 on failure. */
int mgcp_trunk_equip(struct mgcp_trunk *trunk)
{
unsigned int i;
/* Allocate endpoints */
if(mgcp_trunk_alloc_endpts(trunk) != 0)
return -1;
/* Allocate resources */
switch (trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
/* No additional initaliziation required here, virtual
* endpoints will open/close network sockets themselves
* on demand. */
break;
case MGCP_TRUNK_E1:
/* The TS initalization happens once on startup for all
* timeslots. This only affects the i460 multiplexer. Until
* now no E1 resources are claimed yet. This happens on demand
* when the related endpoint is actually used */
memset(trunk->e1.i460_ts, 0, sizeof(trunk->e1.i460_ts));
for (i = 0; i < (NUM_E1_TS-1); i++)
osmo_i460_ts_init(&trunk->e1.i460_ts[i]);
break;
default:
OSMO_ASSERT(false);
}
return 0;
}
/*! get trunk configuration by trunk number (index).
* \param[in] cfg mgcp configuration.
* \param[in] ttype trunk type.
* \param[in] nr trunk number.
* \returns pointer to trunk configuration, NULL on error. */
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
{
struct mgcp_trunk *trunk;
llist_for_each_entry(trunk, &cfg->trunks, entry) {
if (trunk->trunk_nr == nr && trunk->trunk_type == ttype)
return trunk;
}
return NULL;
}
/* Made public for unit-testing, do not use from outside this file */
int e1_trunk_nr_from_epname(const char *epname)
{
unsigned long int trunk_nr;
size_t prefix_len;
char *str_trunk_nr_end;
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
if (strncmp(epname, MGCP_ENDPOINT_PREFIX_E1_TRUNK, prefix_len) != 0)
return -EINVAL;
errno = 0;
trunk_nr = strtoul(epname + prefix_len, &str_trunk_nr_end, 10);
if (errno == ERANGE || trunk_nr > 64
|| epname + prefix_len == str_trunk_nr_end
|| str_trunk_nr_end[0] != '/')
return -EINVAL;
else
return trunk_nr;
}
/*! Find a trunk by the trunk prefix in the endpoint name.
* \param[in] epname endpoint name with trunk prefix to look up.
* \param[in] cfg that contains the trunks where the endpoint is located.
* \returns trunk or NULL if trunk was not found. */
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname)
{
size_t prefix_len;
char epname_lc[MGCP_ENDPOINT_MAXLEN];
int trunk_nr;
osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
epname = epname_lc;
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, prefix_len) == 0) {
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
}
trunk_nr = e1_trunk_nr_from_epname(epname);
if (trunk_nr >= 0)
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_E1, trunk_nr);
/* Earlier versions of osmo-mgw were accepting endpoint names
* without trunk prefix. This is normally not allowed, each MGCP
* request should supply an endpoint name with trunk prefix.
* However in order to stay compatible with old versions of
* osmo-bsc and osmo-msc we still accept endpoint names without
* trunk prefix and just assume that the virtual trunk should
* be selected. There is even a TTCN3 test for this, see also:
* MGCP_Test.TC_crcx_noprefix */
if ((epname[0] >= '0' && epname[0] <= '9') || (epname[0] >= 'a' && epname[0] <= 'f')) {
LOGP(DLMGCP, LOGL_ERROR, "missing trunk prefix in endpoint name \"%s\", assuming trunk \"%s\"!\n", epname,
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
}
LOGP(DLMGCP, LOGL_ERROR, "unable to find trunk for endpoint name \"%s\"!\n", epname);
return NULL;
}
/*! Find a trunk (E1) by its associated E1 line number.
* \param[in] num e1 line number.
* \returns trunk or NULL if trunk was not found. */
struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num)
{
/*! When used on trunks other than E1, the result will always be NULL. */
struct mgcp_trunk *trunk;
llist_for_each_entry(trunk, &cfg->trunks, entry) {
if (trunk->trunk_type == MGCP_TRUNK_E1 && trunk->e1.vty_line_nr == num)
return trunk;
}
return NULL;
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,10 +9,7 @@ AM_CFLAGS = \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(LIBOSMOTRAU_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -29,8 +26,5 @@ osmo_mgw_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(NULL)

View File

@@ -30,18 +30,14 @@
#include <limits.h>
#include <unistd.h>
#include <errno.h>
#include <osmocom/core/msgb.h>
#include <osmocom/abis/e1_input.h>
#include <sys/socket.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/vty.h>
#include <osmocom/mgcp/debug.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_ctrl.h>
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
@@ -52,32 +48,22 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/socket.h>
#include <osmocom/ctrl/control_vty.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/ports.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/misc.h>
#include <osmocom/vty/cpu_sched_vty.h>
#include <osmocom/abis/abis.h>
#include "../../bscconfig.h"
#define _GNU_SOURCE
#include <getopt.h>
/* can be changed once libosmocore 1.4.0 is released */
#ifndef OSMO_CTRL_PORT_MGW
#define OSMO_CTRL_PORT_MGW 4267
#endif
/* FIXME: Make use of the rtp proxy code */
static struct mgcp_config *cfg;
static struct mgcp_trunk *reset_trunk;
static struct mgcp_trunk_config *reset_trunk;
static int reset_endpoints = 0;
static int daemonize = 0;
@@ -93,60 +79,28 @@ const char *osmomgw_copyright =
static char *config_file = "osmo-mgw.cfg";
/* used by msgb and mgcp */
void *tall_mgw_ctx = NULL;
void *tall_bsc_ctx = NULL;
static void print_help()
{
printf("Some useful options:\n");
printf("Some useful help...\n");
printf(" -h --help is printing this text.\n");
printf(" -c --config-file filename The config file to use.\n");
printf(" -s --disable-color\n");
printf(" -D --daemonize Fork the process into a background daemon\n");
printf(" -V --version Print the version number\n");
printf("\nVTY reference generation:\n");
printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n");
printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
}
static void handle_long_options(const char *prog_name, const int long_option)
{
static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
switch (long_option) {
case 1:
vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
if (vty_ref_mode < 0) {
fprintf(stderr, "%s: Unknown VTY reference generation "
"mode '%s'\n", prog_name, optarg);
exit(2);
}
break;
case 2:
fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
exit(0);
default:
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
exit(2);
}
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static int long_option = 0;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"config-file", 1, 0, 'c'},
{"daemonize", 0, 0, 'D'},
{"version", 0, 0, 'V'},
{"disable-color", 0, 0, 's'},
{"vty-ref-mode", 1, &long_option, 1},
{"vty-ref-xml", 0, &long_option, 2},
{0, 0, 0, 0},
};
@@ -160,11 +114,8 @@ static void handle_options(int argc, char **argv)
print_help();
exit(0);
break;
case 0:
handle_long_options(argv[0], long_option);
break;
case 'c':
config_file = talloc_strdup(tall_mgw_ctx, optarg);
config_file = talloc_strdup(tall_bsc_ctx, optarg);
break;
case 's':
log_set_use_color(osmo_stderr_target, 0);
@@ -181,28 +132,24 @@ static void handle_options(int argc, char **argv)
break;
};
}
if (argc > optind) {
fprintf(stderr, "Unsupported positional arguments on command line\n");
exit(2);
}
}
/* Callback function to be called when the RSIP ("Reset in Progress") mgcp
* command is received */
static int mgcp_rsip_cb(struct mgcp_trunk *trunk)
static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
{
/* Set flag so that, when read_call_agent() is called next time
* the reset can progress */
reset_endpoints = 1;
reset_trunk = trunk;
reset_trunk = tcfg;
return 0;
}
static int read_call_agent(struct osmo_fd *fd, unsigned int what)
{
struct osmo_sockaddr addr;
struct sockaddr_in addr;
socklen_t slen = sizeof(addr);
struct msgb *msg;
struct msgb *resp;
@@ -228,7 +175,7 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
msgb_reset(msg);
if (resp) {
sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, &addr.u.sa, sizeof(addr));
sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
msgb_free(resp);
}
@@ -244,7 +191,7 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
/* Walk over all endpoints and trigger a release, this will release all
* endpoints, possible open connections are forcefully dropped */
for (i = 1; i < reset_trunk->number_endpoints; ++i)
mgcp_endp_release(reset_trunk->endpoints[i]);
mgcp_endp_release(&reset_trunk->endpoints[i]);
}
return 0;
@@ -296,13 +243,7 @@ static const struct log_info_cat log_categories[] = {
.description = "RTP stream handling",
.color = "\033[1;30m",
.enabled = 1,.loglevel = LOGL_NOTICE,
},
[DE1] = {
.name = "DE1",
.description = "E1 line handling",
.color = "\033[1;31m",
.enabled = 1,.loglevel = LOGL_NOTICE,
},
},
};
const struct log_info log_info = {
@@ -315,54 +256,40 @@ int main(int argc, char **argv)
unsigned int flags;
int rc;
tall_mgw_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
vty_info.tall_ctx = tall_mgw_ctx;
tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
vty_info.tall_ctx = tall_bsc_ctx;
msgb_talloc_ctx_init(tall_mgw_ctx, 0);
msgb_talloc_ctx_init(tall_bsc_ctx, 0);
osmo_init_ignore_signals();
osmo_init_logging2(tall_mgw_ctx, &log_info);
libosmo_abis_init(tall_mgw_ctx);
osmo_init_logging2(tall_bsc_ctx, &log_info);
cfg = mgcp_config_alloc();
if (!cfg)
return -1;
vty_info.copyright = osmomgw_copyright;
vty_info.usr_attr_desc[MGW_CMD_ATTR_NEWCONN] = \
"This command applies when a new connection is created";
vty_info.usr_attr_letters[MGW_CMD_ATTR_NEWCONN] = 'n';
vty_init(&vty_info);
logging_vty_add_cmds();
logging_vty_add_cmds(NULL);
osmo_talloc_vty_add_cmds();
osmo_stats_vty_add_cmds();
osmo_stats_vty_add_cmds(&log_info);
mgcp_vty_init();
ctrl_vty_init(cfg);
e1inp_vty_init();
osmo_cpu_sched_vty_init(tall_mgw_ctx);
handle_options(argc, argv);
rate_ctr_init(tall_mgw_ctx);
osmo_stats_init(tall_mgw_ctx);
rate_ctr_init(tall_bsc_ctx);
osmo_stats_init(tall_bsc_ctx);
rc = mgcp_parse_config(config_file, cfg, MGCP_BSC);
if (rc < 0)
return rc;
/* start telnet after reading config for vty_get_bind_addr() */
rc = telnet_init_dynif(tall_mgw_ctx, NULL,
rc = telnet_init_dynif(tall_bsc_ctx, NULL,
vty_get_bind_addr(), OSMO_VTY_PORT_MGW);
if (rc < 0)
return rc;
cfg->ctrl = mgw_ctrl_interface_setup(cfg, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
if (!cfg->ctrl) {
fprintf(stderr, "Failed to init the control interface on %s:%u. Exiting\n",
ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
}
/* Set the reset callback function. This functions is called when the
* mgcp-command "RSIP" (Reset in Progress) is received */
cfg->reset_cb = mgcp_rsip_cb;
@@ -372,7 +299,7 @@ int main(int argc, char **argv)
if (cfg->call_agent_addr)
flags |= OSMO_SOCK_F_CONNECT;
rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
cfg->source_addr, cfg->source_port,
cfg->call_agent_addr, cfg->call_agent_addr ? 2727 : 0, flags);
if (rc < 0) {

View File

@@ -11,8 +11,6 @@ AM_CFLAGS = \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(LIBOSMOTRAU_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -37,10 +35,7 @@ mgcp_test_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(LIBRARY_DL) \
$(LIBRARY_DLSYM) \
$(LIBOSMONETIF_LIBS) \
-lm \
$(NULL)

File diff suppressed because it is too large Load Diff

View File

@@ -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
@@ -192,35 +192,11 @@ Response matches our expectations.
---------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_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:
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
================================================
Testing MDCX4_SO
creating message from statically defined input:
---------8<---------
@@ -243,7 +219,7 @@ Response matches our expectations.
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
(response contains a connection id)
================================================
Testing MDCX4_RO
creating message from statically defined input:
@@ -383,7 +359,7 @@ Testing CRCX
I: %s
C: 2
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
@@ -470,7 +446,7 @@ Dummy packets: 2
a=rtpmap:97 GSM-EFR/8000
a=ptime:40
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
@@ -479,36 +455,12 @@ using message as statically defined for comparison
================================================
Testing MDCX_TOO_LONG_CI
Testing MDCX_TOO_LONG_CI
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
creating message from statically defined input:
---------8<---------
MDCX 18983222 1@mgw MGCP 1.0
I: 123456789012345678901234567890123
---------8<---------
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
@@ -526,7 +478,7 @@ Re-transmitting CRCX
v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:40
---------8<---------
@@ -638,7 +590,7 @@ Testing packet loss calculation.
creating message from statically defined input:
---------8<---------
DLCX 7 1@mgw MGCP 1.0
DLCX 7 1@mgw MGCP 1.0
I: %s
C: 2
---------8<---------
@@ -1189,7 +1141,7 @@ Testing no rtpmap name
a=rtpmap:96 iLBC/8000
a=fmtp:96 mode=20
a=rtpmap:97 iLBC/8000
a=rtpmap:97 iLBC/8000
a=fmtp:97 mode=30
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=recvonly
@@ -1218,148 +1170,6 @@ p:10, a:PCMU -> p:10, a:PCMU
p:10, a:PCMU -> p:10, a:PCMU
p:10, a:PCMU -> p:10, a:PCMU
'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU'
'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU'
'XXXX,p:10,a:PCMU' -> 'p:10,a:PCMU'
'10,a:PCMU' -> 'a:PCMU'
'10, a:PCMU' -> 'a:PCMU'
'10,a: PCMU' -> 'a: PCMU'
'10 ,a: PCMU' -> 'a: PCMU'
', a:PCMU' -> 'a:PCMU'
' a:PCMU' -> 'a:PCMU'
'' -> '(null)'
p10, aPCMU -> (null)
'10,a :PCMU' -> '(null)'
Testing mgcp_codec_pt_translate()
#0: same order, but differing payload type numbers
- add codecs on conn0:
0: 112 AMR/8000/1 octet-aligned=1 -> rc=0
1: 0 PCMU/8000/1 -> rc=0
2: 111 GSM-HR-08/8000/1 -> rc=0
- add codecs on conn1:
0: 96 AMR/8000/1 octet-aligned=1 -> rc=0
1: 0 PCMU/8000/1 -> rc=0
2: 97 GSM-HR-08/8000/1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
#1: different order and different payload type numbers
- add codecs on conn0:
0: 0 PCMU/8000/1 -> rc=0
1: 111 GSM-HR-08/8000/1 -> rc=0
2: 112 AMR/8000/1 octet-aligned=1 -> rc=0
- add codecs on conn1:
0: 97 GSM-HR-08/8000/1 -> rc=0
1: 0 PCMU/8000/1 -> rc=0
2: 96 AMR/8000/1 octet-aligned=1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
#2: both sides have the same payload_type numbers assigned to differing codecs
- add codecs on conn0:
0: 0 PCMU/8000/1 -> rc=0
1: 96 GSM-HR-08/8000/1 -> rc=0
2: 97 AMR/8000/1 octet-aligned=1 -> rc=0
- add codecs on conn1:
0: 97 GSM-HR-08/8000/1 -> rc=0
1: 0 PCMU/8000/1 -> rc=0
2: 96 AMR/8000/1 octet-aligned=1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 96) -> 97
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 96
- mgcp_codec_pt_translate(conn0, conn1, 97) -> 96
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 97
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
#3: conn0 has no codecs
- add codecs on conn0:
(none)
- add codecs on conn1:
0: 96 AMR/8000/1 octet-aligned=1 -> rc=0
1: 0 PCMU/8000/1 -> rc=0
2: 97 GSM-HR-08/8000/1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
#4: conn1 has no codecs
- add codecs on conn0:
0: 112 AMR/8000/1 octet-aligned=1 -> rc=0
1: 0 PCMU/8000/1 -> rc=0
2: 111 GSM-HR-08/8000/1 -> rc=0
- add codecs on conn1:
(none)
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
#5: test AMR with differing octet-aligned settings
- add codecs on conn0:
0: 111 AMR/8000 octet-aligned=1 -> rc=0
1: 112 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=0 -> rc=0
1: 121 AMR/8000 octet-aligned=1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 121
- mgcp_codec_pt_translate(conn1, conn0, 121) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
#6: test AMR with missing octet-aligned settings (defaults to 0)
- add codecs on conn0:
0: 111 AMR/8000 octet-aligned=1 -> rc=0
1: 112 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=unset -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
#7: test AMR with NULL param (defaults to 0)
- add codecs on conn0:
0: 111 AMR/8000 octet-aligned=1 -> rc=0
1: 112 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
#8: match FOO/8000/1 and FOO/8000 as identical, single channel is implicit
- add codecs on conn0:
0: 0 PCMU/8000/1 -> rc=0
1: 111 GSM-HR-08/8000/1 -> rc=0
2: 112 AMR/8000/1 octet-aligned=1 -> rc=0
- add codecs on conn1:
0: 97 GSM-HR-08/8000 -> rc=0
1: 0 PCMU/8000 -> rc=0
2: 96 AMR/8000 octet-aligned=1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
#9: match FOO/8000/1 and FOO as identical, 8k and single channel are implicit
- add codecs on conn0:
0: 0 PCMU/8000/1 -> rc=0
1: 111 GSM-HR-08/8000/1 -> rc=0
2: 112 AMR/8000/1 octet-aligned=1 -> rc=0
- add codecs on conn1:
0: 97 GSM-HR-08 -> rc=0
1: 0 PCMU -> rc=0
2: 96 AMR octet-aligned=1 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
#10: test whether channel number matching is waterproof
'XXXX,p:10,a:PCMU' -> 'p:10,a:PCMU'
'10,a:PCMU' -> 'a:PCMU'
'10, a:PCMU' -> 'a:PCMU'

View File

@@ -35,7 +35,6 @@ mgcp_client_test_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBRARY_DL) \
$(LIBRARY_DLSYM) \
$(LIBOSMONETIF_LIBS) \
$(NULL)

View File

@@ -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)
@@ -157,7 +157,6 @@ 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)
@@ -181,7 +180,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");
@@ -191,8 +190,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;
@@ -211,7 +210,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");
@@ -222,8 +221,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;
@@ -255,38 +254,6 @@ 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 =
@@ -294,23 +261,11 @@ void test_mgcp_msg(void)
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
memset(audio_ip_overflow, 'X', sizeof(audio_ip_overflow));
audio_ip_overflow[1] = '.';
audio_ip_overflow[sizeof(audio_ip_overflow) - 1] = '\0';
mgcp_msg.audio_ip = audio_ip_overflow;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
OSMO_ASSERT(msg == NULL);
printf("IPv6 test:\n");
mgcp_msg.verb = MGCP_VERB_MDCX;
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
mgcp_msg.audio_ip = "2001:db8:1::ab9:c0a8:102";
mgcp->actual.remote_addr = "::1";
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", (char *)msg->data);
printf("\n");
msgb_free(msg);
}
@@ -331,7 +286,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__);
@@ -372,7 +327,7 @@ struct sdp_section_start_test {
static struct sdp_section_start_test sdp_section_start_tests[] = {
{
.body = "",
.expect_rc = 0,
.expect_rc = -EINVAL,
},
{
.body = "\n\n",
@@ -411,79 +366,19 @@ static struct sdp_section_start_test sdp_section_start_tests[] = {
.body = "some mgcp header data\r\nand header params"
"\n\r\n"
"m=audio 23\r\n",
.expect_rc = 0,
.expect_rc = -EINVAL,
},
{
.body = "some mgcp header data\r\nand header params"
"\r\n\r"
"m=audio 23\r\n",
.expect_rc = 0,
.expect_rc = -EINVAL,
},
{
.body = "some mgcp header data\r\nand header params"
"\n\r\r"
"m=audio 23\r\n",
.expect_rc = 0,
},
{
.body = "some mgcp header data\r\nand header params"
"\r\n\r\n"
"c=IN IP4 1.2.3.4\r\n",
.expect_params = {
.audio_ip = "1.2.3.4",
},
.expect_rc = 0,
},
{
.body = "some mgcp header data\r\nand header params"
"\r\n\r\n"
"c=IN IP6 2001:db8:1::ab9:c0a8:102\r\n",
.expect_params = {
.audio_ip = "2001:db8:1::ab9:c0a8:102",
},
.expect_rc = 0,
},
{
.body = "some mgcp header data\r\nand header params"
"\r\n\r\n"
"c=IN IP6 1.2.3.4\r\n",
.expect_rc = -22,
},
{
.body = "some mgcp header data\r\nand header params"
"\r\n\r\n"
"c=IN IP4 ::1\r\n",
.expect_rc = -22,
},
{
.body = "some mgcp header data\r\nand header params"
"\r\n\r\n"
"c=IN IP4 notanip\r\n",
.expect_rc = -22,
},
{
.body = "some mgcp header data\r\nand header params"
"\r\n\r\n"
"c=IN IP4 1.2.3.4.5.6\r\n",
.expect_rc = -22,
},
{
.body = "some mgcp header data\r\nand header params"
"\r\n\r\n"
"c=IN IP4 1.2 .3\r\n",
.expect_rc = -22,
},
{
.body = "some mgcp header data\r\nand header params"
"\r\n\r\n"
"c=IN IP4 1.2 .3\r\n",
.expect_rc = -22,
},
{
.body = "some mgcp header data\r\nand header params"
"\r\n\r\n"
"c=IN IP4 \r\n",
.expect_rc = -22,
.expect_rc = -EINVAL,
},
};
@@ -515,12 +410,7 @@ void test_sdp_section_start()
continue;
}
fprintf(stderr, "got audio_ip=\"%s\"\n", r->audio_ip);
if (strcmp(r->audio_ip, t->expect_params.audio_ip)) {
fprintf(stderr, "FAIL: Expected audio_ip=\"%s\"\n", t->expect_params.audio_ip);
failures++;
}
fprintf(stderr, "got audio_port=%u\n", r->audio_port);
fprintf(stderr, "got audio_port=%u\n", t->expect_params.audio_port);
if (r->audio_port != t->expect_params.audio_port) {
fprintf(stderr, "FAIL: Expected audio_port=%u\n", t->expect_params.audio_port);
failures++;
@@ -624,57 +514,6 @@ static void test_map_codec_to_pt_and_map_pt_to_codec(void)
printf("\n");
}
void test_mgcp_client_e1_epname(void)
{
char *epname;
if (mgcp)
talloc_free(mgcp);
mgcp = mgcp_client_init(ctx, &conf);
/* Valid endpoint names */
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 1, 15, 64, 0);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 2, 14, 32, 0);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 3, 13, 32, 4);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 4, 12, 16, 0);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 5, 11, 16, 2);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 6, 10, 16, 4);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 7, 9, 16, 6);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 8, 8, 8, 0);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 9, 7, 8, 1);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 10, 6, 8, 2);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 11, 5, 8, 3);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 12, 4, 8, 4);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 13, 3, 8, 5);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 14, 2, 8, 6);
printf("%s\n", epname);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 1, 8, 7);
printf("%s\n", epname);
/* A few invalid enpoint names */
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 1, 128, 0);
OSMO_ASSERT(epname == NULL);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 1, 8, 16);
OSMO_ASSERT(epname == NULL);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 0, 8, 2);
OSMO_ASSERT(epname == NULL);
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 64, 8, 2);
OSMO_ASSERT(epname == NULL);
}
static const struct log_info_cat log_categories[] = {
};
@@ -689,10 +528,9 @@ int main(int argc, char **argv)
ctx = talloc_named_const(NULL, 1, "mgcp_client_test");
msgb_talloc_ctx_init(ctx, 0);
osmo_init_logging2(ctx, &log_info);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_filename(osmo_stderr_target, 0);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
log_set_category_filter(osmo_stderr_target, DLMGCP, 1, LOGL_DEBUG);
@@ -704,7 +542,6 @@ int main(int argc, char **argv)
test_sdp_section_start();
test_map_codec_to_pt_and_map_pt_to_codec();
test_map_pt_to_codec();
test_mgcp_client_e1_epname();
printf("Done\n");
fprintf(stderr, "Done\n");

View File

@@ -17,121 +17,55 @@ test_mgcp_client_cancel() done
test_sdp_section_start() test [0]:
body: ""
DLMGCP MGCP response contains no SDP parameters
got rc=0
got audio_ip=""
got audio_port=0
DLMGCP MGCP response: cannot find start of SDP parameters
got rc=-22
test_sdp_section_start() test [1]:
body: "\n\n"
got rc=0
got audio_ip=""
got audio_port=0
test_sdp_section_start() test [2]:
body: "\r\n\r\n"
got rc=0
got audio_ip=""
got audio_port=0
test_sdp_section_start() test [3]:
body: "\n\r\n\r"
got rc=0
got audio_ip=""
got audio_port=0
test_sdp_section_start() test [4]:
body: "some mgcp header data\r\nand header params\n\nm=audio 23\r\n"
got rc=0
got audio_ip=""
got audio_port=23
test_sdp_section_start() test [5]:
body: "some mgcp header data\r\nand header params\r\n\r\nm=audio 23\r\n"
got rc=0
got audio_ip=""
got audio_port=23
test_sdp_section_start() test [6]:
body: "some mgcp header data\r\nand header params\n\r\n\rm=audio 23\r\n"
got rc=0
got audio_ip=""
got audio_port=23
test_sdp_section_start() test [7]:
body: "some mgcp header data\r\nand header params\n\r\nm=audio 23\r\n"
DLMGCP MGCP response contains no SDP parameters
got rc=0
got audio_ip=""
got audio_port=0
DLMGCP MGCP response: cannot find start of SDP parameters
got rc=-22
test_sdp_section_start() test [8]:
body: "some mgcp header data\r\nand header params\r\n\rm=audio 23\r\n"
DLMGCP MGCP response contains no SDP parameters
got rc=0
got audio_ip=""
got audio_port=0
DLMGCP MGCP response: cannot find start of SDP parameters
got rc=-22
test_sdp_section_start() test [9]:
body: "some mgcp header data\r\nand header params\n\r\rm=audio 23\r\n"
DLMGCP MGCP response contains no SDP parameters
got rc=0
got audio_ip=""
got audio_port=0
test_sdp_section_start() test [10]:
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 1.2.3.4\r\n"
got rc=0
got audio_ip="1.2.3.4"
got audio_port=0
test_sdp_section_start() test [11]:
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP6 2001:db8:1::ab9:c0a8:102\r\n"
got rc=0
got audio_ip="2001:db8:1::ab9:c0a8:102"
got audio_port=0
test_sdp_section_start() test [12]:
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP6 1.2.3.4\r\n"
DLMGCP Failed to parse MGCP response header (audio ip)
got rc=-22
test_sdp_section_start() test [13]:
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 ::1\r\n"
DLMGCP Failed to parse MGCP response header (audio ip)
got rc=-22
test_sdp_section_start() test [14]:
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 notanip\r\n"
DLMGCP Failed to parse MGCP response header (audio ip)
got rc=-22
test_sdp_section_start() test [15]:
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 1.2.3.4.5.6\r\n"
DLMGCP Failed to parse MGCP response header (audio ip)
got rc=-22
test_sdp_section_start() test [16]:
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 1.2 .3\r\n"
DLMGCP Failed to parse MGCP response header (audio ip)
got rc=-22
test_sdp_section_start() test [17]:
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 1.2 .3\r\n"
DLMGCP Failed to parse MGCP response header (audio ip)
got rc=-22
test_sdp_section_start() test [18]:
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 \r\n"
DLMGCP Failed to parse MGCP response header (audio ip)
DLMGCP MGCP response: cannot find start of SDP parameters
got rc=-22
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
DLMGCP MGCP client: using endpoint domain '@mgw'
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw), rate(128)/offset(0) combination is invalid!
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-0/su8-2@mgw), E1-timeslot number (0) is invalid!
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-64/su8-2@mgw), E1-timeslot number (64) is invalid!
Done

View File

@@ -84,45 +84,7 @@ 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:
IPv6 test:
MDCX 19 23@mgw MGCP 1.0
C: 2f
I: 11
M: sendrecv
v=0
o=- 2f 23 IN IP6 ::1
s=-
c=IN IP6 2001:db8:1::ab9:c0a8:102
t=0 0
m=audio 1234 RTP/AVP 3
a=ptime:20
test_mgcp_client_cancel():
@@ -163,24 +125,6 @@ test_sdp_section_start() test [7]:
test_sdp_section_start() test [8]:
test_sdp_section_start() test [9]:
test_sdp_section_start() test [10]:
test_sdp_section_start() test [11]:
test_sdp_section_start() test [12]:
test_sdp_section_start() test [13]:
test_sdp_section_start() test [14]:
test_sdp_section_start() test [15]:
test_sdp_section_start() test [16]:
test_sdp_section_start() test [17]:
test_sdp_section_start() test [18]:
110 => 96
111 => 97
112 => 98
@@ -210,19 +154,4 @@ test_sdp_section_start() test [18]:
2 <= 2
100 <= 100
ds/e1-1/s-15/su64-0@mgw
ds/e1-2/s-14/su32-0@mgw
ds/e1-3/s-13/su32-4@mgw
ds/e1-4/s-12/su16-0@mgw
ds/e1-5/s-11/su16-2@mgw
ds/e1-6/s-10/su16-4@mgw
ds/e1-7/s-9/su16-6@mgw
ds/e1-8/s-8/su8-0@mgw
ds/e1-9/s-7/su8-1@mgw
ds/e1-10/s-6/su8-2@mgw
ds/e1-11/s-5/su8-3@mgw
ds/e1-12/s-4/su8-4@mgw
ds/e1-13/s-3/su8-5@mgw
ds/e1-14/s-2/su8-6@mgw
ds/e1-15/s-1/su8-7@mgw
Done