Compare commits

..

2 Commits

Author SHA1 Message Date
Pau Espin Pedrol
49f3e6a6ab Bump version: 1.10.0.1-fffe → 1.10.1
Change-Id: I727c89731369cc4f1a15b4f6ee9c85ba6b14536a
2023-03-10 16:04:05 +01:00
Pau Espin Pedrol
fffe261076 mgcp_network: Unregister osmo_fd before closing fd
(cherry picked from commit 6c303664f5)

Change-Id: I2f58dc825de1604fb464858c9cb3b87d029cb4ad
2023-03-10 16:02:12 +01:00
94 changed files with 2517 additions and 9858 deletions

View File

@@ -24,7 +24,7 @@ pkgconfig_DATA = \
BUILT_SOURCES = $(top_srcdir)/.version
EXTRA_DIST = \
.version \
README.md \
contrib/osmo-mgw.spec.in \
debian \
git-version-gen \
osmoappdesc.py \

24
README Normal file
View File

@@ -0,0 +1,24 @@
About OsmoMGW
=============
OsmoMGW originated from the OpenBSC project, which started as a minimalistic
all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached
maturity and diversity (including M3UA SIGTRAN and 3G support in the form of
IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one
approach to fully independent separate programs as in typical GSM networks.
OsmoMGW was one of the parts split off from the old openbsc.git. It originated
as a solution to merely navigate RTP streams through a NAT, but has since
matured to a Media Gateway implementation that is capable of streaming RTP for
2G (AoIP) and 3G (IuCS) GSM networks as well as (still not implemented at time
of writing) transcoding between TRAU, various RTP payloads and IuUP.
The OsmoMGW program exposes an MGCP interface towards clients like OsmoMSC and
OsmoBSC, and receives and sends RTP streams as configured via MGCP.
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC (found
in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
Find OsmoMGW issue tracker and wiki online at
https://osmocom.org/projects/osmo-mgw
https://osmocom.org/projects/osmo-mgw/wiki

102
README.md
View File

@@ -1,102 +0,0 @@
osmo-mgw - Osmocom MGW (Media GateWay) Implementation
=====================================================
This repository contains a C-language implementation of an MGW (Media
GateWay) for use [not only] within the 2G (GSM) and/or 3G (UMTS)
Cellular Network built using Osmocom CNI (Cellular Network
Infrastructure) software.
The OsmoMGW program provides an MGCP interface towards an MGCP call agent
(client) like OsmoMSC and OsmoBSC, and receives and sends RTP streams as
configured via the MGCP control plane.
This Media Gateway implementation is capable of
* streaming RTP for 2G (3GPP AoIP and Abis-over-IP)
* streaming RTP for 3G (IuCS including the IuFP protocol)
* TDM (E1/T1) based Abis interface with TRAU frames on 16k sub-slots
* basic support for LCLS (Local Call, Local Switch) related features
* various built-in translation capabilities
* between Abis TRAU frames and RTP formats
* between 2G AMR/RTP and 3G AMR/IuFP/RTP
* between bandwidth-efficient and octet-aligned AMR
* between different standards for encapsulating GSM HR codec frames in RTP
osmo-mgw is typically co-located with
* osmo-bsc (GSM BSC)
* osmo-msc (GSM/UMTS MSC)
* osmo-hnbgw (UMTS HNBGW); osmo-mgw implements RTP relay between Iuh
and IuCS interfaces
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC
(found in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
Homepage
--------
You can find the OsmoMGW issue tracker and wiki online at
<https://osmocom.org/projects/osmo-mgw> and <https://osmocom.org/projects/osmo-mgw/wiki>.
GIT Repository
--------------
You can clone from the official osmo-mgw.git repository using
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw>
Documentation
-------------
User Manuals and VTY reference manuals are [optionally] built in PDF form
as part of the build process.
Pre-rendered PDF version of the current "master" can be found at
[User Manual](https://ftp.osmocom.org/docs/latest/osmomgw-usermanual.pdf)
as well as the [VTY Reference Manual](https://ftp.osmocom.org/docs/latest/osmomgw-vty-reference.pdf)
Mailing List
------------
Discussions related to osmo-mgw are happening on the
openbsc@lists.osmocom.org mailing list, please see
<https://lists.osmocom.org/mailman/listinfo/openbsc> for subscription
options and the list archive.
Please observe the [Osmocom Mailing List
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
when posting.
Contributing
------------
Our coding standards are described at
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
We use a gerrit based patch submission/review process for managing
contributions. Please see
<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> for
more details
The current patch queue for osmo-mgw can be seen at
<https://gerrit.osmocom.org/#/q/project:osmo-mgw+status:open>
History
-------
OsmoMGW originated from the OpenBSC project, which started as a minimalistic
all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached
maturity and diversity (including M3UA SIGTRAN and 3G support in the form of
IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one
approach to fully independent separate programs as in typical GSM networks.
OsmoMGW was one of the parts split off from the old openbsc.git. It originated
as a solution to merely navigate RTP streams through a NAT, but has since
matured.

View File

@@ -24,20 +24,3 @@
# If any interfaces have been removed or changed since the last public release, a=0.
#
#library what description / commit summary line
libosmo-mgcp-client remove public API These public API items have not been called by any of our osmo-programs
for many years. Any sane caller of libosmo-mgcp-client should use the
higher level osmo_mgcpc_* API instead. Move these to a private header:
struct mgcp_response_head
struct mgcp_response
struct mgcp_msg
mgcp_response_parse_params()
mgcp_client_tx()
mgcp_client_cancel()
mgcp_msg_gen()
mgcp_msg_trans_id()
libosmo-mgcp-client deprecate public API New code should no longer use codecs[], instead use ptmap[].codec. There
is backwards compat code that moves codecs[] entries, if any, over to
ptmap[], so callers may migrate at own leisure.
osmo-mgw remove cfg Remove VTY config item 'sdp audio fmtp-extra' (see OS#6313)
libosmocore bump_dep; workaround Bump libosmocore version dependency after I68328adb952ca8833ba047cb3b49ccc6f8a1f1b5
has been merged to libosmocore.git; then remove my_msgb_copy_c wrapper function.

View File

@@ -44,16 +44,15 @@ AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DLSYM)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.4.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.7.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.7.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.7.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.7.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.2.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.3.0)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.3.0)
CFLAGS="$CFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
CPPFLAGS="$CPPFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
CFLAGS="$CFLAGS -pthread"
LDFLAGS="$LDFLAGS -pthread"
AC_ARG_ENABLE(sanitize,
@@ -80,7 +79,6 @@ AC_ARG_ENABLE(werror,
if test x"$werror" = x"yes"
then
WERROR_FLAGS="-Werror"
WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS"
@@ -189,11 +187,9 @@ AC_OUTPUT(
libosmo-mgcp-client.pc
include/Makefile
include/osmocom/Makefile
include/osmocom/sdp/Makefile
include/osmocom/mgcp_client/Makefile
include/osmocom/mgcp/Makefile
src/Makefile
src/libosmo-sdp/Makefile
src/libosmo-mgcp-client/Makefile
src/libosmo-mgcp/Makefile
src/osmo-mgw/Makefile
@@ -201,10 +197,10 @@ AC_OUTPUT(
tests/atlocal
tests/mgcp_client/Makefile
tests/mgcp/Makefile
tests/sdp/Makefile
doc/Makefile
doc/examples/Makefile
doc/manuals/Makefile
contrib/Makefile
contrib/systemd/Makefile
contrib/osmo-mgw.spec
Makefile)

137
contrib/osmo-mgw.spec.in Normal file
View File

@@ -0,0 +1,137 @@
#
# 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.2.0
BuildRequires: pkgconfig(libosmocore) >= 1.7.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.7.0
BuildRequires: pkgconfig(libosmogsm) >= 1.7.0
BuildRequires: pkgconfig(libosmovty) >= 1.7.0
BuildRequires: pkgconfig(libosmocoding) >= 1.7.0
BuildRequires: pkgconfig(libosmoabis) >= 1.3.0
BuildRequires: pkgconfig(libosmotrau) >= 1.3.0
%{?systemd_requires}
%description
OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks.
%package -n libosmo-mgcp-client9
Summary: Osmocom's Media Gateway Control Protocol client library
Group: System/Libraries
%description -n libosmo-mgcp-client9
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-client9 = %{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-client9 -p /sbin/ldconfig
%postun -n libosmo-mgcp-client9 -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-client9
%{_libdir}/libosmo-mgcp-client.so.9*
%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

View File

@@ -1,24 +1,11 @@
[Unit]
Description=Osmocom Media Gateway (MGW)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
LimitNOFILE=65536
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
Restart=always
User=osmocom
Group=osmocom
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
RestartSec=2
AmbientCapabilities=CAP_SYS_NICE
# CPU scheduling policy:
CPUSchedulingPolicy=rr
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
CPUSchedulingPriority=1
# See sched(7) for further details on real-time policies and priorities
[Install]
WantedBy=multi-user.target

213
debian/changelog vendored
View File

@@ -1,217 +1,8 @@
osmo-mgw (1.12.1) unstable; urgency=medium
osmo-mgw (1.10.1) unstable; urgency=medium
[ Pau Espin Pedrol ]
* mgcp-client: Fix missing include in mgcp_client_pool.h
* mgcp-client: Introduce API osmo_mgcpc_ep_local_name()
* mgw: Configure IuUP if codec set during MDCX
-- Oliver Smith <osmith@sysmocom.de> Thu, 28 Sep 2023 15:58:17 +0200
osmo-mgw (1.12.0) unstable; urgency=medium
[ Philipp Maier ]
* mgcp_sdp: add spec reference
* mgcp_sdp: cosmetic: remove newline
* mgcp_endp: cosmetic move mgcp_endp_release to the end
* mgcp_endp: cosmetic: remove unnecessary new line
* mgcp_e1: fix log output
* mgcp_e1: be more frugal withe E1 line resources
* mgcp_client: fix sourcecode formatting
* mgcp_e1: fix typo
* mgcp_e1: rewrite comment
* mgcp_e1: rename e1_send to e1_send_ts_frame
* mgcp_e1: cosmetic: rewrite comment
* Revert "mgcp_codec: do not differentiate between oa and bwe when comparing codec"
* mgcp_codec: fix oa/bwe comparison in mgcp_codec_pt_translate()
* mgcp_codec: refactor payload type converstion
* mgcp_codec: cosmetic: remove line break in api-doc
* mgcp_network: fix apidoc
* mgcp_vty: add warnings for deprecated config options
* mgcp_codec: move mgcp_codec_decide down
* mgcp_codec: fix codec decision
* mgcp_network: do not deliver RTP packets with unpatched PT
* mgcp_codec: be sensitive about IuFP when checking codecs
* mgcp_client.h: also add spec ref to the other 3gpp defined payload types
[ Harald Welte ]
* cosmetic: Fix grammar suggesting reading _the_ user manual
[ arehbein ]
* Transition to use of 'telnet_init_default'
[ Oliver Smith ]
* mgcp_client: mgcp_msg_gen: add more error logs
* mgcp_client_pool: add mgcp_client_pool_empty()
* Cosmetic: mgcp_client: fix typo
* debian: set compat level to 10
* systemd: depend on networking-online.target
* Cosmetic: fix a typo
* mgcp_client: check rc of map_str_to_codec
[ Pau Espin Pedrol ]
* mgcp_network: Unregister osmo_fd before closing fd
* mgcp-client: Call osmo_fd_unregister() before closing and changing bfd->fd
* mgcp-client: Drop unused struct mgcp_client field
* mgcp_client: Introduce mgcp_client_conf_alloc(), deprecate mgcp_client_conf_init()
* mgcp-client: Move some static functions further above
* mgw: Allow auditing speciall 'null' endpoint
* mgcp-client: Add keepalive feature
* mgcp_client: pool: Only pick clients with an MGCP link considered to be UP
* mgcp-client: Always mark client as UP if keepalive request-interval disabled
* mgcp-client: Mark client as UP when keepalive request-interval/timeout is disabled through VTY
[ Vadim Yanitskiy ]
* */Makefile.am: libraries shall not be in AM_LDFLAGS
* tests: use -no-install libtool flag to avoid ./lt-* scripts
* tests: $(BUILT_SOURCES) is not defined, depend on osmo-mgw
* copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
[ Neels Hofmeyr ]
* mgcp_find_section_end(): skip spaces at start of SDP
* mgcp_client: simpler error handling
* mgcp: fix "L: a:" header parsing: heed ";" separator
* mgcp_client: tweak extract_codec_name() implementation
[ Andreas Eversberg ]
* ASCI: Add new mode for voice group/broadcast call
* ASCI: Support conference briding with 1..n connections
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 14:48:51 +0200
osmo-mgw (1.11.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* mgcp-client: Remove impossible code path
* mgcp_osmux: Drop duplicated conn_osmux_release_cid() in code path
* Use Osmux default port define from libosmo-netif
* osmux: Use new osmux APIs to let libosmo-netif alloc struct osmux_out_handle
* mgw: Fix osmux conn local IP selection
* cosmetic: osmux: Fix wrong indentation
* mgw: Use X-Osmux string define
* Add Osmux log category
* cosmetic: main: Properly format log_info_cat
* osmux: Use better name for function which may allocate a new struct
* osmux: Use osmo_sockaddr wherever possible
* osmux: Log refcounting of osmux_handle_list
* osmux: Fix memleak on error code path
* osmux: don't store conn ptr inside shared osmux_handle
* osmux: set log level of expected code path to INFO
* cosmetic: vty: Fix indentation whitespace
* osmux: Log sendto() error
* cosmetic: mgcp_conn.h: fix indentation whitespace
* osmux: Attach osmux to virtual trunk
* osmux: Clean up helper macro osmux_chunk_length()
* osmux: Get rid of static NULL talloc context
* Fix typo in ratectr description
* mgcp_conn: rename field s/rate_ctr_group/ctrg/g
* Use 'static const' instead of 'const static' everywhere
* osmux: Add connection and global rate counters
* cosmetic: osmux: Fix formatting of if-else brackets
* osmux: Rename field osmux usage policy and define it with proper type
* Use bool type instead of int in config field
* osmux: Support local CID != remote CID
* osmux: Fix incorrect rate_ctr_group used in mgcp_osmux.c
* osmux: Improve per-conn tx rate counters
* vty: show per-connection Osmux VTY stats
* osmux: Rename field s/init/initialized
* osmux: Allocate rate counters during initialization of osmux conn
* cosmetic: osmux: Drop extra empty line
* osmux: Drop unneeded comment block
* osmux: Lower log level when osmux batch received for unknown CID
* osmux: Keep decoding osmux pkt if a batch contains an unknown CID
* Allocate struct osmux_in_handle through new libosmo-netif APIs
* osmux: Drop logging of osmux internal counters
* osmux: cleanup misleading code calling rtp_bridge_cb
* osmux: Unify rtp_conn osmux type into a single type
* osmux: Match remote address in osmux_conn_lookup()
* osmux: Fill in from_addr in struct osmo_rtp_msg_ctx
* osmux: Log remote address upon rx of osmux pkt
* send_dummy: Use proper condition to test if conn is osmux
* Fix regression in detection of legacy dummy packets
* Use new libosmocore API osmo_sockaddr_is_any()
* Get rid of separate rtp_port field
* osmux: Use available API to check if remote end is known
* Add Osmux IPv6 support
* Clean up local var pointers in mgcp_get_local_addr()
* osmux: Change couple log lines to OSMUX category
* osmux: Add square brackets around IPv6 address to distinguish port in log line
* osmux: Drop unused role parameter
* osmux: cosmetic: Fix indentation
* Check once if remote addr is available when sending dummy packet
* osmux: Simplify and constify param passing
* osmux: Set conn->type during osmux_init_conn()
* osmux: Make conn_osmux_{allocate,release}_local_cid() APIs static
* osmux: Define osmux_dummy cfg as boolean
* osmux: Move setting OSMUX_STATE_DISABLED to initializer function
* osmux: Erase references to bsc-nat
* mgcp_conn_dump(): Separate dump for osmux and iuup connections
* osmux: Introduce osmux peer-behind-nat (on|off) and rework conn activation
* osmux: Clean up mgcp_config osmux fields
* cosmetic: Fix typo in comment
* mgcp-client: pool: Improve documentation of some internal fields
* mgcp-client: Avoid double iteration picking client from pool
* mgcp-client: Fix typo in internal function name
* mgcp-client: Rename internal field in mgcp_client_pool
* mgcp-client: Move & rename helper function outside of vty code
* mgcp-client: Create alloc() and free() internal APIs for mgcp_client_pool_member
* mgcp-client: Move internal API acting on mgcp_client_pool to the correct file section
* mgcp-client: Refactor reinit of mgcp clients
* mgcp-client: Rearrange order of structs and APIs in header
* mgcp-client: Rearrange internal backpointers
* mgcp-client: Introduce APIs to manually select mgcp_client from pool
* mgcp-client: vty: Write deprecation warning using non-mgw nodes
* mgcp-client: Use random free local port by default
* mgcp-client: Convert users supporting new MGW Pool VTY node during write-config
* mgcp-client: Introduce API mgcp_client_pool_config_write
* mgcp-client: Add new VTY commands under mgw node without mgw prefix
* mgcp-client: Fix 'mgw endpoint-range' command dropped from old VTY node
* mgcp-client: Refactor system keeping old users not calling mgcp_client_pool_config_write() working
* mgcp-client: Fix no 'mgw ' prefix written in old VTY node
* Fix misleading error log
* mgcp_send: Use mgcp_conn_rtp_is_iuup() helper
* cosmetic: Clarify and fix typos in comment
* mgw: Log unexpected RTP AMR OA-vs-BE payload
* Rename and move func checking if amr mode is explicitly configured
* mgw: rx_rtp(): reorder checks and handlings
* mgw: Rename s/mgcp_send_rtp/mgcp_conn_rtp_dispatch_rtp/
* osmux: Rename function and pass msgb directly to it
* osmux: Make sure RTP AMR feed to osmux is in octet-aligned mode
* Improve logging on AMR OA<->BWE conversion failure
* osmux: Rotate over available Osmux CID when allocating a new one
* osmux: Use new osmux_xfrm_input API to set name on each link
* iuup: Use osmo_amr_ft_valid() API
[ Vadim Yanitskiy ]
* libosmo-mgcp-client: add -no-undefined to *_la_LDFLAGS
[ Philipp Maier ]
* mgcp_e1: fix apidoc
* mgcp_network: improve coment
[ Max ]
* Set working directory in systemd service file
* Add realtime scheduling and set priority in service file
* ctrl: take both address and port from vty config
[ Harald Welte ]
* Make osmo_mgcpc_ep_fsm_pre_term() static
* Support building with -Werror=strict-prototypes / -Werror=old-style-definition
* Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition
* update outdated vty copyright statement
[ Oliver Smith ]
* mgcp_client_pool.h: add missing stdbool include
* Fix various typos
* mgcp_client: add new clearmode codec
[ Neels Hofmeyr ]
* AMR->IuUP: do not crash on AMR data before IuUP Init
* AMR->IuUP: properly translate Q -> FQC
* IuUP->AMR: do not patch payload type a second time
* IuUP->AMR: log whether converting to AMR OA or BE
* AMR->IuUP: log conversion, like for the flipside
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 16:58:41 +0100
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 10 Mar 2023 16:04:04 +0100
osmo-mgw (1.10.0) unstable; urgency=medium

2
debian/compat vendored
View File

@@ -1 +1 @@
10
9

14
debian/control vendored
View File

@@ -2,14 +2,14 @@ Source: osmo-mgw
Section: net
Priority: extra
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
Build-Depends: debhelper (>= 10),
Build-Depends: debhelper (>=9),
dh-autoreconf,
pkg-config,
autotools-dev,
libosmocore-dev (>= 1.9.0),
libosmo-netif-dev (>= 1.4.0),
libosmo-abis-dev (>= 1.5.0),
osmo-gsm-manuals-dev (>= 1.5.0)
libosmocore-dev (>= 1.7.0),
libosmo-netif-dev (>= 1.2.0),
libosmo-abis-dev (>= 1.3.0),
osmo-gsm-manuals-dev (>= 1.3.0)
Standards-Version: 3.9.8
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
@@ -21,7 +21,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-client12
Package: libosmo-mgcp-client9
Section: libs
Architecture: any
Multi-Arch: same
@@ -33,7 +33,7 @@ Package: libosmo-mgcp-client-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-mgcp-client12 (= ${binary:Version}), ${misc:Depends}
Depends: libosmo-mgcp-client9 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
Package: osmo-mgw-doc

4
debian/copyright vendored
View File

@@ -6,7 +6,7 @@ Files: *
Copyright: 2009-2014 On-Waves
2009-2015 Holger Hans Peter Freyther <zecke@selfish.org>
2013 Jacob Erlbeck <jerlbeck@sysmocom.de>
2016-2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
2016-2017 sysmocom s.m.f.c. GmbH <info@sysmocom.de>
License: AGPL-3.0+
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
@@ -22,7 +22,7 @@ License: AGPL-3.0+
along with this program. If not, see <http://www.gnu.org/licenses/>.
Files: src/libosmo-mgcp-client/* include/osmocom/mgcp_client/*
Copyright: 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
Copyright: 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
2009 by Harald Welte <laforge@gnumonks.org>
2009-2011 by Holger Hans Peter Freyther

38
debian/postinst vendored
View File

@@ -1,38 +0,0 @@
#!/bin/sh -e
case "$1" in
configure)
# Create the osmocom group and user (if it doesn't exist yet)
if ! getent group osmocom >/dev/null; then
groupadd --system osmocom
fi
if ! getent passwd osmocom >/dev/null; then
useradd \
--system \
--gid osmocom \
--home-dir /var/lib/osmocom \
--shell /sbin/nologin \
--comment "Open Source Mobile Communications" \
osmocom
fi
# Fix permissions of previous (root-owned) install (OS#4107)
if dpkg --compare-versions "$2" le "1.13.0"; then
if [ -e /etc/osmocom/osmo-mgw.cfg ]; then
chown -v osmocom:osmocom /etc/osmocom/osmo-mgw.cfg
chmod -v 0660 /etc/osmocom/osmo-mgw.cfg
fi
if [ -d /etc/osmocom ]; then
chown -v root:osmocom /etc/osmocom
chmod -v 2775 /etc/osmocom
fi
mkdir -p /var/lib/osmocom
chown -R -v osmocom:osmocom /var/lib/osmocom
fi
;;
esac
# dh_installdeb(1) will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#

View File

@@ -1,25 +1,20 @@
!
! MGCP configuration example
!
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
e1_input
e1_line 0 driver dahdi
e1_line 0 port 0
mgcp
bind ip 127.0.0.1
rtp port-range 4002 16001
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

View File

@@ -1,22 +1,17 @@
!
! MGCP configuration example
!
log stderr
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 0
logging print file basename last
logging print level 1
mgcp
bind ip 127.0.0.1
rtp port-range 4002 16001
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

View File

@@ -91,10 +91,4 @@ 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)
=== The `null` endpoint
OsmoMGW offers a special `null@<domain>` endpoint which can be audited at all times.
This is useful for MGCP clients who wish to submit requests to OsmoMGW
periodically to find out whether it is still reachable and in a working state.
endpoints (including identifiers)

View File

@@ -23,20 +23,3 @@ arguments:
Disable colors for logging to stderr. This has mostly been
deprecated by VTY based logging configuration, see <<logging>>
for more information.
=== Configure limits
When servicing hundreds of media endpoints, it may be necessary to adjust the
operating system's limit on open file descriptors for the osmo-mgw process. A
typical default limit imposed by operating systems is 1024; this would be
exceeded by, for example, about 256 active voice calls with 4 RTP/RTPC ports
each, sockets for other interfaces not considered yet.
It should be ok to set an OS limit on open file descriptors as high as 65536
for osmo-mgw, which practically rules out failure from running out of file
descriptors anywhere (<16,000 active calls).
When using systemd, the file descriptor limit may be adjusted in the service
file by the `LimitNOFILE` setting ("Number of Open FILE descriptors"). OsmoMGW
ships a systemd service file with a high LimitNOFILE setting.

View File

@@ -3,19 +3,10 @@ SUBDIRS = \
$(NULL)
nobase_include_HEADERS = \
osmocom/mgcp_client/defs.h \
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_client_pool.h \
osmocom/sdp/fmtp.h \
osmocom/sdp/sdp_codec.h \
osmocom/sdp/sdp_codec_list.h \
osmocom/sdp/sdp_msg.h \
osmocom/sdp/sdp_strings.h \
$(NULL)
noinst_HEADERS = \
osmocom/mgcp/mgcp.h \
osmocom/mgcp/mgcp_common.h \
osmocom/mgcp/osmux.h \

View File

@@ -1,5 +1,4 @@
SUBDIRS = \
sdp \
mgcp_client \
mgcp \
$(NULL)

View File

@@ -6,6 +6,7 @@ noinst_HEADERS = \
mgcp_endp.h \
mgcp_sdp.h \
mgcp_codec.h \
mgcp_ctrl.h \
mgcp_trunk.h \
debug.h \
mgcp_ratectr.h \

View File

@@ -30,7 +30,6 @@
enum {
DRTP,
DE1,
DOSMUX,
Debug_LastEntry,
};

View File

@@ -24,13 +24,11 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/logging.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/osmux.h>
#include <arpa/inet.h>
#include <sys/types.h>
@@ -71,10 +69,12 @@ typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
/**
* Return:
* < 0 in case no audio was processed
* >= 0 in case audio was processed.
* >= 0 in case audio was processed. The remaining payload
* length will be returned.
*/
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end, struct msgb *msg);
struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
struct mgcp_conn_rtp;
@@ -137,6 +137,8 @@ struct mgcp_config {
mgcp_processing rtp_processing_cb;
mgcp_processing_setup setup_rtp_processing_cb;
mgcp_get_format get_net_downlink_format_cb;
struct osmo_wqueue gw_fd;
struct mgcp_port_range net_ports;
@@ -154,26 +156,24 @@ struct mgcp_config {
enum mgcp_role role;
struct {
/* Osmux usage policy: */
enum osmux_usage usage;
/* addr to bind the server to */
char *local_addr_v4;
char *local_addr_v6;
/* osmux port */
uint16_t local_port;
/* The osmux socket is allocated on demand (1st time used).
* This tells us if the osmux socket is already initialized. */
bool initialized;
/* osmux batch factor: from 1 to 4 maximum */
int batch_factor;
/* osmux batch size (in bytes) */
int batch_size;
/* Pad circuit with dummy AMR frames if no payload to transmit is available */
bool dummy_padding;
/* Whether peer is behind NAT (Retrieve remote addr from 1st received Osmux packet) */
bool peer_behind_nat;
} osmux;
/* osmux translator: 0 means disabled, 1 means enabled */
int osmux;
/* addr to bind the server to */
char osmux_addr[INET6_ADDRSTRLEN];
/* The BSC-NAT may ask for enabling osmux on demand. This tells us if
* the osmux socket is already initialized.
*/
int osmux_init;
/* osmux batch factor: from 1 to 4 maximum */
int osmux_batch;
/* osmux batch size (in bytes) */
int osmux_batch_size;
/* osmux port */
uint16_t osmux_port;
/* Pad circuit with dummy messages until we see the first voice
* message.
*/
uint16_t osmux_dummy;
/* domain name of the media gateway */
char domain[255+1];
@@ -205,5 +205,6 @@ int mgcp_send_reset_ep(struct mgcp_endpoint *endp);
int mgcp_send_reset_all(struct mgcp_config *cfg);
int mgcp_create_bind(const char *source_addr, int port, uint8_t dscp, uint8_t prio);
int mgcp_udp_send(struct osmo_io_fd *iofd, const struct osmo_sockaddr *addr, const char *buf, int len);
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, const char *buf, int len);

View File

@@ -13,10 +13,8 @@ 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_add2(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const char *fmtp);
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst);
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);
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec);
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec);
struct mgcp_rtp_codec *mgcp_codec_from_pt(struct mgcp_conn_rtp *conn, int payload_type);

View File

@@ -47,7 +47,6 @@ enum mgcp_connection_mode {
MGCP_CONN_SEND_ONLY = 2,
MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
MGCP_CONN_LOOPBACK = 4 | MGCP_CONN_RECV_SEND,
MGCP_CONN_CONFECHO = 8 | MGCP_CONN_RECV_SEND,
};
#define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:"
@@ -59,7 +58,7 @@ enum mgcp_x_osmo_ign {
MGCP_X_OSMO_IGN_CALLID = 1,
};
/* Deprecated. Use the new fmtp string instead. */
/* Codec parameters (communicated via SDP/fmtp) */
struct mgcp_codec_param {
bool amr_octet_aligned_present;
bool amr_octet_aligned;
@@ -68,11 +67,11 @@ struct mgcp_codec_param {
/* Ensure that the msg->l2h is NUL terminated. */
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
{
unsigned char *tail = msg->tail; /* char after l2 data */
unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */
if (tail[-1] == '\0')
/* nothing to do */;
else if (msgb_tailroom(msg) > 0)
msgb_put_u8(msg, (uint8_t)'\0');
tail[0] = '\0';
else if (tail[-1] == '\r' || tail[-1] == '\n')
tail[-1] = '\0';
else {

View File

@@ -46,14 +46,10 @@ LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
enum mgcp_conn_rtp_type {
MGCP_RTP_DEFAULT = 0,
MGCP_RTP_OSMUX,
MGCP_OSMUX_BSC,
MGCP_OSMUX_BSC_NAT,
MGCP_RTP_IUUP,
};
extern const struct value_string mgcp_conn_rtp_type_names[];
static inline const char *mgcp_conn_rtp_type_name(enum mgcp_conn_rtp_type val)
{
return get_value_string(mgcp_conn_rtp_type_names, val);
}
/*! Connection type, specifies which member of the union "u" in mgcp_conn
* contains a useful connection description (currently only RTP) */
@@ -84,20 +80,19 @@ struct mgcp_conn_rtp {
struct {
/* Osmux state: disabled, activating, active */
enum osmux_state state;
/* Is local_cid holding valid data? is it allocated from pool? */
bool local_cid_allocated;
/* Allocated local Osmux circuit ID for this conn */
uint8_t local_cid;
/* Is remote_cid holding valid data? was it already received from client? */
bool remote_cid_present;
/* Received remote Osmux circuit ID for this conn */
uint8_t remote_cid;
/* handle to batch messages, shared (refcounted) among several conns */
/* 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, one allocated and owned per conn */
struct osmux_out_handle *out;
/* statistics: */
struct rate_ctr_group *ctrg;
/* handle to unbatch messages */
struct osmux_out_handle out;
/* statistics */
struct {
uint32_t chunks;
uint32_t octets;
} stats;
} osmux;
struct {
@@ -108,7 +103,7 @@ struct mgcp_conn_rtp {
struct osmo_iuup_rnl_prim *init_ind;
} iuup;
struct rate_ctr_group *ctrg;
struct rate_ctr_group *rate_ctr_group;
};
/*! MGCP connection (untyped) */
@@ -150,12 +145,12 @@ struct mgcp_conn {
enum {
IN_STREAM_ERR_TSTMP_CTR,
OUT_STREAM_ERR_TSTMP_CTR,
RTP_PACKETS_RX_CTR,
RTP_OCTETS_RX_CTR,
RTP_PACKETS_TX_CTR,
RTP_OCTETS_TX_CTR,
RTP_DROPPED_PACKETS_CTR,
RTP_NUM_CONNECTIONS,
RTP_PACKETS_RX_CTR,
RTP_OCTETS_RX_CTR,
RTP_PACKETS_TX_CTR,
RTP_OCTETS_TX_CTR,
RTP_DROPPED_PACKETS_CTR,
RTP_NUM_CONNECTIONS,
};
/* RTP per-connection statistics. Instances of the corresponding rate counter group
@@ -168,7 +163,7 @@ static const struct rate_ctr_desc mgcp_conn_rate_ctr_desc[] = {
[RTP_OCTETS_RX_CTR] = {"rtp:octets_rx", "Inbound rtp octets."},
[RTP_PACKETS_TX_CTR] = {"rtp:packets_tx", "Outbound rtp packets."},
[RTP_OCTETS_TX_CTR] = {"rtp:octets_tx", "Outbound rtp octets."},
[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "Dropped rtp packets."}
[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."}
};
/* Aggregated RTP connection stats. These are updated when an RTP connection is freed.
@@ -186,49 +181,9 @@ 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."}
};
/* Osmux connection related counters */
enum {
OSMUX_CHUNKS_RX_CTR,
OSMUX_OCTETS_RX_CTR,
OSMUX_RTP_PACKETS_TX_CTR,
OSMUX_RTP_PACKETS_TX_DROPPED_CTR,
OSMUX_AMR_OCTETS_TX_CTR,
/* Only available in global stats: */
OSMUX_NUM_CONNECTIONS,
OSMUX_PACKETS_RX_CTR,
OSMUX_PACKETS_TX_CTR,
OSMUX_DROPPED_PACKETS_CTR,
};
/* RTP per-connection statistics. Instances of the corresponding rate counter group
* exist for the lifetime of an RTP connection.
* Must be kept in sync with all_rtp_conn_rate_ctr_desc below */
static const struct rate_ctr_desc mgcp_conn_osmux_rate_ctr_desc[] = {
[OSMUX_CHUNKS_RX_CTR] = {"osmux:chunks_rx", "Inbound Osmux chunks."},
[OSMUX_OCTETS_RX_CTR] = {"osmux:octets_rx", "Inbound Osmux octets."},
[OSMUX_RTP_PACKETS_TX_CTR] = {"osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."},
[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."},
[OSMUX_AMR_OCTETS_TX_CTR] = {"osmux:amr_octets_tx", "Tx outbound AMD payload octets."},
};
/* Aggregated Osmux connection stats. These are updated when an Osmux connection is freed.
* Must be kept in sync with mgcp_conn_osmux_rate_ctr_desc above */
static const struct rate_ctr_desc all_osmux_conn_rate_ctr_desc[] = {
[OSMUX_CHUNKS_RX_CTR] = {"all_osmux:chunks_rx", "Inbound Osmux chunks."},
[OSMUX_OCTETS_RX_CTR] = {"all_osmux:octets_rx", "Inbound Osmux octets."},
[OSMUX_RTP_PACKETS_TX_CTR] = {"all_osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."},
[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"all_osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."},
[OSMUX_AMR_OCTETS_TX_CTR] = {"all_osmux:amr_octets_tx", "Tx outbound AMD payload octets."},
/* These last counters below do not exist in per-connection stats, only here: */
[OSMUX_NUM_CONNECTIONS] = {"all_osmux:num_closed_conns", "Total number of osmux connections closed."},
[OSMUX_PACKETS_RX_CTR] = {"all_osmux:packets_rx", "Total inbound UDP/Osmux packets."},
[OSMUX_PACKETS_TX_CTR] = {"all_osmux:packets_tx", "Total outbound UDP/Osmux packets."},
[OSMUX_DROPPED_PACKETS_CTR] = {"all_osmux:dropped_packets", "Dropped outbound UDP/Osmux packets."}
};
/* Was conn configured to handle Osmux? */
static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) {
return conn->type == MGCP_RTP_OSMUX;
return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT;
}
/* Was conn configured to handle Osmux? */

View File

@@ -0,0 +1,24 @@
/*
* (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

@@ -23,5 +23,5 @@ static const uint8_t e1_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6,
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, uint8_t ts);
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

@@ -40,13 +40,8 @@ LOGP(cat, level, "endpoint:%s " fmt, \
endp ? endp->name : "none", \
## args)
enum rtp_proto {
MGCP_PROTO_RTP,
MGCP_PROTO_RTCP,
};
struct osmo_rtp_msg_ctx {
enum rtp_proto proto;
int proto;
struct mgcp_conn_rtp *conn_src;
struct osmo_sockaddr *from_addr;
};
@@ -130,10 +125,10 @@ struct mgcp_endpoint {
};
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int index);
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);
bool mgcp_endp_is_wildcarded(const char *epname);
bool mgcp_endp_is_null(const char *epname);
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,
@@ -145,4 +140,3 @@ void mgcp_endp_strip_name(char *epname_stripped, const char *epname,
const struct mgcp_trunk *trunk);
struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
const struct mgcp_trunk *trunk);
void mgcp_endp_release(struct mgcp_endpoint *endp);

View File

@@ -4,14 +4,12 @@
#include <stdbool.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/mgcp/mgcp.h>
/* The following constant defines an RTP dummy payload that is used for
* "UDP Hole Punching" (NAT) */
#define MGCP_DUMMY_LOAD 0x23
static const char rtp_dummy_payload[] = { MGCP_DUMMY_LOAD };
static const char rtp_dummy_payload[] = { 0x23 };
/* Check if the data in a given message buffer matches the rtp dummy payload
* defined above */
@@ -84,11 +82,8 @@ struct mgcp_rtp_codec {
char audio_name[64];
char subtype_name[64];
/* Deprecated. Use the new fmtp string instead. */
bool param_present;
struct mgcp_codec_param param;
char fmtp[256];
};
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
@@ -97,7 +92,7 @@ struct mgcp_rtp_end {
struct osmo_sockaddr addr;
/* in network byte order */
int rtcp_port;
int rtp_port, rtcp_port;
/* currently selected audio codec */
struct mgcp_rtp_codec *codec;
@@ -112,6 +107,7 @@ struct mgcp_rtp_end {
int frames_per_packet;
uint32_t packet_duration_ms;
int maximum_packet_time; /* -1: not set */
char *fmtp_extra;
/* are we transmitting packets (true) or dropping (false) outbound packets */
bool output_enabled;
/* FIXME: This parameter can be set + printed, but is nowhere used! */
@@ -124,8 +120,8 @@ struct mgcp_rtp_end {
bool rfc5993_hr_convert;
/* Each end has a separate socket for RTP and RTCP */
struct osmo_io_fd *rtp;
struct osmo_io_fd *rtcp;
struct osmo_fd rtp;
struct osmo_fd rtcp;
/* local UDP port number of the RTP socket; RTCP is +1 */
int local_port;
@@ -160,10 +156,11 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
struct mgcp_rtp_state *state,
struct mgcp_rtp_end *rtp_end,
struct osmo_sockaddr *addr, struct msgb *msg);
int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
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, struct msgb *msg);
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,
@@ -181,9 +178,5 @@ void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_stat
void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
int id, int inc);
void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
int id);
void forward_data_tap(struct osmo_io_fd *iofd, struct mgcp_rtp_tap *tap, struct msgb *msg);
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg);
uint32_t mgcp_get_current_ts(unsigned codec_rate);
int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg, bool target_is_oa);

View File

@@ -80,8 +80,6 @@ struct mgcp_ratectr_trunk {
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 aggregates stats of individual Osmux connections. */
struct rate_ctr_group *all_osmux_conn_stats;
/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */
struct rate_ctr_group *e1_stats;
};

View File

@@ -27,9 +27,12 @@ struct mgcp_trunk {
unsigned 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;
@@ -63,7 +66,7 @@ struct mgcp_trunk {
/* E1 specific */
struct {
unsigned int vty_line_nr;
uint8_t ts_usecount[NUM_E1_TS-1];
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

View File

@@ -1,22 +1,25 @@
#pragma once
#include <stdint.h>
#include <osmocom/core/socket.h>
#include <osmocom/netif/osmux.h>
struct mgcp_conn_rtp;
struct mgcp_trunk;
struct mgcp_endpoint;
struct mgcp_conn_rtp;
int osmux_init(struct mgcp_trunk *trunk);
int osmux_init_conn(struct mgcp_conn_rtp *conn);
int conn_osmux_enable(struct mgcp_conn_rtp *conn);
#define OSMUX_PORT 1984
enum {
OSMUX_ROLE_BSC = 0,
OSMUX_ROLE_BSC_NAT,
};
int osmux_init(int role, struct mgcp_config *cfg);
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
struct osmo_sockaddr *addr, uint16_t port);
void conn_osmux_disable(struct mgcp_conn_rtp *conn);
int conn_osmux_event_rx_crcx_mdcx(struct mgcp_conn_rtp *conn);
int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg);
int osmux_send_dummy(struct mgcp_conn_rtp *conn);
int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid);
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn);
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
void osmux_cid_pool_get(uint8_t osmux_cid);
int osmux_cid_pool_get_next(void);
@@ -26,14 +29,10 @@ int osmux_cid_pool_count_used(void);
enum osmux_state {
OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */
OSMUX_STATE_ACTIVATING, /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref conn_osmux_enable. */
OSMUX_STATE_ENABLED, /* Osmux was initialized by \ref conn_osmux_enable and can process frames */
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 */
};
extern const struct value_string osmux_state_strs[];
static inline const char *osmux_state_str(enum osmux_state val)
{ return get_value_string(osmux_state_strs, val); }
enum osmux_usage {
OSMUX_USAGE_OFF = 0,
OSMUX_USAGE_ON = 1,

View File

@@ -1,7 +0,0 @@
#include <osmocom/core/defs.h>
#if BUILDING_LIBOSMOMGCPCLIENT
# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT(text)
#else
# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT(text) OSMO_DEPRECATED(text)
#endif

View File

@@ -3,15 +3,14 @@
#include <stdint.h>
#include <arpa/inet.h>
#include <osmocom/mgcp_client/defs.h>
#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_PORT_DEFAULT 0
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727
#define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
#define MGCP_CLIENT_KEEPALIVE_DEFAULT_ENDP "null"
#define MGCP_CLIENT_MGW_STR "Configure MGCP connection to Media Gateway\n"
struct msgb;
@@ -36,12 +35,6 @@ struct mgcp_client_conf {
/* human readable name / description */
char *description;
struct {
uint32_t timeout_sec;
uint32_t req_interval_sec;
char req_endpoint_name[MGCP_ENDPOINT_MAXLEN];
} keepalive;
};
typedef unsigned int mgcp_trans_id_t;
@@ -52,12 +45,11 @@ enum mgcp_codecs {
CODEC_GSM_8000_1 = 3,
CODEC_PCMA_8000_1 = 8,
CODEC_G729_8000_1 = 18,
CODEC_GSMEFR_8000_1 = 110, /* 3GPP TS 48.103 table 5.4.2.2.1 */
CODEC_GSMHR_8000_1 = 111, /* 3GPP TS 48.103 table 5.4.2.2.1 */
CODEC_AMR_8000_1 = 112, /* 3GPP TS 48.103 table 5.4.2.2.1 */
CODEC_AMRWB_16000_1 = 113, /* 3GPP TS 48.103 table 5.4.2.2.1 */
CODEC_GSMEFR_8000_1 = 110,
CODEC_GSMHR_8000_1 = 111,
CODEC_AMR_8000_1 = 112,
CODEC_AMRWB_16000_1 = 113,
CODEC_IUFP = 96,
CODEC_CLEARMODE = 120, /* 3GPP TS 48.103 table 5.4.2.2.1 */
};
/* Note: when new codec types are added, the corresponding value strings
* in mgcp_client.c (codec_table) must be updated as well. Enumerations
@@ -77,12 +69,29 @@ struct ptmap {
/*! payload type number (96-127) */
unsigned int pt;
/*! the MGCP 'a=fmtp:N <...>' string, e.g. "mode-set=1,2,3;octet-align=0". */
char fmtp[256];
};
int ptmap_cmp(const struct ptmap *a, const struct ptmap *b);
struct mgcp_response_head {
int response_code;
mgcp_trans_id_t trans_id;
char comment[MGCP_COMMENT_MAXLEN];
char conn_id[MGCP_CONN_ID_MAXLEN];
char endpoint[MGCP_ENDPOINT_MAXLEN];
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];
unsigned int ptime;
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
unsigned int codecs_len;
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
};
enum mgcp_verb {
MGCP_VERB_CRCX,
@@ -92,8 +101,38 @@ enum mgcp_verb {
MGCP_VERB_RSIP,
};
struct mgcp_client_conf *mgcp_client_conf_alloc(void *ctx);
void mgcp_client_conf_init(struct mgcp_client_conf *conf) OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use mgcp_client_conf_alloc() (or even better, switch to the mgcp_client_pool API!)");
#define MGCP_MSG_PRESENCE_ENDPOINT 0x0001
#define MGCP_MSG_PRESENCE_CALL_ID 0x0002
#define MGCP_MSG_PRESENCE_CONN_ID 0x0004
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
#define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000
#define MGCP_MSG_PRESENCE_X_OSMO_IGN 0x8000
struct mgcp_msg {
enum mgcp_verb verb;
/* See MGCP_MSG_PRESENCE_* constants */
uint32_t presence;
char endpoint[MGCP_ENDPOINT_MAXLEN];
unsigned int call_id;
char *conn_id;
uint16_t audio_port;
char *audio_ip;
enum mgcp_connection_mode conn_mode;
unsigned int ptime;
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
unsigned int codecs_len;
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
uint32_t x_osmo_ign;
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);
void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf);
int mgcp_client_config_write(struct vty *vty, const char *indent);
struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp);
@@ -101,7 +140,7 @@ struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp);
struct mgcp_client *mgcp_client_init(void *ctx,
struct mgcp_client_conf *conf);
int mgcp_client_connect(struct mgcp_client *mgcp);
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports) OSMO_DEPRECATED("Use mgcp_client_connect() instead");
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports);
void mgcp_client_disconnect(struct mgcp_client *mgcp);
const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp);
@@ -113,8 +152,20 @@ 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. */
typedef void (* mgcp_response_cb_t )(struct mgcp_response *response, void *priv);
int mgcp_response_parse_params(struct mgcp_response *r);
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
mgcp_response_cb_t response_cb, void *priv);
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
enum mgcp_connection_mode;
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
extern const struct value_string mgcp_client_connection_mode_strs[];
static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
{

View File

@@ -45,7 +45,6 @@ static inline void osmo_mgcpc_ep_ci_dlcx(struct osmo_mgcpc_ep_ci *ci)
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_local_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);
struct mgcp_client *osmo_mgcpc_ep_client(const struct osmo_mgcpc_ep *ep);

View File

@@ -29,11 +29,11 @@ struct mgcp_conn_peer {
/*! RTP packetization interval (optional) */
unsigned int ptime;
/*! Deprecated. Use only ptmap[].codec in new code. */
enum mgcp_codecs codecs[MGCP_MAX_CODECS]
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[i].codec instead");
unsigned int codecs_len
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[] and ptmap_len instead");
/*! RTP codec list (optional) */
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
/*! Number of codecs in RTP codec list (optional) */
unsigned int codecs_len;
/*! RTP payload type map (optional, only needed when payload types are
* used that differ from what IANA/3GPP defines) */
@@ -57,23 +57,18 @@ struct mgcp_conn_peer {
* address is set. If != MGCP_CONN_NONE, force this conn mode. */
enum mgcp_connection_mode conn_mode;
/*! Deprectated, use ptmap[].fmtp instead.
* Global codec params. In case the codec requires additional format parameters (fmtp), those can be set
* here, see also mgcp_common.h. The format parameters will be applied on all codecs where applicable. */
bool param_present OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[].fmtp instead");
struct mgcp_codec_param param OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[].fmtp instead");
/*! 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,
uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_alloc() and osmo_mgcpc_ep_ci_add() instead");
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci_request() instead");
void mgcp_conn_delete(struct osmo_fsm_inst *fi)
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci_dlcx() instead");
uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
void mgcp_conn_delete(struct osmo_fsm_inst *fi);
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi)
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci.mgcp_ci_str instead");
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
struct mgcp_client *mgcp_conn_get_client(struct osmo_fsm_inst *fi);
const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info);

View File

@@ -1,7 +1,6 @@
#pragma once
#include <osmocom/core/osmo_io.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/write_queue.h>
#define MSGB_CB_MGCP_TRANS_ID 0
@@ -13,39 +12,18 @@ struct reset_ep {
struct mgcp_client {
struct mgcp_client_conf actual;
struct osmo_io_fd *iofd;
struct osmo_wqueue wq;
mgcp_trans_id_t next_trans_id;
struct llist_head responses_pending;
struct mgcp_client_pool_member *pool_member;
struct osmo_timer_list keepalive_tx_timer;
struct osmo_timer_list keepalive_rx_timer;
bool conn_up;
struct llist_head inuse_endpoints;
struct mgcp_client_pool *pool;
};
struct mgcp_response_head {
int response_code;
mgcp_trans_id_t trans_id;
char comment[MGCP_COMMENT_MAXLEN];
char conn_id[MGCP_CONN_ID_MAXLEN];
char endpoint[MGCP_ENDPOINT_MAXLEN];
bool x_osmo_osmux_use;
uint8_t x_osmo_osmux_cid;
struct mgcp_inuse_endpoint {
struct llist_head entry;
uint16_t id;
};
struct mgcp_response {
char *body;
struct mgcp_response_head head;
uint16_t audio_port;
char audio_ip[INET6_ADDRSTRLEN];
unsigned int ptime;
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
};
/* Invoked when an MGCP response is received or sending failed. When the
* response is passed as NULL, this indicates failure during transmission. */
typedef void (*mgcp_response_cb_t)(struct mgcp_response *response, void *priv);
struct mgcp_response_pending {
struct llist_head entry;
@@ -61,41 +39,3 @@ struct mgcp_response_pending * mgcp_client_pending_add(
mgcp_trans_id_t trans_id,
mgcp_response_cb_t response_cb,
void *priv);
#define MGCP_MSG_PRESENCE_ENDPOINT 0x0001
#define MGCP_MSG_PRESENCE_CALL_ID 0x0002
#define MGCP_MSG_PRESENCE_CONN_ID 0x0004
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
#define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000
#define MGCP_MSG_PRESENCE_X_OSMO_IGN 0x8000
struct mgcp_msg {
enum mgcp_verb verb;
/* See MGCP_MSG_PRESENCE_* constants */
uint32_t presence;
char endpoint[MGCP_ENDPOINT_MAXLEN];
unsigned int call_id;
char *conn_id;
uint16_t audio_port;
char *audio_ip;
enum mgcp_connection_mode conn_mode;
unsigned int ptime;
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;
};
int mgcp_response_parse_params(struct mgcp_response *r);
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
mgcp_response_cb_t response_cb, void *priv);
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);

View File

@@ -1,24 +1,11 @@
#pragma once
#include <stdbool.h>
#include <osmocom/vty/vty.h>
struct mgcp_client;
struct mgcp_client_pool;
struct mgcp_client_pool_member;
struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx);
void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool);
int mgcp_client_pool_config_write(struct vty *vty, const char *indent);
unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool);
void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp_client *mgcp_client);
bool mgcp_client_pool_empty(const struct mgcp_client_pool *pool);
struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool);
void mgcp_client_pool_put(struct mgcp_client *mgcp_client);
struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr);
struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member);
bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member);

View File

@@ -1,31 +1,9 @@
#pragma once
/* Struct to handle a pool of MGWs. (Use _pool functions) */
struct mgcp_client_pool {
/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using
* mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */
struct mgcp_client *mgcp_client_single;
/* A list that manages the pool members (see mgcp_client_pool_member->list above) */
struct llist_head member_list;
/* String to use for indentation when writing the configuration file to the VTY. This field is populated by
* mgcp_client_pool_vty_init() */
char *vty_indent;
/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */
struct cmd_node *vty_node;
};
/* Struct to handle a member of a pool of MGWs. */
struct mgcp_client_pool_member {
/* Entry in llist mgcp_client_pool->pool. */
struct llist_head list;
/* The pool managing this object: */
struct mgcp_client_pool *pool;
/* Reference number assinged by VTY. This number is used to manage the pool from the VTY and to identify it in
* the log. */
unsigned int nr;
@@ -46,7 +24,22 @@ struct mgcp_client_pool_member {
unsigned int refcount;
};
struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr);
void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member);
int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member);
/* Struct to handle a pool of MGWs. (Use _pool functions) */
struct mgcp_client_pool {
/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using
* mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */
struct mgcp_client *mgcp_client_single;
/* A list that manages the pool members (see above) */
struct llist_head pool;
/* String to use for indentation when writing the configuration file to the VTY. This field is populated by
* mgcp_client_pool_vty_init() */
char *vty_indent;
/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */
struct cmd_node *vty_node;
};
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member);

View File

@@ -1,3 +0,0 @@
noinst_HEADERS = \
sdp_internal.h \
$(NULL)

View File

@@ -1,34 +0,0 @@
/* Public API for codec management in SDP messages: managing SDP fmtp strings. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const char *option_name);
int64_t osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int64_t default_value);
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp);
bool osmo_sdp_fmtp_amr_match(const char *a, const char *b);

View File

@@ -1,126 +0,0 @@
/* Public API for codec management in SDP messages. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
/* RFC-8866 5.14 and 6.6.
*
* Represent the items describing an SDP codec entry, as in:
*
* m=audio 1234 RTP/AVP <payload_type>
* a=rtpmap:<payload_type> <encoding-name>/<clock-rate>
* a=fmtp:<payload_type> <fmtp>
*
* For example:
*
* m=audio 1234 RTP/AVP 98
* a=rtpmap:98 AMR/8000
* a=fmtp:98 octet-align=1;mode-set=0,2,4,7
*/
struct osmo_sdp_codec {
/* Payload type number ("payload-type"), like 3 for GSM-FR. Limited to 0..127. */
int8_t payload_type;
/* Encoding name like "GSM", "AMR", "GSM-EFR".
*
* RFC-8866 defines no length limit on the encoding name. This API leaves it up to the caller to provide
* sufficient space, via the SDP_SIZES_* definitions.
*
* encoding-name = token
* token = 1*(token-char)
* token-char = ALPHA / DIGIT
* / "!" / "#" / "$" / "%" / "&"
* / "'" ; (single quote)
* / "*" / "+" / "-" / "." / "^" / "_"
* / "`" ; (Grave accent)
*/
char *encoding_name;
/* Samplerate ("clock-rate"), usually 8000 for GSM. */
unsigned int rate;
/* Codec parameters as supplied in SDP line 'a=fmtp:<payload-type> <format-specific-params>'. This holds only
* the 'format-specific-params' bytestring. For example, for SDP line 'a=fmtp:123 param1=val1;param2=val2', this
* holds only the , "param1=val1;param2=val2" part. For the buffer size, see fmtp_size. */
char *fmtp;
/* Entry used by osmo_sdp_codec_list. */
struct llist_head entry;
/* For future extension, always set to false. */
bool v2;
};
struct osmo_sdp_codec *osmo_sdp_codec_alloc(void *ctx);
int osmo_sdp_codec_set(struct osmo_sdp_codec *c,
int8_t payload_type, const char *encoding_name, unsigned int rate, const char *fmtp);
int osmo_sdp_codec_set_encoding_name(struct osmo_sdp_codec *c, const char *encoding_name);
int osmo_sdp_codec_set_fmtp(struct osmo_sdp_codec *c, const char *fmtp);
bool osmo_sdp_codec_is_set(const struct osmo_sdp_codec *a);
int osmo_sdp_codec_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec *codec);
char *osmo_sdp_codec_to_str_c(void *ctx, const struct osmo_sdp_codec *codec);
int osmo_sdp_codec_from_str(struct osmo_sdp_codec *dst, const char *str, int str_len);
enum osmo_sdp_cmp {
OSMO_SDP_CMP_IGNORE = 0,
OSMO_SDP_CMP_EQUIVALENT,
OSMO_SDP_CMP_EXACT,
};
/*! Indicate how to match SDP codecs to various osmo_sdp_*() functions.
* Callers may define own flags, or use predefined instances:
* osmo_sdp_codec_cmp_exact, osmo_sdp_codec_cmp_equivalent, ...
*
* For example, to trigger some action if any item has changed, set all items to true / OSMO_SDP_CMP_EXACT (see
* osmo_sdp_codec_cmp_exact).
* To find codecs that are the same between two SDP sessions, set payload_type=false and fmtp=OSMO_SDP_CMP_EQUIVALENT
* (see osmo_sdp_codec_cmp_equivalent).
* To just list all contained "AMR" codecs, set only encoding_name=true (see osmo_sdp_codec_cmp_name).
*/
struct osmo_sdp_codec_cmp_flags {
/*! true = compare payload type numbers 1:1; false = ignore. */
bool payload_type;
/*! true = compare encoding_name 1:1; false = ignore. */
bool encoding_name;
/*! true = compare rate 1:1; false = ignore. */
bool rate;
/*! OSMO_SDP_CMP_IGNORE = ignore fmtp;
* OSMO_SDP_CMP_EQUIVALENT = use osmo_sdp_fmtp_amr_match() for AMR, otherwise compare 1:1;
* OSMO_SDP_CMP_EXACT = compare 1:1.
*/
enum osmo_sdp_cmp fmtp;
};
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_exact;
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent;
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_name;
int osmo_sdp_codec_cmp(const struct osmo_sdp_codec *a, const struct osmo_sdp_codec *b,
const struct osmo_sdp_codec_cmp_flags *cmp);

View File

@@ -1,69 +0,0 @@
/* Public API for codec management in SDP messages: list of struct osmo_sdp_codec. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/sdp/sdp_codec.h>
struct osmo_sdp_codec_list {
struct llist_head list;
/* For future extension, always set to false. */
bool v2;
};
struct osmo_sdp_codec_list *osmo_sdp_codec_list_alloc(void *ctx);
void osmo_sdp_codec_list_free_items(struct osmo_sdp_codec_list *codec_list);
int8_t osmo_sdp_codec_list_get_unused_dyn_pt_nr(const struct osmo_sdp_codec_list *codec_list, int8_t suggest_pt_nr);
struct osmo_sdp_codec *osmo_sdp_codec_list_add_empty(struct osmo_sdp_codec_list *codec_list);
struct osmo_sdp_codec *osmo_sdp_codec_list_add(struct osmo_sdp_codec_list *codec_list,
const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *once, bool pick_unused_pt_nr);
int osmo_sdp_codec_list_remove(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *cmpf);
void osmo_sdp_codec_list_remove_entry(struct osmo_sdp_codec *codec);
int osmo_sdp_codec_list_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec_list *codec_list, bool summarize);
char *osmo_sdp_codec_list_to_str_c(void *ctx, const struct osmo_sdp_codec_list *codec_list, bool summarize);
struct osmo_sdp_codec *osmo_sdp_codec_list_first(const struct osmo_sdp_codec_list *list);
int osmo_sdp_codec_list_move_to_first(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *cmpf);
#define osmo_sdp_codec_list_foreach(STRUCT_SDP_CODEC_P, SDP_CODEC_LIST) \
llist_for_each_entry(STRUCT_SDP_CODEC_P, &(SDP_CODEC_LIST)->list, entry)
#define osmo_sdp_codec_list_foreach_safe(STRUCT_SDP_CODEC_P, SAFE_P, SDP_CODEC_LIST) \
llist_for_each_entry_safe(STRUCT_SDP_CODEC_P, SAFE_P, &(SDP_CODEC_LIST)->list, entry)
int osmo_sdp_codec_list_cmp(const struct osmo_sdp_codec_list *a, const struct osmo_sdp_codec_list *b,
const struct osmo_sdp_codec_cmp_flags *cmpf);
void osmo_sdp_codec_list_intersection(struct osmo_sdp_codec_list *dst, const struct osmo_sdp_codec_list *other,
const struct osmo_sdp_codec_cmp_flags *cmpf,
bool translate_payload_type_numbers);
struct osmo_sdp_codec *osmo_sdp_codec_list_by_payload_type(struct osmo_sdp_codec_list *codec_list, int8_t payload_type);
bool osmo_sdp_codec_list_is_empty(const struct osmo_sdp_codec_list *codec_list);

View File

@@ -1,11 +0,0 @@
/* Internal header for non-public API shared across .c files */
#pragma once
struct token {
const char *start;
const char *end;
};
/* Copy a string from [start,end[, return as talloc allocated under ctx in *dst.
* If *dst is non-NULL, talloc_free(*dst) first. */
void token_copy(void *ctx, char **dst, const struct token *t);

View File

@@ -1,98 +0,0 @@
/* Public API for SDP message encoding and decoding */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/sdp/sdp_codec.h>
#include <osmocom/sdp/sdp_codec_list.h>
/* Media Direction Attributes "a=recvonly", "a=sendrecv", "a=sendonly", "a=inactive" RFC-8866 6.7. */
enum osmo_sdp_media_direcion_e {
OSMO_SDP_MDIR_UNSET = 0,
OSMO_SDP_MDIR_RECVONLY = 1,
OSMO_SDP_MDIR_SENDRECV = 2,
OSMO_SDP_MDIR_SENDONLY = 3,
OSMO_SDP_MDIR_INACTIVE = 4,
};
/* Session Description Protocol (SDP) message, RFC-8866. */
struct osmo_sdp_msg {
/* 5.2 Origin ("o="). */
struct {
struct osmo_sockaddr_str addr;
char *username;
char *sess_id;
char *sess_version;
} origin;
/* 5.3 Session Name ("s="). */
char *session_name;
/* 5.7 Connection Information ("c=") and port from 5.14 Media Descriptions ("m="). */
struct osmo_sockaddr_str rtp;
/* 5.9. Time Active ("t="). */
struct {
int64_t start;
int64_t stop;
} time_active;
/* 6.4 "a=ptime:<val>". */
unsigned int ptime;
/* 6.7 "a=sendrecv"... */
enum osmo_sdp_media_direcion_e media_direction;
/* List of codecs defined in the SDP message.
* This should not be NULL -- osmo_sdp_msg_alloc() returns an empty osmo_sdp_codec_list instance, ready for
* adding codecs.
* Combination of:
* - payload_type numbers from 5.14 Media Descriptions ("m="),
* - 6.6 "a=rtpmap",
* - 6.15 Format Parameters "a=fmtp".
*/
struct osmo_sdp_codec_list *codecs;
/* For future extension, always set to false. */
bool v2;
};
struct osmo_sdp_err {
int rc;
/* Point at the position that caused the error, in the src string. */
const char *at_input_str;
/* Nr of characters at *src_str that are relevant to the error. */
size_t at_input_str_len;
};
struct osmo_sdp_msg *osmo_sdp_msg_alloc(void *ctx);
struct osmo_sdp_msg *osmo_sdp_msg_decode(void *ctx, const char *src, struct osmo_sdp_err *err);
int osmo_sdp_msg_encode_buf(char *dst, size_t dst_size, const struct osmo_sdp_msg *sdp);
char *osmo_sdp_msg_encode_c(void *ctx, const struct osmo_sdp_msg *sdp);
int osmo_sdp_msg_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_msg *sdp, bool summarize);
char *osmo_sdp_msg_to_str_c(void *ctx, const struct osmo_sdp_msg *sdp, bool summarize);

View File

@@ -1,39 +0,0 @@
/* Central definition of string tokens used for parsing and composing SDP messages */
#pragma once
#define OSMO_SDP_STR_MEDIA "m"
#define OSMO_SDP_STR_ATTRIB "a"
#define OSMO_SDP_STR_TIME_ACTIVE "t"
#define OSMO_SDP_STR_RTPMAP "rtpmap"
#define OSMO_SDP_STR_FMTP "fmtp"
#define OSMO_SDP_STR_PTIME "ptime"
/*! "a=foo:" */
#define OSMO_SDP_A_PREFIX(STR) OSMO_SDP_STR_ATTRIB "=" STR ":"
/*! "a=fmtp:" */
#define OSMO_SDP_STR_A_FMTP OSMO_SDP_A_PREFIX(OSMO_SDP_STR_FMTP)
/* Media Direction Attributes "a=recvonly", "a=sendrecv", "a=sendonly", "a=inactive" RFC-8866 6.7. */
#define OSMO_SDP_STR_RECVONLY "recvonly"
#define OSMO_SDP_STR_SENDRECV "sendrecv"
#define OSMO_SDP_STR_SENDONLY "sendonly"
#define OSMO_SDP_STR_INACTIVE "inactive"
/* AMR related tokens */
#define OSMO_SDP_STR_AMR_OCTET_ALIGN "octet-align"
/*! "octet-align=1" */
#define OSMO_SDP_STR_AMR_OCTET_ALIGN_1 OSMO_SDP_STR_AMR_OCTET_ALIGN "=1"
/*! "octet-align=0".
* According to spec [1], "octet-align=0" is identical to omitting 'octet-align' entirely. In Osmocom practice, whether
* or not "octet-align=0" is present can make a big difference for osmo-mgw versions 1.12 and older, which do not heed
* [1].
*
* spec [1]: RFC4867, see details in description of osmo_sdp_fmtp_amr_is_octet_aligned().
*/
#define OSMO_SDP_STR_AMR_OCTET_ALIGN_0 OSMO_SDP_STR_AMR_OCTET_ALIGN "=0"

View File

@@ -13,9 +13,14 @@ AM_CFLAGS = \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
# Libraries
SUBDIRS = \
libosmo-sdp \
libosmo-mgcp-client \
libosmo-mgcp \
$(NULL)

View File

@@ -14,12 +14,14 @@ AM_CFLAGS = \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
# This is not at all related to the release version, but a range of supported
# API versions. Read TODO_RELEASE in the source tree's root!
MGCP_CLIENT_LIBVERSION=13:0:1
MGCP_CLIENT_LIBVERSION=10:0:1
lib_LTLIBRARIES = \
libosmo-mgcp-client.la \
@@ -33,14 +35,4 @@ libosmo_mgcp_client_la_SOURCES = \
mgcp_client_pool.c \
$(NULL)
libosmo_mgcp_client_la_LDFLAGS = \
$(AM_LDFLAGS) \
-version-info $(MGCP_CLIENT_LIBVERSION) \
-no-undefined \
$(NULL)
libosmo_mgcp_client_la_LIBADD = \
$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(NULL)
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)

File diff suppressed because it is too large Load Diff

View File

@@ -173,30 +173,6 @@ const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep)
return osmo_fsm_inst_name(ep->fi);
}
/*! Get "local endpoint name" part of the endpoint name: (local-endpoint-name@domain-name)
*
* \param ep The MGCP Endpoint
* \returns the local endpoint name if found, NULL on error.
*/
const char *osmo_mgcpc_ep_local_name(const struct osmo_mgcpc_ep *ep)
{
static char buf[1024];
const char *sep;
OSMO_ASSERT(ep);
sep = strchr(ep->endpoint, '@');
if (!sep) {
OSMO_STRLCPY_ARRAY(buf, ep->endpoint);
return buf;
}
if (sep - ep->endpoint >= sizeof(buf))
return NULL;
memcpy(buf, ep->endpoint, sep - ep->endpoint);
buf[sep - ep->endpoint] = '\0';
return buf;
}
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
@@ -251,7 +227,7 @@ 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(void)
static void fill_event_names()
{
int i;
for (i = 0; i < (ARRAY_SIZE(osmo_mgcpc_ep_fsm_event_names) - 1); i++) {
@@ -267,7 +243,7 @@ static void fill_event_names(void)
}
}
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init(void)
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init()
{
OSMO_ASSERT(osmo_fsm_register(&osmo_mgcpc_ep_fsm) == 0);
fill_event_names();
@@ -533,42 +509,10 @@ static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data)
mgcp_conn_peer_name(ci->got_port_info? &ci->rtp_info : NULL),
ci->notify.fi ? "" : " (not sending a notification)");
/* Below ordering is a delicate decision:
*
* We want to
* - emit the resulting event to ci->notify.fi,
* - check whether we want to tx the next pending MGCP message.
* Both these steps may terminate (=deallocate) the ep.
* So whichever one goes first may cause a use-after-free in the other.
*
* When dispatching the FSM event, we don't get an rc indicating dealloc of the FSM -- it may deallocate and we
* cannot tell. The common mechanism for that is osmo_fsm_set_dealloc_ctx(OTC_SELECT) and query the still
* allocated FSM state after termination (here we would check 'if (ci->ep != NULL)'), but we cannot assume the
* caller has actually set up an osmo_fsm_set_dealloc_ctx(). At time of writing, e.g. osmo-hnbgw does not use
* it.
*
* In osmo_mgcpc_ep_fsm_check_state_chg_after_response(), we do get an rc: false means FSM has terminated.
* On termination, the ep emits a term event to the FSM's parent.
* That may cause the notify.fi to be terminated in turn, depending on how the caller set things up.
* So: we cannot store notify.fi before, then call osmo_mgcpc_ep_fsm_check_state_chg_after_response(), and then
* emit the event, because notify.fi may have deallocated. We cannot look up whether
* osmo_mgcpc_ep_cancel_notify() has been called, because ci may have deallocated along with ci->ep.
*
* We have to skip emitting below success event in case the ep is now terminated.
* - It may be the final DLCX OK: not a problem, osmo_mgcpc_ep_ci_dlcx() has no notify args on purpose, so we do
* make all callers not set a notify event for DLCX by design. notify.fi should always be NULL when the final
* DLCX OK terminates the local endpoint state.
* - It may also be sudden termination due to a bad problem, in which case we shouldn't emit success.
* The osmo_fsm_inst.parent_term_event should suffice as feedback to the caller.
*/
if (osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi) == false) {
/* false means, the ci->ep has been terminated. */
return;
}
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 local RTP port information for this connection, i.e. the local port that MGW is receiving on, as
@@ -718,11 +662,11 @@ void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
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;
LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify.fi));
if (ep->endpoint[0]) {
if (ci->verb_info.endpoint[0] && strcmp(ci->verb_info.endpoint, ep->endpoint))
LOG_CI(ci, LOGL_ERROR,
@@ -1053,7 +997,7 @@ static int osmo_mgcpc_ep_fsm_timer_cb(struct osmo_fsm_inst *fi)
return 0;
}
static void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
int i;
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);

View File

@@ -19,7 +19,6 @@
*/
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
#include <osmocom/mgcp_client/mgcp_client_fsm.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/fsm.h>
@@ -115,10 +114,12 @@ static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info
.call_id = info->call_id,
.conn_mode = MGCP_CONN_RECV_ONLY,
.ptime = info->ptime,
.codecs_len = info->codecs_len,
.ptmap_len = info->ptmap_len,
.param_present = info->param_present
};
osmo_strlcpy(mgcp_msg->endpoint, info->endpoint, MGCP_ENDPOINT_MAXLEN);
memcpy(mgcp_msg->codecs, info->codecs, sizeof(mgcp_msg->codecs));
memcpy(mgcp_msg->ptmap, info->ptmap, sizeof(mgcp_msg->ptmap));
memcpy(&mgcp_msg->param, &info->param, sizeof(mgcp_msg->param));
@@ -135,19 +136,10 @@ static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info
static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
{
bool ip_is_set = info->addr[0] != '\0' &&
strncmp(info->addr, "::", sizeof(info->addr)) != 0 &&
strncmp(info->addr, "0.0.0.0", sizeof(info->addr)) != 0;
if (ip_is_set) {
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP;
mgcp_msg->audio_ip = info->addr;
}
if (info->port) {
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_PORT;
mgcp_msg->audio_port = info->port;
}
if (ip_is_set && info->port)
mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT;
mgcp_msg->audio_ip = info->addr;
mgcp_msg->audio_port = info->port;
mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
}
static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer)
@@ -171,10 +163,12 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
.audio_ip = mgcp_ctx->conn_peer_local.addr,
.audio_port = mgcp_ctx->conn_peer_local.port,
.ptime = mgcp_ctx->conn_peer_local.ptime,
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len,
.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len,
.param_present = mgcp_ctx->conn_peer_local.param_present
};
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
memcpy(mgcp_msg.ptmap, mgcp_ctx->conn_peer_local.ptmap, sizeof(mgcp_msg.ptmap));
memcpy(&mgcp_msg.param, &mgcp_ctx->conn_peer_local.param, sizeof(mgcp_ctx->conn_peer_local.param));
@@ -227,7 +221,8 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
mgcp_ctx->conn_peer_local.endpoint);
make_crcx_msg(&mgcp_msg, &mgcp_ctx->conn_peer_local);
add_audio(&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);
@@ -369,21 +364,13 @@ static void fsm_ready_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
switch (event) {
case EV_MDCX:
msg = make_mdcx_msg(mgcp_ctx);
if (!msg) {
/* make_mdcx_msg() should already have logged the error */
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
OSMO_ASSERT(msg);
rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_resp_cb, fi);
new_state = ST_MDCX_RESP;
break;
case EV_DLCX:
msg = make_dlcx_msg(mgcp_ctx);
if (!msg) {
/* make_dlcx_msg() should already have logged the error */
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
return;
}
OSMO_ASSERT(msg);
rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_resp_cb, fi);
new_state = ST_DLCX_RESP;
break;
@@ -624,72 +611,6 @@ static struct osmo_fsm fsm_mgcp_client = {
.log_subsys = DLMGCP,
};
/* Provide backwards compat for deprecated conn_peer->codecs[]: when the caller passes in an mgcp_conn_peer instance
* that has codecs[] set, apply it to ptmap[] instead. */
static void mgcp_conn_peer_compat(struct mgcp_conn_peer *conn_peer)
{
struct ptmap ptmap[MGCP_MAX_CODECS];
unsigned int ptmap_len;
if (!conn_peer->codecs_len)
return;
/* Before dropping codecs[], codecs[] would indicate the order in which the codecs should appear in SDP. ptmap[]
* would indicate payload type numbers when not using a default payload type number (may omit entries).
* Now, ptmap[] just indicates both at the same time; codecs[] should be empty, and ptmap[] lists all codecs.
* So if any codecs[] are present, recreate ptmap[] in the order of codecs[]. */
ptmap_len = 0;
for (int i = 0; i < conn_peer->codecs_len; i++) {
enum mgcp_codecs codec = conn_peer->codecs[i];
struct ptmap *found = NULL;
/* Look up whether a specific pt was indicated for this codec */
for (int p = 0; p < conn_peer->ptmap_len; p++) {
if (conn_peer->ptmap[p].codec != codec)
continue;
found = &conn_peer->ptmap[p];
break;
}
if (found) {
ptmap[ptmap_len] = *found;
} else {
ptmap[ptmap_len] = (struct ptmap){
.codec = codec,
/* some enum mgcp_codecs correspond to their standard PT nr, so for compat: */
.pt = codec,
};
}
ptmap_len++;
}
/* Are there any entries in the old ptmap that were omitted by codecs[]? */
for (int p = 0; p < conn_peer->ptmap_len; p++) {
bool exists = false;
for (int i = 0; i < ptmap_len; i++) {
if (ptmap_cmp(&ptmap[i], &conn_peer->ptmap[p]))
continue;
exists = true;
break;
}
if (exists)
continue;
if (ptmap_len >= ARRAY_SIZE(ptmap))
break;
/* Not present yet, add it to the end */
ptmap[ptmap_len] = conn_peer->ptmap[p];
ptmap_len++;
}
/* Use the new ptmap[], and clear out legacy codecs[]. */
memcpy(conn_peer->ptmap, ptmap, sizeof(conn_peer->ptmap));
conn_peer->ptmap_len = ptmap_len;
conn_peer->codecs_len = 0;
}
/*! allocate FSM, and create a new connection on the MGW.
* \param[in] mgcp MGCP client descriptor.
* \param[in] parent_fi Parent FSM instance.
@@ -704,7 +625,6 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm
struct osmo_fsm_inst *fi;
struct in6_addr ip_test;
mgcp_conn_peer_compat(conn_peer);
OSMO_ASSERT(parent_fi);
OSMO_ASSERT(mgcp);
@@ -744,8 +664,6 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
struct mgcp_ctx *mgcp_ctx = fi->priv;
struct in6_addr ip_test;
mgcp_conn_peer_compat(conn_peer);
OSMO_ASSERT(mgcp_ctx);
OSMO_ASSERT(conn_peer);
@@ -842,7 +760,7 @@ const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info)
return buf;
}
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init(void)
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init()
{
OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0);
}

View File

@@ -18,7 +18,6 @@
*
*/
#include <asm-generic/errno.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
#include <osmocom/mgcp_client/mgcp_client_pool_internal.h>
@@ -28,6 +27,32 @@
#define LOGPPMGW(pool_member, level, fmt, args...) \
LOGP(DLMGCP, level, "MGW-pool(%s) " fmt, mgcp_client_pool_member_name(pool_member), ## args)
/* Get a human readable name for a given pool member. */
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member)
{
const struct mgcp_client *mpcp_client;
struct mgcp_client mpcp_client_dummy;
static char name[512];
const char *description;
if (!pool_member)
return "(null)";
/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the
* initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */
if (!pool_member->client) {
memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual));
mpcp_client = &mpcp_client_dummy;
} else {
mpcp_client = pool_member->client;
}
description = mgcp_client_name(mpcp_client);
snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description);
return name;
}
/*! Allocate MGCP client pool. This is called once on startup and before the pool is used with
* mgcp_client_pool_vty_init(). Since the pool is linked with the VTY it must exist througout the entire runtime.
* \param[in] talloc_ctx talloc context. */
@@ -39,7 +64,7 @@ struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx)
if (!pool)
return NULL;
INIT_LLIST_HEAD(&pool->member_list);
INIT_LLIST_HEAD(&pool->pool);
return pool;
}
@@ -52,11 +77,29 @@ unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool)
struct mgcp_client_pool_member *pool_member;
unsigned int pool_members_initialized = 0;
llist_for_each_entry(pool_member, &pool->member_list, list) {
llist_for_each_entry(pool_member, &pool->pool, list) {
/* Initialize client */
if (mgcp_client_pool_member_reinit_client(pool_member) == 0)
pool_members_initialized++;
pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
if (!pool_member->client) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n");
continue;
}
/* Set backpointer so that we can detect later that this MGCP client is managed
* by this pool. */
pool_member->client->pool = pool;
/* Connect client */
if (mgcp_client_connect2(pool_member->client, 0)) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n",
pool_member->conf.remote_addr, pool_member->conf.remote_port);
talloc_free(pool_member->client);
pool_member->client = NULL;
continue;
}
pool_members_initialized++;
}
return pool_members_initialized;
@@ -75,43 +118,23 @@ void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp
pool->mgcp_client_single = mgcp_client;
}
bool mgcp_client_pool_empty(const struct mgcp_client_pool *pool)
{
return llist_empty(&pool->member_list);
}
/*! Lookup the selected MGCP client config by its reference number */
struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr)
{
struct mgcp_client_pool_member *pool_member;
llist_for_each_entry(pool_member, &pool->member_list, list) {
if (pool_member->nr == nr)
return pool_member;
}
return NULL;
}
/* Not every pool member may have a functional MGCP client, we will run through the pool once until we meet a
* pool member that is suitable (is not blocked, has a client with a working link, has a low load). */
* pool member that is suitable (has a client, is not blocked, has a low load). */
static struct mgcp_client_pool_member *mgcp_client_pool_pick(struct mgcp_client_pool *pool)
{
struct mgcp_client_pool_member *pool_member;
struct mgcp_client_pool_member *pool_member_picked = NULL;
unsigned int n_pool_members = 0;
unsigned int n_pool_members = llist_count(&pool->pool);
llist_for_each_entry(pool_member, &pool->member_list, list) {
n_pool_members++;
bool conn_up = pool_member->client && pool_member->client->conn_up;
if (pool_member->blocked == false && conn_up) {
llist_for_each_entry(pool_member, &pool->pool, list) {
if (pool_member->blocked == false && pool_member->client) {
if (!pool_member_picked)
pool_member_picked = pool_member;
else if (pool_member_picked->refcount > pool_member->refcount)
pool_member_picked = pool_member;
} else {
LOGPPMGW(pool_member, LOGL_DEBUG, "%s -- MGW %u is unusable (blocked=%u, cli=%u, link=%u)\n",
__func__, pool_member->nr, pool_member->blocked, !!pool_member->client, conn_up);
LOGPPMGW(pool_member, LOGL_DEBUG, "MGW pool has %u members -- MGW %u is unusable\n", n_pool_members,
pool_member->nr);
}
}
@@ -140,24 +163,26 @@ struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool)
* by the application code. */
/* When the pool is empty, return a single MGCP client if it is registered. */
if (llist_empty(&pool->member_list) && pool->mgcp_client_single) {
if (llist_empty(&pool->pool) && pool->mgcp_client_single) {
LOGP(DLMGCP, LOGL_DEBUG, "MGW pool is empty -- using (single) MGW %s\n",
mgcp_client_name(pool->mgcp_client_single));
return pool->mgcp_client_single;
}
/* Abort when the pool is empty */
if (llist_empty(&pool->member_list)) {
if (llist_empty(&pool->pool)) {
LOGP(DLMGCP, LOGL_ERROR, "MGW pool is empty -- no MGW available!\n");
return NULL;
}
/* Pick a suitable pool member */
pool_member = mgcp_client_pool_pick(pool);
if (!pool_member)
return NULL;
if (pool_member) {
pool_member->refcount++;
return pool_member->client;
}
return mgcp_client_pool_member_get(pool_member);
return NULL;
}
/*! put an MGCP client back into the pool (decrement reference counter).
@@ -169,130 +194,23 @@ struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool)
void mgcp_client_pool_put(struct mgcp_client *mgcp_client)
{
struct mgcp_client_pool_member *pool_member;
struct mgcp_client_pool *pool;
if (!mgcp_client)
return;
if (!mgcp_client->pool_member)
if (mgcp_client->pool)
pool = mgcp_client->pool;
else
return;
pool_member = mgcp_client->pool_member;
if (pool_member->refcount == 0) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n");
return;
llist_for_each_entry(pool_member, &pool->pool, list) {
if (pool_member->client == mgcp_client) {
if (pool_member->refcount == 0) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n");
return;
}
pool_member->refcount--;
}
}
pool_member->refcount--;
}
/***************************
* mgcp_client_pool_member:
***************************/
/*! Allocate an mgcp_client_pool_member.
* \param[in] pool MGCP client pool descriptor.
* \param[in] nr Reference number of the pool member.
*/
struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr)
{
struct mgcp_client_pool_member *pool_member;
pool_member = talloc_zero(pool, struct mgcp_client_pool_member);
OSMO_ASSERT(pool_member);
mgcp_client_conf_init(&pool_member->conf);
pool_member->pool = pool;
pool_member->nr = nr;
llist_add_tail(&pool_member->list, &pool->member_list);
return pool_member;
}
/*! Free an mgcp_client_pool_member allocated through mgcp_client_pool_member_alloc().
* \param[in] pool_member MGCP client pool descriptor.
*
* It also frees the associated MGCP client if present.
*/
void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member)
{
llist_del(&pool_member->list);
if (pool_member->client) {
mgcp_client_disconnect(pool_member->client);
talloc_free(pool_member->client);
}
talloc_free(pool_member);
}
/*! Recreate and reconnect the MGCP client associated to the pool descriptor.
* \param[in] pool_member MGCP client pool descriptor.
*/
int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member)
{
/* Get rid of a possibly existing old MGCP client instance first */
if (pool_member->client) {
mgcp_client_disconnect(pool_member->client);
talloc_free(pool_member->client);
}
/* Initialize client */
pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
if (!pool_member->client) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n");
return -EINVAL;
}
/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */
pool_member->client->pool_member = pool_member;
/* Connect client */
if (mgcp_client_connect(pool_member->client)) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n",
pool_member->conf.remote_addr, pool_member->conf.remote_port);
talloc_free(pool_member->client);
pool_member->client = NULL;
return -ECONNABORTED;
}
return 0;
}
/* Get a human readable name for a given pool member. */
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member)
{
const struct mgcp_client *mpcp_client;
struct mgcp_client mpcp_client_dummy;
static char name[512];
const char *description;
if (!pool_member)
return "(null)";
/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the
* initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */
if (!pool_member->client) {
memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual));
mpcp_client = &mpcp_client_dummy;
} else {
mpcp_client = pool_member->client;
}
description = mgcp_client_name(mpcp_client);
snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description);
return name;
}
/*! Get the MGCP client associated with the pool reference from the pool (increment reference counter).
* \param[in] pool_member MGCP client pool descriptor.
* \returns MGCP client descriptor, NULL if no member was not ready.
*/
struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member)
{
pool_member->refcount++;
return pool_member->client;
}
/*! Get whether the MGCP client associated with the pool reference is blocked by policy.
* \param[in] pool_member MGCP client pool descriptor.
* \returns true if blocked, false otherwise
*/
bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member)
{
return pool_member->blocked;
}

View File

@@ -1,5 +1,5 @@
/* MGCP client interface to quagga VTY */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
* Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
* (C) 2009 by Harald Welte <laforge@gnumonks.org>
* (C) 2009-2011 by Holger Hans Peter Freyther
@@ -28,16 +28,14 @@
#include <osmocom/vty/command.h>
#include <osmocom/vty/misc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/timer.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
#include <osmocom/mgcp_client/mgcp_client_pool_internal.h>
#include <osmocom/mgcp_client/mgcp_client_pool.h>
#define MGW_STR MGCP_CLIENT_MGW_STR
/* Only common (non-pooled) VTY commands will use this talloc context. All
/* Only common (non-pooled) VTY connands will use this talloc context. All
* pooled VTY commands will use the pool (global_mgcp_client_pool) as
* talloc context. */
static void *global_mgcp_client_ctx = NULL;
@@ -49,46 +47,17 @@ static struct mgcp_client_conf *global_mgcp_client_conf = NULL;
/* Pointer to the MGCP pool that is managed by mgcp_client_pool_vty_init() */
static struct mgcp_client_pool *global_mgcp_client_pool = NULL;
static struct mgcp_client_conf *get_mgcp_client_config(struct vty *vty)
struct mgcp_client_conf *get_mgcp_client_config(struct vty *vty)
{
if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node)
return vty->index;
/* Global single MGCP config, deprecated: */
vty_out(vty, "%% MGCP commands outside of 'mgw' nodes are deprecated. "
"You should consider reading the User Manual and migrating to 'mgw' node.%s",
VTY_NEWLINE);
return global_mgcp_client_conf;
else
return global_mgcp_client_conf;
}
static struct mgcp_client *get_mgcp_client(struct vty *vty)
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
struct mgcp_client_pool_member *pool_member;
if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node) {
llist_for_each_entry(pool_member, &global_mgcp_client_pool->member_list, list) {
/* Find matching the conf pointer: */
if (&pool_member->conf != conf)
continue;
return pool_member->client;
}
}
/* Global single MGCP config, deprecated: */
vty_out(vty, "%% MGCP commands outside of 'mgw' nodes are deprecated. "
"You should consider reading the User Manual and migrating to 'mgw' node.%s",
VTY_NEWLINE);
/* There's no way to obtain the struct mgcp_client in old interface, but anyway it's deprecated. */
return NULL;
}
DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
"local-ip " VTY_IPV46_CMD,
"local bind to connect to MGW from\n"
"mgw local-ip " VTY_IPV46_CMD,
MGW_STR "local bind to connect to MGW from\n"
"local bind IPv4 address\n"
"local bind IPv6 address\n")
{
@@ -103,16 +72,10 @@ ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
"mgcpgw local-ip A.B.C.D",
MGW_STR "local bind to connect to MGCP gateway with\n"
"local bind IP address\n")
ALIAS_DEPRECATED(cfg_mgw_local_ip,
cfg_mgw_mgw_local_ip_cmd,
"mgw local-ip " VTY_IPV46_CMD,
MGW_STR "local bind to connect to MGW from\n"
"local bind IPv4 address\n"
"local bind IPv6 address\n")
DEFUN(cfg_mgw_local_port, cfg_mgw_local_port_cmd,
"local-port <0-65535>",
"local port to connect to MGW from\n"
"mgw local-port <0-65535>",
MGW_STR "local port to connect to MGW from\n"
"local bind port\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
@@ -124,15 +87,10 @@ ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd,
"mgcpgw local-port <0-65535>",
MGW_STR "local bind to connect to MGCP gateway with\n"
"local bind port\n")
ALIAS_DEPRECATED(cfg_mgw_local_port,
cfg_mgw_mgw_local_port_cmd,
"mgw local-port <0-65535>",
MGW_STR "local port to connect to MGW from\n"
"local bind port\n")
DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
"remote-ip " VTY_IPV46_CMD,
"remote IP address to reach the MGW at\n"
"mgw remote-ip " VTY_IPV46_CMD,
MGW_STR "remote IP address to reach the MGW at\n"
"remote IPv4 address\n"
"remote IPv6 address\n")
{
@@ -146,16 +104,10 @@ ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
"mgcpgw remote-ip A.B.C.D",
MGW_STR "remote bind to connect to MGCP gateway with\n"
"remote bind IP address\n")
ALIAS_DEPRECATED(cfg_mgw_remote_ip,
cfg_mgw_mgw_remote_ip_cmd,
"mgw remote-ip " VTY_IPV46_CMD,
MGW_STR "remote IP address to reach the MGW at\n"
"remote IPv4 address\n"
"remote IPv6 address\n")
DEFUN(cfg_mgw_remote_port, cfg_mgw_remote_port_cmd,
"remote-port <0-65535>",
"remote port to reach the MGW at\n"
"mgw remote-port <0-65535>",
MGW_STR "remote port to reach the MGW at\n"
"remote port\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
@@ -167,13 +119,8 @@ ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
"mgcpgw remote-port <0-65535>",
MGW_STR "remote bind to connect to MGCP gateway with\n"
"remote bind port\n")
ALIAS_DEPRECATED(cfg_mgw_remote_port,
cfg_mgw_mgw_remote_port_cmd,
"mgw remote-port <0-65535>",
MGW_STR "remote port to reach the MGW at\n"
"remote port\n")
DEFUN_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgw_mgw_endpoint_range_cmd,
DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
"mgw endpoint-range <1-65534> <1-65534>",
MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
"-\n" "-\n")
@@ -183,7 +130,7 @@ DEFUN_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgw_mgw_endpoint_range_cmd,
VTY_NEWLINE);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
"mgcpgw endpoint-range <1-65534> <1-65534>",
MGW_STR "usable range of endpoint identifiers\n"
"set first useable endpoint identifier\n"
@@ -211,8 +158,8 @@ ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
DEFUN(cfg_mgw_endpoint_domain_name,
cfg_mgw_endpoint_domain_name_cmd,
"endpoint-domain NAME",
"Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
"mgw endpoint-domain NAME",
MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
"Domain name, should be alphanumeric.\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
@@ -225,16 +172,11 @@ DEFUN(cfg_mgw_endpoint_domain_name,
}
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_endpoint_domain_name,
cfg_mgw_mgw_endpoint_domain_name_cmd,
"mgw endpoint-domain NAME",
MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
"Domain name, should be alphanumeric.\n")
DEFUN(cfg_mgw_reset_ep_name,
cfg_mgw_reset_ep_name_cmd,
"reset-endpoint NAME",
"Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
"mgw reset-endpoint NAME",
MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
"e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
{
@@ -272,17 +214,11 @@ DEFUN(cfg_mgw_reset_ep_name,
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_reset_ep_name,
cfg_mgw_mgw_reset_ep_name_cmd,
"mgw reset-endpoint NAME",
MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
"e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
DEFUN(cfg_mgw_no_reset_ep_name,
cfg_mgw_no_reset_ep_name_cmd,
"no reset-endpoint NAME",
NO_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
"no mgw reset-endpoint NAME",
NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
{
struct reset_ep *reset_ep;
@@ -299,96 +235,6 @@ DEFUN(cfg_mgw_no_reset_ep_name,
vty_out(vty, "%% no such endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
ALIAS_DEPRECATED(cfg_mgw_no_reset_ep_name,
cfg_mgw_mgw_no_reset_ep_name_cmd,
"no mgw reset-endpoint NAME",
NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
DEFUN(cfg_mgw_mgw_keepalive_req_interval,
cfg_mgw_mgw_keepalive_req_interval_cmd,
"keepalive request-interval <0-4294967295>",
"Monitor if the MGCP link against MGW is still usable\n"
"Send an MGCP command to the MGW at given interval if no other commands are sent\n"
"The interval at which send MGCP commands (s), 0 to disable\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
struct mgcp_client *mgcp = get_mgcp_client(vty);
conf->keepalive.req_interval_sec = atoi(argv[0]);
if (!mgcp)
return CMD_SUCCESS;
/* If client already exists, apply the change immediately if possible: */
mgcp->actual.keepalive.req_interval_sec = atoi(argv[0]);
if (mgcp->iofd) { /* UDP MGCP socket connected */
if (mgcp->actual.keepalive.req_interval_sec > 0) {
/* Re-schedule: */
osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0);
} else {
if (osmo_timer_pending(&mgcp->keepalive_tx_timer))
osmo_timer_del(&mgcp->keepalive_tx_timer);
/* Assume link is UP by default, so that this MGW can be selected: */
mgcp->conn_up = true;
}
} /* else: wait until connect() to do first scheduling */
return CMD_SUCCESS;
}
DEFUN(cfg_mgw_mgw_keepalive_req_endpoint,
cfg_mgw_mgw_keepalive_req_endpoint_cmd,
"keepalive request-endpoint NAME",
"Monitor if the MGCP link against MGW is still usable\n"
"Use a given endpoint name when sending an MGCP command to the MGW for keepalive purposes\n"
"The name of the endpoint to use\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
struct mgcp_client *mgcp = get_mgcp_client(vty);
OSMO_STRLCPY_ARRAY(conf->keepalive.req_endpoint_name, argv[0]);
if (!mgcp)
return CMD_SUCCESS;
/* If client already exists, apply the change immediately if possible: */
OSMO_STRLCPY_ARRAY(mgcp->actual.keepalive.req_endpoint_name, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgw_mgw_keepalive_timeout,
cfg_mgw_mgw_keepalive_timeout_cmd,
"keepalive timeout <0-4294967295>",
"Monitor if the MGCP link against MGW is still usable\n"
"Consider the link to the MGW to be down after time without receiving any message from it\n"
"The timeout (s), 0 to disable\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
struct mgcp_client *mgcp = get_mgcp_client(vty);
conf->keepalive.timeout_sec = atoi(argv[0]);
if (!mgcp)
return CMD_SUCCESS;
/* If client already exists, apply the change immediately if possible: */
mgcp->actual.keepalive.timeout_sec = atoi(argv[0]);
if (mgcp->iofd) { /* UDP MGCP socket connected */
if (mgcp->actual.keepalive.timeout_sec > 0) {
/* Re-schedule: */
osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0);
} else {
if (osmo_timer_pending(&mgcp->keepalive_rx_timer))
osmo_timer_del(&mgcp->keepalive_rx_timer);
/* Assume link is UP by default, so that this MGW can be selected: */
mgcp->conn_up = true;
}
} /* else: wait until connect() to do first scheduling */
return CMD_SUCCESS;
}
static int config_write(struct vty *vty, const char *indent, struct mgcp_client_conf *conf)
{
@@ -396,47 +242,33 @@ static int config_write(struct vty *vty, const char *indent, struct mgcp_client_
int port;
struct reset_ep *reset_ep;
/* If caller doesn't the MGW pool API (mgcp_client_pool_vty_init was never called),
* then the "mgw" cmd prefix must be added since the old node always contained it.
*/
const char *mgw_prefix = global_mgcp_client_pool ? "" : "mgw ";
if (conf->description) /* description never had "mgw" prefix even on old node: */
if (conf->description)
vty_out(vty, "%sdescription %s%s", indent, conf->description, VTY_NEWLINE);
addr = conf->local_addr;
if (addr)
vty_out(vty, "%s%slocal-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE);
vty_out(vty, "%smgw local-ip %s%s", indent, addr,
VTY_NEWLINE);
port = conf->local_port;
if (port >= 0)
vty_out(vty, "%s%slocal-port %u%s", indent, mgw_prefix,
vty_out(vty, "%smgw local-port %u%s", indent,
(uint16_t)port, VTY_NEWLINE);
addr = conf->remote_addr;
if (addr)
vty_out(vty, "%s%sremote-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE);
vty_out(vty, "%smgw remote-ip %s%s", indent, addr,
VTY_NEWLINE);
port = conf->remote_port;
if (port >= 0)
vty_out(vty, "%s%sremote-port %u%s", indent, mgw_prefix,
vty_out(vty, "%smgw remote-port %u%s", indent,
(uint16_t)port, VTY_NEWLINE);
if (conf->endpoint_domain_name[0])
vty_out(vty, "%s%sendpoint-domain %s%s", indent, mgw_prefix,
vty_out(vty, "%smgw endpoint-domain %s%s", indent,
conf->endpoint_domain_name, VTY_NEWLINE);
llist_for_each_entry(reset_ep, &conf->reset_epnames, list)
vty_out(vty, "%s%sreset-endpoint %s%s", indent, mgw_prefix, reset_ep->name, VTY_NEWLINE);
if (conf->keepalive.req_interval_sec != 0)
vty_out(vty, "%s%skeepalive request-interval %u%s", indent, mgw_prefix,
conf->keepalive.req_interval_sec, VTY_NEWLINE);
if (strncmp(conf->keepalive.req_endpoint_name, MGCP_CLIENT_KEEPALIVE_DEFAULT_ENDP,
sizeof(conf->keepalive.req_endpoint_name)) != 0)
vty_out(vty, "%s%skeepalive request-endpoint %s%s", indent, mgw_prefix,
conf->keepalive.req_endpoint_name, VTY_NEWLINE);
if (conf->keepalive.timeout_sec != 0)
vty_out(vty, "%s%skeepalive timeout %u%s", indent, mgw_prefix,
conf->keepalive.timeout_sec, VTY_NEWLINE);
vty_out(vty, "%smgw reset-endpoint %s%s", indent, reset_ep->name, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -447,11 +279,6 @@ static int config_write(struct vty *vty, const char *indent, struct mgcp_client_
* \returns CMD_SUCCESS on success, CMD_WARNING on error */
int mgcp_client_config_write(struct vty *vty, const char *indent)
{
/* If caller supports MGW pool API (mgcp_client_pool_vty_init was
* called), then skip printing any config in this node and print it when
* the whole 'mgw' node is printed. */
if (global_mgcp_client_pool)
return CMD_SUCCESS;
return config_write(vty, indent, global_mgcp_client_conf);
}
@@ -459,18 +286,15 @@ static void vty_init_common(void *talloc_ctx, int node)
{
global_mgcp_client_ctx = talloc_ctx;
/* deprecated 'mgw' commands ('mgw' prepended as first arg) */
install_lib_element(node, &cfg_mgw_mgw_local_ip_cmd);
install_lib_element(node, &cfg_mgw_mgw_local_port_cmd);
install_lib_element(node, &cfg_mgw_mgw_remote_ip_cmd);
install_lib_element(node, &cfg_mgw_mgw_remote_port_cmd);
install_lib_element(node, &cfg_mgw_mgw_endpoint_range_cmd);
install_lib_element(node, &cfg_mgw_mgw_endpoint_domain_name_cmd);
install_lib_element(node, &cfg_mgw_mgw_reset_ep_name_cmd);
install_lib_element(node, &cfg_mgw_mgw_no_reset_ep_name_cmd);
install_lib_element(node, &cfg_mgw_mgw_keepalive_req_interval_cmd);
install_lib_element(node, &cfg_mgw_mgw_keepalive_req_endpoint_cmd);
install_lib_element(node, &cfg_mgw_mgw_keepalive_timeout_cmd);
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_lib_element(node, &cfg_mgw_reset_ep_name_cmd);
install_lib_element(node, &cfg_mgw_no_reset_ep_name_cmd);
osmo_fsm_vty_add_cmds();
}
@@ -495,62 +319,38 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
vty_init_common(talloc_ctx, node);
}
/* Mark whether user called mgcp_client_pool_config_write() and hence support new API */
static bool mgcp_client_pool_config_write_called = false;
static int _mgcp_client_pool_config_write(struct vty *vty, const char *indent)
static int config_write_pool(struct vty *vty)
{
struct mgcp_client_pool *pool = global_mgcp_client_pool;
struct mgcp_client_pool_member *pool_member;
unsigned int subindent_buf_len;
char *subindent;
unsigned int indent_buf_len = strlen(pool->vty_indent) + 1 + 1;
char *indent = talloc_zero_size(vty, indent_buf_len);
if (!indent)
indent = pool->vty_indent ? : "";
subindent_buf_len = strlen(indent) + 1 + 1;
subindent = talloc_zero_size(vty, subindent_buf_len);
snprintf(indent, indent_buf_len, "%s ", pool->vty_indent);
snprintf(subindent, subindent_buf_len, "%s ", indent);
llist_for_each_entry(pool_member, &pool->member_list, list) {
vty_out(vty, "%smgw %u%s", indent, pool_member->nr, VTY_NEWLINE);
config_write(vty, subindent, &pool_member->conf);
llist_for_each_entry(pool_member, &pool->pool, list) {
vty_out(vty, "%smgw %u%s", pool->vty_indent, pool_member->nr, VTY_NEWLINE);
config_write(vty, indent, &pool_member->conf);
}
/* MGW pool API is supported by user (global_mgcp_client_pool is set
* because mgcp_client_pool_vty_init was called). If single MGW was
* configured through old VTY and no mgw in the new MGW pool VTY is
* replacing it, then output the single MGW converted to the new MGW
* pool VTY. */
if (llist_empty(&pool->member_list) && pool->mgcp_client_single) {
vty_out(vty, "%smgw 0%s", indent, VTY_NEWLINE);
config_write(vty, subindent, global_mgcp_client_conf);
}
talloc_free(subindent);
talloc_free(indent);
return CMD_SUCCESS;
}
/* Deprecated, used for backward compatibility with older users which didn't call
* mgcp_client_pool_config_write(): */
static int config_write_pool(struct vty *vty)
/* Lookup the selected MGCP client config by its reference number */
static struct mgcp_client_pool_member *pool_member_by_nr(unsigned int nr)
{
if (mgcp_client_pool_config_write_called)
return CMD_SUCCESS;
struct mgcp_client_pool_member *pool_member = NULL;
struct mgcp_client_pool_member *pool_member_tmp;
return _mgcp_client_pool_config_write(vty, NULL);
}
llist_for_each_entry(pool_member_tmp, &global_mgcp_client_pool->pool, list) {
if (pool_member_tmp->nr == nr) {
pool_member = pool_member_tmp;
break;
}
}
/*! Write out MGCP client config to VTY.
* \param[in] vty VTY to which we should print.
* \param[in] indent string used for indentation (e.g. " ").
If NULL, indentation passed during mgcp_client_pool_vty_init() will be used.
* \returns CMD_SUCCESS on success, CMD_WARNING on error */
int mgcp_client_pool_config_write(struct vty *vty, const char *indent)
{
/* Tell internal node write function that the user supports calling proper API: */
mgcp_client_pool_config_write_called = true;
return _mgcp_client_pool_config_write(vty, indent);
return pool_member;
}
DEFUN_ATTR(cfg_mgw,
@@ -559,10 +359,13 @@ DEFUN_ATTR(cfg_mgw,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
pool_member = pool_member_by_nr(nr);
if (!pool_member) {
pool_member = mgcp_client_pool_member_alloc(global_mgcp_client_pool, nr);
pool_member = talloc_zero(global_mgcp_client_pool, struct mgcp_client_pool_member);
OSMO_ASSERT(pool_member);
mgcp_client_conf_init(&pool_member->conf);
pool_member->nr = nr;
llist_add_tail(&pool_member->list, &global_mgcp_client_pool->pool);
}
vty->index = &pool_member->conf;
@@ -579,7 +382,7 @@ DEFUN_ATTR(cfg_no_mgw,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
pool_member = pool_member_by_nr(nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
@@ -592,7 +395,12 @@ DEFUN_ATTR(cfg_no_mgw,
return CMD_WARNING;
}
mgcp_client_pool_member_free(pool_member);
llist_del(&pool_member->list);
if (pool_member->client) {
mgcp_client_disconnect(pool_member->client);
talloc_free(pool_member->client);
}
talloc_free(pool_member);
return CMD_SUCCESS;
}
@@ -604,7 +412,7 @@ DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member = NULL;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
pool_member = pool_member_by_nr(nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
@@ -617,10 +425,32 @@ DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd,
return CMD_WARNING;
}
if (mgcp_client_pool_member_reinit_client(pool_member) < 0) {
/* Get rid of a possibly existing old MGCP client instance first */
if (pool_member->client) {
mgcp_client_disconnect(pool_member->client);
talloc_free(pool_member->client);
}
/* Create a new MGCP client instance with the current config */
pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
if (!pool_member->client) {
LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s initalization failed\n",
mgcp_client_pool_member_name(pool_member));
vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s",
mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */
pool_member->client->pool = global_mgcp_client_pool;
/* Connect client */
if (mgcp_client_connect(pool_member->client)) {
LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s connect failed at (%s:%u)\n",
mgcp_client_pool_member_name(pool_member), pool_member->conf.remote_addr,
pool_member->conf.remote_port);
talloc_free(pool_member->client);
pool_member->client = NULL;
vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s",
mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE);
return CMD_WARNING;
@@ -636,7 +466,7 @@ DEFUN_ATTR(mgw_block, mgw_block_cmd,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member = NULL;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
pool_member = pool_member_by_nr(nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
@@ -653,7 +483,7 @@ DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member = NULL;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
pool_member = pool_member_by_nr(nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
@@ -663,27 +493,22 @@ DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd,
return CMD_SUCCESS;
}
DEFUN(mgw_show, mgw_show_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n")
DEFUN(mgw_show, mgw_snow_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n")
{
vty_out(vty, "%% MGW-Pool:%s", VTY_NEWLINE);
struct mgcp_client_pool_member *pool_member;
if (llist_empty(&global_mgcp_client_pool->member_list) && global_mgcp_client_pool->mgcp_client_single) {
if (llist_empty(&global_mgcp_client_pool->pool) && global_mgcp_client_pool->mgcp_client_single) {
vty_out(vty, "%% (pool is empty, single MGCP client will be used)%s", VTY_NEWLINE);
return CMD_SUCCESS;
} else if (llist_empty(&global_mgcp_client_pool->member_list)) {
} else if (llist_empty(&global_mgcp_client_pool->pool)) {
vty_out(vty, "%% (pool is empty)%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
llist_for_each_entry(pool_member, &global_mgcp_client_pool->member_list, list) {
const struct mgcp_client *cli = pool_member->client;
llist_for_each_entry(pool_member, &global_mgcp_client_pool->pool, list) {
vty_out(vty, "%% MGW %s%s", mgcp_client_pool_member_name(pool_member), VTY_NEWLINE);
vty_out(vty, "%% MGCP link: %s,%s%s",
cli && cli->iofd ? "connected" : "disconnected",
cli && cli->conn_up ?
((cli->actual.keepalive.timeout_sec > 0) ? "UP" : "MAYBE") :
"DOWN",
vty_out(vty, "%% mgcp-client: %s%s", pool_member->client ? "connected" : "disconnected",
VTY_NEWLINE);
vty_out(vty, "%% service: %s%s", pool_member->blocked ? "blocked" : "unblocked", VTY_NEWLINE);
vty_out(vty, "%% ongoing calls: %u%s", pool_member->refcount, VTY_NEWLINE);
@@ -695,8 +520,7 @@ DEFUN(mgw_show, mgw_show_cmd, "show mgw-pool", SHOW_STR "Display information abo
* (called once at startup by the application process).
* \param[in] parent_node identifier of the parent node on which the mgw node appears.
* \param[in] mgw_node identifier that should be used with the newly installed MGW node.
* \param[in] indent indentation string to match the indentation in the VTY config.
If NULL, it must be passed explicitly each time mgcp_client_pool_config_write() is called.
* \param[in] indent indentation string to match the indentation in the VTY config
* \param[in] pool user provided memory to store the configured MGCP client (MGW) pool. */
void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool)
{
@@ -704,12 +528,11 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
OSMO_ASSERT(pool);
/* Never allow this function to be called twice on the same pool */
OSMO_ASSERT(!pool->vty_indent);
OSMO_ASSERT(!pool->vty_node);
if (indent) {
pool->vty_indent = talloc_strdup(pool, indent);
OSMO_ASSERT(pool->vty_indent);
}
pool->vty_indent = talloc_strdup(pool, indent);
OSMO_ASSERT(pool->vty_indent);
pool->vty_node = talloc_zero(pool, struct cmd_node);
OSMO_ASSERT(pool->vty_node);
pool->vty_node->node = mgw_node;
@@ -719,19 +542,8 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
install_lib_element(parent_node, &cfg_mgw_cmd);
install_lib_element(parent_node, &cfg_no_mgw_cmd);
/* Note: config_write_pool is deprecated and user is expected to
* manually call mgcp_client_pool_config_write() when printing the VTY
* config */
install_node(pool->vty_node, config_write_pool);
vty_init_common(pool, mgw_node);
install_lib_element(mgw_node, &cfg_mgw_local_ip_cmd);
install_lib_element(mgw_node, &cfg_mgw_local_port_cmd);
install_lib_element(mgw_node, &cfg_mgw_remote_ip_cmd);
install_lib_element(mgw_node, &cfg_mgw_remote_port_cmd);
install_lib_element(mgw_node, &cfg_mgw_rtp_bts_base_port_cmd);
install_lib_element(mgw_node, &cfg_mgw_endpoint_domain_name_cmd);
install_lib_element(mgw_node, &cfg_mgw_reset_ep_name_cmd);
install_lib_element(mgw_node, &cfg_mgw_no_reset_ep_name_cmd);
install_element(mgw_node, &cfg_description_cmd);
@@ -739,7 +551,7 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
install_lib_element(ENABLE_NODE, &mgw_block_cmd);
install_lib_element(ENABLE_NODE, &mgw_unblock_cmd);
install_lib_element_ve(&mgw_show_cmd);
install_lib_element_ve(&mgw_snow_cmd);
global_mgcp_client_pool = pool;
}

View File

@@ -16,18 +16,24 @@ AM_CFLAGS = \
$(NULL)
AM_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
noinst_LTLIBRARIES = \
libosmo-mgcp.la \
noinst_LIBRARIES = \
libosmo-mgcp.a \
$(NULL)
noinst_HEADERS = \
g711common.h \
$(NULL)
libosmo_mgcp_la_SOURCES = \
libosmo_mgcp_a_SOURCES = \
mgcp_protocol.c \
mgcp_network.c \
mgcp_vty.c \
@@ -39,17 +45,8 @@ libosmo_mgcp_la_SOURCES = \
mgcp_stat.c \
mgcp_endp.c \
mgcp_trunk.c \
mgcp_ctrl.c \
mgcp_ratectr.c \
mgcp_e1.c \
mgcp_iuup.c \
$(NULL)
libosmo_mgcp_la_LIBADD = \
$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(NULL)

View File

@@ -24,10 +24,6 @@
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/sdp/sdp_strings.h>
#include <osmocom/sdp/fmtp.h>
#include <errno.h>
/* Helper function to dump codec information of a specified codec to a printable
@@ -120,9 +116,9 @@ void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
* \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] fmtp optional codec parameters (set to NULL when unused).
* \param[in] param optional codec parameters (set to NULL when unused).
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codec_add2(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const char *fmtp)
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
{
int rate;
int channels;
@@ -265,16 +261,12 @@ int mgcp_codec_add2(struct mgcp_conn_rtp *conn, int payload_type, const char *au
}
}
if (fmtp) {
OSMO_STRLCPY_ARRAY(codec->fmtp, fmtp);
if (strlen(codec->fmtp) != strlen(fmtp)) {
LOGP(DLMGCP, LOGL_ERROR, "fmtp too long: %zu > %zu\n", strlen(fmtp), strlen(codec->fmtp));
/* let's just hope what is there is still useful, worst case the call's audio doesn't work */
}
}
/* legacy */
codec->param_present = false;
/* 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;
@@ -284,44 +276,98 @@ error:
return -EINVAL;
}
/*! Legacy compat, use mgcp_codec_add2() instead to be able to pass any fmtp besides AMR octet-align=1.
* 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)
/* 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)
{
const char *fmtp = NULL;
if (param && param->amr_octet_aligned_present)
fmtp = (param->amr_octet_aligned ? OSMO_SDP_STR_AMR_OCTET_ALIGN_1 : OSMO_SDP_STR_AMR_OCTET_ALIGN_0);
/* 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 (!strlen(codec->subtype_name))
return false;
return mgcp_codec_add2(conn, payload_type, audio_name, fmtp);
/* FIXME: implement meaningful checks to make sure that the given codec
* is compatible with the given endpoint */
return true;
}
/*! Decide for one suitable codec
* \param[in] conn related rtp-connection.
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
{
struct mgcp_rtp_end *rtp;
unsigned int i;
struct mgcp_endpoint *endp;
bool codec_assigned = false;
endp = conn->conn->endp;
rtp = &conn->end;
/* This function works on the results the SDP/LCO parser has extracted
* from the MGCP message. The goal is to select a suitable codec for
* the given connection. When transcoding is available, the first codec
* from the codec list is taken without further checking. When
* transcoding is not available, then the choice must be made more
* carefully. Each codec in the list is checked until one is found that
* is rated compatible. The rating is done by the helper function
* is_codec_compatible(), which does the actual checking. */
for (i = 0; i < rtp->codecs_assigned; i++) {
/* When no transcoding is available, avoid codecs that would
* require transcoding. */
if (endp->trunk->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
continue;
}
rtp->codec = &rtp->codecs[i];
codec_assigned = true;
break;
}
/* FIXME: To the reviewes: This is problematic. I do not get why we
* need to reset the packet_duration_ms depending on the codec
* selection. I thought it were all 20ms? Is this to address some
* cornercase. (This piece of code was in the code path before,
* together with the note: "TODO/XXX: Store this per codec and derive
* it on use" */
if (codec_assigned) {
if (rtp->maximum_packet_time >= 0
&& rtp->maximum_packet_time * rtp->codec->frame_duration_den >
rtp->codec->frame_duration_num * 1500)
rtp->packet_duration_ms = 0;
return 0;
}
return -EINVAL;
}
/* 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
*/
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
{
/* Legacy */
if (!codec->fmtp[0]
&& codec->param_present
&& codec->param.amr_octet_aligned_present)
return codec->param.amr_octet_aligned;
return osmo_sdp_fmtp_amr_is_octet_aligned(codec->fmtp);
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 */
/* 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)
{
/* All codec properties must match up, except the payload type number. Even though standardisd payload numbers
* exist for certain situations, the call agent may still assign them freely. Hence we must not insist on equal
* payload type numbers. Also the audio_name is not checked since it is already parsed into subtype_name, rate,
* and channels, which are checked. */
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
return false;
if (codec_a->rate != codec_b->rate)
return false;
if (codec_a->channels != codec_b->channels)
@@ -330,172 +376,61 @@ 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;
/* AMR payload may be formatted in two different payload formats, it is still the same codec but since the
* formatting of the payload is different, conversation is required, so we must treat it as a different
* codec here. */
if (strcmp(codec_a->subtype_name, "AMR") == 0) {
if (mgcp_codec_amr_is_octet_aligned(codec_a) != mgcp_codec_amr_is_octet_aligned(codec_b))
return false;
}
return true;
}
/* Compare two codecs, all parameters must match up, except parameters related to payload formatting (not checked). */
static bool codecs_convertible(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
{
/* OsmoMGW currently has no ability to transcode from one codec to another. However OsmoMGW is still able to
* translate between different payload formats as long as the encoded voice data itself does not change.
* Therefore we must insist on equal codecs but still allow different payload formatting. */
/* In 3G IuUP, AMR may be encapsulated in IuFP, this means even though the codec name and negotiated rate is
* different, the formatting can still be converted by OsmoMGW. Therefore we won't insist on equal
* subtype_name and rate if we detect IuFP and AMR is used on the same tandem. */
if (strcmp(codec_a->subtype_name, "AMR") == 0 && strcmp(codec_b->subtype_name, "VND.3GPP.IUFP") == 0)
goto iufp;
if (strcmp(codec_a->subtype_name, "VND.3GPP.IUFP") == 0 && strcmp(codec_b->subtype_name, "AMR") == 0)
goto iufp;
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
return false;
if (codec_a->rate != codec_b->rate)
return false;
iufp:
if (codec_a->channels != codec_b->channels)
return false;
if (codec_a->frame_duration_num != codec_b->frame_duration_num)
return false;
if (codec_a->frame_duration_den != codec_b->frame_duration_den)
return false;
/* Note: AMR allows to set the RTP payload format to octet-aligned or bandwith-efficient (octet-aligned=0)
* via SDP. This difference concerns payload format only, but not the actual codec. It is not a difference
* within the meaning of this function. */
return true;
}
struct mgcp_rtp_codec *mgcp_codec_find_same(struct mgcp_conn_rtp *conn, struct mgcp_rtp_codec *codec)
/*! Translate a given payload type number that belongs to the packet of a
* source connection to the equivalent payload type number that matches the
* configuration of a destination connection.
* \param[in] conn_src related source rtp-connection.
* \param[in] conn_dst related destination rtp-connection.
* \param[in] payload_type number from the source packet or source connection.
* \returns translated payload type number on success, -EINVAL on failure. */
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type)
{
struct mgcp_rtp_end *rtp_end;
struct mgcp_rtp_end *rtp_src;
struct mgcp_rtp_end *rtp_dst;
struct mgcp_rtp_codec *codec_src = NULL;
struct mgcp_rtp_codec *codec_dst = NULL;
unsigned int i;
unsigned int codecs_assigned;
rtp_end = &conn->end;
rtp_src = &conn_src->end;
rtp_dst = &conn_dst->end;
/* Use the codec information from the source and try to find the equivalent of it on the destination side. In
* the first run we will look for an exact match. */
codecs_assigned = rtp_end->codecs_assigned;
/* Find the codec information that is used on the source side */
codecs_assigned = rtp_src->codecs_assigned;
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
for (i = 0; i < codecs_assigned; i++) {
if (codecs_same(codec, &rtp_end->codecs[i])) {
return &rtp_end->codecs[i];
if (payload_type == rtp_src->codecs[i].payload_type) {
codec_src = &rtp_src->codecs[i];
break;
}
}
if (!codec_src)
return -EINVAL;
return NULL;
}
/* For a given codec, find a convertible codec in the given connection. */
static struct mgcp_rtp_codec *codec_find_convertible(struct mgcp_conn_rtp *conn, struct mgcp_rtp_codec *codec)
{
struct mgcp_rtp_end *rtp_end;
unsigned int i;
unsigned int codecs_assigned;
struct mgcp_rtp_codec *codec_convertible = NULL;
rtp_end = &conn->end;
/* Use the codec information from the source and try to find the equivalent of it on the destination side. In
* the first run we will look for an exact match. */
codec_convertible = mgcp_codec_find_same(conn, codec);
if (codec_convertible)
return codec_convertible;
/* In case we weren't able to find an exact match, we will try to find a match that is the same codec, but the
* payload format may be different. This alternative will require a frame format conversion (i.e. AMR bwe->oe) */
codecs_assigned = rtp_end->codecs_assigned;
/* Use the codec information from the source and try to find the
* equivalent of it on the destination side */
codecs_assigned = rtp_dst->codecs_assigned;
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
for (i = 0; i < codecs_assigned; i++) {
if (codecs_convertible(codec, &rtp_end->codecs[i])) {
codec_convertible = &rtp_end->codecs[i];
if (codecs_same(codec_src, &rtp_dst->codecs[i])) {
codec_dst = &rtp_dst->codecs[i];
break;
}
}
return codec_convertible;
}
/*! Decide for one suitable codec on both of the given connections. In case a destination connection is not available,
* a tentative decision is made.
* \param[inout] conn_src related rtp-connection.
* \param[inout] conn_dst related destination rtp-connection (NULL if not present).
* \returns 0 on success, -EINVAL on failure. */
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst)
{
unsigned int i;
/* In case no destination connection is available (yet), or in case the destination connection exists but has
* no codecs assigned, we are forced to make a simple tentative decision:
* We just use the first codec of the source connection (conn_src) */
OSMO_ASSERT(conn_src->end.codecs_assigned <= MGCP_MAX_CODECS);
if (!conn_dst || conn_dst->end.codecs_assigned == 0) {
if (conn_src->end.codecs_assigned >= 1) {
conn_src->end.codec = &conn_src->end.codecs[0];
return 0;
} else
return -EINVAL;
}
/* Compare all codecs of the source connection (conn_src) to the codecs of the destination connection (conn_dst). In case
* of a match set this codec on both connections. This would be an ideal selection since no codec conversion would be
* required. */
for (i = 0; i < conn_src->end.codecs_assigned; i++) {
struct mgcp_rtp_codec *codec_conn_src = &conn_src->end.codecs[i];
struct mgcp_rtp_codec *codec_conn_dst = mgcp_codec_find_same(conn_dst, codec_conn_src);
if (codec_conn_dst) {
/* We found the a codec that is exactly the same (same codec, same payload format etc.) on both
* sides. We now set this codec on both connections. */
conn_dst->end.codec = codec_conn_dst;
conn_src->end.codec = codec_conn_src;
return 0;
}
}
/* In case we could not find a codec that is exactly the same, let's at least try to find a codec that we are able
* to convert. */
for (i = 0; i < conn_src->end.codecs_assigned; i++) {
struct mgcp_rtp_codec *codec_conn_src = &conn_src->end.codecs[i];
struct mgcp_rtp_codec *codec_conn_dst = codec_find_convertible(conn_dst, codec_conn_src);
if (codec_conn_dst) {
/* We found the a codec that we can convert to. Set each side to its codec. */
conn_dst->end.codec = codec_conn_dst;
conn_src->end.codec = codec_conn_src;
return 0;
}
}
if (conn_dst->end.codecs_assigned)
conn_dst->end.codec = &conn_dst->end.codecs[0];
else
if (!codec_dst)
return -EINVAL;
if (conn_src->end.codecs_assigned)
conn_src->end.codec = &conn_src->end.codecs[0];
else
return -EINVAL;
return 0;
}
/* Check if the codec has a specific AMR mode (octet-aligned or bandwith-efficient) set. */
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
{
if (!codec->fmtp[0]) {
/* Legacy */
return codec->param_present && codec->param.amr_octet_aligned_present;
}
/* Just check for presence, not the actual value. */
return osmo_sdp_fmtp_get_val(NULL, 0, codec->fmtp, OSMO_SDP_STR_AMR_OCTET_ALIGN);
return codec_dst->payload_type;
}
/* Find the payload type number configured for a specific codec by SDP.
@@ -505,7 +440,8 @@ bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
* \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. */
* \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)
{
@@ -521,26 +457,3 @@ const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn
}
return NULL;
}
/*! Lookup a codec that is assigned to a connection by its payload type number.
* \param[in] conn related rtp-connection.
* \param[in] payload_type number of the codec to look up.
* \returns pointer to codec struct on success, NULL on failure. */
struct mgcp_rtp_codec *mgcp_codec_from_pt(struct mgcp_conn_rtp *conn, int payload_type)
{
struct mgcp_rtp_end *rtp_end = &conn->end;
unsigned int codecs_assigned = rtp_end->codecs_assigned;
struct mgcp_rtp_codec *codec = NULL;
size_t i;
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
for (i = 0; i < codecs_assigned; i++) {
if (payload_type == rtp_end->codecs[i].payload_type) {
codec = &rtp_end->codecs[i];
break;
}
}
return codec;
}

View File

@@ -37,7 +37,7 @@
#include <osmocom/core/timer.h>
#include <ctype.h>
static const struct rate_ctr_group_desc rate_ctr_group_desc = {
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
.group_name_prefix = "conn_rtp",
.group_description = "rtp connection statistics",
.class_id = 1,
@@ -95,21 +95,17 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
static atomic_uint rate_ctr_index = 0;
conn_rtp->type = MGCP_RTP_DEFAULT;
/* Osmux specific defaults, only used if conn is later on Osmux-enabled: */
conn_rtp->osmux.state = OSMUX_STATE_DISABLED;
conn_rtp->osmux.local_cid_allocated = false;
conn_rtp->osmux.local_cid = 0;
conn_rtp->osmux.remote_cid_present = false;
conn_rtp->osmux.remote_cid = 0;
conn_rtp->osmux.cid_allocated = false;
conn_rtp->osmux.cid = 0;
/* backpointer to the generic part of the connection */
conn->u.rtp.conn = conn;
end->rtp = NULL;
end->rtcp = NULL;
memset(&end->addr, 0, sizeof(end->addr));
end->rtcp_port = 0;
end->rtp.fd = -1;
end->rtcp.fd = -1;
end->rtp_port = end->rtcp_port = 0;
talloc_free(end->fmtp_extra);
end->fmtp_extra = NULL;
/* Set default values */
end->frames_per_packet = 0; /* unknown */
@@ -117,12 +113,12 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
end->output_enabled = false;
end->maximum_packet_time = -1;
conn_rtp->ctrg = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
if (!conn_rtp->ctrg)
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 = rate_ctr_group_get_ctr(conn_rtp->ctrg, IN_STREAM_ERR_TSTMP_CTR);
conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, OUT_STREAM_ERR_TSTMP_CTR);
conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, IN_STREAM_ERR_TSTMP_CTR);
conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, OUT_STREAM_ERR_TSTMP_CTR);
/* Make sure codec table is reset */
mgcp_codec_reset_all(conn_rtp);
@@ -138,7 +134,7 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
if (mgcp_conn_rtp_is_iuup(conn_rtp))
mgcp_conn_iuup_cleanup(conn_rtp);
mgcp_free_rtp_port(&conn_rtp->end);
rate_ctr_group_free(conn_rtp->ctrg);
rate_ctr_group_free(conn_rtp->rate_ctr_group);
mgcp_codec_reset_all(conn_rtp);
}
@@ -171,8 +167,8 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
struct mgcp_conn *conn;
int rc;
/* Do not allow more than the maximum number of connections */
if (endp->type->max_conns > 0 && llist_count(&endp->conns) >= endp->type->max_conns)
/* Do not allow more then two connections */
if (llist_count(&endp->conns) >= endp->type->max_conns)
return NULL;
/* Create new connection and add it to the list */
@@ -267,7 +263,7 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
{
struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
struct rate_ctr_group *conn_stats = conn_rtp->ctrg;
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
if (all_stats == NULL || conn_stats == NULL)
return;
@@ -356,37 +352,29 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
{
static char str[sizeof(conn->name)+sizeof(conn->id)+256];
char ipbuf[INET6_ADDRSTRLEN];
struct osmo_strbuf sb = { .buf = str, .len = sizeof(str) };
if (!conn)
return "NULL";
if (!conn) {
snprintf(str, sizeof(str), "(null connection)");
return str;
}
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
OSMO_STRBUF_PRINTF(sb, "(%s/%s C:%s r=%s:%u<->l=%s:%u",
conn->name,
mgcp_conn_rtp_type_name(conn->type),
conn->id,
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf) ? : "NULL",
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa),
conn->u.rtp.end.local_addr ? : "NULL",
conn->u.rtp.end.local_port);
switch (conn->u.rtp.type) {
case MGCP_RTP_OSMUX:
OSMO_STRBUF_PRINTF(sb, " CID=%u", conn->u.rtp.osmux.local_cid);
break;
default:
break;
}
OSMO_STRBUF_PRINTF(sb, ")");
/* Dump RTP connection */
snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
"rtp:%u rtcp:%u)",
conn->name,
conn->id,
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
ntohs(conn->u.rtp.end.rtp_port),
ntohs(conn->u.rtp.end.rtcp_port));
break;
default:
/* Should not happen, we should be able to dump
* every possible connection type. */
return "(unknown connection type)";
snprintf(str, sizeof(str), "(unknown connection type)");
break;
}
return str;
@@ -423,10 +411,3 @@ struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp)
return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
}
const struct value_string mgcp_conn_rtp_type_names[] = {
{ MGCP_RTP_DEFAULT, "rtp" },
{ MGCP_RTP_OSMUX, "osmux" },
{ MGCP_RTP_IUUP, "iuup" },
{}
};

View File

@@ -0,0 +1,36 @@
/*
* (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

@@ -222,7 +222,7 @@ static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_dat
osmo_i460_mux_enqueue(endp->e1.schan, msg);
}
/* called by I.460 de-multiplexer; feed output of I.460 demux into TRAU frame sync */
/* 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)
{
@@ -301,6 +301,7 @@ static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int
mgcp_send(endp, 1, NULL, msg, &conn_dst->u.rtp, &conn_dst->u.rtp);
msgb_free(msg);
return;
skip:
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_RX_FAIL_CTR));
@@ -308,8 +309,8 @@ skip:
return;
}
/* handle outgoing E1 traffic */
static void e1_send_ts_frame(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
/* Function to handle outgoing E1 traffic */
static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
{
struct msgb *msg = msgb_alloc_c(trunk, E1_TS_BYTES, "E1-TX-timeslot-bytes");
uint8_t *ptr;
@@ -358,17 +359,19 @@ static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
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_frame(ts, trunk);
e1_send(ts, trunk);
/* e1inp_rx_ts(), the caller of this callback does not free() msgb. */
/* e1inp_rx_ts() does not free() msgb */
msgb_free(msg);
}
static int e1_open(struct mgcp_trunk *trunk, uint8_t ts_nr)
static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
{
/*! One E1 timeslot may serve multiple I.460 subslots. The timeslot is opened as soon as an I.460 subslot is
* opened and will stay open until the last I.460 subslot is closed (see e1_close below). This function must
* be called any time a new I.460 subslot is opened in order to maintain constancy of the ts_usecount counter. */
/*! 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;
@@ -376,14 +379,12 @@ static int e1_open(struct mgcp_trunk *trunk, uint8_t ts_nr)
OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
cfg = trunk->cfg;
if (trunk->e1.ts_usecount[ts_nr - 1] > 0) {
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u already set up and in use by %u subslot(s), using it as it is...\n",
ts_nr, trunk->e1.ts_usecount[ts_nr - 1]);
trunk->e1.ts_usecount[ts_nr - 1]++;
if (trunk->e1.ts_in_use[ts_nr - 1]) {
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u already set up, skipping...\n", ts_nr);
return 0;
}
/* Find E1 line */
/* Get E1 line */
e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
if (!e1_line) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n",
@@ -400,61 +401,12 @@ static int e1_open(struct mgcp_trunk *trunk, uint8_t ts_nr)
}
rc = e1inp_line_update(e1_line);
if (rc < 0) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 line %u.\n", ts_nr);
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 timeslot %u.\n", ts_nr);
return -EINVAL;
}
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u set up successfully.\n", ts_nr);
trunk->e1.ts_usecount[ts_nr - 1]++;
OSMO_ASSERT(trunk->e1.ts_usecount[ts_nr - 1] == 1);
return 0;
}
static int e1_close(struct mgcp_trunk *trunk, uint8_t ts_nr)
{
/* See also comment above (e1_open). This function must be called any time an I.460 subslot is closed */
struct e1inp_line *e1_line;
int rc;
OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
cfg = trunk->cfg;
if (trunk->e1.ts_usecount[ts_nr - 1] > 1) {
trunk->e1.ts_usecount[ts_nr - 1]--;
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u still in use by %u other subslot(s), leaving it open...\n",
ts_nr, trunk->e1.ts_usecount[ts_nr - 1]);
return 0;
} else if (trunk->e1.ts_usecount[ts_nr - 1] == 0) {
/* This should not be as it means we close the timeslot too often. */
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "E1 timeslot %u already closed, leaving it as it is...\n", ts_nr);
return -EINVAL;
}
/* Find E1 line */
e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
if (!e1_line) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n",
trunk->e1.vty_line_nr);
return -EINVAL;
}
/* Release E1 timeslot */
rc = e1inp_ts_config_none(&e1_line->ts[ts_nr - 1], e1_line);
if (rc < 0) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to disable E1 timeslot %u.\n", ts_nr);
return -EINVAL;
}
rc = e1inp_line_update(e1_line);
if (rc < 0) {
LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 line %u.\n", trunk->e1.vty_line_nr);
return -EINVAL;
}
LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u closed.\n", ts_nr);
trunk->e1.ts_usecount[ts_nr - 1]--;
OSMO_ASSERT(trunk->e1.ts_usecount[ts_nr - 1] == 0);
trunk->e1.ts_in_use[ts_nr - 1] = true;
return 0;
}
@@ -544,7 +496,7 @@ static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
}
}
/*! Equip E1 endpoint with I.460 mux and E1 timeslot resources.
/* !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.
@@ -565,7 +517,7 @@ int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8
endp->e1.last_amr_ft = AMR_4_75;
/* Set up E1 line / timeslot */
rc = e1_open(endp->trunk, ts);
rc = e1_init(endp->trunk, ts);
if (rc != 0)
return -EINVAL;
@@ -652,15 +604,9 @@ void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
}
/*! Remove E1 resources from endpoint
* \param[in] endp endpoint to release.
* \param[in] ts E1 timeslot number. */
void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts)
* \param[in] endp endpoint to release. */
void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
{
/* Guard against multiple calls. In case we don't see a subchannel anymore we can safely assume that all work
* is done. */
if (!(endp->e1.schan || endp->e1.trau_rtp_st || endp->e1.trau_sync_fi))
return;
LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
if (endp->e1.schan)
@@ -669,10 +615,8 @@ void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts)
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));
/* Close E1 timeslot */
e1_close(endp->trunk, ts);
memset(&endp->e1, 0, sizeof(endp->e1));
}
/*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit.

View File

@@ -38,6 +38,7 @@
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,
},
@@ -109,6 +110,36 @@ struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
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");
/* Normally this function should only be called when
* all connections have been removed already. In case
* that there are still connections open (e.g. when
* RSIP is executed), free them all at once. */
mgcp_conn_free_all(endp);
/* We must only decrement the stat item when the endpoint as actually
* claimed. An endpoint is claimed when a call-id is set */
if (endp->callid)
osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common,
TRUNK_STAT_ENDPOINTS_USED), 1);
/* Reset endpoint parameters and states */
talloc_free(endp->callid);
endp->callid = NULL;
talloc_free(endp->local_options.string);
endp->local_options.string = NULL;
talloc_free(endp->local_options.codec);
endp->local_options.codec = NULL;
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,
@@ -156,6 +187,7 @@ static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
}
}
/*! 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
@@ -228,17 +260,6 @@ bool mgcp_endp_is_wildcarded(const char *epname)
return false;
}
/*! Check if the given epname refers to a "null" endpoint.
* \param[in] epname endpoint name to check
* \returns true if epname refers to "null"" endpoint, else false. */
bool mgcp_endp_is_null(const char *epname)
{
if (strncasecmp(epname, "null@", 5) == 0)
return true;
return false;
}
/*! 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.
@@ -654,36 +675,3 @@ void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
if (llist_empty(&endp->conns))
mgcp_endp_release(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");
/* Normally this function should only be called when
* all connections have been removed already. In case
* that there are still connections open (e.g. when
* RSIP is executed), free them all at once. */
mgcp_conn_free_all(endp);
/* We must only decrement the stat item when the endpoint as actually
* claimed. An endpoint is claimed when a call-id is set */
if (endp->callid)
osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common,
TRUNK_STAT_ENDPOINTS_USED), 1);
/* Reset endpoint parameters and states */
talloc_free(endp->callid);
endp->callid = NULL;
talloc_free(endp->local_options.string);
endp->local_options.string = NULL;
talloc_free(endp->local_options.codec);
endp->local_options.codec = NULL;
if (endp->trunk->trunk_type == MGCP_TRUNK_E1) {
uint8_t ts = e1_ts_nr_from_epname(endp->name);
mgcp_e1_endp_release(endp, ts);
}
}

View File

@@ -98,11 +98,7 @@ static int8_t _conn_iuup_amr_ft_2_rfci(struct mgcp_conn_rtp *conn_rtp, uint8_t f
uint8_t rfci_cnt = 0;
unsigned match_bytes = (unsigned)osmo_amr_bytes(ft);
struct osmo_iuup_rnl_prim *irp = conn_rtp->iuup.init_ind;
if (!irp) {
/* No IuUP Initialization has occured on the IuUP side yet. Return error and drop the RTP data, until
* the IuUP Initialization has configured the link. */
return -1;
}
OSMO_ASSERT(irp);
/* TODO: cache this somehow */
for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) {
@@ -274,9 +270,9 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
return ft;
}
msgb_pull_to_l3(msg);
LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR: ft %d, len %d\n", ft, msgb_l3len(msg));
if (mgcp_codec_amr_is_octet_aligned(conn_rtp_dst->end.codec)) {
LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR OA: ft %d, len %d\n", ft, msgb_length(msg));
amr_hdr = (struct amr_hdr *) msgb_push(msg, sizeof(struct amr_hdr));
amr_hdr->cmr = 15; /* no change */
amr_hdr->f = 0;
@@ -295,7 +291,6 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
amr_data[1] |= ((ft & 0x1) << 7) | (((!fqc) & 0x1) << 6);
amr_length = (osmo_amr_bits(ft) + 10 + 7) / 8;
msgb_trim(msg, amr_length);
LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR BE: ft %d, len %zd\n", ft, amr_length);
}
rtp_hdr = (struct rtp_hdr *) msgb_push(msg, sizeof(*rtp_hdr));
*rtp_hdr = (struct rtp_hdr){
@@ -311,6 +306,7 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
};
rc = mgcp_send(conn_rtp_dst->conn->endp, true, NULL, msg, conn_rtp_src, conn_rtp_dst);
msgb_free(msg);
return rc;
}
@@ -346,7 +342,8 @@ static int _conn_iuup_rx_rnl_data(struct mgcp_conn_rtp *conn_rtp_src, struct osm
return bridge_iuup_to_iuup_peer(conn_rtp_src, conn_rtp_dst, irp);
case MGCP_RTP_DEFAULT:
return bridge_iuup_to_rtp_peer(conn_rtp_src, conn_rtp_dst, irp);
case MGCP_RTP_OSMUX:
case MGCP_OSMUX_BSC:
case MGCP_OSMUX_BSC_NAT:
default:
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
"Forward of IuUP payload to RTP connection type %u not supported!\n",
@@ -468,7 +465,7 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
struct rtp_hdr *hdr = (struct rtp_hdr *)msgb_data(msg);
int buflen = msgb_length(msg);
char *dest_name;
int rc;
int len;
OSMO_ASSERT(conn_src);
OSMO_ASSERT(conn_dst);
@@ -493,7 +490,7 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
"rtp_port:%u rtcp_port:%u\n",
dest_name,
osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
);
return 0;
}
@@ -505,25 +502,27 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
hdr->sequence = osmo_htons(rtp_state->alt_rtp_tx_sequence);
hdr->ssrc = rtp_state->alt_rtp_tx_ssrc;
rtp_state->alt_rtp_tx_sequence++;
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"process/send IuUP to %s %s rtp_port:%u rtcp_port:%u\n",
dest_name, osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port));
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port));
/* Forward a copy of the RTP data to a debug ip/port */
forward_data_tap(rtp_end->rtp, &conn_src->tap_out, msg);
forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
msg);
rc = mgcp_udp_send(rtp_end->rtp, &rtp_end->addr, (char *)hdr, buflen);
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
(char *)hdr, buflen);
if (rc < 0)
return rc;
if (len <= 0)
return len;
rtpconn_rate_ctr_add(conn_dst, endp, RTP_PACKETS_TX_CTR, 1);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, buflen);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
rtp_state->alt_rtp_tx_sequence++;
return 0;
return len;
}
/* Received TNL primitive from IuUP layer FSM, transmit it further down to the
@@ -619,8 +618,9 @@ int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg)
force_output_enabled = true;
/* Fill in the peer address so that we can send Init-ACK back: */
prev_rem_addr = conn_rtp_src->end.addr;
prev_rem_rtp_port = osmo_sockaddr_port(&conn_rtp_src->end.addr.u.sa);
prev_rem_rtp_port = conn_rtp_src->end.rtp_port;
conn_rtp_src->end.addr = *mc->from_addr;
conn_rtp_src->end.rtp_port = htons(osmo_sockaddr_port(&mc->from_addr->u.sa));
}
rc = _conn_iuup_rtp_pl_up(conn_rtp_src, msg);
@@ -628,7 +628,7 @@ int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg)
if (force_output_enabled) {
conn_rtp_src->end.output_enabled = prev_output_enabled;
conn_rtp_src->end.addr = prev_rem_addr;
osmo_sockaddr_set_port(&conn_rtp_src->end.addr.u.sa, prev_rem_rtp_port);
conn_rtp_src->end.rtp_port = prev_rem_rtp_port;
}
return rc;
@@ -638,7 +638,7 @@ free_ret:
}
/* Build IuUP RNL Data primitive from msg containing an incoming RTP pkt from
* peer and send it down the IuUP layer towards the destination as IuUP/RTP. Takes ownership of msg. */
* peer and send it down the IuUP layer towards the destination as IuUP/RTP: */
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg)
{
struct osmo_iuup_rnl_prim *irp;
@@ -670,7 +670,7 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
"Bridge RTP=>IuUP: too short for AMR OA hdr (%u)\n", msgb_length(msg));
goto free_ret;
}
if (!osmo_amr_ft_valid(amr_hdr->ft)) {
if (amr_hdr->ft >= AMR_FT_MAX) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR OA ft=%u\n", amr_hdr->ft);
goto free_ret;
}
@@ -678,22 +678,19 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR OA ft=%u\n", amr_hdr->ft);
goto free_ret;
}
irp->u.data.fqc = amr_hdr->q ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD;
irp->u.data.fqc = amr_hdr->q;
irp->u.data.rfci = rfci;
msgb_pull(msg, 2);
LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR OA -> IuUP: ft %d -> rfci %d len %d\n",
amr_hdr->ft, rfci, msgb_length(msg));
} else {
uint8_t *amr_bwe_hdr = (uint8_t *) msgb_data(msg);
int8_t ft;
uint8_t q;
if (msgb_length(msg) < 2) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE,
"Bridge RTP=>IuUP: too short for AMR BE hdr (%u)\n", msgb_length(msg));
goto free_ret;
}
ft = ((amr_bwe_hdr[0] & 0x07) << 1) | ((amr_bwe_hdr[1] & 0x80) >> 7);
if (!osmo_amr_ft_valid(ft)) {
if (ft >= AMR_FT_MAX) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR BE ft=%u\n", ft);
goto free_ret;
}
@@ -701,8 +698,7 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR BE ft=%u\n", ft);
goto free_ret;
}
q = amr_bwe_hdr[1] & 0x40;
irp->u.data.fqc = q ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD;
irp->u.data.fqc = ((amr_bwe_hdr[1] & 0x40) >> 6);
irp->u.data.rfci = rfci;
rc = iuup_length = osmo_amr_bwe_to_iuup(msgb_data(msg), msgb_length(msg));
if (rc < 0) {
@@ -710,8 +706,6 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
return rc;
}
msgb_trim(msg, iuup_length);
LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR BE -> IuUP: ft %d -> rfci %d len %d\n",
ft, rfci, msgb_length(msg));
}
irp->oph.msg->l3h = msgb_put(irp->oph.msg, msgb_length(msg));

View File

@@ -75,7 +75,7 @@ void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
}
/*! Parse connection mode.
* \param[in] mode as string (recvonly, sendrecv, sendonly confecho or loopback)
* \param[in] mode as string (recvonly, sendrecv, sendonly or loopback)
* \param[in] endp pointer to endpoint (only used for log output)
* \param[out] associated connection to be modified accordingly
* \returns 0 on success, -1 on error */
@@ -100,8 +100,6 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
conn->mode = MGCP_CONN_RECV_SEND;
else if (strcasecmp(mode, "sendonly") == 0)
conn->mode = MGCP_CONN_SEND_ONLY;
else if (strcasecmp(mode, "confecho") == 0)
conn->mode = MGCP_CONN_CONFECHO;
else if (strcasecmp(mode, "loopback") == 0)
conn->mode = MGCP_CONN_LOOPBACK;
else {
@@ -204,7 +202,7 @@ int mgcp_parse_osmux_cid(const char *line)
osmux_cid, OSMUX_CID_MAX);
return -2;
}
LOGP(DLMGCP, LOGL_DEBUG, "MGCP client offered Osmux CID %u\n", osmux_cid);
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
return osmux_cid;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -47,7 +47,6 @@
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_iuup.h>
#include <osmocom/mgcp/debug.h>
/* Contains the last successfully resolved endpoint name. This variable is used
* for the unit-tests to verify that the endpoint was correctly resolved. */
@@ -86,9 +85,6 @@ struct mgcp_request_data {
/* set to true when the request has been classified as wildcarded */
bool wildcarded;
/* Set to true when the request is targeted at the "null" endpoint */
bool null_endp;
/* contains cause code in case of problems during endp/trunk resolution */
int mgcp_cause;
};
@@ -154,7 +150,8 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
switch (conn->type) {
case MGCP_RTP_DEFAULT:
case MGCP_RTP_OSMUX:
case MGCP_OSMUX_BSC:
case MGCP_OSMUX_BSC_NAT:
case MGCP_RTP_IUUP:
break;
default:
@@ -304,7 +301,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
/* Attach optional OSMUX parameters */
if (mgcp_conn_rtp_is_osmux(conn)) {
rc = msgb_printf(sdp, MGCP_X_OSMO_OSMUX_HEADER " %u\r\n", conn->osmux.local_cid);
rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
if (rc < 0)
goto error;
}
@@ -327,13 +324,8 @@ error:
* osmux connection, send the dummy packet via OSMUX */
static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
/* Avoid sending dummy packet if the remote address was not yet
* configured through CRCX/MDCX: */
if (!mgcp_rtp_end_remote_addr_available(&conn->end))
return;
if (mgcp_conn_rtp_is_osmux(conn))
osmux_send_dummy(conn);
if (conn->osmux.state != OSMUX_STATE_DISABLED)
osmux_send_dummy(endp, conn);
else
mgcp_send_dummy(endp, conn);
}
@@ -393,10 +385,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
/* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
rq.pdata = &pdata;
rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);
if (!rq.wildcarded)
rq.null_endp = mgcp_endp_is_null(pdata.epname);
if (!rq.null_endp)
rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
rq.mgcp_cause = rc;
if (!rq.endp) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
@@ -413,14 +402,14 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
rq.name, pdata.epname);
return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
}
} else if (!rq.null_endp) {
} else {
/* If the endpoint name suggests that the request refers to a specific endpoint, then the
* request cannot be handled and we must stop early. */
LOGP(DLMGCP, LOGL_NOTICE,
"%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
pdata.epname, -rq.mgcp_cause);
return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
} /* else: Handle special "null" endpoint below (with rq.endp=NULL, rq.trunk=NULL) */
}
} else {
osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));
rq.trunk = rq.endp->trunk;
@@ -466,11 +455,6 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
{
LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
/* Auditing "null" endpoint is allowed for keepalive purposes. There's no rq->endp nor rq->trunk in this case. */
if (rq->null_endp)
return create_ok_response(rq->pdata->cfg, NULL, 200, "AUEP", rq->pdata->trans);
if (!rq->endp || !mgcp_endp_avail(rq->endp)) {
LOGPENDP(rq->endp, DLMGCP, LOGL_ERROR, "AUEP: selected endpoint not available!\n");
return create_err_response(rq->trunk, NULL, 501, "AUEP", rq->pdata->trans);
@@ -672,8 +656,8 @@ static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
case 'a':
/* FIXME: LCO also supports the negotiation of more than one codec.
* (e.g. a:PCMU;G726-32) But this implementation only supports a single
* codec only. Ignoring all but the first codec. */
if (sscanf(lco_id + 1, ":%16[^,;]", codec) == 1) {
* codec only. */
if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
talloc_free(lco->codec);
/* MGCP header is case insensive, and we'll need
codec in uppercase when using it later: */
@@ -725,7 +709,7 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
"Configuring RTP endpoint: local port %d%s%s\n",
osmo_sockaddr_port(&rtp->addr.u.sa),
ntohs(rtp->rtp_port),
rtp->force_aligned_timing ? ", force constant timing" : "",
rtp->force_constant_ssrc ? ", force constant ssrc" : "");
}
@@ -756,12 +740,12 @@ uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
*/
static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
{
if (!endp->trunk->cfg->osmux.initialized) {
if (osmux_init(endp->trunk) < 0) {
LOGPENDP(endp, DOSMUX, LOGL_ERROR, "Cannot init OSMUX\n");
if (!endp->trunk->cfg->osmux_init) {
if (osmux_init(OSMUX_ROLE_BSC, endp->trunk->cfg) < 0) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
return -3;
}
LOGPENDP(endp, DOSMUX, LOGL_NOTICE, "OSMUX socket has been set up\n");
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
}
return mgcp_parse_osmux_cid(line);
@@ -772,9 +756,6 @@ static int handle_codec_info(struct mgcp_conn_rtp *conn,
struct mgcp_request_data *rq, int have_sdp, bool crcx)
{
struct mgcp_endpoint *endp = rq->endp;
struct mgcp_conn *conn_dst;
struct mgcp_conn_rtp *conn_dst_rtp;
int rc;
char *cmd;
@@ -816,15 +797,8 @@ static int handle_codec_info(struct mgcp_conn_rtp *conn,
goto error;
}
/* Try to find an destination RTP connection that we can include in the codec decision. */
conn_dst = mgcp_find_dst_conn(conn->conn);
if (conn_dst && conn_dst->type == MGCP_CONN_TYPE_RTP)
conn_dst_rtp = &conn_dst->u.rtp;
else
conn_dst_rtp = NULL;
/* Make codec decision */
if (mgcp_codec_decide(conn, conn_dst_rtp) != 0)
if (mgcp_codec_decide(conn) != 0)
goto error;
return 0;
@@ -866,13 +840,13 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
struct mgcp_parse_data *pdata = rq->pdata;
struct mgcp_trunk *trunk = rq->trunk;
struct mgcp_endpoint *endp = rq->endp;
struct rate_ctr_group *rate_ctrs;
struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
int error_code = 400;
const char *local_options = NULL;
const char *callid = NULL;
const char *mode = NULL;
char *line;
int have_sdp = 0, remote_osmux_cid = -2;
int have_sdp = 0, osmux_cid = -2;
struct mgcp_conn_rtp *conn = NULL;
struct mgcp_conn *_conn = NULL;
char conn_name[512];
@@ -880,14 +854,6 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
if (rq->null_endp) {
/* trunk not available so rate_ctr aren't available either. */
LOGP(DLMGCP, LOGL_ERROR, "CRCX: Not allowed in 'null' endpoint!\n");
return create_err_response(pdata->cfg, NULL, 502, "CRCX", pdata->trans);
}
rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
/* we must have a free ep */
if (!endp) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
@@ -927,9 +893,9 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
case 'X':
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
/* If osmux is disabled, just skip setting it up */
if (rq->endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
if (!rq->endp->trunk->cfg->osmux)
break;
remote_osmux_cid = mgcp_osmux_setup(endp, line);
osmux_cid = mgcp_osmux_setup(endp, line);
break;
}
@@ -967,7 +933,7 @@ mgcp_header_done:
}
/* Check if we are able to accept the creation of another connection */
if (endp->type->max_conns > 0 && llist_count(&endp->conns) >= endp->type->max_conns) {
if (llist_count(&endp->conns) >= endp->type->max_conns) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
"CRCX: endpoint full, max. %i connections allowed!\n",
endp->type->max_conns);
@@ -1032,17 +998,16 @@ mgcp_header_done:
goto error2;
}
/* If X-Osmux (remote CID) was received (-1 is wilcard), alloc next avail CID as local CID */
if (remote_osmux_cid >= -1) {
if (osmux_init_conn(conn) < 0) {
/* Annotate Osmux circuit ID and set it to negotiating state until this
* is fully set up from the dummy load. */
conn->osmux.state = OSMUX_STATE_DISABLED;
if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
conn->osmux.state = OSMUX_STATE_ACTIVATING;
if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
goto error2;
}
if (remote_osmux_cid >= 0) {
conn->osmux.remote_cid_present = true;
conn->osmux.remote_cid = remote_osmux_cid;
}
} else if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_ONLY) {
} else if (endp->trunk->cfg->osmux == OSMUX_USAGE_ONLY) {
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
"CRCX: osmux only and no osmux offered\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
@@ -1055,7 +1020,7 @@ mgcp_header_done:
&endp->local_options, local_options);
if (rc != 0) {
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
"CRCX: invalid local connection options!\n");
"CRCX: inavlid local connection options!\n");
error_code = rc;
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
goto error2;
@@ -1076,6 +1041,9 @@ mgcp_header_done:
rc = mgcp_conn_iuup_init(conn);
}
conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
trunk->audio_fmtp_extra);
if (pdata->cfg->force_ptime) {
conn->end.packet_duration_ms = pdata->cfg->force_ptime;
conn->end.force_output_ptime = 1;
@@ -1083,12 +1051,20 @@ mgcp_header_done:
mgcp_rtp_end_config(endp, 0, &conn->end);
/* Find a local address for conn based on policy and initial SDP remote
information, then find a free port for it */
if (mgcp_get_local_addr(conn->end.local_addr, conn) < 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
/* check connection mode setting */
if (conn->conn->mode != MGCP_CONN_LOOPBACK
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
&& conn->end.rtp_port == 0) {
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
"CRCX: selected connection mode type requires an opposite end!\n");
error_code = 527;
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
goto error2;
}
/* Find a local address for conn based on policy and initial SDP remote
information, then find a free port for it */
mgcp_get_local_addr(conn->end.local_addr, conn);
if (allocate_port(endp, conn) != 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
goto error2;
@@ -1101,26 +1077,18 @@ mgcp_header_done:
goto error2;
}
/* Notify Osmux conn that CRCX was received */
if (mgcp_conn_rtp_is_osmux(conn)) {
if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n");
goto error2;
}
}
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
"CRCX: Creating connection: port: %u\n", conn->end.local_port);
/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
mgcp_rtp_end_remote_addr_available(&conn->end) &&
trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
send_dummy(endp, conn);
LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
"CRCX: connection successfully created\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
mgcp_endp_update(endp);
@@ -1140,7 +1108,7 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
struct mgcp_parse_data *pdata = rq->pdata;
struct mgcp_trunk *trunk = rq->trunk;
struct mgcp_endpoint *endp = rq->endp;
struct rate_ctr_group *rate_ctrs;
struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
char new_local_addr[INET6_ADDRSTRLEN];
int error_code = 500;
int silent = 0;
@@ -1150,19 +1118,11 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
const char *mode = NULL;
struct mgcp_conn_rtp *conn = NULL;
const char *conn_id = NULL;
int remote_osmux_cid = -2;
int osmux_cid = -2;
int rc;
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
if (rq->null_endp) {
/* trunk not available so rate_ctr aren't available either. */
LOGP(DLMGCP, LOGL_ERROR, "MDCX: Not allowed in 'null' endpoint!\n");
return create_err_response(pdata->cfg, NULL, 502, "MDCX", pdata->trans);
}
rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
/* Prohibit wildcarded requests */
if (rq->wildcarded) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
@@ -1214,9 +1174,9 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
case 'X':
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
/* If osmux is disabled, just skip setting it up */
if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
if (!endp->trunk->cfg->osmux)
break;
remote_osmux_cid = mgcp_osmux_setup(endp, line);
osmux_cid = mgcp_osmux_setup(endp, line);
break;
}
/* Ignore unknown X-headers */
@@ -1280,45 +1240,43 @@ mgcp_header_done:
error_code = rc;
goto error3;
}
/* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */
/* TODO: "codec" probably needs to be moved from endp to conn */
if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0)
rc = mgcp_conn_iuup_init(conn);
/* check connection mode setting */
if (conn->conn->mode != MGCP_CONN_LOOPBACK
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
&& !mgcp_rtp_end_remote_addr_available(&conn->end)) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: selected connection mode type requires an opposite end!\n");
error_code = 527;
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
goto error3;
}
if (mgcp_conn_rtp_is_osmux(conn)) {
OSMO_ASSERT(conn->osmux.local_cid_allocated);
if (remote_osmux_cid < -1) {
OSMO_ASSERT(conn->osmux.cid_allocated);
if (osmux_cid < -1) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: Failed to parse Osmux CID!\n");
goto error3;
} else if (remote_osmux_cid == -1) {
} else if (osmux_cid == -1) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: wilcard in MDCX is not supported!\n");
goto error3;
} else if (conn->osmux.remote_cid_present &&
remote_osmux_cid != conn->osmux.remote_cid) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: changing already allocated CID is not supported!\n");
goto error3;
} else {
conn->osmux.remote_cid_present = true;
conn->osmux.remote_cid = remote_osmux_cid;
if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: Osmux handling failed!\n");
goto error3;
}
} else if (osmux_cid != (int) conn->osmux.cid) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: changing already allocated CID is not supported!\n");
goto error3;
}
/* TODO: In the future (when we have recvCID!=sendCID), we need to
tell Osmux code that osmux_cid is to be used as sendCID for
that conn. */
}
/* MDCX may have provided a new remote address, which means we may need
to update our announced IP addr and re-bind our local end. This can
happen for instance if MGW initially provided an IPv4 during CRCX
ACK, and now MDCX tells us the remote has an IPv6 address. */
if (mgcp_get_local_addr(new_local_addr, conn) < 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
goto error3;
}
mgcp_get_local_addr(new_local_addr, conn);
if (strcmp(new_local_addr, conn->end.local_addr)) {
osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
mgcp_free_rtp_port(&conn->end);
@@ -1342,6 +1300,7 @@ mgcp_header_done:
/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
mgcp_rtp_end_remote_addr_available(&conn->end) &&
trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
send_dummy(endp, conn);
@@ -1367,7 +1326,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
struct mgcp_parse_data *pdata = rq->pdata;
struct mgcp_trunk *trunk = rq->trunk;
struct mgcp_endpoint *endp = rq->endp;
struct rate_ctr_group *rate_ctrs;
struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
int error_code = 400;
int silent = 0;
char *line;
@@ -1377,18 +1336,10 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
unsigned int i;
/* NOTE: In this handler we can not take it for granted that the endp
* pointer will be populated, however a trunk is always guaranteed (except for 'null' endp).
*/
* pointer will be populated, however a trunk is always guaranteed. */
LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n");
if (rq->null_endp) {
/* trunk not available so rate_ctr aren't available either. */
LOGP(DLMGCP, LOGL_ERROR, "DLCX: Not allowed in 'null' endpoint!\n");
return create_err_response(pdata->cfg, NULL, 502, "DLCX", pdata->trans);
}
rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
if (endp && !mgcp_endp_avail(endp)) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
@@ -1539,12 +1490,6 @@ static struct msgb *handle_rsip(struct mgcp_request_data *rq)
LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
if (rq->null_endp) {
/* trunk not available so rate_ctr aren't available either. */
LOGP(DLMGCP, LOGL_ERROR, "RSIP: Not allowed in 'null' endpoint!\n");
return create_err_response(rq->pdata->cfg, NULL, 502, "RSIP", rq->pdata->trans);
}
if (rq->pdata->cfg->reset_cb)
rq->pdata->cfg->reset_cb(rq->endp->trunk);
return NULL;
@@ -1570,12 +1515,6 @@ static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
if (rq->null_endp) {
/* trunk not available so rate_ctr aren't available either. */
LOGP(DLMGCP, LOGL_ERROR, "RQNT: Not allowed in 'null' endpoint!\n");
return create_err_response(rq->pdata->cfg, NULL, 502, "RQNT", rq->pdata->trans);
}
for_each_line(line, rq->pdata->save) {
switch (toupper(line[0])) {
case 'S':
@@ -1630,7 +1569,8 @@ static void mgcp_keepalive_timer_cb(void *_trunk)
struct mgcp_endpoint *endp = trunk->endpoints[i];
llist_for_each_entry(conn, &endp->conns, entry) {
if (conn->type == MGCP_CONN_TYPE_RTP &&
conn->mode == MGCP_CONN_RECV_ONLY)
conn->mode == MGCP_CONN_RECV_ONLY &&
mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
send_dummy(endp, &conn->u.rtp);
}
}
@@ -1684,10 +1624,13 @@ struct mgcp_config *mgcp_config_alloc(void)
cfg->source_port = 2427;
osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));
osmo_strlcpy(cfg->osmux_addr, "0.0.0.0", sizeof(cfg->osmux_addr));
cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
INIT_LLIST_HEAD(&cfg->trunks);
/* Allocate virtual trunk */

View File

@@ -43,7 +43,7 @@ static const struct rate_ctr_desc mgcp_general_ctr_desc[] = {
{ "mgcp:err_rx_no_endpoint", "can't find MGCP endpoint, probably we've used all allocated endpoints." },
};
static const struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
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,
@@ -71,7 +71,7 @@ static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
[MGCP_CRCX_FAIL_CLAIM] = { "crcx:claim", "endpoint can not be claimed." },
};
static const struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
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,
@@ -97,7 +97,7 @@ static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
[MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." },
};
static const struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
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,
@@ -116,7 +116,7 @@ static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
[MGCP_DLCX_FAIL_AVAIL] = { "dlcx:unavailable", "endpoint unavailable." },
};
static const struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
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,
@@ -130,7 +130,7 @@ static const struct rate_ctr_desc e1_rate_ctr_desc[] = {
[E1_I460_TRAU_MUX_EMPTY_CTR] = { "e1:i460", "Outbound I.460 MUX queue empty." }
};
static const struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
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,
@@ -138,7 +138,7 @@ static const struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
.ctr_desc = e1_rate_ctr_desc
};
static const struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_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,
@@ -146,14 +146,6 @@ static const struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
.ctr_desc = all_rtp_conn_rate_ctr_desc
};
static const struct rate_ctr_group_desc all_osmux_conn_rate_ctr_group_desc = {
.group_name_prefix = "all_osmux_conn",
.group_description = "aggregated statistics for all osmux connections",
.class_id = 1,
.num_ctr = ARRAY_SIZE(all_osmux_conn_rate_ctr_desc),
.ctr_desc = all_osmux_conn_rate_ctr_desc
};
/*! allocate global rate counters
* (called once at startup).
* \param[in] cfg mgw configuration for which the rate counters are allocated.
@@ -199,7 +191,6 @@ int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk)
static atomic_uint mdcx_rate_ctr_index = 0;
static atomic_uint dlcx_rate_ctr_index = 0;
static atomic_uint all_rtp_conn_rate_ctr_index = 0;
static atomic_uint all_osmux_conn_rate_ctr_index = 0;
char ctr_name[256];
if (ratectr->mgcp_crcx_ctr_group == NULL) {
@@ -238,15 +229,6 @@ int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk)
trunk->trunk_nr);
rate_ctr_group_set_name(ratectr->all_rtp_conn_stats, ctr_name);
}
if (ratectr->all_osmux_conn_stats == NULL) {
ratectr->all_osmux_conn_stats = rate_ctr_group_alloc(trunk, &all_osmux_conn_rate_ctr_group_desc,
all_osmux_conn_rate_ctr_index++);
if (!ratectr->all_osmux_conn_stats)
return -EINVAL;
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:osmux_conn", mgcp_trunk_type_strs_str(trunk->trunk_type),
trunk->trunk_nr);
rate_ctr_group_set_name(ratectr->all_osmux_conn_stats, ctr_name);
}
/* E1 specific */
if (trunk->trunk_type == MGCP_TRUNK_E1 && ratectr->e1_stats == NULL) {
@@ -283,10 +265,6 @@ void mgcp_ratectr_trunk_free(struct mgcp_trunk *trunk)
rate_ctr_group_free(ratectr->all_rtp_conn_stats);
ratectr->all_rtp_conn_stats = NULL;
}
if (ratectr->all_osmux_conn_stats) {
rate_ctr_group_free(ratectr->all_osmux_conn_stats);
ratectr->all_osmux_conn_stats = NULL;
}
/* E1 specific */
if (ratectr->e1_stats) {

View File

@@ -35,9 +35,6 @@
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/sdp/fmtp.h>
#include <osmocom/sdp/sdp_strings.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
@@ -58,7 +55,7 @@ struct sdp_rtp_map {
};
struct sdp_fmtp_param {
int payload_type;
const char *fmtp;
struct mgcp_codec_param param;
};
@@ -198,7 +195,11 @@ 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));
@@ -217,13 +218,41 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
goto error;
fmtp_param->payload_type = pt;
/* Advance pointer to the beginning of the parameter section */
/* Advance pointer to the beginning of the parameter section and
* tokenize string */
str_ptr = strstr(str_ptr, " ");
if (!str_ptr)
goto error;
str_ptr++;
fmtp_param->fmtp = talloc_strdup(ctx, 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);
@@ -271,13 +300,13 @@ static int audio_ip_from_sdp(struct osmo_sockaddr *dst_addr, char *sdp)
/* Pick optional fmtp parameters by payload type, if there are no fmtp
* parameters, a nullpointer is returned */
static const char *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
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].fmtp;
return &fmtp_params[i].param;
}
return NULL;
@@ -298,6 +327,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
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;
@@ -354,7 +384,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
case 'm':
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
if (rc == 1) {
osmo_sockaddr_set_port(&rtp->addr.u.sa, port);
rtp->rtp_port = htons(port);
rtp->rtcp_port = htons(port + 1);
}
@@ -392,8 +422,8 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
/* Store parsed codec information */
for (i = 0; i < codecs_used; i++) {
const char *fmtp = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
rc = mgcp_codec_add2(conn, codecs[i].payload_type, codecs[i].map_line, fmtp);
codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
if (rc < 0)
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
}
@@ -402,17 +432,15 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
"Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
osmo_sockaddr_port(&rtp->addr.u.sa), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
ntohs(rtp->rtp_port), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
rtp->packet_duration_ms);
if (codecs_used == 0)
LOGPC(DLMGCP, LOGL_NOTICE, "none");
for (i = 0; i < codecs_used; i++) {
LOGPC(DLMGCP, LOGL_NOTICE, " %d=%s%s%s%s",
LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
rtp->codecs[i].payload_type,
strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown",
rtp->codecs[i].fmtp[0] ? ",fmtp='" : "",
rtp->codecs[i].fmtp,
rtp->codecs[i].fmtp[0] ? "'" : "");
strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown");
LOGPC(DLMGCP, LOGL_NOTICE, " ");
}
LOGPC(DLMGCP, LOGL_NOTICE, "\n");
@@ -461,21 +489,55 @@ static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_
}
/* Add fmtp strings to sdp payload */
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
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++) {
bool first = true;
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].fmtp) {
msgb_printf(sdp, "%s%s", first ? " " : ";", fmtp_params[i].fmtp);
first = false;
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");
@@ -497,8 +559,10 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
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;
@@ -511,7 +575,10 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
OSMO_ASSERT(sdp);
OSMO_ASSERT(addr);
codec = conn->end.codec;
/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
endp->trunk->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
&codec, &fmtp_extra,
(struct mgcp_conn_rtp *)conn);
audio_name = codec->audio_name;
payload_type = codec->payload_type;
@@ -534,7 +601,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
payload_types[0] = payload_type;
if (mgcp_conn_rtp_is_osmux(conn))
local_port = endp->trunk->cfg->osmux.local_port;
local_port = endp->trunk->cfg->osmux_port;
else
local_port = conn->end.local_port;
rc = add_audio(sdp, payload_types, 1, local_port);
@@ -547,23 +614,13 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
goto buffer_too_small;
}
if (codec->fmtp[0]) {
fmtp_params[0] = (struct sdp_fmtp_param){
.payload_type = payload_type,
.fmtp = codec->fmtp,
};
if (codec->param_present) {
fmtp_param.payload_type = payload_type;
fmtp_param.param = codec->param;
fmtp_params[0] = fmtp_param;
fmtp_params_len = 1;
} else if (codec->param_present) {
/* Legacy */
fmtp_params[0] = (struct sdp_fmtp_param){
.payload_type = payload_type,
};
fmtp_params_len = 1;
fmtp_params[0].fmtp = (codec->param.amr_octet_aligned ?
OSMO_SDP_STR_AMR_OCTET_ALIGN_1 : OSMO_SDP_STR_AMR_OCTET_ALIGN_0);
}
rc = add_fmtp(sdp, fmtp_params, fmtp_params_len);
rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra);
if (rc < 0)
goto buffer_too_small;
}

View File

@@ -39,7 +39,7 @@ __attribute__((no_sanitize("undefined")))
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)
{
struct mgcp_rtp_state *state = &conn->state;
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
*expected = state->stats.cycles + state->stats.max_seq;
*expected = *expected - state->stats.base_seq + 1;
@@ -80,10 +80,10 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
int ploss;
int nchars;
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR);
struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR);
struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR);
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR);
struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR);
struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR);
calc_loss(conn, &expected, &ploss);
jitter = calc_jitter(&conn->state);
@@ -99,7 +99,7 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
str += nchars;
str_len -= nchars;
if (conn->conn->endp->trunk->cfg->osmux.usage != OSMUX_USAGE_OFF) {
if (conn->conn->endp->trunk->cfg->osmux != OSMUX_USAGE_OFF) {
/* Error Counter */
nchars = snprintf(str, str_len,
"\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64,
@@ -112,12 +112,9 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
str_len -= nchars;
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
struct rate_ctr *osmux_chunks_rx, *osmux_octets_rx;
osmux_chunks_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_CHUNKS_RX_CTR);
osmux_octets_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_OCTETS_RX_CTR);
snprintf(str, str_len,
"\r\nX-Osmux-ST: CR=%" PRIu64 ", BR=%" PRIu64,
osmux_chunks_rx->current, osmux_octets_rx->current);
"\r\nX-Osmux-ST: CR=%u, BR=%u",
conn->osmux.stats.chunks, conn->osmux.stats.octets);
}
}

View File

@@ -111,12 +111,17 @@ static int config_write_mgcp(struct vty *vty)
VTY_NEWLINE);
} else
vty_out(vty, " no rtp-patch%s", VTY_NEWLINE);
if (trunk->audio_fmtp_extra)
vty_out(vty, " sdp audio fmtp-extra %s%s",
trunk->audio_fmtp_extra, VTY_NEWLINE);
vty_out(vty, " %ssdp audio-payload send-ptime%s",
trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE);
vty_out(vty, " %ssdp audio-payload send-name%s",
trunk->audio_send_name ? "" : "no ", VTY_NEWLINE);
vty_out(vty, " number endpoints %u%s",
trunk->v.vty_number_endpoints, VTY_NEWLINE);
vty_out(vty, " %sallow-transcoding%s",
trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE);
if (strlen(g_cfg->call_agent_addr))
vty_out(vty, " call-agent ip %s%s", g_cfg->call_agent_addr,
VTY_NEWLINE);
@@ -124,7 +129,7 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " rtp force-ptime %d%s", g_cfg->force_ptime,
VTY_NEWLINE);
switch (g_cfg->osmux.usage) {
switch (g_cfg->osmux) {
case OSMUX_USAGE_ON:
vty_out(vty, " osmux on%s", VTY_NEWLINE);
break;
@@ -136,23 +141,17 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " osmux off%s", VTY_NEWLINE);
break;
}
if (g_cfg->osmux.usage != OSMUX_USAGE_OFF) {
if (g_cfg->osmux.local_addr_v4)
vty_out(vty, " osmux bind-ip %s%s",
g_cfg->osmux.local_addr_v4, VTY_NEWLINE);
if (g_cfg->osmux.local_addr_v6)
vty_out(vty, " osmux bind-ip-v6 %s%s",
g_cfg->osmux.local_addr_v6, VTY_NEWLINE);
if (g_cfg->osmux) {
vty_out(vty, " osmux bind-ip %s%s",
g_cfg->osmux_addr, VTY_NEWLINE);
vty_out(vty, " osmux batch-factor %d%s",
g_cfg->osmux.batch_factor, VTY_NEWLINE);
g_cfg->osmux_batch, VTY_NEWLINE);
vty_out(vty, " osmux batch-size %u%s",
g_cfg->osmux.batch_size, VTY_NEWLINE);
g_cfg->osmux_batch_size, VTY_NEWLINE);
vty_out(vty, " osmux port %u%s",
g_cfg->osmux.local_port, VTY_NEWLINE);
g_cfg->osmux_port, VTY_NEWLINE);
vty_out(vty, " osmux dummy %s%s",
g_cfg->osmux.dummy_padding ? "on" : "off", VTY_NEWLINE);
vty_out(vty, " osmux peer-behind-nat %s%s",
g_cfg->osmux.peer_behind_nat ? "on" : "off", VTY_NEWLINE);
g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
}
if (g_cfg->conn_timeout)
@@ -170,11 +169,11 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
struct rate_ctr *rx_packets, *rx_bytes;
struct rate_ctr *dropped_packets;
tx_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR);
tx_bytes = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR);
rx_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
rx_bytes = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR);
dropped_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_DROPPED_PACKETS_CTR);
tx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR);
tx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR);
rx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
rx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR);
dropped_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_DROPPED_PACKETS_CTR);
vty_out(vty,
" Packets Sent: %" PRIu64 " (%" PRIu64 " bytes total)%s"
@@ -184,44 +183,20 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
" Payload Type: %d Rate: %u Channels: %d %s"
" Frame Duration: %u Frame Denominator: %u%s"
" FPP: %d Packet Duration: %u%s"
" Audio-Name: %s Sub-Type: %s%s"
" FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"
" Output-Enabled: %d Force-PTIME: %d%s",
tx_packets->current, tx_bytes->current, VTY_NEWLINE,
rx_packets->current, rx_bytes->current, VTY_NEWLINE,
state->in_stream.err_ts_ctr->current,
state->out_stream.err_ts_ctr->current,
VTY_NEWLINE,
VTY_NEWLINE,
dropped_packets->current, VTY_NEWLINE,
codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
codec->frame_duration_num, codec->frame_duration_den,
VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms,
VTY_NEWLINE, codec->audio_name,
VTY_NEWLINE, end->fmtp_extra, codec->audio_name,
codec->subtype_name, VTY_NEWLINE, end->output_enabled,
end->force_output_ptime, VTY_NEWLINE);
if (mgcp_conn_rtp_is_osmux(conn)) {
struct rate_ctr *rx_chunks, *rx_octets, *rtp_tx, *rtp_tx_dropped, *octets_tx;
rx_chunks = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_CHUNKS_RX_CTR);
rx_octets = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_OCTETS_RX_CTR);
rtp_tx = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_RTP_PACKETS_TX_CTR);
rtp_tx_dropped = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
octets_tx = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_AMR_OCTETS_TX_CTR);
vty_out(vty,
" Osmux:%s"
" State: %s%s"
" Local CID: %d%s"
" Remote CID: %d%s"
" Chunks received: %" PRIu64 " (%" PRIu64 " bytes total)%s"
" RTP Packets encoded (Tx): %" PRIu64 " (%" PRIu64 " AMR octets total)%s"
" AMR payloads Dropped (Tx): %" PRIu64 "%s",
VTY_NEWLINE, osmux_state_str(conn->osmux.state), VTY_NEWLINE,
conn->osmux.local_cid_allocated ? conn->osmux.local_cid : -1, VTY_NEWLINE,
conn->osmux.remote_cid_present ? conn->osmux.remote_cid : -1, VTY_NEWLINE,
rx_chunks->current, rx_octets->current, VTY_NEWLINE,
rtp_tx->current, octets_tx->current, VTY_NEWLINE,
rtp_tx_dropped->current, VTY_NEWLINE
);
}
}
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
@@ -314,14 +289,6 @@ static void dump_ratectr_trunk(struct vty *vty, struct mgcp_trunk *trunk)
" %25n: %10c (%S/s %M/m %H/h %D/d) %d",
ratectr->all_rtp_conn_stats);
}
if (ratectr->all_osmux_conn_stats) {
vty_out(vty, " %s:%s",
ratectr->all_osmux_conn_stats->desc->group_description,
VTY_NEWLINE);
vty_out_rate_ctr_group_fmt(vty,
" %25n: %10c (%S/s %M/m %H/h %D/d) %d",
ratectr->all_osmux_conn_stats);
}
if (ratectr->e1_stats && trunk->trunk_type == MGCP_TRUNK_E1) {
vty_out(vty, " %s:%s",
@@ -375,7 +342,7 @@ static int mgcp_show(struct vty *vty, int argc, const char **argv,
llist_for_each_entry(trunk, &g_cfg->trunks, entry)
dump_trunk(vty, trunk, show_stats, active_only);
if (g_cfg->osmux.usage != OSMUX_USAGE_OFF)
if (g_cfg->osmux)
vty_out(vty, "Osmux used CID: %d%s", osmux_cid_pool_count_used(),
VTY_NEWLINE);
@@ -508,7 +475,6 @@ DEFUN_DEPRECATED(cfg_mgcp_bind_early,
BIND_STR
"Bind local ports on start up\n" "Bind on demand\n" "Bind on startup\n")
{
vty_out(vty, "%% Deprecated 'bind early (0|1)' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -678,40 +644,53 @@ DEFUN_USRATTR(cfg_mgcp_no_rtp_force_ptime,
return CMD_SUCCESS;
}
DEFUN_USRATTR(cfg_mgcp_sdp_fmtp_extra,
cfg_mgcp_sdp_fmtp_extra_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"sdp audio fmtp-extra .NAME",
"Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
"Extra Information\n")
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
char *txt = argv_concat(argv, argc, 0);
if (!txt)
return CMD_WARNING;
osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt);
talloc_free(txt);
return CMD_SUCCESS;
}
DEFUN_USRATTR(cfg_mgcp_allow_transcoding,
cfg_mgcp_allow_transcoding_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"allow-transcoding", "Allow transcoding\n")
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
trunk->no_audio_transcoding = 0;
return CMD_SUCCESS;
}
DEFUN_USRATTR(cfg_mgcp_no_allow_transcoding,
cfg_mgcp_no_allow_transcoding_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"no allow-transcoding", NO_STR "Allow transcoding\n")
{
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
OSMO_ASSERT(trunk);
trunk->no_audio_transcoding = 1;
return CMD_SUCCESS;
}
#define SDP_STR "SDP File related options\n"
#define AUDIO_STR "Audio payload options\n"
DEFUN_DEPRECATED(cfg_mgcp_sdp_fmtp_extra,
cfg_mgcp_sdp_fmtp_extra_cmd,
"sdp audio fmtp-extra .NAME",
SDP_STR AUDIO_STR "Deprecated, without effect since osmo-mgw v1.13\n" "Deprecated, without effect\n")
{
vty_out(vty, "%% deprecated: the config option 'sdp audio fmtp-extra' has been removed.%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_mgcp_allow_transcoding,
cfg_mgcp_allow_transcoding_cmd,
"allow-transcoding", "Allow transcoding\n")
{
vty_out(vty, "%% Deprecated 'allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_mgcp_no_allow_transcoding,
cfg_mgcp_no_allow_transcoding_cmd,
"no allow-transcoding", NO_STR "Allow transcoding\n")
{
vty_out(vty, "%% Deprecated 'no allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_mgcp_sdp_payload_number,
cfg_mgcp_sdp_payload_number_cmd,
"sdp audio-payload number <0-255>",
SDP_STR AUDIO_STR "Number\n" "Payload number\n")
{
vty_out(vty, "%% Deprecated 'sdp audio-payload number <0-255>' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -725,7 +704,6 @@ DEFUN_DEPRECATED(cfg_mgcp_sdp_payload_name,
"sdp audio-payload name NAME",
SDP_STR AUDIO_STR "Name\n" "Payload name\n")
{
vty_out(vty, "%% Deprecated 'sdp audio-payload name NAME' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -786,7 +764,6 @@ DEFUN_DEPRECATED(cfg_mgcp_loop,
"loop (0|1)",
"Loop audio for all endpoints on main trunk\n" "Don't Loop\n" "Loop\n")
{
vty_out(vty, "%% Deprecated 'loop (0|1)' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1051,17 +1028,30 @@ static int config_write_trunk(struct vty *vty)
VTY_NEWLINE);
} else
vty_out(vty, " no rtp-patch%s", VTY_NEWLINE);
if (trunk->audio_fmtp_extra)
vty_out(vty, " sdp audio fmtp-extra %s%s",
trunk->audio_fmtp_extra, VTY_NEWLINE);
vty_out(vty, " %sallow-transcoding%s",
trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE);
}
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_trunk_sdp_fmtp_extra,
DEFUN_USRATTR(cfg_trunk_sdp_fmtp_extra,
cfg_trunk_sdp_fmtp_extra_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"sdp audio fmtp-extra .NAME",
SDP_STR AUDIO_STR "Deprecated, without effect since osmo-mgw v1.13\n" "Deprecated, without effect\n")
"Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
"Extra Information\n")
{
vty_out(vty, "%% deprecated: the config option 'sdp audio fmtp-extra' has been removed.%s", VTY_NEWLINE);
struct mgcp_trunk *trunk = vty->index;
char *txt = argv_concat(argv, argc, 0);
if (!txt)
return CMD_WARNING;
osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt);
talloc_free(txt);
return CMD_SUCCESS;
}
@@ -1070,7 +1060,6 @@ DEFUN_DEPRECATED(cfg_trunk_payload_number,
"sdp audio-payload number <0-255>",
SDP_STR AUDIO_STR "Number\n" "Payload Number\n")
{
vty_out(vty, "%% Deprecated 'sdp audio-payload number <0-255>' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1083,7 +1072,6 @@ DEFUN_DEPRECATED(cfg_trunk_payload_name,
"sdp audio-payload name NAME",
SDP_STR AUDIO_STR "Payload\n" "Payload Name\n")
{
vty_out(vty, "%% Deprecated 'sdp audio-payload name NAME' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1096,7 +1084,6 @@ DEFUN_DEPRECATED(cfg_trunk_loop,
"loop (0|1)",
"Loop audio for all endpoints on this trunk\n" "Don't Loop\n" "Loop\n")
{
vty_out(vty, "%% Deprecated 'loop (0|1)' config no longer has any effect%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1294,19 +1281,23 @@ DEFUN_ATTR(cfg_trunk_no_rtp_keepalive,
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_trunk_allow_transcoding,
cfg_trunk_allow_transcoding_cmd,
"allow-transcoding", "Allow transcoding\n")
DEFUN_USRATTR(cfg_trunk_allow_transcoding,
cfg_trunk_allow_transcoding_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"allow-transcoding", "Allow transcoding\n")
{
vty_out(vty, "%% Deprecated 'allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
struct mgcp_trunk *trunk = vty->index;
trunk->no_audio_transcoding = 0;
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_trunk_no_allow_transcoding,
cfg_trunk_no_allow_transcoding_cmd,
"no allow-transcoding", NO_STR "Allow transcoding\n")
DEFUN_USRATTR(cfg_trunk_no_allow_transcoding,
cfg_trunk_no_allow_transcoding_cmd,
X(MGW_CMD_ATTR_NEWCONN),
"no allow-transcoding", NO_STR "Allow transcoding\n")
{
vty_out(vty, "%% Deprecated 'no allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
struct mgcp_trunk *trunk = vty->index;
trunk->no_audio_transcoding = 1;
return CMD_SUCCESS;
}
@@ -1548,12 +1539,12 @@ DEFUN(cfg_mgcp_osmux,
OSMO_ASSERT(trunk);
if (strcmp(argv[0], "off") == 0) {
g_cfg->osmux.usage = OSMUX_USAGE_OFF;
g_cfg->osmux = OSMUX_USAGE_OFF;
return CMD_SUCCESS;
} else if (strcmp(argv[0], "on") == 0)
g_cfg->osmux.usage = OSMUX_USAGE_ON;
g_cfg->osmux = OSMUX_USAGE_ON;
else if (strcmp(argv[0], "only") == 0)
g_cfg->osmux.usage = OSMUX_USAGE_ONLY;
g_cfg->osmux = OSMUX_USAGE_ONLY;
return CMD_SUCCESS;
@@ -1561,29 +1552,12 @@ DEFUN(cfg_mgcp_osmux,
DEFUN(cfg_mgcp_osmux_ip,
cfg_mgcp_osmux_ip_cmd,
"osmux bind-ip " VTY_IPV4_CMD,
OSMUX_STR IP_STR
"IPv4 Address to bind to\n")
{
osmo_talloc_replace_string(g_cfg, &g_cfg->osmux.local_addr_v4, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_ip_v6,
cfg_mgcp_osmux_ip_v6_cmd,
"osmux bind-ip-v6 " VTY_IPV6_CMD,
"osmux bind-ip " VTY_IPV46_CMD,
OSMUX_STR IP_STR
"IPv4 Address to bind to\n"
"IPv6 Address to bind to\n")
{
osmo_talloc_replace_string(g_cfg, &g_cfg->osmux.local_addr_v6, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_port,
cfg_mgcp_osmux_port_cmd,
"osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n")
{
g_cfg->osmux.local_port = atoi(argv[0]);
osmo_strlcpy(g_cfg->osmux_addr, argv[0], sizeof(g_cfg->osmux_addr));
return CMD_SUCCESS;
}
@@ -1592,7 +1566,7 @@ DEFUN(cfg_mgcp_osmux_batch_factor,
"osmux batch-factor <1-8>",
OSMUX_STR "Batching factor\n" "Number of messages in the batch\n")
{
g_cfg->osmux.batch_factor = atoi(argv[0]);
g_cfg->osmux_batch = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -1601,7 +1575,15 @@ DEFUN(cfg_mgcp_osmux_batch_size,
"osmux batch-size <1-65535>",
OSMUX_STR "batch size\n" "Batch size in bytes\n")
{
g_cfg->osmux.batch_size = atoi(argv[0]);
g_cfg->osmux_batch_size = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_port,
cfg_mgcp_osmux_port_cmd,
"osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n")
{
g_cfg->osmux_port = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -1612,24 +1594,9 @@ DEFUN(cfg_mgcp_osmux_dummy,
"Disable dummy padding\n")
{
if (strcmp(argv[0], "on") == 0)
g_cfg->osmux.dummy_padding = true;
g_cfg->osmux_dummy = 1;
else if (strcmp(argv[0], "off") == 0)
g_cfg->osmux.dummy_padding = false;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_peer_behind_nat,
cfg_mgcp_osmux_peer_behind_nat_cmd,
"osmux peer-behind-nat (on|off)",
OSMUX_STR "Define whether peer is behind NAT\n"
"Peer is behind NAT\n"
"Peer is NOT behind NAT\n")
{
if (strcmp(argv[0], "on") == 0)
g_cfg->osmux.peer_behind_nat = true;
else if (strcmp(argv[0], "off") == 0)
g_cfg->osmux.peer_behind_nat = false;
g_cfg->osmux_dummy = 0;
return CMD_SUCCESS;
}
@@ -1719,12 +1686,10 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_name_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_v6_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_factor_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_size_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_peer_behind_nat_cmd);
install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_domain_cmd);
@@ -1769,9 +1734,9 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
int rc;
struct mgcp_trunk *trunk;
cfg->osmux.local_port = OSMUX_DEFAULT_PORT;
cfg->osmux.batch_factor = 4;
cfg->osmux.batch_size = OSMUX_BATCH_DEFAULT_MAX;
cfg->osmux_port = OSMUX_PORT;
cfg->osmux_batch = 4;
cfg->osmux_batch_size = OSMUX_BATCH_DEFAULT_MAX;
g_cfg = cfg;
rc = vty_read_config_file(config_file, NULL);

View File

@@ -1,28 +0,0 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir) \
$(NULL)
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(TALLOC_CFLAGS) \
$(NULL)
noinst_LTLIBRARIES = \
libosmo-sdp.la \
$(NULL)
libosmo_sdp_la_SOURCES = \
sdp_codec.c \
sdp_codec_list.c \
sdp_msg.c \
sdp_internal.c \
fmtp.c \
$(NULL)
libosmo_sdp_la_LIBADD = \
$(LIBOSMOCORE_LIBS) \
$(TALLOC_LIBS) \
$(NULL)

View File

@@ -1,193 +0,0 @@
/*
* (C) 2023-2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr
*
* 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 <string.h>
#include <ctype.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/sdp/sdp_strings.h>
#include <osmocom/sdp/fmtp.h>
/* End of current fmtp parameter. Return a pointer to the next ';' character, if present, or the terminating '\0'. */
static const char *osmo_sdp_fmtp_end(const char *fmtp)
{
if (!fmtp)
return NULL;
for (; *fmtp && *fmtp != ';'; fmtp++);
return fmtp;
}
/* Start of next fmtp parameter. Return a pointer to the first character of the next fmtp parameter's name, or the
* terminating '\0'. */
static const char *osmo_sdp_fmtp_next(const char *fmtp)
{
if (!fmtp)
return NULL;
fmtp = osmo_sdp_fmtp_end(fmtp);
for (; *fmtp && (*fmtp == ';' || isspace(*fmtp)); fmtp++);
return fmtp;
}
/*! Parse a given SDP fmtp value string, returning the value of a specific option, if present.
*
* Example:
*
* const char *fmtp_vals = "octet-align=1;mode-set=0,2,4,7";
*
* char mode_set_str[23];
* if (osmo_sdp_fmtp_get_val(mode_set_str, sizeof(mode_set_str), fmtp_vals, "mode-set")) {
* // option 'mode-set' is present, now mode_set_str == "0,2,4,7"
* use_modeset(mode_set_str);
* } else {
* // if 'mode-set' were not present...
* use_modeset(MY_DEFAULT_MODESET);
* }
*
* \param[out] val Buffer to write the option's value to.
* \param[in] val_size Space available in val.
* \param[in] fmtp fmtp value string to parse -- must not contain the "a=fmtp:N " prefix, only the value part.
* \param[in] option_name Which fmtp option to get the value for.
* \return true when the option was found, false when it was not present.
*/
bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const char *option_name)
{
const char *pos = fmtp;
const char *end;
int option_name_len = strlen(option_name);
for (; pos && *pos; pos = osmo_sdp_fmtp_next(pos)) {
if (!osmo_str_startswith(pos, option_name))
continue;
pos += option_name_len;
if (*pos != '=')
continue;
pos++;
break;
}
if (!pos || !*pos)
return false;
end = osmo_sdp_fmtp_end(pos);
OSMO_ASSERT(end);
if (val && val_size)
osmo_strlcpy(val, pos, OSMO_MIN(val_size, end - pos + 1));
return true;
}
/*! Parse a given SDP fmtp value string, returning the value of a specific integer option, if present.
*
* Example:
*
* const char *fmtp_vals = "octet-align=1;mode-set=0,2,4,7";
* bool oa = osmo_sdp_fmtp_get_int(fmtp_vals, OSMO_SDP_AMR_OCTET_ALIGN_NAME, 1);
*
* \param[in] fmtp fmtp value string to parse -- must not contain the "a=fmtp:N " prefix, only the value part.
* \param[in] option_name Which fmtp option to get the value for.
* \param[in] default_value If option_name is not present or cannot be parsed as integer, return this instead.
* \return the integer value when the option was found and actually an integer, default_value otherwise.
*/
int64_t osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int64_t default_value)
{
char val[128];
if (!osmo_sdp_fmtp_get_val(val, sizeof(val), fmtp, option_name))
return default_value;
if (!val[0])
return default_value;
int64_t i;
if (osmo_str_to_int64(&i, val, 10, INT64_MIN, INT64_MAX)) {
/* error parsing number */
return default_value;
}
return i;
}
/*! Return true if octet-align is present and set to 1 in the given AMR related fmtp value.
* Default to octet-align=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-align
* operation SHALL be used. If 0 or if not present,
* bandwidth-efficient operation is employed.
*
* https://tools.ietf.org/html/rfc4867
*/
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp)
{
return osmo_sdp_fmtp_get_int(fmtp, OSMO_SDP_STR_AMR_OCTET_ALIGN, 0) == 1;
}
static void strip_whitespace(char *str)
{
char *i = str;
char *o = str;
for (; *i; i++, o++) {
while (isspace(*i))
i++;
*o = *i;
if (!*i)
break;
}
}
/* Return true when the two AMR type fmtp strings can be considered equivalent.
* - Omission of octet-align is equivalent to having octet-align=0 present (0 is the default).
* - Omission of 'mode-set' means, match any and all codec modes. So if either a or b have no 'mode-set', it's a match.
* If both have 'mode-set' present, they must be identical to match. Do not sort the mode-set string, but strip
* whitespace.
* - TODO all other parameters are currently completely ignored.
*/
bool osmo_sdp_fmtp_amr_match(const char *a, const char *b)
{
char a_modeset[32] = {};
char b_modeset[32] = {};
bool a_ok;
bool b_ok;
if (!a)
a = "";
if (!b)
b = "";
/* octet-align=1. Omission means octet-align=0 */
if (osmo_sdp_fmtp_amr_is_octet_aligned(a) != osmo_sdp_fmtp_amr_is_octet_aligned(b))
return false;
/* mode-set=0,1,2,3,4,5,6,7 */
a_ok = osmo_sdp_fmtp_get_val(a_modeset, sizeof(a_modeset), a, "mode-set");
b_ok = osmo_sdp_fmtp_get_val(b_modeset, sizeof(b_modeset), b, "mode-set");
if (a_ok && b_ok) {
/* Strip whitespace: We don't know what remote SDP peers may throw at us. There could be whitespace
* around the separators like 'mode-set=2,3 ; octet-align=1', which may show up here as whitespace in
* the value string as "2,3 ", which would mismatch "2,3". */
strip_whitespace(a_modeset);
strip_whitespace(b_modeset);
if (strcmp(a_modeset, b_modeset))
return false;
}
/* TODO: treat other AMR traits, see RFC4867 8.1. Maybe generically match all values that are present?
* So far we have no need for other values than octet-align and mode-set. */
/* No mismatch found, it's a match */
return true;
}

View File

@@ -1,251 +0,0 @@
/* Codec management in SDP messages. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <ctype.h>
#include <osmocom/core/utils.h>
#include <osmocom/sdp/fmtp.h>
#include <osmocom/sdp/sdp_codec.h>
#include <osmocom/sdp/sdp_internal.h>
struct osmo_sdp_codec *osmo_sdp_codec_alloc(void *ctx)
{
return talloc_zero(ctx, struct osmo_sdp_codec);
}
int osmo_sdp_codec_set(struct osmo_sdp_codec *c,
int8_t payload_type, const char *encoding_name, unsigned int rate, const char *fmtp)
{
c->rate = rate;
osmo_sdp_codec_set_encoding_name(c, encoding_name);
osmo_sdp_codec_set_fmtp(c, fmtp);
c->payload_type = payload_type;
return 0;
}
int osmo_sdp_codec_set_encoding_name(struct osmo_sdp_codec *c, const char *encoding_name)
{
osmo_talloc_replace_string(c, &c->encoding_name, encoding_name);
return 0;
}
int osmo_sdp_codec_set_fmtp(struct osmo_sdp_codec *c, const char *fmtp)
{
osmo_talloc_replace_string(c, &c->fmtp, fmtp);
return 0;
}
bool osmo_sdp_codec_is_set(const struct osmo_sdp_codec *a)
{
return a && a->encoding_name && a->encoding_name[0];
}
int osmo_sdp_codec_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec *codec)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
if (!codec) {
OSMO_STRBUF_PRINTF(sb, "NULL");
return sb.chars_needed;
}
if (codec->encoding_name && codec->encoding_name[0])
OSMO_STRBUF_PRINTF(sb, "%s", codec->encoding_name);
if (codec->rate != 8000)
OSMO_STRBUF_PRINTF(sb, "/%u", codec->rate);
if (codec->fmtp && codec->fmtp[0])
OSMO_STRBUF_PRINTF(sb, ":%s", codec->fmtp);
OSMO_STRBUF_PRINTF(sb, "#%d", codec->payload_type);
return sb.chars_needed;
}
char *osmo_sdp_codec_to_str_c(void *ctx, const struct osmo_sdp_codec *codec)
{
OSMO_NAME_C_IMPL(ctx, 32, "osmo_sdp_codec_to_str_c-ERROR", osmo_sdp_codec_to_str_buf, codec)
}
/*! Parse a codec string as from osmo_sdp_codec_to_str_buf() back to an osmo_sdp_codec struct.
* Write the parsed result to *dst, using ctx as talloc parent.
* The input string is like <encoding_name>[:<fmtp-string>][#<payload-type-nr>]
* for example:
* "FOO:my-fmtp=1;my-other-fmtp=2#42"
* Note that ';' are separators only within the fmtp string. This function does not separate those. In above example,
* the fmtp string part is "my-fmtp=val;my-other-fmtp=val2" and ends up in dst->ftmp as-is.
* Parse at most str_len characters, or the entire string when str_len < 0 or str_len > strlen(str).
* Return 0 on success, negative on failure. */
int osmo_sdp_codec_from_str(struct osmo_sdp_codec *dst, const char *str, int str_len)
{
const char *pos = str;
const char *str_end = str + (str_len >= 0 ? str_len : strlen(str));
const char *p2;
struct token token_encoding_name = {};
struct token token_rate = {};
struct token token_fmtp = {};
struct token token_payload_type = {};
struct token *new_t = NULL;
/* start with the encoding name */
struct token *t = &token_encoding_name;
t->start = pos;
for (; pos < str_end; pos++) {
new_t = NULL;
switch (*pos) {
case '/':
new_t = &token_rate;
break;
case ':':
new_t = &token_fmtp;
break;
case '#':
/* count this '#' only if there is no other one following. It might be part of a fmtp. */
for (p2 = pos + 1; p2 < str_end; p2++)
if (*p2 == '#')
break;
if (p2 < str_end && *p2 == '#')
break;
/* This is the last '#' in the string. Count it only when a digit follows. */
if (!isdigit(pos[1]))
break;
new_t = &token_payload_type;
break;
default:
break;
}
if (!new_t)
continue;
/* If we already have a token for a start character, don't start it again. These may be part of a fmtp
* string. */
if (new_t == t)
continue;
t->end = pos;
t = new_t;
t->start = pos + 1;
}
t->end = pos;
token_copy(dst, &dst->encoding_name, &token_encoding_name);
if (token_rate.start)
dst->rate = atoi(token_rate.start);
else
dst->rate = 8000;
token_copy(dst, &dst->fmtp, &token_fmtp);
if (token_payload_type.start)
dst->payload_type = atoi(token_payload_type.start);
return 0;
}
/* Compare both payload type number and fmtp string 1:1 */
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_exact = {
.payload_type = true,
.encoding_name = true,
.rate = true,
.fmtp = OSMO_SDP_CMP_EXACT,
};
/* Ignore payload type number; compare fmtp string by meaning when possible, else 1:1 */
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent = {
.payload_type = false,
.encoding_name = true,
.rate = true,
.fmtp = OSMO_SDP_CMP_EQUIVALENT,
};
/* Compare only encoding name */
const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_name = {
.payload_type = false,
.encoding_name = true,
.rate = false,
.fmtp = OSMO_SDP_CMP_IGNORE,
};
extern const struct osmo_sdp_codec_cmp_flags osmo_sdp_codec_cmp_equivalent;
static inline int strcmp_safe(const char *a, const char *b)
{
return strcmp(a ? : "", b ? : "");
}
/*! Compare encoding name, rate and fmtp, returning cmp result: -1 if a < b, 0 if a == b, 1 if a > b.
* Compare as defined in 'cmp':
* If cmpf->payload_type is false, ignore payload_type numbers.
* If cmpf->rate is false, ignore rate.
* If cmpf->fmtp is OSMO_SDP_CMP_IGNORE, ignore fmtp strings.
* If cmpf->fmtp is OSMO_SDP_CMP_EXACT, use strcmp() to match fmtp 1:1.
* If cmpf->fmtp is OSMO_SDP_CMP_EQUIVALENT, use specific fmtp knowledge to match equivalent entries;
* - AMR fmtp matching is done by osmo_sdp_fmtp_amr_match().
* - for all others, still compare fmtp 1:1.
*/
int osmo_sdp_codec_cmp(const struct osmo_sdp_codec *a, const struct osmo_sdp_codec *b,
const struct osmo_sdp_codec_cmp_flags *cmpf)
{
int cmp;
if (a == b)
return 0;
if (!a)
return -1;
if (!b)
return 1;
if (!cmpf)
cmpf = &osmo_sdp_codec_cmp_exact;
if (cmpf->encoding_name) {
cmp = strcmp_safe(a->encoding_name, b->encoding_name);
if (cmp)
return cmp;
}
if (cmpf->rate) {
cmp = OSMO_CMP(a->rate, b->rate);
if (cmp)
return cmp;
}
switch (cmpf->fmtp) {
default:
case OSMO_SDP_CMP_EXACT:
cmp = strcmp_safe(a->fmtp, b->fmtp);
break;
case OSMO_SDP_CMP_EQUIVALENT:
/* In case of AMR, allow logical matching; we only need to do that if the strings differ. */
cmp = strcmp_safe(a->fmtp, b->fmtp);
if (cmp
&& !strcmp_safe("AMR", a->encoding_name)
&& osmo_sdp_fmtp_amr_match(a->fmtp, b->fmtp))
cmp = 0;
break;
case OSMO_SDP_CMP_IGNORE:
cmp = 0;
break;
}
if (cmp)
return cmp;
if (cmpf->payload_type)
cmp = OSMO_CMP(a->payload_type, b->payload_type);
return cmp;
}

View File

@@ -1,367 +0,0 @@
/* Codec management in SDP messages. */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <osmocom/core/utils.h>
#include <osmocom/sdp/sdp_codec_list.h>
struct osmo_sdp_codec_list *osmo_sdp_codec_list_alloc(void *ctx)
{
struct osmo_sdp_codec_list *codec_list = talloc_zero(ctx, struct osmo_sdp_codec_list);
INIT_LLIST_HEAD(&codec_list->list);
return codec_list;
}
/*! Free all items contained in this list, do not free the list itself (leave an empty list). */
void osmo_sdp_codec_list_free_items(struct osmo_sdp_codec_list *codec_list)
{
struct osmo_sdp_codec *c;
while ((c = osmo_sdp_codec_list_first(codec_list))) {
osmo_sdp_codec_list_remove_entry(c);
talloc_free(c);
}
}
struct osmo_sdp_codec *osmo_sdp_codec_list_add_empty(struct osmo_sdp_codec_list *codec_list)
{
struct osmo_sdp_codec *c = osmo_sdp_codec_alloc(codec_list);
llist_add_tail(&c->entry, &codec_list->list);
return c;
}
int8_t osmo_sdp_codec_list_get_unused_dyn_pt_nr(const struct osmo_sdp_codec_list *codec_list, int8_t suggest_pt_nr)
{
bool present[127 - 96 + 1] = {};
const struct osmo_sdp_codec *c;
bool suggest_pt_nr_exists = false;
int i;
osmo_sdp_codec_list_foreach (c, codec_list) {
if (c->payload_type >= 96 && c->payload_type <= 127)
present[c->payload_type - 96] = true;
if (c->payload_type == suggest_pt_nr)
suggest_pt_nr_exists = true;
}
if (!suggest_pt_nr_exists)
return suggest_pt_nr;
/* The desired number is already taken, see which of the dynamic types is not taken yet */
for (i = 96; i <= 127; i++) {
/* For dynamic allocations, skip these predefined numbers, taken from enum mgcp_codecs:
* CODEC_GSMEFR_8000_1 = 110, 3GPP TS 48.103 table 5.4.2.2.1
* CODEC_GSMHR_8000_1 = 111, 3GPP TS 48.103 table 5.4.2.2.1
* CODEC_AMR_8000_1 = 112, 3GPP TS 48.103 table 5.4.2.2.1
* CODEC_AMRWB_16000_1 = 113, 3GPP TS 48.103 table 5.4.2.2.1
* CODEC_CLEARMODE = 120, 3GPP TS 48.103 table 5.4.2.2.1
*/
if (i >= 110 && i <= 113)
continue;
else if (i == 120)
continue;
if (!present[i - 96])
return i;
}
return -1;
}
/*! Allocate a new entry in codec_list and copy codec's values to it.
* If once is NULL, unconditionally add a new codec entry.
* If once is non-NULL, do not add a new entry when the list already contains a matching entry; for determining a match,
* use the once->flags. For example, if once = &osmo_sdp_codec_cmp_equivalent, look up if codec_list has a similar
* codec, and add the new entry only if it is not listed.
* See osmo_sdp_codec_cmp() and osmo_sdp_fmtp_amr_match() for details.
* Return the new entry, or the equivalent entry already present in the list.
*/
struct osmo_sdp_codec *osmo_sdp_codec_list_add(struct osmo_sdp_codec_list *codec_list,
const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *once, bool pick_unused_pt_nr)
{
struct osmo_sdp_codec *new_entry;
int8_t payload_type;
if (once) {
struct osmo_sdp_codec *c;
osmo_sdp_codec_list_foreach (c, codec_list)
if (!osmo_sdp_codec_cmp(codec, c, once))
return c;
}
/* Adjust payload_type number? */
payload_type = codec->payload_type;
if (pick_unused_pt_nr)
payload_type = osmo_sdp_codec_list_get_unused_dyn_pt_nr(codec_list, payload_type);
/* Take provided values, possibly modified payload_type */
new_entry = osmo_sdp_codec_list_add_empty(codec_list);
osmo_sdp_codec_set(new_entry, payload_type, codec->encoding_name, codec->rate, codec->fmtp);
return new_entry;
}
/*! Remove and free all entries from the codec_list that match the given codec according to osmo_sdp_codec_cmp(cmpf).
* Return the number of entries freed. */
int osmo_sdp_codec_list_remove(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *cmpf)
{
struct osmo_sdp_codec *i, *j;
int count = 0;
osmo_sdp_codec_list_foreach_safe (i, j, codec_list) {
if (osmo_sdp_codec_cmp(i, codec, cmpf))
continue;
osmo_sdp_codec_list_remove_entry(i);
talloc_free(i);
count++;
}
return count;
}
/*! Unlink an osmo_sdp_codec from an osmo_sdp_codec_list, if the codec instance is part of a list. Do not free the
* struct osmo_sdp_codec.
*/
void osmo_sdp_codec_list_remove_entry(struct osmo_sdp_codec *codec)
{
/* The codec is not part of a list in these cases:
* After talloc_zero(), next == NULL.
* After llist_del(), next == LLIST_POISON1. */
if (codec->entry.next != NULL
&& codec->entry.next != (struct llist_head *)LLIST_POISON1)
llist_del(&codec->entry);
}
static inline int strcmp_safe(const char *a, const char *b)
{
return strcmp(a ? : "", b ? : "");
}
/*! Short single-line representation of a list of SDP audio codecs, convenient for logging.
* If summarize == true, collapse variants of the same encoding_name (in practice, don't show all of the various AMR
* fmtp permutations). If summarize == false, print each and every codec in full.
*/
int osmo_sdp_codec_list_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_codec_list *codec_list, bool summarize)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
const struct osmo_sdp_codec *codec;
bool first;
if (llist_empty(&codec_list->list)) {
OSMO_STRBUF_PRINTF(sb, "(no-codecs)");
return sb.chars_needed;
}
if (!summarize) {
first = true;
osmo_sdp_codec_list_foreach (codec, codec_list) {
if (!first)
OSMO_STRBUF_PRINTF(sb, " ");
OSMO_STRBUF_APPEND(sb, osmo_sdp_codec_to_str_buf, codec);
first = false;
}
return sb.chars_needed;
}
/* summarize */
first = true;
osmo_sdp_codec_list_foreach (codec, codec_list) {
const struct osmo_sdp_codec *c2;
int count = 0;
bool various_pt = false;
/* When this encoding name has been handled before, skip it now. */
osmo_sdp_codec_list_foreach (c2, codec_list) {
if (c2 == codec)
break;
if (!strcmp_safe(codec->encoding_name, c2->encoding_name)) {
count = 1;
break;
}
}
if (count)
continue;
/* Not seen this encoding_name before, count total occurences */
count = 0;
osmo_sdp_codec_list_foreach (c2, codec_list) {
if (!strcmp_safe(codec->encoding_name, c2->encoding_name)) {
count++;
if (codec->payload_type != c2->payload_type)
various_pt = true;
}
}
if (!first)
OSMO_STRBUF_PRINTF(sb, " ");
if (count > 1)
OSMO_STRBUF_PRINTF(sb, "%d*", count);
OSMO_STRBUF_PRINTF(sb, "%s", codec->encoding_name);
if (!various_pt)
OSMO_STRBUF_PRINTF(sb, "#%d", codec->payload_type);
first = false;
}
return sb.chars_needed;
}
char *osmo_sdp_codec_list_to_str_c(void *ctx, const struct osmo_sdp_codec_list *codec_list, bool summarize)
{
OSMO_NAME_C_IMPL(ctx, 128, "osmo_sdp_codec_list_to_str_c-ERROR", osmo_sdp_codec_list_to_str_buf, codec_list, summarize)
}
/*! Return first entry, or NULL if the list is empty. */
struct osmo_sdp_codec *osmo_sdp_codec_list_first(const struct osmo_sdp_codec_list *list)
{
return llist_first_entry_or_null(&list->list, struct osmo_sdp_codec, entry);
}
/*! Move entries matching 'codec' to the front of the list. Matching is done via osmo_sdp_codec_cmp(cmpf).
* Return the number of matches that are now at the front of the list.
*/
int osmo_sdp_codec_list_move_to_first(struct osmo_sdp_codec_list *codec_list, const struct osmo_sdp_codec *codec,
const struct osmo_sdp_codec_cmp_flags *cmpf)
{
struct llist_head *head = &codec_list->list;
struct osmo_sdp_codec *i, *j;
int matches_found = 0;
osmo_sdp_codec_list_foreach_safe (i, j, codec_list) {
if (osmo_sdp_codec_cmp(codec, i, cmpf))
continue;
/* It's a match, move to the head */
osmo_sdp_codec_list_remove_entry(i);
llist_add(&i->entry, head);
matches_found++;
/* If more matches show up later, add them *after* the one just moved to the front. */
head = &i->entry;
}
return matches_found;
}
/*! Compare two lists of SDP codecs, returning cmp result: -1 if a < b, 0 if a == b, 1 if a > b.
* The two lists are compared in order, item by item, using osmo_sdp_codec_cmp(cmpf).
*/
int osmo_sdp_codec_list_cmp(const struct osmo_sdp_codec_list *a, const struct osmo_sdp_codec_list *b,
const struct osmo_sdp_codec_cmp_flags *cmpf)
{
const struct llist_head *a_start;
const struct llist_head *a_pos;
const struct llist_head *b_start;
const struct llist_head *b_pos;
int cmp;
/* NULL pointer == empty list */
if (a && llist_empty(&a->list))
a = NULL;
if (b && llist_empty(&a->list))
b = NULL;
/* are one or both empty? */
if (a == b)
return 0;
if (!a)
return -1;
if (!b)
return 1;
/* compare item by item */
a_start = &a->list;
a_pos = a_start->next;
b_start = &b->list;
b_pos = b_start->next;
for (; a_pos != a_start; a_pos = a_pos->next, b_pos = b_pos->next) {
const struct osmo_sdp_codec *codec_a;
const struct osmo_sdp_codec *codec_b;
if (b_pos == b_start) {
/* there is an entry in a, but b has already ended. mismatch. */
return 1;
}
codec_a = llist_entry(a_pos, struct osmo_sdp_codec, entry);
codec_b = llist_entry(b_pos, struct osmo_sdp_codec, entry);
cmp = osmo_sdp_codec_cmp(codec_a, codec_b, cmpf);
if (cmp)
return cmp;
}
if (b_pos != b_start) {
/* 'a' has ended, but 'b' has more items. mismatch. */
return -1;
}
/* full match. */
return 0;
}
/*! Leave only those codecs in 'dst' that are also present in 'other'.
* The matching is made by osmo_sdp_codec_cmp(cmpf).
* If translate_payload_type_numbers has an effect if 'dst' and 'other' have mismatching payload_type numbers for the
* same SDP codec descriptions. If translate_payload_type_numbers is true, take the payload_type numbers from 'other'.
* If false, keep payload_type numbers in 'dst' unchanged. */
void osmo_sdp_codec_list_intersection(struct osmo_sdp_codec_list *dst, const struct osmo_sdp_codec_list *other,
const struct osmo_sdp_codec_cmp_flags *cmpf,
bool translate_payload_type_numbers)
{
struct osmo_sdp_codec *i, *j;
osmo_sdp_codec_list_foreach_safe (i, j, dst) {
struct osmo_sdp_codec *o;
struct osmo_sdp_codec *match = NULL;
osmo_sdp_codec_list_foreach (o, other) {
if (osmo_sdp_codec_cmp(i, o, cmpf))
continue;
match = o;
break;
}
if (!match) {
osmo_sdp_codec_list_remove_entry(i);
talloc_free(i);
continue;
}
if (translate_payload_type_numbers)
i->payload_type = match->payload_type;
}
}
/* Find an entry for the given payload_type number in the given list of codecs. */
struct osmo_sdp_codec *osmo_sdp_codec_list_by_payload_type(struct osmo_sdp_codec_list *codec_list, int8_t payload_type)
{
struct osmo_sdp_codec *codec;
osmo_sdp_codec_list_foreach(codec, codec_list) {
if (codec->payload_type == payload_type)
return codec;
}
return NULL;
}
bool osmo_sdp_codec_list_is_empty(const struct osmo_sdp_codec_list *codec_list)
{
if (!codec_list)
return true;
return llist_empty(&codec_list->list);
}

View File

@@ -1,47 +0,0 @@
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stddef.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/sdp/sdp_internal.h>
/* Copy a string from t->start to t->end, return as talloc allocated under ctx in *dst.
* If *dst is non-NULL, talloc_free(*dst) first. */
void token_copy(void *ctx, char **dst, const struct token *t)
{
size_t len;
if (*dst)
talloc_free(*dst);
if (!t->start || !(t->end > t->start)) {
*dst = NULL;
return;
}
len = t->end - t->start;
*dst = talloc_size(ctx, len + 1);
osmo_strlcpy(*dst, t->start, len + 1);
talloc_set_name_const(*dst, *dst);
}

View File

@@ -1,471 +0,0 @@
/* Implementation for SDP message encoding and decoding */
/*
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <inttypes.h>
#include <errno.h>
#include <ctype.h>
#include <osmocom/core/utils.h>
#include <osmocom/sdp/sdp_msg.h>
#include <osmocom/sdp/sdp_strings.h>
#include <osmocom/sdp/sdp_internal.h>
static const char * const mdir_str[] = {
[OSMO_SDP_MDIR_UNSET] = "-",
[OSMO_SDP_MDIR_SENDONLY] = OSMO_SDP_STR_SENDONLY,
[OSMO_SDP_MDIR_RECVONLY] = OSMO_SDP_STR_RECVONLY,
[OSMO_SDP_MDIR_SENDRECV] = OSMO_SDP_STR_SENDRECV,
[OSMO_SDP_MDIR_INACTIVE] = OSMO_SDP_STR_INACTIVE,
};
/*! Convert struct osmo_sdp_msg to the actual SDP protocol representation. */
int osmo_sdp_msg_encode_buf(char *dst, size_t dst_size, const struct osmo_sdp_msg *sdp)
{
const struct osmo_sdp_codec *codec;
struct osmo_strbuf sb = { .buf = dst, .len = dst_size };
const char *oip;
char oipv;
const char *ip;
char ipv;
if (!sdp) {
OSMO_STRBUF_PRINTF(sb, "%s", "");
return sb.chars_needed;
}
oip = sdp->origin.addr.ip[0] ? sdp->origin.addr.ip : "0.0.0.0";
oipv = (osmo_ip_str_type(oip) == AF_INET6) ? '6' : '4';
ip = sdp->rtp.ip[0] ? sdp->rtp.ip : "0.0.0.0";
ipv = (osmo_ip_str_type(oip) == AF_INET6) ? '6' : '4';
OSMO_STRBUF_PRINTF(sb,
"v=0\r\n"
"o=%s %s %s IN IP%c %s\r\n"
"s=%s\r\n"
"c=IN IP%c %s\r\n"
"t=%"PRId64" %"PRId64"\r\n"
"m=audio %d RTP/AVP",
sdp->origin.username ? : "libosmo-sdp",
sdp->origin.sess_id ? : "0", sdp->origin.sess_version ? : "0",
oipv, oip,
sdp->session_name ? : "-",
ipv, ip,
sdp->time_active.start,
sdp->time_active.stop,
sdp->rtp.port);
/* Append all payload type numbers to 'm=audio <port> RTP/AVP 3 4 112' line */
osmo_sdp_codec_list_foreach(codec, sdp->codecs)
OSMO_STRBUF_PRINTF(sb, " %d", codec->payload_type);
OSMO_STRBUF_PRINTF(sb, "\r\n");
/* Add details for all codecs */
osmo_sdp_codec_list_foreach(codec, sdp->codecs) {
if (!osmo_sdp_codec_is_set(codec))
continue;
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_RTPMAP) "%d %s/%d\r\n", codec->payload_type, codec->encoding_name,
codec->rate > 0 ? codec->rate : 8000);
if (codec->fmtp && codec->fmtp[0])
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_FMTP) "%d %s\r\n", codec->payload_type, codec->fmtp);
}
if (sdp->ptime)
OSMO_STRBUF_PRINTF(sb, OSMO_SDP_A_PREFIX(OSMO_SDP_STR_PTIME) "%d\r\n", sdp->ptime);
if (sdp->media_direction != OSMO_SDP_MDIR_UNSET && sdp->media_direction < ARRAY_SIZE(mdir_str))
OSMO_STRBUF_PRINTF(sb, "a=%s\r\n", mdir_str[sdp->media_direction]);
return sb.chars_needed;
}
char *osmo_sdp_msg_encode_c(void *ctx, const struct osmo_sdp_msg *sdp)
{
OSMO_NAME_C_IMPL(ctx, 256, "osmo_sdp_msg_to_str_c-ERROR", osmo_sdp_msg_encode_buf, sdp)
}
/* Return the first line ending (or the end of the string) at or after the given string position. */
const char *get_line_end(const char *src)
{
const char *line_end = strchr(src, '\r');
if (!line_end)
line_end = strchr(src, '\n');
if (!line_end)
line_end = src + strlen(src);
return line_end;
}
static bool str_is_attrib(const char *str, const char *attrib_name, char expect_next_char)
{
char next_c;
if (!osmo_str_startswith(str, attrib_name))
return false;
next_c = str[strlen(attrib_name)];
if (expect_next_char == next_c)
return true;
/* Treat \0 as equivalent with line end */
if (!expect_next_char && (next_c == '\r' || next_c == '\n'))
return true;
/* It started with the string, but continued otherwise */
return false;
}
static enum osmo_sdp_media_direcion_e check_for_media_direction(const char *str)
{
int i;
for (i = 0; i < ARRAY_SIZE(mdir_str); i++) {
if (i == OSMO_SDP_MDIR_UNSET)
continue;
if (str_is_attrib(str, mdir_str[i], 0))
return i;
}
return OSMO_SDP_MDIR_UNSET;
}
static struct osmo_sdp_codec *find_or_create_payload_type(struct osmo_sdp_msg *sdp, unsigned int payload_type)
{
struct osmo_sdp_codec *codec;
codec = osmo_sdp_codec_list_by_payload_type(sdp->codecs, payload_type);
if (!codec) {
codec = osmo_sdp_codec_list_add_empty(sdp->codecs);
codec->payload_type = payload_type;
codec->rate = 8000;
}
return codec;
}
/* parse a line like 'a=rtpmap:0 PCMU/8000', 'a=fmtp:112 octet-align=1; mode-set=4', 'a=ptime:20'.
* The src should point at the character after 'a=', e.g. at the start of 'rtpmap', 'fmtp', 'ptime'
*/
int sdp_parse_attrib(struct osmo_sdp_msg *sdp, const char *src)
{
unsigned int payload_type;
struct osmo_sdp_codec *codec;
enum osmo_sdp_media_direcion_e mdir;
const char *line_end = get_line_end(src);
if (str_is_attrib(src, OSMO_SDP_STR_RTPMAP, ':')) {
/* "a=rtpmap:96 AMR/8000" */
struct token audio_name;
const char *slash;
if (sscanf(src, OSMO_SDP_STR_RTPMAP ":%u", &payload_type) != 1)
return -EINVAL;
codec = find_or_create_payload_type(sdp, payload_type);
audio_name.start = strchr(src, ' ');
if (!audio_name.start)
return -EINVAL;
audio_name.start++;
if (audio_name.start >= get_line_end(src))
return -EINVAL;
slash = strchr(audio_name.start, '/');
audio_name.end = slash ? : line_end;
token_copy(codec, &codec->encoding_name, &audio_name);
if (audio_name.end >= line_end) {
/* There should be a "/8000" here. If it is missing, let's not be strict about it. */
codec->rate = 8000;
} else {
unsigned int channels = 1;
if (sscanf(audio_name.end, "/%u/%u", &codec->rate, &channels) < 1)
return -EINVAL;
if (channels != 1)
return -ENOTSUP;
}
}
else if (str_is_attrib(src, OSMO_SDP_STR_FMTP, ':')) {
/* "a=fmtp:112 octet-align=1;mode-set=0,1,2,3" */
struct token fmtp_str;
const char *line_end = get_line_end(src);
if (sscanf(src, OSMO_SDP_STR_FMTP ":%u", &payload_type) != 1)
return -EINVAL;
codec = find_or_create_payload_type(sdp, payload_type);
fmtp_str.start = strchr(src, ' ');
if (!fmtp_str.start)
return -EINVAL;
fmtp_str.start++;
if (fmtp_str.start >= line_end)
return -EINVAL;
fmtp_str.end = line_end;
token_copy(codec, &codec->fmtp, &fmtp_str);
}
else if (str_is_attrib(src, OSMO_SDP_STR_PTIME, ':')) {
/* "a=ptime:20" */
if (sscanf(src, OSMO_SDP_STR_PTIME ":%u", &sdp->ptime) != 1)
return -EINVAL;
}
/* "a=sendrecv" ... */
else if ((mdir = check_for_media_direction(src)) != OSMO_SDP_MDIR_UNSET) {
sdp->media_direction = mdir;
}
return 0;
}
static const struct value_string fixed_payload_types[] = {
{ 0, "PCMU" },
{ 3, "GSM" },
{ 8, "PCMA" },
{ 18, "G729" },
{ 110, "GSM-EFR" },
{ 111, "GSM-HR-08" },
{ 112, "AMR" },
{ 113, "AMR-WB" },
{}
};
/* Parse a line like 'm=audio 16398 RTP/AVP 0 3 8 96 112', starting after the '=' */
static int sdp_parse_media_description(struct osmo_sdp_msg *sdp, const char *src)
{
unsigned int port;
int i;
const char *payload_type_str;
const char *line_end = get_line_end(src);
if (sscanf(src, "audio %u RTP/AVP", &port) < 1)
return -ENOTSUP;
if (port > 0xffff)
return -EINVAL;
sdp->rtp.port = port;
/* skip "audio 12345 RTP/AVP ", i.e. 3 spaces on */
payload_type_str = src;
for (i = 0; i < 3; i++) {
payload_type_str = strchr(payload_type_str, ' ');
if (!payload_type_str)
return -EINVAL;
while (*payload_type_str == ' ')
payload_type_str++;
if (payload_type_str >= line_end)
return -EINVAL;
}
/* Parse listing of payload type numbers after "RTP/AVP" */
while (payload_type_str < line_end) {
unsigned int payload_type;
struct osmo_sdp_codec *codec;
const char *encoding_name;
if (sscanf(payload_type_str, "%u", &payload_type) < 1)
return -EINVAL;
codec = find_or_create_payload_type(sdp, payload_type);
/* Fill in encoding name for fixed payload types */
encoding_name = get_value_string_or_null(fixed_payload_types, codec->payload_type);
if (encoding_name)
osmo_talloc_replace_string(codec, &codec->encoding_name, encoding_name);
payload_type_str = strchr(payload_type_str, ' ');
if (!payload_type_str)
payload_type_str = line_end;
while (*payload_type_str == ' ')
payload_type_str++;
}
return 0;
}
/* parse a line like 'c=IN IP4 192.168.11.151' starting after the '=' */
static int sdp_parse_connection_info(struct osmo_sdp_msg *sdp, const char *src)
{
char ipv[10];
char addr_str[INET6_ADDRSTRLEN];
if (sscanf(src, "IN %s %s", ipv, addr_str) < 2)
return -EINVAL;
if (strcmp(ipv, "IP4") && strcmp(ipv, "IP6"))
return -ENOTSUP;
return osmo_sockaddr_str_from_str(&sdp->rtp, addr_str, sdp->rtp.port);
}
static void next_token(struct token *t, const char *str, const char *end)
{
t->start = str;
while (*t->start == ' ' && t->start < end)
t->start++;
t->end = t->start;
while (*t->end != ' ' && t->end < end)
t->end++;
}
/* parse a line like 'o=jdoe 3724394400 3724394405 IN IP4 198.51.100.1' starting after the '=' */
static int sdp_parse_origin(struct osmo_sdp_msg *sdp, const char *src)
{
struct token t;
char addr_str[INET6_ADDRSTRLEN + 1] = {};
const char *line_end = get_line_end(src);
next_token(&t, src, line_end);
token_copy(sdp, &sdp->origin.username, &t);
next_token(&t, t.end, line_end);
token_copy(sdp, &sdp->origin.sess_id, &t);
next_token(&t, t.end, line_end);
token_copy(sdp, &sdp->origin.sess_version, &t);
next_token(&t, t.end, line_end);
if (strncmp("IN", t.start, t.end - t.start))
return -ENOTSUP;
next_token(&t, t.end, line_end);
if (strncmp("IP4", t.start, t.end - t.start)
&& strncmp("IP6", t.start, t.end - t.start))
return -ENOTSUP;
next_token(&t, t.end, line_end);
osmo_strlcpy(addr_str, t.start, OSMO_MIN(sizeof(addr_str), t.end - t.start + 1));
return osmo_sockaddr_str_from_str(&sdp->origin.addr, addr_str, 0);
}
static int sdp_parse_session_name(struct osmo_sdp_msg *sdp, const char *src)
{
const char *line_end = get_line_end(src);
if (sdp->session_name)
talloc_free(sdp->session_name);
if (line_end <= src)
sdp->session_name = NULL;
else
sdp->session_name = talloc_strndup(sdp, src, line_end - src);
return 0;
}
struct osmo_sdp_msg *osmo_sdp_msg_alloc(void *ctx)
{
struct osmo_sdp_msg *sdp;
sdp = talloc_zero(ctx, struct osmo_sdp_msg);
sdp->codecs = osmo_sdp_codec_list_alloc(sdp);
return sdp;
}
/* Parse SDP string into struct osmo_sdp_msg. Return 0 on success, negative on error.
* Return a new osmo_sdp_msg instance allocated from ctx, or NULL on error.
* When NULL is returned and if err is non-NULL, details of the error are returned in err->*.
*/
struct osmo_sdp_msg *osmo_sdp_msg_decode(void *ctx, const char *src, struct osmo_sdp_err *err)
{
struct osmo_sdp_msg *sdp;
const char *pos;
if (err)
*err = (struct osmo_sdp_err){};
sdp = osmo_sdp_msg_alloc(ctx);
for (pos = src; pos && *pos; pos++) {
char attrib;
int rc = 0;
if (*pos == '\r' || *pos == '\n')
continue;
/* Expecting only lines starting with 'X='. Not being too strict about it is probably alright. */
if (pos[1] != '=')
goto next_line;
attrib = *pos;
pos += 2;
switch (attrib) {
/* a=... */
case 'a':
rc = sdp_parse_attrib(sdp, pos);
break;
case 'm':
rc = sdp_parse_media_description(sdp, pos);
break;
case 'c':
rc = sdp_parse_connection_info(sdp, pos);
break;
case 'o':
rc = sdp_parse_origin(sdp, pos);
break;
case 's':
rc = sdp_parse_session_name(sdp, pos);
break;
default:
/* ignore any other parameters */
break;
}
if (rc) {
if (err) {
const char *line_end = get_line_end(pos);
/* shift back to include the 'x=' part as well */
pos -= 2;
*err = (struct osmo_sdp_err){
.rc = rc,
.at_input_str = pos,
.at_input_str_len = line_end - pos,
};
}
talloc_free(sdp);
return NULL;
}
next_line:
pos = strstr(pos, "\r\n");
if (!pos)
break;
}
return sdp;
}
/*! Short single-line representation of an SDP message, convenient for logging.
* To obtain a valid SDP message, use osmo_sdp_msg_encode_buf() instead.
*/
int osmo_sdp_msg_to_str_buf(char *buf, size_t buflen, const struct osmo_sdp_msg *sdp, bool summarize)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
if (!sdp) {
OSMO_STRBUF_PRINTF(sb, "NULL");
return sb.chars_needed;
}
OSMO_STRBUF_PRINTF(sb, OSMO_SOCKADDR_STR_FMT, OSMO_SOCKADDR_STR_FMT_ARGS(&sdp->rtp));
OSMO_STRBUF_PRINTF(sb, "{");
OSMO_STRBUF_APPEND(sb, osmo_sdp_codec_list_to_str_buf, sdp->codecs, summarize);
OSMO_STRBUF_PRINTF(sb, "}");
return sb.chars_needed;
}
char *osmo_sdp_msg_to_str_c(void *ctx, const struct osmo_sdp_msg *sdp, bool summarize)
{
OSMO_NAME_C_IMPL(ctx, 128, "sdp_msg_to_str_c-ERROR", osmo_sdp_msg_to_str_buf, sdp, summarize)
}

View File

@@ -25,6 +25,12 @@ osmo_mgw_SOURCES = \
$(NULL)
osmo_mgw_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOTRAU_LIBS) \
$(NULL)

View File

@@ -41,6 +41,7 @@
#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>
@@ -50,7 +51,7 @@
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/socket.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/ctrl/control_vty.h>
#include <osmocom/vty/telnet_interface.h>
@@ -82,7 +83,7 @@ static int daemonize = 0;
const char *osmomgw_copyright =
"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
"Copyright (C) 2017-2022 by sysmocom s.f.m.c. GmbH\r\n"
"Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
"Contributions by Pablo Neira Ayuso, Jacob Erlbeck, Neels Hofmeyr\r\n"
"Philipp Maier\r\n\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
@@ -94,7 +95,7 @@ static char *config_file = "osmo-mgw.cfg";
/* used by msgb and mgcp */
void *tall_mgw_ctx = NULL;
static void print_help(void)
static void print_help()
{
printf("Some useful options:\n");
printf(" -h --help is printing this text.\n");
@@ -280,31 +281,6 @@ int mgcp_vty_go_parent(struct vty *vty)
return vty->node;
}
static void signal_handler(int signum)
{
fprintf(stdout, "signal %u received\n", signum);
switch (signum) {
case SIGABRT:
/* in case of abort, we want to obtain a talloc report and
* then run default SIGABRT handler, who will generate coredump
* and abort the process. abort() should do this for us after we
* return, but program wouldn't exit if an external SIGABRT is
* received.
*/
talloc_report(tall_vty_ctx, stderr);
talloc_report_full(tall_mgw_ctx, stderr);
signal(SIGABRT, SIG_DFL);
raise(SIGABRT);
break;
case SIGUSR1:
talloc_report(tall_vty_ctx, stderr);
talloc_report_full(tall_mgw_ctx, stderr);
break;
default:
break;
}
}
static struct vty_app_info vty_info = {
.name = "OsmoMGW",
@@ -319,22 +295,13 @@ static const struct log_info_cat log_categories[] = {
.name = "DRTP",
.description = "RTP stream handling",
.color = "\033[1;30m",
.enabled = 1,
.loglevel = LOGL_NOTICE,
.enabled = 1,.loglevel = LOGL_NOTICE,
},
[DE1] = {
.name = "DE1",
.description = "E1 line handling",
.color = "\033[1;31m",
.enabled = 1,
.loglevel = LOGL_NOTICE,
},
[DOSMUX] = {
.name = "DOSMUX",
.description = "Osmux (Osmocom RTP multiplexing)",
.color = "\033[1;32m",
.enabled = 1,
.loglevel = LOGL_NOTICE,
.enabled = 1,.loglevel = LOGL_NOTICE,
},
};
@@ -353,8 +320,6 @@ int main(int argc, char **argv)
msgb_talloc_ctx_init(tall_mgw_ctx, 0);
signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
osmo_init_ignore_signals();
osmo_init_logging2(tall_mgw_ctx, &log_info);
libosmo_abis_init(tall_mgw_ctx);
@@ -387,11 +352,12 @@ int main(int argc, char **argv)
return rc;
/* start telnet after reading config for vty_get_bind_addr() */
rc = telnet_init_default(tall_mgw_ctx, NULL, OSMO_VTY_PORT_MGW);
rc = telnet_init_dynif(tall_mgw_ctx, NULL,
vty_get_bind_addr(), OSMO_VTY_PORT_MGW);
if (rc < 0)
return rc;
cfg->ctrl = ctrl_interface_setup(cfg, OSMO_CTRL_PORT_MGW, NULL);
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);

View File

@@ -1,7 +1,6 @@
SUBDIRS = \
mgcp_client \
mgcp \
sdp \
$(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
@@ -35,11 +34,11 @@ DISTCLEANFILES = \
$(NULL)
if ENABLE_EXT_TESTS
python-tests: $(top_builddir)/src/osmo-mgw/osmo-mgw
python-tests: $(BUILT_SOURCES)
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
else
python-tests:
python-tests: $(BUILT_SOURCES)
echo "Not running python-based tests (determined at configure-time)"
endif

View File

@@ -18,7 +18,6 @@ AM_CFLAGS = \
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
-no-install \
$(NULL)
EXTRA_DIST = \
@@ -34,7 +33,7 @@ mgcp_test_SOURCES = \
$(NULL)
mgcp_test_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
@@ -45,6 +44,3 @@ mgcp_test_LDADD = \
$(LIBOSMONETIF_LIBS) \
-lm \
$(NULL)
update_exp:
$(builddir)/mgcp_test >$(srcdir)/mgcp_test.ok

View File

@@ -77,8 +77,6 @@ static void test_strline(void)
#define AUEP1_RET "500 158663169 FAIL\r\n"
#define AUEP2 "AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0\r\n"
#define AUEP2_RET "500 18983213 FAIL\r\n"
#define AUEP_NULL "AUEP 18983215 null@mgw MGCP 1.0\r\n"
#define AUEP_NULL_RET "200 18983215 OK\r\n"
#define EMPTY "\r\n"
#define EMPTY_RET NULL
#define SHORT "CRCX \r\n"
@@ -103,7 +101,7 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define MDCX3A_RET \
"200 18983215 OK\r\n" \
@@ -115,7 +113,7 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define MDCX3_FMTP_RET \
"200 18983215 OK\r\n" \
@@ -127,49 +125,41 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16006 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
"a=fmtp:126 0/1/2\r\n" \
"a=ptime:40\r\n"
#define MDCX4_ADDR0000 \
"MDCX 18983216 1@mgw MGCP 1.0\r\n" \
"M: sendrecv\r\n" \
"M: sendrecv\r" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: p:20, a:AMR, nt:IN\r\n" \
"\r\n" \
"\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define MDCX4_ADDR0000_RET \
"200 18983216 OK\r\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:20\r\n"
"527 18983216 FAIL\r\n"
#define MDCX4 \
"MDCX 18983217 1@mgw MGCP 1.0\r\n" \
"M: sendrecv\r\n" \
"M: sendrecv\r" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: p:20, a:AMR, nt:IN\r\n" \
"\r\n" \
"\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
"c=IN IP4 5.6.7.8\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define MDCX4_RET(Ident) \
"200 " Ident " OK\r\n" \
@@ -181,7 +171,7 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define MDCX4_RO_RET(Ident) \
"200 " Ident " OK\r\n" \
@@ -193,87 +183,87 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 112\r\n" \
"a=rtpmap:112 AMR\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define MDCX4_PT1 \
"MDCX 18983218 1@mgw MGCP 1.0\r\n" \
"M: SENDRECV\r\n" \
"M: SENDRECV\r" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: p:20-40, a:AMR, nt:IN\r\n" \
"\r\n" \
"\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
"c=IN IP4 5.6.7.8\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define MDCX4_PT2 \
"MDCX 18983219 1@mgw MGCP 1.0\r\n" \
"M: sendrecv\r\n" \
"M: sendrecv\r" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: p:20-20, a:AMR, nt:IN\r\n" \
"\r\n" \
"\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
"c=IN IP4 5.6.7.8\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define MDCX4_PT3 \
"MDCX 18983220 1@mgw MGCP 1.0\r\n" \
"M: sendrecv\r\n" \
"M: sendrecv\r" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: a:AMR, nt:IN\r\n" \
"\r\n" \
"\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
"c=IN IP4 5.6.7.8\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
/* Test different upper/lower case in options */
#define MDCX4_PT4 \
"MDCX 18983221 1@mgw MGCP 1.0\r\n" \
"m: sendrecv\r\n" \
"m: sendrecv\r" \
"c: 2\r\n" \
"i: %s\r\n" \
"l: A:amr, NT:IN\r\n" \
"\r\n" \
"\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
"c=IN IP4 5.6.7.8\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define MDCX4_SO \
"MDCX 18983222 1@mgw MGCP 1.0\r\n" \
"M: sendonly\r\n" \
"M: sendonly\r" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: p:20, a:AMR, nt:IN\r\n" \
"\r\n" \
"\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 5.6.7.8\r\n" \
"c=IN IP4 5.6.7.8\r\n" \
"t=0 0\r\n" \
"m=audio 4441 RTP/AVP 99\r\n" \
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define MDCX4_RO \
"MDCX 18983223 1@mgw MGCP 1.0\r\n" \
"M: recvonly\r\n" \
"M: recvonly\r" \
"C: 2\r\n" \
"I: %s\r\n" \
"L: p:20, a:AMR, nt:IN\r\n"
@@ -284,12 +274,6 @@ static void test_strline(void)
#define MDCX_TOO_LONG_CI_RET "510 18983224 FAIL\r\n"
#define MDCX_NULL \
"MDCX 9 null@mgw MGCP 1.0\r\n" \
"I: %s\n"
#define MDCX_NULL_RET "502 9 FAIL\r\n"
#define SHORT2 "CRCX 1"
#define SHORT2_RET "510 000000 FAIL\r\n"
#define SHORT3 "CRCX 1 1@mgw"
@@ -306,7 +290,7 @@ static void test_strline(void)
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 5904 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define CRCX_RET \
"200 2 OK\r\n" \
@@ -319,7 +303,7 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define CRCX_RET_NO_RTPMAP \
"200 2 OK\r\n" \
@@ -331,7 +315,7 @@ static void test_strline(void)
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 16002 RTP/AVP 97\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define CRCX_FMTP_RET \
"200 2 OK\r\n" \
@@ -344,17 +328,18 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16006 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
"a=fmtp:126 0/1/2\r\n" \
"a=ptime:40\r\n"
#define CRCX_ZYN \
"CRCX 2 1@mgw MGCP 1.0\r\n" \
"M: recvonly\r\n" \
"CRCX 2 1@mgw MGCP 1.0\r" \
"M: recvonly\r" \
"C: 2\r\n" \
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 5904 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n"
"\n" \
"v=0\r" \
"c=IN IP4 123.12.12.123\r" \
"m=audio 5904 RTP/AVP 97\r" \
"a=rtpmap:97 GSM-EFR/8000\r"
#define CRCX_ZYN_RET \
"200 2 OK\r\n" \
@@ -380,7 +365,7 @@ static void test_strline(void)
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 5904 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define CRCX_X_OSMO_IGN_RET \
"200 2 OK\r\n" \
@@ -393,95 +378,7 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16010 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
#define CRCX_PORT_0 \
"CRCX 3 1@mgw MGCP 1.0\r\n" \
"m: recvonly\r\n" \
"C: 2\r\n" \
"L: p:20\r\n" \
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 0 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
#define CRCX_PORT_0_RET \
"200 3 OK\r\n" \
"I: %s\r\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 16014 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
#define CRCX_PORT_0_IUFP \
"CRCX 4 1@mgw MGCP 1.0\r\n" \
"m: recvonly\r\n" \
"C: 2\r\n" \
"L: p:20\r\n" \
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 0 RTP/AVP 96\r\n" \
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
"a=ptime:20\r\n"
#define CRCX_PORT_0_IUFP_RET \
"200 4 OK\r\n" \
"I: %s\r\n" \
"\r\n" \
"v=0\r\n" \
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
"s=-\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 16016 RTP/AVP 96\r\n" \
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
"a=ptime:20\r\n"
/* Do a CRCX in m=sendrecv */
#define CRCX_PORT_0_IUFP_SENDRECV \
"CRCX 4 1@mgw MGCP 1.0\r\n" \
"M: sendrecv\r\n" \
"C: 2\r\n" \
"L: p:20\r\n" \
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 0 RTP/AVP 96\r\n" \
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
"a=ptime:20\r\n"
/* Do a CRCX using sendrecv mode in the SDP part */
#define CRCX_PORT_0_IUFP_SENDRECV2 \
"CRCX 4 1@mgw MGCP 1.0\r\n" \
"C: 2\r\n" \
"L: p:20\r\n" \
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"a=sendrecv\r\n" \
"m=audio 0 RTP/AVP 96\r\n" \
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
"a=ptime:20\r\n"
/* Do a CRCX entirely omitting a mode, i.e. implcit sendrecv */
#define CRCX_PORT_0_IUFP_SENDRECV3 \
"CRCX 4 1@mgw MGCP 1.0\r\n" \
"C: 2\r\n" \
"L: p:20\r\n" \
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 0 RTP/AVP 96\r\n" \
"a=rtpmap:96 VND.3GPP.IUFP/16000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define DLCX \
"DLCX 7 1@mgw MGCP 1.0\r\n" \
@@ -495,13 +392,6 @@ static void test_strline(void)
#define DLCX_RET_OSMUX DLCX_RET \
"X-Osmo-CP: EC TI=0, TO=0\r\n"
#define DLCX_NULL \
"DLCX 8 null@mgw MGCP 1.0\r\n" \
"I: %s\r\n" \
"C: 2\r\n"
#define DLCX_NULL_RET "502 8 FAIL\r\n"
#define RQNT \
"RQNT 186908780 1@mgw MGCP 1.0\r\n" \
"X: B244F267488\r\n" \
@@ -515,13 +405,6 @@ static void test_strline(void)
#define RQNT1_RET "200 186908780 OK\r\n"
#define RQNT2_RET "200 186908781 OK\r\n"
#define RQNT_NULL \
"RQNT 186908782 null@mgw MGCP 1.0\r\n" \
"X: B244F267488\r\n" \
"S: D/9\r\n"
#define RQNT_NULL_RET "502 186908782 FAIL\r\n"
#define PTYPE_IGNORE 0 /* == default initializer */
#define PTYPE_NONE 128
#define PTYPE_NYI PTYPE_NONE
@@ -538,7 +421,7 @@ static void test_strline(void)
"m=audio 5904 RTP/AVP 18 97\r\n" \
"a=rtpmap:18 G729/8000\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define CRCX_MULT_2 \
"CRCX 2 2@mgw MGCP 1.0\r\n" \
@@ -553,7 +436,7 @@ static void test_strline(void)
"a=rtpmap:18 G729/8000\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=rtpmap:101 FOO/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define CRCX_MULT_3 \
"CRCX 2 3@mgw MGCP 1.0\r\n" \
@@ -568,7 +451,7 @@ static void test_strline(void)
"a=rtpmap:18 G729/8000\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=rtpmap:101 FOO/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define CRCX_MULT_4 \
"CRCX 2 4@mgw MGCP 1.0\r\n" \
@@ -583,7 +466,7 @@ static void test_strline(void)
"a=rtpmap:18 G729/8000\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=rtpmap:101 FOO/8000\r\n" \
"a=ptime:20\r\n"
"a=ptime:40\r\n"
#define CRCX_MULT_GSM_EXACT \
"CRCX 259260421 5@mgw MGCP 1.0\r\n" \
@@ -647,7 +530,7 @@ static void test_strline(void)
"t=0 0\r\n" \
"m=audio 16012 RTP/AVP 111\r\n" \
"a=rtpmap:111 AMR/8000/1\r\n" \
"a=fmtp:111 mode-change-capability=2; octet-align=1\r\n" \
"a=fmtp:111 octet-align=1\r\n" \
"a=ptime:20\r\n"
#define CRCX_NO_LCO_NO_SDP_RET \
@@ -662,25 +545,12 @@ static void test_strline(void)
"m=audio 16008 RTP/AVP 0\r\n" \
"a=ptime:20\r\n"
#define CRCX_NULL \
"CRCX 2 null@mgw MGCP 1.0\r\n" \
"m: recvonly\r\n" \
"C: 2\r\n" \
"L: p:20\r\n" \
"\r\n" \
"v=0\r\n" \
"c=IN IP4 123.12.12.123\r\n" \
"m=audio 5904 RTP/AVP 97\r\n" \
"a=rtpmap:97 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
#define CRCX_NULL_RET "502 2 FAIL\r\n"
struct mgcp_test {
const char *name;
const char *req;
const char *exp_resp;
int ptype;
const char *extra_fmtp;
};
static const struct mgcp_test tests[] = {
@@ -708,23 +578,14 @@ static const struct mgcp_test tests[] = {
{"RQNT1", RQNT, RQNT1_RET},
{"RQNT2", RQNT2, RQNT2_RET},
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
{"CRCX", CRCX, CRCX_FMTP_RET, 97},
{"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE},
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
{"CRCX", CRCX, CRCX_FMTP_RET, 97,.extra_fmtp = "a=fmtp:126 0/1/2"},
{"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE,.extra_fmtp =
"a=fmtp:126 0/1/2"},
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
{"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
{"CRCX", CRCX_X_OSMO_IGN, CRCX_X_OSMO_IGN_RET, 97},
{"MDCX_TOO_LONG_CI", MDCX_TOO_LONG_CI, MDCX_TOO_LONG_CI_RET},
{"CRCX", CRCX_AMR_WITH_FMTP, CRCX_AMR_WITH_FMTP_RET},
{"AUEP_NULL", AUEP_NULL, AUEP_NULL_RET},
{"CRCX_NULL", CRCX_NULL, CRCX_NULL_RET},
{"MDCX_NULL", MDCX_NULL, MDCX_NULL_RET},
{"DLCX_NULL", DLCX_NULL, DLCX_NULL_RET},
{"RQNT_NULL", RQNT_NULL, RQNT_NULL_RET},
{"CRCX_PORT_0", CRCX_PORT_0, CRCX_PORT_0_RET, 97},
{"CRCX_PORT_0_IUFP", CRCX_PORT_0_IUFP, CRCX_PORT_0_IUFP_RET, 96},
{"CRCX_PORT_0_IUFP_SENDRECV", CRCX_PORT_0_IUFP_SENDRECV, CRCX_PORT_0_IUFP_RET, 96},
{"CRCX_PORT_0_IUFP_SENDRECV2", CRCX_PORT_0_IUFP_SENDRECV2, CRCX_PORT_0_IUFP_RET, 96},
{"CRCX_PORT_0_IUFP_SENDRECV3", CRCX_PORT_0_IUFP_SENDRECV3, CRCX_PORT_0_IUFP_RET, 96},
};
static const struct mgcp_test retransmit[] = {
@@ -755,13 +616,12 @@ static struct msgb *create_msg(const char *str, const char *conn_id)
static int dummy_packets = 0;
/* override and forward */
int osmo_iofd_sendto_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int flags, const struct osmo_sockaddr *addr)
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen)
{
uint32_t dest_host =
htonl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
int dest_port = htons(((struct sockaddr_in *)addr)->sin_port);
const uint8_t *buf = msgb_data(msg);
size_t len = msgb_length(msg);
htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr);
int dest_port = htons(((struct sockaddr_in *)dest_addr)->sin_port);
if (len == sizeof(rtp_dummy_payload)
&& memcmp(buf, rtp_dummy_payload, sizeof(rtp_dummy_payload)) == 0) {
@@ -775,8 +635,6 @@ int osmo_iofd_sendto_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int flags,
OSMO_ASSERT(dest_host);
OSMO_ASSERT(dest_port);
msgb_free(msg);
return len;
}
@@ -932,6 +790,9 @@ static void test_messages(void)
dummy_packets = 0;
osmo_talloc_replace_string(cfg, &trunk->audio_fmtp_extra,
t->extra_fmtp);
inp = create_msg(t->req, last_conn_id);
msg = mgcp_handle_message(cfg, inp);
msgb_free(inp);
@@ -943,7 +804,6 @@ static void test_messages(void)
}
} else if (check_response(msg->data, t->exp_resp) != 0) {
printf("%s failed.\n", t->name);
fflush(stdout);
OSMO_ASSERT(false);
}
@@ -978,7 +838,7 @@ static void test_messages(void)
if (endp->local_options.pkt_period_min ||
endp->local_options.pkt_period_max)
printf
("Requested packetization period: "
("Requested packetetization period: "
"%d-%d\n",
endp->local_options.pkt_period_min,
endp->
@@ -1211,7 +1071,7 @@ static void test_packet_loss_calc(void)
"test-connection");
conn = mgcp_conn_get_rtp(&endp, _conn->id);
state = &conn->state;
packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
state->stats.initialized = 1;
state->stats.base_seq = pl_test_dat[i].base_seq;
@@ -1607,6 +1467,7 @@ static void test_multilple_codec(void)
/* Allocate 5@mgw and let osmo-mgw pick a codec from the list */
last_endpoint[0] = '\0';
inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
trunk->no_audio_transcoding = 1;
resp = mgcp_handle_message(cfg, inp);
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
sizeof(conn_id)) == 0);
@@ -1631,7 +1492,7 @@ static void test_multilple_codec(void)
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 3);
OSMO_ASSERT(osmo_sockaddr_port(&conn->end.addr.u.sa) == 16434);
OSMO_ASSERT(conn->end.rtp_port == htons(16434));
memset(&addr, 0, sizeof(addr));
inet_aton("8.8.8.8", &addr);
OSMO_ASSERT(conn->end.addr.u.sa.sa_family == AF_INET);
@@ -1650,6 +1511,7 @@ static void test_multilple_codec(void)
last_endpoint[0] = '\0';
inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
trunk->no_audio_transcoding = 0;
resp = mgcp_handle_message(cfg, inp);
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
sizeof(conn_id)) == 0);
@@ -1770,8 +1632,7 @@ static void test_osmux_cid(void)
for (i = 0; i < 256; ++i) {
id = osmux_cid_pool_get_next();
/* We called osmux_cid_pool_get_next() above, so next CID is i+1. */
OSMO_ASSERT(id == ((i + 1) & 0xff));
OSMO_ASSERT(id == i);
OSMO_ASSERT(osmux_cid_pool_count_used() == i + 1);
}
@@ -1907,25 +1768,26 @@ static const struct mgcp_codec_param amr_param_octet_aligned_unset = {
.amr_octet_aligned_present = false,
};
struct testcase_mgcp_codec_decide_codec {
struct testcase_mgcp_codec_pt_translate_codec {
int payload_type;
const char *audio_name;
const struct mgcp_codec_param *param;
int expect_rc;
};
struct testcase_mgcp_codec_decide_expect {
struct testcase_mgcp_codec_pt_translate_expect {
bool end;
int payload_type_map[2];
};
struct testcase_mgcp_codec_decide {
struct testcase_mgcp_codec_pt_translate {
const char *descr;
/* two conns on an endpoint, each with N configured codecs */
struct testcase_mgcp_codec_decide_codec codecs[2][10];
struct testcase_mgcp_codec_decide_expect expect[2];
struct testcase_mgcp_codec_pt_translate_codec codecs[2][10];
struct testcase_mgcp_codec_pt_translate_expect expect[32];
};
static const struct testcase_mgcp_codec_decide test_mgcp_codec_find_convertible_cases[] = {
static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translate_cases[] = {
{
.descr = "same order, but differing payload type numbers",
.codecs = {
@@ -1942,7 +1804,10 @@ static const struct testcase_mgcp_codec_decide test_mgcp_codec_find_convertible_
},
.expect = {
{ .payload_type_map = {112, 96}, },
{ .payload_type_map = {112, 96}, },
{ .payload_type_map = {0, 0}, },
{ .payload_type_map = {111, 97} },
{ .payload_type_map = {123, -EINVAL} },
{ .end = true },
},
},
{
@@ -1960,8 +1825,11 @@ static const struct testcase_mgcp_codec_decide test_mgcp_codec_find_convertible_
},
},
.expect = {
{ .payload_type_map = {112, 96}, },
{ .payload_type_map = {0, 0}, },
{ .payload_type_map = {111, 97}, },
{ .payload_type_map = {111, 97} },
{ .payload_type_map = {123, -EINVAL} },
{ .end = true },
},
},
{
@@ -1979,8 +1847,10 @@ static const struct testcase_mgcp_codec_decide test_mgcp_codec_find_convertible_
},
},
.expect = {
{ .payload_type_map = {0, 0}, },
{ .payload_type_map = {96, 97}, },
{ .payload_type_map = {97, 96}, },
{ .payload_type_map = {0, 0}, },
{ .end = true },
},
},
{
@@ -1996,8 +1866,10 @@ static const struct testcase_mgcp_codec_decide test_mgcp_codec_find_convertible_
},
},
.expect = {
{ .payload_type_map = {-EINVAL, -EINVAL}, },
{ .payload_type_map = {-EINVAL, 96}, },
{ .payload_type_map = {112, -EINVAL}, },
{ .payload_type_map = {0, -EINVAL}, },
{ .payload_type_map = {111, -EINVAL} },
{ .end = true },
},
},
{
@@ -2014,60 +1886,28 @@ static const struct testcase_mgcp_codec_decide test_mgcp_codec_find_convertible_
},
.expect = {
{ .payload_type_map = {112, -EINVAL}, },
{ .payload_type_map = {-EINVAL, -EINVAL}, },
{ .payload_type_map = {0, -EINVAL}, },
{ .payload_type_map = {111, -EINVAL} },
{ .end = true },
},
},
{
.descr = "test AMR with differing octet-aligned settings (both <-> both)",
.codecs = {
{
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
},
{
{ 122, "AMR/8000", &amr_param_octet_aligned_false, },
{ 121, "AMR/8000", &amr_param_octet_aligned_true, },
},
},
.expect = {
{ .payload_type_map = {111, 121}, },
{ .payload_type_map = {112, 122} },
},
},
{
.descr = "test AMR with differing octet-aligned settings (oa <-> both)",
.descr = "test AMR with differing octet-aligned settings",
.codecs = {
{
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
},
{
{ 122, "AMR/8000", &amr_param_octet_aligned_false, },
{ 121, "AMR/8000", &amr_param_octet_aligned_true, },
},
},
.expect = {
{ .payload_type_map = {111, 121}, },
{ .payload_type_map = {111, 121}, },
{ .payload_type_map = {111, 122}, },
{ .end = true },
},
},
{
.descr = "test AMR with differing octet-aligned settings (bwe <-> both)",
.codecs = {
{
{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
},
{
{ 122, "AMR/8000", &amr_param_octet_aligned_false, },
{ 121, "AMR/8000", &amr_param_octet_aligned_true, },
},
},
.expect = {
{ .payload_type_map = {112, 122}, },
{ .payload_type_map = {112, 122}, },
},
},
{
.descr = "test AMR with missing octet-aligned settings (oa <-> unset)",
.descr = "test AMR with missing octet-aligned settings (defaults to 0)",
.codecs = {
{
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
@@ -2078,54 +1918,22 @@ static const struct testcase_mgcp_codec_decide test_mgcp_codec_find_convertible_
},
.expect = {
{ .payload_type_map = {111, 122}, },
{ .payload_type_map = {111, 122}, },
{ .end = true },
},
},
{
.descr = "test AMR with missing octet-aligned settings (bwe <-> unset)",
.descr = "test AMR with NULL param (defaults to 0)",
.codecs = {
{
{ 111, "AMR/8000", &amr_param_octet_aligned_false, },
},
{
{ 122, "AMR/8000", &amr_param_octet_aligned_unset, },
},
},
.expect = {
{ .payload_type_map = {111, 122}, },
{ .payload_type_map = {111, 122}, },
},
},
{
.descr = "test AMR with NULL param (oa <-> null)",
.codecs = {
{
{ 112, "AMR/8000", &amr_param_octet_aligned_true, },
{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
},
{
{ 122, "AMR/8000", NULL, },
},
},
.expect = {
/* Note: Both 111, anbd 112 will translate to 122. The translation from 112 */
{ .payload_type_map = {112, 122} },
{ .payload_type_map = {112, 122}, },
},
},
{
.descr = "test AMR with NULL param (bwe <-> null)",
.codecs = {
{
{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
},
{
{ 122, "AMR/8000", NULL, },
},
},
.expect = {
/* Note: Both 111, anbd 112 will translate to 122. The translation from 112 */
{ .payload_type_map = {112, 122} },
{ .payload_type_map = {112, 122}, },
{ .payload_type_map = {111, 122}, },
{ .end = true },
},
},
{
@@ -2143,8 +1951,11 @@ static const struct testcase_mgcp_codec_decide test_mgcp_codec_find_convertible_
},
},
.expect = {
{ .payload_type_map = {112, 96}, },
{ .payload_type_map = {0, 0}, },
{ .payload_type_map = {111, 97}, },
{ .payload_type_map = {111, 97} },
{ .payload_type_map = {123, -EINVAL} },
{ .end = true },
},
},
{
@@ -2162,8 +1973,11 @@ static const struct testcase_mgcp_codec_decide test_mgcp_codec_find_convertible_
},
},
.expect = {
{ .payload_type_map = {112, 96}, },
{ .payload_type_map = {0, 0}, },
{ .payload_type_map = {111, 97}, },
{ .payload_type_map = {111, 97} },
{ .payload_type_map = {123, -EINVAL} },
{ .end = true },
},
},
{
@@ -2181,98 +1995,45 @@ static const struct testcase_mgcp_codec_decide test_mgcp_codec_find_convertible_
},
.expect = {
{ .payload_type_map = {111, 121}, },
{ .payload_type_map = {111, 121} },
{ .payload_type_map = {112, -EINVAL} },
{ .payload_type_map = {113, -EINVAL} },
{ .end = true },
},
},
};
static bool codec_decision(struct mgcp_conn_rtp *conn, unsigned int index_conn_src, unsigned int index_conn_dst,
const struct testcase_mgcp_codec_decide_expect *expect)
{
bool ok = true;
int payload_type_conn_src;
int payload_type_conn_dst;
printf(" - mgcp_codec_decide(&conn[%u], &conn[%u]):\n", index_conn_src, index_conn_dst);
if (mgcp_codec_decide(&conn[index_conn_src], &conn[index_conn_dst]) != 0) {
if (expect->payload_type_map[index_conn_src] == -EINVAL
&& expect->payload_type_map[index_conn_dst] == -EINVAL)
printf(" codec decision failed (expected)!\n");
else {
printf(" ERROR: codec decision failed!\n");
ok = false;
}
} else {
printf(" Codec decision result:\n");
if (conn[index_conn_src].end.codec) {
payload_type_conn_src = conn[index_conn_src].end.codec->payload_type;
printf(" conn[%u]: codec:%s, pt:%d\n",
index_conn_src, conn[index_conn_src].end.codec->subtype_name, payload_type_conn_src);
} else {
payload_type_conn_src = -EINVAL;
printf(" conn[%u]: codec:none, pt:none\n", index_conn_src);
}
if (conn[index_conn_dst].end.codec) {
payload_type_conn_dst = conn[index_conn_dst].end.codec->payload_type;
printf(" conn[%u]: codec:%s, pt:%d\n",
index_conn_dst, conn[index_conn_dst].end.codec->subtype_name,
payload_type_conn_dst);
} else {
payload_type_conn_dst = -EINVAL;
printf(" conn[%u]: codec:none, pt:none\n", index_conn_dst);
}
if (payload_type_conn_src != expect->payload_type_map[index_conn_src]) {
printf(" ERROR: conn[%u] unexpected codec decision, expected pt=%d, got pt=%d\n",
index_conn_src, expect->payload_type_map[index_conn_src], payload_type_conn_src);
ok = false;
}
if (payload_type_conn_dst != expect->payload_type_map[index_conn_dst]) {
printf(" ERROR: conn[%u] unexpected codec decision, expected pt=%d, got pt=%d\n",
index_conn_dst, expect->payload_type_map[index_conn_dst],
payload_type_conn_dst);
ok = false;
}
}
return ok;
}
static void test_mgcp_codec_decide(void)
static void test_mgcp_codec_pt_translate(void)
{
int i;
bool ok_all = true;
printf("\nTesting mgcp_codec_find_convertible()\n");
bool ok = true;
printf("\nTesting mgcp_codec_pt_translate()\n");
for (i = 0; i < ARRAY_SIZE(test_mgcp_codec_find_convertible_cases); i++) {
const struct testcase_mgcp_codec_decide *t = &test_mgcp_codec_find_convertible_cases[i];
struct mgcp_conn_rtp conn[2] = { };
for (i = 0; i < ARRAY_SIZE(test_mgcp_codec_pt_translate_cases); i++) {
const struct testcase_mgcp_codec_pt_translate *t = &test_mgcp_codec_pt_translate_cases[i];
struct mgcp_conn_rtp conn[2] = {};
int rc;
int conn_i;
int c;
bool ok = true;
printf("#%d: %s\n", i, t->descr);
/* Build testvector (add codecs to conn, set properties etc... */
for (conn_i = 0; conn_i < 2; conn_i++) {
printf(" - add codecs on conn%d:\n", conn_i);
for (c = 0; c < ARRAY_SIZE(t->codecs[conn_i]); c++) {
const struct testcase_mgcp_codec_decide_codec *codec = &t->codecs[conn_i][c];
const struct testcase_mgcp_codec_pt_translate_codec *codec = &t->codecs[conn_i][c];
if (!codec->audio_name)
break;
rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name,
codec->param);
rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name, codec->param);
printf(" %2d: %3d %s%s -> rc=%d\n", c, codec->payload_type, codec->audio_name,
codec->param ?
(codec->param->amr_octet_aligned_present ?
(codec->param->amr_octet_aligned ? " octet-aligned=1" : " octet-aligned=0")
: " octet-aligned=unset")
: "", rc);
(codec->param->amr_octet_aligned_present?
(codec->param->amr_octet_aligned ?
" octet-aligned=1" : " octet-aligned=0")
: " octet-aligned=unset")
: "",
rc);
if (rc != codec->expect_rc) {
printf(" ERROR: expected rc=%d\n", codec->expect_rc);
ok = false;
@@ -2282,26 +2043,42 @@ static void test_mgcp_codec_decide(void)
printf(" (none)\n");
}
/* Run codec decision and check expectation */
if (!codec_decision(conn, 0, 1, &t->expect[0]))
ok = false;
for (c = 0; c < ARRAY_SIZE(t->expect); c++) {
const struct testcase_mgcp_codec_pt_translate_expect *expect = &t->expect[c];
int result;
if (!codec_decision(conn, 1, 0, &t->expect[1]))
ok = false;
if (expect->end)
break;
if (ok)
printf(" ===> SUCCESS: codec decision as expected!\n");
else
printf(" ===> FAIL: unexpected codec decision!\n");
result = mgcp_codec_pt_translate(&conn[0], &conn[1], expect->payload_type_map[0]);
printf(" - mgcp_codec_pt_translate(conn0, conn1, %d) -> %d\n",
expect->payload_type_map[0], result);
if (result != expect->payload_type_map[1]) {
printf(" ERROR: expected -> %d\n", expect->payload_type_map[1]);
ok = false;
}
if (!ok)
ok_all = false;
/* If the expected result is an error, don't do reverse map test */
if (expect->payload_type_map[1] < 0)
continue;
result = mgcp_codec_pt_translate(&conn[1], &conn[0], expect->payload_type_map[1]);
printf(" - mgcp_codec_pt_translate(conn1, conn0, %d) -> %d\n",
expect->payload_type_map[1], result);
if (result != expect->payload_type_map[0]) {
printf(" ERROR: expected -> %d\n", expect->payload_type_map[0]);
ok = false;
}
}
for (conn_i = 0; conn_i < 2; conn_i++)
mgcp_codec_reset_all(&conn[conn_i]);
}
OSMO_ASSERT(ok_all);
OSMO_ASSERT(ok);
}
void test_conn_id_matching(void)
void test_conn_id_matching()
{
struct mgcp_endpoint endp = {};
struct mgcp_conn *conn;
@@ -2341,7 +2118,7 @@ void test_conn_id_matching(void)
talloc_free(conn);
}
void test_e1_trunk_nr_from_epname(void)
void test_e1_trunk_nr_from_epname()
{
unsigned int trunk_nr;
int rc;
@@ -2391,7 +2168,7 @@ void test_e1_trunk_nr_from_epname(void)
return;
}
void test_mgcp_is_rtp_dummy_payload(void)
void test_mgcp_is_rtp_dummy_payload()
{
/* realistic rtp packet */
static const char rtp_payload[] =
@@ -2455,7 +2232,7 @@ int main(int argc, char **argv)
test_osmux_cid();
test_get_lco_identifier();
test_check_local_cx_options(ctx);
test_mgcp_codec_decide();
test_mgcp_codec_pt_translate();
test_conn_id_matching();
test_e1_trunk_nr_from_epname();
test_mgcp_is_rtp_dummy_payload();

View File

@@ -73,7 +73,7 @@ v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:20
a=ptime:40
---------8<---------
checking response:
@@ -101,42 +101,40 @@ Testing MDCX4_ADDR000
creating message from statically defined input:
---------8<---------
MDCX 18983216 1@mgw MGCP 1.0
M: sendrecv
C: 2
M: sendrecv
C: 2
I: %s
L: p:20, 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=ptime:20
a=rtpmap:99 AMR/8000
a=ptime:40
---------8<---------
using message with patched conn_id for comparison
checking response:
using message as statically defined for comparison
(response contains a connection id)
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing MDCX4
creating message from statically defined input:
---------8<---------
M: sendrecv
C: 2
MDCX 18983217 1@mgw MGCP 1.0
M: sendrecv
C: 2
I: %s
L: p:20, a:AMR, nt:IN
v=0
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
t=0 0
a=ptime:20
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
a=ptime:40
@@ -150,18 +148,17 @@ Testing MDCX4_PT1
================================================
Testing MDCX4_PT1
creating message from statically defined input:
M: SENDRECV
C: 2
---------8<---------
MDCX 18983218 1@mgw MGCP 1.0
M: SENDRECV
C: 2
I: %s
L: p:20-40, a:AMR, nt:IN
v=0
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
a=ptime:20
t=0 0
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
a=ptime:40
@@ -175,18 +172,17 @@ Testing MDCX4_PT2
================================================
Testing MDCX4_PT2
M: sendrecv
C: 2
creating message from statically defined input:
---------8<---------
MDCX 18983219 1@mgw MGCP 1.0
M: sendrecv
C: 2
I: %s
L: p:20-20, a:AMR, nt:IN
v=0
o=- %s 23 IN IP4 5.6.7.8
a=ptime:20
c=IN IP4 5.6.7.8
t=0 0
m=audio 4441 RTP/AVP 99
a=rtpmap:99 AMR/8000
@@ -200,18 +196,17 @@ Testing MDCX4_PT3
Dummy packets: 2
================================================
M: sendrecv
C: 2
Testing MDCX4_PT3
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
a=ptime:20
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
t=0 0
m=audio 4441 RTP/AVP 99
@@ -225,18 +220,17 @@ Testing MDCX4_PT4
(response contains a connection id)
Dummy packets: 2
m: sendrecv
c: 2
================================================
Testing MDCX4_PT4
creating message from statically defined input:
---------8<---------
MDCX 18983221 1@mgw MGCP 1.0
m: sendrecv
c: 2
i: %s
l: A:amr, NT:IN
a=ptime:20
v=0
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
t=0 0
@@ -250,18 +244,17 @@ Testing MDCX4_SO
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
M: sendonly
C: 2
================================================
Testing MDCX4_SO
creating message from statically defined input:
---------8<---------
MDCX 18983222 1@mgw MGCP 1.0
M: sendonly
C: 2
I: %s
L: p:20, a:AMR, nt:IN
a=ptime:20
v=0
o=- %s 23 IN IP4 5.6.7.8
c=IN IP4 5.6.7.8
@@ -274,8 +267,7 @@ Testing MDCX4_RO
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
M: recvonly
C: 2
(response contains a connection id)
================================================
Testing MDCX4_RO
@@ -304,15 +296,9 @@ Response matches our expectations.
---------8<---------
checking response:
CRCX 2 1@mgw MGCP 1.0
M: recvonly
C: 2
v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing CRCX_ZYN
@@ -428,7 +414,7 @@ v=0
Response matches our expectations.
(response does not contain a connection id)
a=ptime:20
================================================
Testing CRCX
creating message from statically defined input:
---------8<---------
@@ -493,7 +479,7 @@ v=0
(response contains a connection id)
================================================
a=ptime:20
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
@@ -539,184 +525,6 @@ Response matches our expectations.
L: p:20
v=0
================================================
Testing AUEP_NULL
creating message from statically defined input:
---------8<---------
AUEP 18983215 null@mgw MGCP 1.0
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing CRCX_NULL
creating message from statically defined input:
---------8<---------
CRCX 2 null@mgw MGCP 1.0
m: recvonly
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:20
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing MDCX_NULL
creating message from statically defined input:
---------8<---------
MDCX 9 null@mgw MGCP 1.0
I: %s
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing DLCX_NULL
creating message from statically defined input:
---------8<---------
DLCX 8 null@mgw MGCP 1.0
I: %s
C: 2
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing RQNT_NULL
creating message from statically defined input:
---------8<---------
RQNT 186908782 null@mgw MGCP 1.0
X: B244F267488
S: D/9
---------8<---------
checking response:
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing CRCX_PORT_0
creating message from statically defined input:
---------8<---------
CRCX 3 1@mgw MGCP 1.0
m: recvonly
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
================================================
Testing CRCX_PORT_0_IUFP
creating message from statically defined input:
---------8<---------
CRCX 4 1@mgw MGCP 1.0
m: recvonly
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
================================================
Testing CRCX_PORT_0_IUFP_SENDRECV
creating message from statically defined input:
---------8<---------
CRCX 4 1@mgw MGCP 1.0
M: sendrecv
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
================================================
Testing CRCX_PORT_0_IUFP_SENDRECV2
creating message from statically defined input:
---------8<---------
CRCX 4 1@mgw MGCP 1.0
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
a=sendrecv
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
================================================
Testing CRCX_PORT_0_IUFP_SENDRECV3
creating message from statically defined input:
---------8<---------
CRCX 4 1@mgw MGCP 1.0
C: 2
L: p:20
v=0
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 111
a=rtpmap:111 AMR/8000/1
@@ -730,7 +538,7 @@ v=0
(response contains a connection id)
Dummy packets: 2
a=ptime:20
================================================
Testing CRCX
creating message from statically defined input:
---------8<---------
@@ -748,7 +556,7 @@ v=0
---------8<---------
checking response:
using message with patched conn_id for comparison
a=ptime:20
Response matches our expectations.
Re-transmitting CRCX
creating message from statically defined input:
---------8<---------
@@ -860,7 +668,7 @@ v=0
---------8<---------
checking response:
using message as statically defined for comparison
a=ptime:20
Response matches our expectations.
Testing packet loss calculation.
creating message from statically defined input:
---------8<---------
@@ -1314,7 +1122,7 @@ c=IN IP4 123.12.12.123
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
Stats: Jitter = 5, Transit = -163760
Testing multiple payload types
a=ptime:20
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
M: recvonly
@@ -1331,7 +1139,7 @@ m=audio 5904 RTP/AVP 18 97 101
---------8<---------
creating message from statically defined input:
a=ptime:20
---------8<---------
CRCX 2 2@mgw MGCP 1.0
M: recvonly
C: 2
@@ -1348,7 +1156,7 @@ m=audio 5904 RTP/AVP
---------8<---------
creating message from statically defined input:
a=ptime:20
---------8<---------
CRCX 2 3@mgw MGCP 1.0
M: recvonly
C: 2
@@ -1365,7 +1173,7 @@ m=audio 5904 RTP/AVP 18
---------8<---------
creating message from statically defined input:
a=ptime:20
---------8<---------
CRCX 2 4@mgw MGCP 1.0
M: recvonly
C: 2
@@ -1447,7 +1255,7 @@ v=0
a=recvonly
---------8<---------
a=ptime:20
Testing no sequence flow on initial packet
Testing no rtpmap name
creating message from statically defined input:
---------8<---------
@@ -1468,7 +1276,7 @@ p:10, a:PCMU -> p:10, a:PCMU
Response matches our expectations.
Testing get_lco_identifier()
p:10, a:PCMU -> p:10, a:PCMU
Testing mgcp_codec_find_convertible()
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'
@@ -1478,15 +1286,13 @@ Testing mgcp_codec_find_convertible()
', a:PCMU' -> 'a:PCMU'
' a:PCMU' -> 'a:PCMU'
'' -> '(null)'
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:AMR, pt:96
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:96
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
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:
@@ -1496,15 +1302,13 @@ Testing mgcp_codec_find_convertible()
- 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_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:PCMU, pt:0
conn[1]: codec:PCMU, pt:0
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:97
conn[0]: codec:GSM-HR-08, pt:111
===> SUCCESS: codec decision as expected!
- 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:
@@ -1514,15 +1318,12 @@ Testing mgcp_codec_find_convertible()
- 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_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:PCMU, pt:0
conn[1]: codec:PCMU, pt:0
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:97
conn[0]: codec:GSM-HR-08, pt:96
===> SUCCESS: codec decision as expected!
- 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
@@ -1530,13 +1331,9 @@ Testing mgcp_codec_find_convertible()
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_decide(&conn[0], &conn[1]):
codec decision failed (expected)!
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:96
conn[0]: codec:none, pt:none
===> SUCCESS: codec decision as expected!
- 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
@@ -1544,116 +1341,31 @@ Testing mgcp_codec_find_convertible()
- add codecs on conn0:
(none)
- add codecs on conn1:
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:none, pt:none
- mgcp_codec_decide(&conn[1], &conn[0]):
codec decision failed (expected)!
===> SUCCESS: codec decision as expected!
#5: test AMR with differing octet-aligned settings (both <-> both)
- 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_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:111
conn[1]: codec:AMR, pt:121
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
#6: test AMR with differing octet-aligned settings (oa <-> both)
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:
1: 121 AMR/8000 octet-aligned=1 -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:111
conn[1]: codec:AMR, pt:121
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:121
conn[0]: codec:AMR, pt:111
===> SUCCESS: codec decision as expected!
#7: test AMR with differing octet-aligned settings (bwe <-> both)
- add codecs on conn0:
0: 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_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
#8: test AMR with missing octet-aligned settings (oa <-> unset)
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_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:111
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:111
===> SUCCESS: codec decision as expected!
#9: test AMR with missing octet-aligned settings (bwe <-> unset)
- 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
0: 111 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=unset -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:111
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:111
===> SUCCESS: codec decision as expected!
#10: test AMR with NULL param (oa <-> null)
- add codecs on conn0:
0: 112 AMR/8000 octet-aligned=1 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=0 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 122
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
#11: test AMR with NULL param (bwe <-> null)
- add codecs on conn0:
0: 112 AMR/8000 octet-aligned=0 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:AMR, pt:112
conn[1]: codec:AMR, pt:122
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:AMR, pt:122
conn[0]: codec:AMR, pt:112
===> SUCCESS: codec decision as expected!
#12: match FOO/8000/1 and FOO/8000 as identical, single channel is implicit
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 111
#6: test AMR with missing octet-aligned settings (defaults to 0)
- add codecs on conn0:
0: 111 AMR/8000 octet-aligned=1 -> rc=0
- add codecs on conn1:
0: 122 AMR/8000 octet-aligned=unset -> rc=0
@@ -1662,16 +1374,14 @@ Testing mgcp_codec_find_convertible()
#7: test AMR with NULL param (defaults to 0)
- add codecs on conn0:
0: 111 AMR/8000 octet-aligned=1 -> rc=0
- mgcp_codec_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:PCMU, pt:0
conn[1]: codec:PCMU, pt:0
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:97
conn[0]: codec:GSM-HR-08, pt:111
===> SUCCESS: codec decision as expected!
#13: match FOO/8000/1 and FOO as identical, 8k and single channel are implicit
- add codecs on conn1:
0: 122 AMR/8000 -> rc=0
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 122
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 111
#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
@@ -1680,16 +1390,14 @@ Testing mgcp_codec_find_convertible()
- 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_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:PCMU, pt:0
conn[1]: codec:PCMU, pt:0
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:97
conn[0]: codec:GSM-HR-08, pt:111
===> SUCCESS: codec decision as expected!
#14: test whether channel number matching is waterproof
- 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
@@ -1697,15 +1405,10 @@ Testing mgcp_codec_find_convertible()
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_decide(&conn[0], &conn[1]):
Codec decision result:
conn[0]: codec:GSM-HR-08, pt:111
conn[1]: codec:GSM-HR-08, pt:121
- mgcp_codec_decide(&conn[1], &conn[0]):
Codec decision result:
conn[1]: codec:GSM-HR-08, pt:121
conn[0]: codec:GSM-HR-08, pt:111
===> SUCCESS: codec decision as expected!
- 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
- add codecs on conn0:

View File

@@ -15,7 +15,6 @@ AM_CFLAGS = \
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
-no-install \
$(NULL)
EXTRA_DIST = \

View File

@@ -73,7 +73,7 @@ static struct msgb *from_str(const char *str)
return msg;
}
static struct mgcp_client_conf *conf;
static struct mgcp_client_conf conf;
struct mgcp_client *mgcp = NULL;
static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
@@ -107,6 +107,9 @@ void test_response_cb(struct mgcp_response *response, void *priv)
printf(" audio_port = %u\n", response->audio_port);
printf(" audio_ip = %s\n", response->audio_ip);
printf(" ptime = %u\n", response->ptime);
printf(" codecs_len = %u\n", response->codecs_len);
for(i=0;i<response->codecs_len;i++)
printf(" codecs[%u] = %u\n", i, response->codecs[i]);
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);
@@ -146,11 +149,12 @@ void test_mgcp_msg(void)
.conn_id = "11",
.conn_mode = MGCP_CONN_RECV_SEND,
.ptime = 20,
.ptmap = {
{ .codec = CODEC_GSM_8000_1, .pt = CODEC_GSM_8000_1 },
{ .codec = CODEC_AMR_8000_1, .pt = CODEC_AMR_8000_1 },
{ .codec = CODEC_GSMEFR_8000_1, .pt = 96 },
},
.codecs[0] = CODEC_GSM_8000_1,
.codecs[1] = CODEC_AMR_8000_1,
.codecs[2] = CODEC_GSMEFR_8000_1,
.codecs_len = 1,
.ptmap[0].codec = CODEC_GSMEFR_8000_1,
.ptmap[0].pt = 96,
.ptmap_len = 1,
.x_osmo_ign = MGCP_X_OSMO_IGN_CALLID,
.x_osmo_osmux_cid = -1, /* wildcard */
@@ -158,7 +162,7 @@ void test_mgcp_msg(void)
if (mgcp)
talloc_free(mgcp);
mgcp = mgcp_client_init(ctx, conf);
mgcp = mgcp_client_init(ctx, &conf);
printf("\n");
@@ -175,9 +179,9 @@ void test_mgcp_msg(void)
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
mgcp_msg.ptmap_len = 2;
mgcp_msg.codecs_len = 2;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.ptmap_len = 1;
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated CRCX message (three codecs, one with custom pt):\n");
@@ -185,9 +189,9 @@ void test_mgcp_msg(void)
mgcp_msg.presence =
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
mgcp_msg.ptmap_len = 3;
mgcp_msg.codecs_len = 3;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.ptmap_len = 1;
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated MDCX message:\n");
@@ -205,9 +209,9 @@ void test_mgcp_msg(void)
(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.ptmap_len = 2;
mgcp_msg.codecs_len = 2;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.ptmap_len = 1;
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated MDCX message (three codecs, one with custom pt):\n");
@@ -216,9 +220,9 @@ void test_mgcp_msg(void)
(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.ptmap_len = 3;
mgcp_msg.codecs_len = 3;
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
mgcp_msg.ptmap_len = 1;
mgcp_msg.codecs_len = 1;
printf("%s\n", (char *)msg->data);
printf("Generated DLCX message:\n");
@@ -311,7 +315,7 @@ void test_mgcp_msg(void)
msgb_free(msg);
}
void test_mgcp_client_cancel(void)
void test_mgcp_client_cancel()
{
mgcp_trans_id_t trans_id;
struct msgb *msg;
@@ -326,10 +330,8 @@ void test_mgcp_client_cancel(void)
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
| MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE),
.ptime = 20,
.ptmap = {
{ .codec = CODEC_AMR_8000_1, .pt = CODEC_AMR_8000_1 },
},
.ptmap_len = 1
.codecs[0] = CODEC_AMR_8000_1,
.codecs_len = 1
};
printf("\n%s():\n", __func__);
@@ -337,7 +339,7 @@ void test_mgcp_client_cancel(void)
if (mgcp)
talloc_free(mgcp);
mgcp = mgcp_client_init(ctx, conf);
mgcp = mgcp_client_init(ctx, &conf);
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
trans_id = mgcp_msg_trans_id(msg);
@@ -485,7 +487,7 @@ static struct sdp_section_start_test sdp_section_start_tests[] = {
},
};
void test_sdp_section_start(void)
void test_sdp_section_start()
{
int i;
int failures = 0;
@@ -529,7 +531,7 @@ void test_sdp_section_start(void)
OSMO_ASSERT(!failures);
}
static void test_map_str_to_codec(void)
static void test_map_pt_to_codec(void)
{
/* Full form */
OSMO_ASSERT(map_str_to_codec("PCMU/8000/1") == CODEC_PCMU_8000_1);
@@ -628,7 +630,7 @@ void test_mgcp_client_e1_epname(void)
if (mgcp)
talloc_free(mgcp);
mgcp = mgcp_client_init(ctx, conf);
mgcp = mgcp_client_init(ctx, &conf);
/* Valid endpoint names */
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 1, 15, 64, 0);
@@ -673,214 +675,6 @@ void test_mgcp_client_e1_epname(void)
OSMO_ASSERT(epname == NULL);
}
struct parse_response_test {
const char *body;
int expect_rc;
struct mgcp_response expect_params;
};
static struct parse_response_test parse_response_tests[] = {
{
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 112 3\r\n" /* <-- implicit: 3 = GSM-FR */
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n",
.expect_rc = 0,
.expect_params = {
.audio_port = 23,
.audio_ip = "1.2.3.4",
.ptmap = {
{ .codec = CODEC_AMR_8000_1, .pt = 112 },
{ .codec = CODEC_GSM_8000_1, .pt = 3 },
},
.ptmap_len = 2,
},
},
{
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 112 3\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=rtpmap:3 GSM/8000\r\n" /* 3 == GSM-FR implicitly, is an explicit entry a problem? */
"a=ptime:20\r\n",
.expect_rc = 0,
.expect_params = {
.audio_port = 23,
.audio_ip = "1.2.3.4",
.ptmap = {
{ .codec = CODEC_AMR_8000_1, .pt = 112 },
{ .codec = CODEC_GSM_8000_1, .pt = 3 }, /* no, not a problem */
},
.ptmap_len = 2,
},
},
{
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 3\r\n" /* <-- 112 is missing here. Will it still appear? */
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n",
.expect_rc = 0,
.expect_params = {
.audio_port = 23,
.audio_ip = "1.2.3.4",
.ptmap = {
{ .codec = CODEC_GSM_8000_1, .pt = 3 },
{ .codec = CODEC_AMR_8000_1, .pt = 112 }, /* <-- yes, it was added to the end. */
},
.ptmap_len = 2,
},
},
{
/* test MGCP_MAX_CODECS */
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110\r\n" /* <-- 10 codecs max */
"a=rtpmap:101 AMR/8000\r\n"
"a=rtpmap:102 AMR/8000\r\n"
"a=rtpmap:103 AMR/8000\r\n"
"a=rtpmap:104 AMR/8000\r\n"
"a=rtpmap:105 AMR/8000\r\n"
"a=rtpmap:106 AMR/8000\r\n"
"a=rtpmap:107 AMR/8000\r\n"
"a=rtpmap:108 AMR/8000\r\n"
"a=rtpmap:109 AMR/8000\r\n"
"a=rtpmap:110 AMR/8000\r\n"
"a=ptime:20\r\n",
.expect_rc = 0,
.expect_params = {
.audio_port = 23,
.audio_ip = "1.2.3.4",
.ptmap = {
{ .codec = CODEC_AMR_8000_1, .pt = 101 },
{ .codec = CODEC_AMR_8000_1, .pt = 102 },
{ .codec = CODEC_AMR_8000_1, .pt = 103 },
{ .codec = CODEC_AMR_8000_1, .pt = 104 },
{ .codec = CODEC_AMR_8000_1, .pt = 105 },
{ .codec = CODEC_AMR_8000_1, .pt = 106 },
{ .codec = CODEC_AMR_8000_1, .pt = 107 },
{ .codec = CODEC_AMR_8000_1, .pt = 108 },
{ .codec = CODEC_AMR_8000_1, .pt = 109 },
{ .codec = CODEC_AMR_8000_1, .pt = 110 },
},
.ptmap_len = 10,
},
},
{
/* test MGCP_MAX_CODECS */
.body = "200 2 OK\r\n"
"I: foo\r\n"
"\r\n"
"v=0\r\n"
"o=- name 23 IN IP4 0.0.0.0\r\n"
"s=-\r\n"
"c=IN IP4 1.2.3.4\r\n"
"t=0 0\r\n"
"m=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110 3\r\n" /* <-- 11 > MGCP_MAX_CODECS */
"a=rtpmap:101 AMR/8000\r\n"
"a=rtpmap:102 AMR/8000\r\n"
"a=rtpmap:103 AMR/8000\r\n"
"a=rtpmap:104 AMR/8000\r\n"
"a=rtpmap:105 AMR/8000\r\n"
"a=rtpmap:106 AMR/8000\r\n"
"a=rtpmap:107 AMR/8000\r\n"
"a=rtpmap:108 AMR/8000\r\n"
"a=rtpmap:109 AMR/8000\r\n"
"a=rtpmap:110 AMR/8000\r\n"
"a=ptime:20\r\n",
.expect_rc = -EINVAL,
},
};
static void test_parse_response(void)
{
int i;
int failures = 0;
for (i = 0; i < ARRAY_SIZE(parse_response_tests); i++) {
int rc;
struct parse_response_test *t = &parse_response_tests[i];
struct mgcp_response *r = talloc_zero(ctx, struct mgcp_response);
int p;
r->body = talloc_strdup(r, t->body);
//printf("\n%s() test [%d]:\n", __func__, i);
fprintf(stderr, "\n%s() test [%d]:\n", __func__, i);
fprintf(stderr, "body: \"%s\"\n", osmo_escape_str(r->body, -1));
rc = mgcp_response_parse_params(r);
fprintf(stderr, "got rc=%d\n", rc);
if (rc != t->expect_rc) {
fprintf(stderr, "FAIL: Expected rc=%d\n", t->expect_rc);
failures++;
}
if (rc) {
talloc_free(r);
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);
if (r->audio_port != t->expect_params.audio_port) {
fprintf(stderr, "FAIL: Expected audio_port=%u\n", t->expect_params.audio_port);
failures++;
}
for (p = 0; p < r->ptmap_len; p++) {
struct ptmap *got = &r->ptmap[p];
struct ptmap *expect = NULL;
fprintf(stderr, " %d %s\n", got->pt, osmo_mgcpc_codec_name(got->codec));
if (p >= t->expect_params.ptmap_len) {
fprintf(stderr, " - ERROR: too many codec entries\n");
failures++;
continue;
}
expect = &t->expect_params.ptmap[p];
if (ptmap_cmp(got, expect)) {
fprintf(stderr, " - ERROR: expected: %d %s\n",
expect->pt, osmo_mgcpc_codec_name(expect->codec));
failures++;
}
}
talloc_free(r);
}
OSMO_ASSERT(!failures);
}
static const struct log_info_cat log_categories[] = {
};
@@ -903,17 +697,15 @@ int main(int argc, char **argv)
log_set_category_filter(osmo_stderr_target, DLMGCP, 1, LOGL_DEBUG);
conf = mgcp_client_conf_alloc(ctx);
mgcp_client_conf_init(&conf);
test_mgcp_msg();
test_mgcp_client_cancel();
test_sdp_section_start();
test_map_codec_to_pt_and_map_pt_to_codec();
test_map_str_to_codec();
test_map_pt_to_codec();
test_mgcp_client_e1_epname();
test_parse_response();
printf("Done\n");
fprintf(stderr, "Done\n");
return EXIT_SUCCESS;

View File

@@ -1,6 +1,5 @@
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
DLMGCP MGW(mgw) Message buffer too small, can not generate MGCP message (SDP)
DLMGCP MGW(mgw) Failed to add SDP, can not generate MGCP message
DLMGCP MGW(mgw) Message buffer to small, can not generate MGCP message (SDP)
test_mgcp_client_cancel():
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
@@ -11,7 +10,6 @@ DLMGCP MGW(mgw) Cannot cancel, no such transaction: 1
- cancel succeeds
DLMGCP MGW(mgw) Canceled transaction 1
- late response gets discarded
DLMGCP MGW(mgw) MGCP link to MGW now considered UP
DLMGCP MGW(mgw) MGCP client: Rx 200 1 OK
DLMGCP MGW(mgw) Cannot find matching MGCP transaction for trans_id 1
- canceling again does nothing
@@ -137,51 +135,4 @@ DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw),
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-0/su8-2@mgw), E1-timeslot number (0) is invalid!
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-64/su8-2@mgw), E1-timeslot number (64) is invalid!
test_parse_response() test [0]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 112 3\r\na=rtpmap:112 AMR/8000\r\na=ptime:20\r\n"
got rc=0
got audio_ip="1.2.3.4"
got audio_port=23
112 AMR/8000/1
3 GSM/8000/1
test_parse_response() test [1]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 112 3\r\na=rtpmap:112 AMR/8000\r\na=rtpmap:3 GSM/8000\r\na=ptime:20\r\n"
got rc=0
got audio_ip="1.2.3.4"
got audio_port=23
112 AMR/8000/1
3 GSM/8000/1
test_parse_response() test [2]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 3\r\na=rtpmap:112 AMR/8000\r\na=ptime:20\r\n"
DLMGCP error in MGCP message: 'a=rtpmap:112' has no matching entry in 'm=audio ... 112'
got rc=0
got audio_ip="1.2.3.4"
got audio_port=23
3 GSM/8000/1
112 AMR/8000/1
test_parse_response() test [3]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110\r\na=rtpmap:101 AMR/8000\r\na=rtpmap:102 AMR/8000\r\na=rtpmap:103 AMR/8000\r\na=rtpmap:104 AMR/8"
got rc=0
got audio_ip="1.2.3.4"
got audio_port=23
101 AMR/8000/1
102 AMR/8000/1
103 AMR/8000/1
104 AMR/8000/1
105 AMR/8000/1
106 AMR/8000/1
107 AMR/8000/1
108 AMR/8000/1
109 AMR/8000/1
110 AMR/8000/1
test_parse_response() test [4]:
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110 3\r\na=rtpmap:101 AMR/8000\r\na=rtpmap:102 AMR/8000\r\na=rtpmap:103 AMR/8000\r\na=rtpmap:104 AMR"
DLMGCP SDP: can parse only up to 10 payload type numbers
DLMGCP Failed to parse SDP parameter payload types (RTP/AVP)
got rc=-22
Done

View File

@@ -1,62 +0,0 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir)/include \
-I$(top_srcdir) \
$(NULL)
AM_CFLAGS = \
-Wall \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
-no-install \
$(NULL)
EXTRA_DIST = \
sdp_fmtp_test.ok \
sdp_fmtp_test.err \
sdp_codec_test.ok \
sdp_codec_test.err \
$(NULL)
check_PROGRAMS = \
sdp_fmtp_test \
sdp_codec_test \
sdp_msg_test \
$(NULL)
sdp_fmtp_test_SOURCES = \
sdp_fmtp_test.c \
$(NULL)
sdp_fmtp_test_LDADD = \
$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
$(NULL)
sdp_codec_test_SOURCES = \
sdp_codec_test.c \
$(NULL)
sdp_codec_test_LDADD = \
$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
$(LIBOSMOCORE_LIBS) \
$(NULL)
sdp_msg_test_SOURCES = \
sdp_msg_test.c \
$(NULL)
sdp_msg_test_LDADD = \
$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
$(LIBOSMOCORE_LIBS) \
$(NULL)
update_exp:
$(builddir)/sdp_fmtp_test >$(srcdir)/sdp_fmtp_test.ok 2>$(srcdir)/sdp_fmtp_test.err
$(builddir)/sdp_codec_test >$(srcdir)/sdp_codec_test.ok 2>$(srcdir)/sdp_codec_test.err
$(builddir)/sdp_msg_test >$(srcdir)/sdp_msg_test.ok 2>$(srcdir)/sdp_msg_test.err

View File

@@ -1,645 +0,0 @@
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/sdp/sdp_codec_list.h>
#include <osmocom/sdp/fmtp.h>
void *test_ctx = NULL;
static void report_callback(const void *ptr, int depth, int max_depth, int is_ref, void *priv)
{
const char *name = talloc_get_name(ptr);
printf(" |%*s%3zu %s\n", depth, "", talloc_total_blocks(ptr), name);
}
/* Print a talloc report that is reproducible for test output verification. It contains no pointer addresses. */
#define report(CTX) _report(CTX, #CTX)
static void _report(void *ctx, const char *label)
{
fflush(stdout);
fflush(stderr);
printf("%s\n", label);
talloc_report_depth_cb(ctx, 0, 100, report_callback, NULL);
fflush(stdout);
}
struct codec_test {
struct osmo_sdp_codec set;
int expect_rc;
const char *expect_str;
bool expect_is_set;
};
struct codec_test codec_tests[] = {
{
.set = { 23, "encoding-name", 8000, NULL },
.expect_str = "encoding-name#23",
.expect_is_set = true,
},
{
.set = { 112, "AMR", 8000, "octet-align=1;mode-set=0,2,4" },
.expect_str = "AMR:octet-align=1;mode-set=0,2,4#112",
.expect_is_set = true,
},
{
.set = { 96, "AMR", 8000, "mode-set=0,2,4;octet-align=1" },
.expect_str = "AMR:mode-set=0,2,4;octet-align=1#96",
.expect_is_set = true,
},
{
.set = { 114, "AMR", 8000, "mode-set=0,2,4" },
.expect_str = "AMR:mode-set=0,2,4#114",
.expect_is_set = true,
},
{
.set = { 97, "AMR", 8000, "mode-set=0,2,4;octet-align=0" },
.expect_str = "AMR:mode-set=0,2,4;octet-align=0#97",
.expect_is_set = true,
},
{
.set = { 98, "AMR", 8000, "octet-align=1" },
.expect_str = "AMR:octet-align=1#98",
.expect_is_set = true,
},
{
.set = { 96, "AMR-WB", 16000 },
.expect_str = "AMR-WB/16000#96",
.expect_is_set = true,
},
{
.set = { 3, "GSM", 8000 },
.expect_str = "GSM#3",
.expect_is_set = true,
},
{
.set = { },
.expect_str = "/0#0",
.expect_is_set = false,
},
{
.set = { 112, NULL, 8000, "octet-align=1" },
.expect_str = ":octet-align=1#112",
.expect_is_set = false,
},
{
.set = { 112, "", 8000, "octet-align=1" },
.expect_str = ":octet-align=1#112",
.expect_is_set = false,
},
};
void test_codec(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
struct codec_test *t;
struct codec_test *t2;
printf("\n\n--- %s()\n", __func__);
printf("- osmo_sdp_codec_set():\n");
for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
struct osmo_sdp_codec *codec = osmo_sdp_codec_alloc(ctx);
char *str;
bool is_set;
osmo_sdp_codec_set(codec, t->set.payload_type, t->set.encoding_name, t->set.rate, t->set.fmtp);
str = osmo_sdp_codec_to_str_c(ctx, codec);
printf("osmo_sdp_codec_set [%d] '%s'\n", (int)(t - codec_tests), str);
if (strcmp(str, t->expect_str))
printf(" *** ERROR: expected '%s'\n", t->expect_str);
if (!osmo_sdp_codec_cmp(codec, &t->set, &osmo_sdp_codec_cmp_exact))
printf(" osmo_sdp_codec_cmp() ok\n");
else
printf(" osmo_sdp_codec_cmp() *** ERROR: mismatches original values\n");
is_set = osmo_sdp_codec_is_set(codec);
printf(" osmo_sdp_codec_is_set() = %s\n", is_set ? "true" : "false");
if (is_set != t->expect_is_set)
printf(" *** ERROR: expected is_set = %s\n", t->expect_is_set ? "true" : "false");
if (is_set != osmo_sdp_codec_is_set(&t->set))
printf(" *** ERROR: is_set(copy) != is_set(orig)\n");
talloc_free(str);
talloc_free(codec);
if (talloc_total_blocks(ctx) != 1)
printf(" *** ERROR: ctx has %zu items, should be 1\n", talloc_total_blocks(ctx));
}
printf("\n- osmo_sdp_codec_cmp(equivalent):\n");
for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
for (t2 = codec_tests; (t2 - codec_tests) < ARRAY_SIZE(codec_tests); t2++) {
int cmp = osmo_sdp_codec_cmp(&t->set, &t2->set, &osmo_sdp_codec_cmp_equivalent);
int reverse_cmp = osmo_sdp_codec_cmp(&t2->set, &t->set, &osmo_sdp_codec_cmp_equivalent);
printf(" %s %s %s %s %s\n",
osmo_sdp_codec_to_str_c(ctx, &t->set),
(cmp == 0) ? "=="
: ((cmp < 0) ? "<" : ">"),
osmo_sdp_codec_to_str_c(ctx, &t2->set),
(reverse_cmp == 0) ? "=="
: ((reverse_cmp < 0) ? "<" : ">"),
osmo_sdp_codec_to_str_c(ctx, &t->set));
if (reverse_cmp != -cmp)
printf(" *** ERROR: osmo_sdp_codec_cmp(reverse args) == %d, expected %d\n",
reverse_cmp, -cmp);
talloc_free_children(ctx);
}
}
printf("\n- osmo_sdp_codec_from_str():\n");
for (t = codec_tests; (t - codec_tests) < ARRAY_SIZE(codec_tests); t++) {
struct osmo_sdp_codec *codec = osmo_sdp_codec_alloc(ctx);
int rc = osmo_sdp_codec_from_str(codec, t->expect_str, -1);
printf(" osmo_sdp_codec_from_str('%s') rc=%d",
t->expect_str, rc);
if (!rc) {
printf(" res=%s", osmo_sdp_codec_to_str_c(ctx, codec));
rc = osmo_sdp_codec_cmp(codec, &t->set, &osmo_sdp_codec_cmp_exact);
if (rc)
printf(" *** ERROR: osmo_sdp_codec_cmp(res,orig) = %d", rc);
}
printf("\n");
talloc_free_children(ctx);
}
talloc_free(ctx);
}
void test_codec_list(void)
{
void *list_ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
int rc;
struct osmo_sdp_codec *codec;
const struct osmo_sdp_codec all_codecs[] = {
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
};
struct osmo_sdp_codec_list *codec_list;
printf("\n\n--- %s()\n", __func__);
codec_list = osmo_sdp_codec_list_alloc(list_ctx);
printf("osmo_sdp_codec_list_first() = %s\n",
osmo_sdp_codec_to_str_c(print_ctx, osmo_sdp_codec_list_first(codec_list)));
report(list_ctx);
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, false);
printf("[%d] osmo_sdp_codec_list_add(%s)\n", i, osmo_sdp_codec_to_str_c(print_ctx, added));
}
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
printf("osmo_sdp_codec_list_first() = %s\n",
osmo_sdp_codec_to_str_c(print_ctx, osmo_sdp_codec_list_first(codec_list)));
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("\n");
printf("- add same entries again with once=exact, nothing should change\n");
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i],
&osmo_sdp_codec_cmp_exact, false);
printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
}
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("\n");
printf("- add same entries again with once=NULL, duplicates are added\n");
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, false);
printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
}
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("\n");
printf("- add same entries again with once=NULL,pick_unused_pt_nr=true, duplicates are added with new #nr\n");
for (i = 0; i < ARRAY_SIZE(all_codecs); i++) {
struct osmo_sdp_codec *added = osmo_sdp_codec_list_add(codec_list, &all_codecs[i], NULL, true);
printf("[] osmo_sdp_codec_list_add(%s)\n", osmo_sdp_codec_to_str_c(print_ctx, added));
}
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("\n");
printf("- remove all 'GSM#3' entries, with osmo_sdp_codec_cmp_exact\n");
rc = osmo_sdp_codec_list_remove(codec_list, &all_codecs[1], &osmo_sdp_codec_cmp_exact);
printf(" osmo_sdp_codec_list_remove() = %d\n", rc);
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("- remove all 'GSM' entries, with osmo_sdp_codec_cmp_equivalent\n");
rc = osmo_sdp_codec_list_remove(codec_list, &all_codecs[1], &osmo_sdp_codec_cmp_equivalent);
printf(" osmo_sdp_codec_list_remove() = %d\n", rc);
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
rc = osmo_sdp_codec_list_move_to_first(codec_list, &all_codecs[0], &osmo_sdp_codec_cmp_equivalent);
printf("- osmo_sdp_codec_list_move_to_first('%s', equivalent) = %d\n",
osmo_sdp_codec_to_str_c(print_ctx, &all_codecs[0]), rc);
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
printf("- osmo_sdp_codec_list_free_items()\n");
osmo_sdp_codec_list_free_items(codec_list);
i = 0;
osmo_sdp_codec_list_foreach(codec, codec_list) {
printf("codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
printf(" %d entries\n", i);
report(list_ctx);
printf("osmo_sdp_codec_list_to_str_c(summarize=true):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, true));
printf("osmo_sdp_codec_list_to_str_c(summarize=false):\n '%s'\n",
osmo_sdp_codec_list_to_str_c(print_ctx, codec_list, false));
talloc_free(print_ctx);
talloc_free(list_ctx);
}
static struct osmo_sdp_codec_list *init_codec_list(void *ctx, const struct osmo_sdp_codec *init_array)
{
struct osmo_sdp_codec_list *dst = osmo_sdp_codec_list_alloc(ctx);
for (; osmo_sdp_codec_is_set(init_array); init_array++)
osmo_sdp_codec_list_add(dst, init_array, NULL, false);
return dst;
}
void test_codec_list_cmp(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
const struct osmo_sdp_codec codec_a[] = {
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
{}
};
const struct osmo_sdp_codec codec_b[][5] = {
/* same */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different payload_type */
{
{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* AMR fmtp in different order */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "mode-set=0,2,4;octet-align=1" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different AMR mode-set */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=7" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* empty AMR mode-set */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different AMR octet-align */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=0;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* omitted AMR octet-align is identical to octet-align=0 */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* different order */
{
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
},
/* one less item */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
},
/* one more item */
{
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
},
};
const struct osmo_sdp_codec_cmp_flags *test_cmpf[] = {
&osmo_sdp_codec_cmp_name,
&osmo_sdp_codec_cmp_equivalent,
&osmo_sdp_codec_cmp_exact,
};
printf("\n\n--- %s()\n", __func__);
for (i = 0; i < ARRAY_SIZE(codec_b); i++) {
struct osmo_sdp_codec_list *list_a = init_codec_list(ctx, codec_a);
struct osmo_sdp_codec_list *list_b = init_codec_list(ctx, codec_b[i]);
int j;
printf("A = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
printf("B = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
for (j = 0; j < ARRAY_SIZE(test_cmpf); j++) {
const struct osmo_sdp_codec_cmp_flags *cmpf = test_cmpf[j];
int cmp = osmo_sdp_codec_list_cmp(list_a, list_b, cmpf);
int reverse_cmp = osmo_sdp_codec_list_cmp(list_b, list_a, cmpf);
printf(" cmpf[%d]: payload_type=%s rate=%s fmtp=%d: A %s B %s A\n",
j,
cmpf->payload_type ? "true" : "false",
cmpf->rate ? "true" : "false",
cmpf->fmtp,
(cmp == 0) ? "=="
: ((cmp < 0) ? "<" : ">"),
(reverse_cmp == 0) ? "=="
: ((reverse_cmp < 0) ? "<" : ">"));
if (reverse_cmp != -cmp)
printf(" *** ERROR: osmo_sdp_codec_list_cmp(reverse args) == %d, expected %d\n",
reverse_cmp, -cmp);
}
talloc_free(list_a);
talloc_free(list_b);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak:\n");
report(ctx);
}
printf("\n");
talloc_free_children(print_ctx);
}
talloc_free(print_ctx);
talloc_free(ctx);
}
void test_codec_list_intersection(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
const struct osmo_sdp_codec codec_a[] = {
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
{}
};
const struct osmo_sdp_codec codec_b[][5] = {
/* same */
{
{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
},
/* same in different order */
{
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 96, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
},
/* two matches */
{
{ .payload_type = 98, .encoding_name = "GSM-HR-08", .rate = 8000 },
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
{ .payload_type = 97, .encoding_name = "GSM", .rate = 8000 },
},
/* no match */
{
{ .payload_type = 97, .encoding_name = "AMR-WB", .rate = 16000 },
{ .payload_type = 98, .encoding_name = "FOO", .rate = 8000 },
{ .payload_type = 110, .encoding_name = "GSM-EFR", .rate = 8000 },
},
};
printf("\n\n--- %s()\n", __func__);
for (i = 0; i < ARRAY_SIZE(codec_b); i++) {
struct osmo_sdp_codec_list *list_a = init_codec_list(ctx, codec_a);
struct osmo_sdp_codec_list *list_b = init_codec_list(ctx, codec_b[i]);
printf("A = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
printf("B = %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
osmo_sdp_codec_list_intersection(list_a, list_b, &osmo_sdp_codec_cmp_equivalent, false);
printf("osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
talloc_free(list_a);
list_a = init_codec_list(ctx, codec_a);
osmo_sdp_codec_list_intersection(list_a, list_b, &osmo_sdp_codec_cmp_equivalent, true);
printf("osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_a, false));
talloc_free(list_a);
list_a = init_codec_list(ctx, codec_a);
osmo_sdp_codec_list_intersection(list_b, list_a, &osmo_sdp_codec_cmp_equivalent, false);
printf("osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
talloc_free(list_a);
list_a = init_codec_list(ctx, codec_a);
osmo_sdp_codec_list_intersection(list_b, list_a, &osmo_sdp_codec_cmp_equivalent, true);
printf("osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)\n = %s\n",
osmo_sdp_codec_list_to_str_c(print_ctx, list_b, false));
talloc_free(list_a);
talloc_free(list_b);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak:\n");
report(ctx);
}
printf("\n");
talloc_free_children(print_ctx);
}
talloc_free(print_ctx);
talloc_free(ctx);
}
struct my_obj {
struct osmo_sdp_codec *codec;
struct osmo_sdp_codec_list *codec_list;
};
struct my_obj *my_obj_alloc(void *ctx)
{
struct my_obj *o = talloc_zero(ctx, struct my_obj);
o->codec_list = osmo_sdp_codec_list_alloc(o);
return o;
}
void test_obj_members(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
struct osmo_sdp_codec *codec;
struct my_obj *o;
printf("\n\n--- %s()\n", __func__);
o = my_obj_alloc(ctx);
o->codec = osmo_sdp_codec_alloc(o);
osmo_sdp_codec_set(o->codec, 96, "AMR", 8000, "octet-align=1");
printf("o->codec = %s\n", osmo_sdp_codec_to_str_c(print_ctx, o->codec));
report(ctx);
osmo_sdp_codec_list_add(o->codec_list, o->codec, false, false);
osmo_sdp_codec_list_add(o->codec_list, o->codec, false, true);
i = 0;
osmo_sdp_codec_list_foreach(codec, o->codec_list) {
printf("o->codec_list[%d] = %s\n", i++, osmo_sdp_codec_to_str_c(print_ctx, codec));
}
report(ctx);
printf("talloc_free(o)\n");
talloc_free(o);
report(ctx);
talloc_free(ctx);
talloc_free(print_ctx);
}
typedef void (*test_func_t)(void);
test_func_t test_func[] = {
test_codec,
test_codec_list,
test_codec_list_cmp,
test_codec_list_intersection,
test_obj_members,
};
int main(void)
{
int i;
test_ctx = talloc_named_const(NULL, 0, "sdp_codec_test");
for (i = 0; i < ARRAY_SIZE(test_func); i++) {
test_func[i]();
if (talloc_total_blocks(test_ctx) != 1) {
talloc_report_full(test_ctx, stderr);
printf("ERROR after test %d: memory leak\n", i);
return -1;
}
}
talloc_free(test_ctx);
return 0;
}

View File

@@ -1,538 +0,0 @@
--- test_codec()
- osmo_sdp_codec_set():
osmo_sdp_codec_set [0] 'encoding-name#23'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [1] 'AMR:octet-align=1;mode-set=0,2,4#112'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [2] 'AMR:mode-set=0,2,4;octet-align=1#96'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [3] 'AMR:mode-set=0,2,4#114'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [4] 'AMR:mode-set=0,2,4;octet-align=0#97'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [5] 'AMR:octet-align=1#98'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [6] 'AMR-WB/16000#96'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [7] 'GSM#3'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = true
osmo_sdp_codec_set [8] '/0#0'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = false
osmo_sdp_codec_set [9] ':octet-align=1#112'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = false
osmo_sdp_codec_set [10] ':octet-align=1#112'
osmo_sdp_codec_cmp() ok
osmo_sdp_codec_is_set() = false
- osmo_sdp_codec_cmp(equivalent):
encoding-name#23 == encoding-name#23 == encoding-name#23
encoding-name#23 > AMR:octet-align=1;mode-set=0,2,4#112 < encoding-name#23
encoding-name#23 > AMR:mode-set=0,2,4;octet-align=1#96 < encoding-name#23
encoding-name#23 > AMR:mode-set=0,2,4#114 < encoding-name#23
encoding-name#23 > AMR:mode-set=0,2,4;octet-align=0#97 < encoding-name#23
encoding-name#23 > AMR:octet-align=1#98 < encoding-name#23
encoding-name#23 > AMR-WB/16000#96 < encoding-name#23
encoding-name#23 > GSM#3 < encoding-name#23
encoding-name#23 > /0#0 < encoding-name#23
encoding-name#23 > :octet-align=1#112 < encoding-name#23
encoding-name#23 > :octet-align=1#112 < encoding-name#23
AMR:octet-align=1;mode-set=0,2,4#112 < encoding-name#23 > AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4#114 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1#98 == AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 < AMR-WB/16000#96 > AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 < GSM#3 > AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > /0#0 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112
AMR:mode-set=0,2,4;octet-align=1#96 < encoding-name#23 > AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4#114 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1#98 == AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 < AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 < GSM#3 > AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > /0#0 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96
AMR:mode-set=0,2,4#114 < encoding-name#23 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < AMR:octet-align=1#98 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < AMR-WB/16000#96 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 < GSM#3 > AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 > /0#0 < AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 > :octet-align=1#112 < AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4#114 > :octet-align=1#112 < AMR:mode-set=0,2,4#114
AMR:mode-set=0,2,4;octet-align=0#97 < encoding-name#23 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1;mode-set=0,2,4#112 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < AMR:mode-set=0,2,4;octet-align=1#96 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4#114 == AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4;octet-align=0#97 == AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1#98 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 < GSM#3 > AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 > /0#0 < AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97
AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97
AMR:octet-align=1#98 < encoding-name#23 > AMR:octet-align=1#98
AMR:octet-align=1#98 == AMR:octet-align=1;mode-set=0,2,4#112 == AMR:octet-align=1#98
AMR:octet-align=1#98 == AMR:mode-set=0,2,4;octet-align=1#96 == AMR:octet-align=1#98
AMR:octet-align=1#98 > AMR:mode-set=0,2,4#114 < AMR:octet-align=1#98
AMR:octet-align=1#98 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR:octet-align=1#98
AMR:octet-align=1#98 == AMR:octet-align=1#98 == AMR:octet-align=1#98
AMR:octet-align=1#98 < AMR-WB/16000#96 > AMR:octet-align=1#98
AMR:octet-align=1#98 < GSM#3 > AMR:octet-align=1#98
AMR:octet-align=1#98 > /0#0 < AMR:octet-align=1#98
AMR:octet-align=1#98 > :octet-align=1#112 < AMR:octet-align=1#98
AMR:octet-align=1#98 > :octet-align=1#112 < AMR:octet-align=1#98
AMR-WB/16000#96 < encoding-name#23 > AMR-WB/16000#96
AMR-WB/16000#96 > AMR:octet-align=1;mode-set=0,2,4#112 < AMR-WB/16000#96
AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=1#96 < AMR-WB/16000#96
AMR-WB/16000#96 > AMR:mode-set=0,2,4#114 < AMR-WB/16000#96
AMR-WB/16000#96 > AMR:mode-set=0,2,4;octet-align=0#97 < AMR-WB/16000#96
AMR-WB/16000#96 > AMR:octet-align=1#98 < AMR-WB/16000#96
AMR-WB/16000#96 == AMR-WB/16000#96 == AMR-WB/16000#96
AMR-WB/16000#96 < GSM#3 > AMR-WB/16000#96
AMR-WB/16000#96 > /0#0 < AMR-WB/16000#96
AMR-WB/16000#96 > :octet-align=1#112 < AMR-WB/16000#96
AMR-WB/16000#96 > :octet-align=1#112 < AMR-WB/16000#96
GSM#3 < encoding-name#23 > GSM#3
GSM#3 > AMR:octet-align=1;mode-set=0,2,4#112 < GSM#3
GSM#3 > AMR:mode-set=0,2,4;octet-align=1#96 < GSM#3
GSM#3 > AMR:mode-set=0,2,4#114 < GSM#3
GSM#3 > AMR:mode-set=0,2,4;octet-align=0#97 < GSM#3
GSM#3 > AMR:octet-align=1#98 < GSM#3
GSM#3 > AMR-WB/16000#96 < GSM#3
GSM#3 == GSM#3 == GSM#3
GSM#3 > /0#0 < GSM#3
GSM#3 > :octet-align=1#112 < GSM#3
GSM#3 > :octet-align=1#112 < GSM#3
/0#0 < encoding-name#23 > /0#0
/0#0 < AMR:octet-align=1;mode-set=0,2,4#112 > /0#0
/0#0 < AMR:mode-set=0,2,4;octet-align=1#96 > /0#0
/0#0 < AMR:mode-set=0,2,4#114 > /0#0
/0#0 < AMR:mode-set=0,2,4;octet-align=0#97 > /0#0
/0#0 < AMR:octet-align=1#98 > /0#0
/0#0 < AMR-WB/16000#96 > /0#0
/0#0 < GSM#3 > /0#0
/0#0 == /0#0 == /0#0
/0#0 < :octet-align=1#112 > /0#0
/0#0 < :octet-align=1#112 > /0#0
:octet-align=1#112 < encoding-name#23 > :octet-align=1#112
:octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4#114 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112
:octet-align=1#112 < AMR:octet-align=1#98 > :octet-align=1#112
:octet-align=1#112 < AMR-WB/16000#96 > :octet-align=1#112
:octet-align=1#112 < GSM#3 > :octet-align=1#112
:octet-align=1#112 > /0#0 < :octet-align=1#112
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
:octet-align=1#112 < encoding-name#23 > :octet-align=1#112
:octet-align=1#112 < AMR:octet-align=1;mode-set=0,2,4#112 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=1#96 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4#114 > :octet-align=1#112
:octet-align=1#112 < AMR:mode-set=0,2,4;octet-align=0#97 > :octet-align=1#112
:octet-align=1#112 < AMR:octet-align=1#98 > :octet-align=1#112
:octet-align=1#112 < AMR-WB/16000#96 > :octet-align=1#112
:octet-align=1#112 < GSM#3 > :octet-align=1#112
:octet-align=1#112 > /0#0 < :octet-align=1#112
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
:octet-align=1#112 == :octet-align=1#112 == :octet-align=1#112
- osmo_sdp_codec_from_str():
osmo_sdp_codec_from_str('encoding-name#23') rc=0 res=encoding-name#23
osmo_sdp_codec_from_str('AMR:octet-align=1;mode-set=0,2,4#112') rc=0 res=AMR:octet-align=1;mode-set=0,2,4#112
osmo_sdp_codec_from_str('AMR:mode-set=0,2,4;octet-align=1#96') rc=0 res=AMR:mode-set=0,2,4;octet-align=1#96
osmo_sdp_codec_from_str('AMR:mode-set=0,2,4#114') rc=0 res=AMR:mode-set=0,2,4#114
osmo_sdp_codec_from_str('AMR:mode-set=0,2,4;octet-align=0#97') rc=0 res=AMR:mode-set=0,2,4;octet-align=0#97
osmo_sdp_codec_from_str('AMR:octet-align=1#98') rc=0 res=AMR:octet-align=1#98
osmo_sdp_codec_from_str('AMR-WB/16000#96') rc=0 res=AMR-WB/16000#96
osmo_sdp_codec_from_str('GSM#3') rc=0 res=GSM#3
osmo_sdp_codec_from_str('/0#0') rc=0 res=/0#0
osmo_sdp_codec_from_str(':octet-align=1#112') rc=0 res=:octet-align=1#112
osmo_sdp_codec_from_str(':octet-align=1#112') rc=0 res=:octet-align=1#112
--- test_codec_list()
osmo_sdp_codec_list_first() = NULL
list_ctx
| 2 test_codec_list
| 1 struct osmo_sdp_codec_list
[0] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
[1] osmo_sdp_codec_list_add(GSM#3)
[2] osmo_sdp_codec_list_add(GSM-HR-08#111)
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM#3
codec_list[2] = GSM-HR-08#111
osmo_sdp_codec_list_first() = AMR:octet-align=1;mode-set=0,2,4#112
list_ctx
| 9 test_codec_list
| 8 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'AMR#112 GSM#3 GSM-HR-08#111'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
- add same entries again with once=exact, nothing should change
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
[] osmo_sdp_codec_list_add(GSM#3)
[] osmo_sdp_codec_list_add(GSM-HR-08#111)
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM#3
codec_list[2] = GSM-HR-08#111
list_ctx
| 9 test_codec_list
| 8 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'AMR#112 GSM#3 GSM-HR-08#111'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
- add same entries again with once=NULL, duplicates are added
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#112)
[] osmo_sdp_codec_list_add(GSM#3)
[] osmo_sdp_codec_list_add(GSM-HR-08#111)
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM#3
codec_list[2] = GSM-HR-08#111
codec_list[3] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[4] = GSM#3
codec_list[5] = GSM-HR-08#111
list_ctx
| 16 test_codec_list
| 15 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'2*AMR#112 2*GSM#3 2*GSM-HR-08#111'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111'
- add same entries again with once=NULL,pick_unused_pt_nr=true, duplicates are added with new #nr
[] osmo_sdp_codec_list_add(AMR:octet-align=1;mode-set=0,2,4#96)
[] osmo_sdp_codec_list_add(GSM#97)
[] osmo_sdp_codec_list_add(GSM-HR-08#98)
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM#3
codec_list[2] = GSM-HR-08#111
codec_list[3] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[4] = GSM#3
codec_list[5] = GSM-HR-08#111
codec_list[6] = AMR:octet-align=1;mode-set=0,2,4#96
codec_list[7] = GSM#97
codec_list[8] = GSM-HR-08#98
list_ctx
| 23 test_codec_list
| 22 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'3*AMR 3*GSM 3*GSM-HR-08'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98'
- remove all 'GSM#3' entries, with osmo_sdp_codec_cmp_exact
osmo_sdp_codec_list_remove() = 2
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM-HR-08#111
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[3] = GSM-HR-08#111
codec_list[4] = AMR:octet-align=1;mode-set=0,2,4#96
codec_list[5] = GSM#97
codec_list[6] = GSM-HR-08#98
list_ctx
| 19 test_codec_list
| 18 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 2 struct osmo_sdp_codec
| 1 GSM
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'3*AMR 3*GSM-HR-08 GSM#97'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98'
- remove all 'GSM' entries, with osmo_sdp_codec_cmp_equivalent
osmo_sdp_codec_list_remove() = 1
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = GSM-HR-08#111
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[3] = GSM-HR-08#111
codec_list[4] = AMR:octet-align=1;mode-set=0,2,4#96
codec_list[5] = GSM-HR-08#98
list_ctx
| 17 test_codec_list
| 16 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'3*AMR 3*GSM-HR-08'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#96 GSM-HR-08#98'
- osmo_sdp_codec_list_move_to_first('AMR:octet-align=1;mode-set=0,2,4#112', equivalent) = 3
codec_list[0] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[1] = AMR:octet-align=1;mode-set=0,2,4#112
codec_list[2] = AMR:octet-align=1;mode-set=0,2,4#96
codec_list[3] = GSM-HR-08#111
codec_list[4] = GSM-HR-08#111
codec_list[5] = GSM-HR-08#98
list_ctx
| 17 test_codec_list
| 16 struct osmo_sdp_codec_list
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
| 2 struct osmo_sdp_codec
| 1 GSM-HR-08
| 3 struct osmo_sdp_codec
| 1 octet-align=1;mode-set=0,2,4
| 1 AMR
osmo_sdp_codec_list_to_str_c(summarize=true):
'3*AMR 3*GSM-HR-08'
osmo_sdp_codec_list_to_str_c(summarize=false):
'AMR:octet-align=1;mode-set=0,2,4#112 AMR:octet-align=1;mode-set=0,2,4#112 AMR:octet-align=1;mode-set=0,2,4#96 GSM-HR-08#111 GSM-HR-08#111 GSM-HR-08#98'
- osmo_sdp_codec_list_free_items()
0 entries
list_ctx
| 2 test_codec_list
| 1 struct osmo_sdp_codec_list
osmo_sdp_codec_list_to_str_c(summarize=true):
'(no-codecs)'
osmo_sdp_codec_list_to_str_c(summarize=false):
'(no-codecs)'
--- test_codec_list_cmp()
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A == B == A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#96 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:mode-set=0,2,4;octet-align=1#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=7#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A == B == A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=0;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
cmpf[0]: payload_type=false rate=false fmtp=0: A == B == A
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = GSM#3 GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112
cmpf[0]: payload_type=false rate=false fmtp=0: A < B > A
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3
cmpf[0]: payload_type=false rate=false fmtp=0: A > B < A
cmpf[1]: payload_type=false rate=true fmtp=1: A > B < A
cmpf[2]: payload_type=true rate=true fmtp=2: A > B < A
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111 GSM-EFR#110
cmpf[0]: payload_type=false rate=false fmtp=0: A < B > A
cmpf[1]: payload_type=false rate=true fmtp=1: A < B > A
cmpf[2]: payload_type=true rate=true fmtp=2: A < B > A
--- test_codec_list_intersection()
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = GSM-HR-08#98 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= AMR:octet-align=1;mode-set=0,2,4#96 GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= GSM-HR-08#98 AMR:octet-align=1;mode-set=0,2,4#96 GSM#97
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= GSM-HR-08#111 AMR:octet-align=1;mode-set=0,2,4#112 GSM#3
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = GSM-HR-08#98 GSM-EFR#110 GSM#97
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= GSM#3 GSM-HR-08#111
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= GSM#97 GSM-HR-08#98
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= GSM-HR-08#98 GSM#97
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= GSM-HR-08#111 GSM#3
A = AMR:octet-align=1;mode-set=0,2,4#112 GSM#3 GSM-HR-08#111
B = AMR-WB/16000#97 FOO#98 GSM-EFR#110
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=false)
= (no-codecs)
osmo_sdp_codec_list_intersection(A, B, equivalent, translate_pt=true)
= (no-codecs)
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=false)
= (no-codecs)
osmo_sdp_codec_list_intersection(B, A, equivalent, translate_pt=true)
= (no-codecs)
--- test_obj_members()
o->codec = AMR:octet-align=1#96
ctx
| 6 test_obj_members
| 5 struct my_obj
| 3 struct osmo_sdp_codec
| 1 octet-align=1
| 1 AMR
| 1 struct osmo_sdp_codec_list
o->codec_list[0] = AMR:octet-align=1#96
o->codec_list[1] = AMR:octet-align=1#97
ctx
| 12 test_obj_members
| 11 struct my_obj
| 3 struct osmo_sdp_codec
| 1 octet-align=1
| 1 AMR
| 7 struct osmo_sdp_codec_list
| 3 struct osmo_sdp_codec
| 1 octet-align=1
| 1 AMR
| 3 struct osmo_sdp_codec
| 1 octet-align=1
| 1 AMR
talloc_free(o)
ctx
| 1 test_obj_members

View File

@@ -1,126 +0,0 @@
#include <inttypes.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/sdp/fmtp.h>
struct get_val_test {
const char *fmtp_string;
const char *val_name;
bool expect_rc;
const char *expect_val;
};
const struct get_val_test get_val_tests[] = {
{
"foo=123;bar=success;baz=456", "foo",
true, "123"
},
{
"foo=123;bar=success;baz=456", "bar",
true, "success"
},
{
"foo=123;bar=success;baz=456", "baz",
true, "456"
},
};
void test_get_val(void)
{
int i;
printf("\n--- %s()\n", __func__);
for (i = 0; i < ARRAY_SIZE(get_val_tests); i++) {
const struct get_val_test *t = &get_val_tests[i];
char val[128] = {};
bool rc = osmo_sdp_fmtp_get_val(val, sizeof(val), t->fmtp_string, t->val_name);
bool ok;
printf("osmo_sdp_fmtp_get_val('%s', '%s') rc=%s",
t->fmtp_string, t->val_name,
rc ? "true" : "false");
if (rc)
printf(" val='%s'", val);
ok = true;
if (rc != t->expect_rc) {
printf(" ERROR: expected rc=%s", t->expect_rc ? "true" : "false");
ok = false;
}
if (t->expect_val && strcmp(val, t->expect_val)) {
printf(" ERROR: expected val='%s'", t->expect_val);
ok = false;
}
if (ok)
printf(" ok");
printf("\n");
}
printf("\n--- %s() DONE\n", __func__);
}
struct get_int_test {
const char *fmtp_string;
const char *val_name;
int64_t defval;
int64_t expect_rc;
};
const struct get_int_test get_int_tests[] = {
{
"foo=123;bar=success;baz=456", "foo", -1,
123
},
{
"foo=123;bar=success;baz=456", "bar", -1,
-1
},
{
"foo=123;bar=success;baz=456", "baz", -1,
456
},
};
void test_get_int(void)
{
int i;
printf("\n--- %s()\n", __func__);
for (i = 0; i < ARRAY_SIZE(get_int_tests); i++) {
const struct get_int_test *t = &get_int_tests[i];
int64_t rc = osmo_sdp_fmtp_get_int(t->fmtp_string, t->val_name, t->defval);
printf("osmo_sdp_fmtp_get_int('%s', '%s') rc=%"PRId64,
t->fmtp_string, t->val_name, rc);
if (rc != t->expect_rc) {
printf(" ERROR: expected rc=%"PRId64, t->expect_rc);
}
else {
printf(" ok");
}
printf("\n");
}
printf("\n--- %s() DONE\n", __func__);
}
static const struct log_info_cat log_categories[] = {
};
const struct log_info log_info = {
.cat = log_categories,
.num_cat = ARRAY_SIZE(log_categories),
};
int main(void)
{
void *ctx = talloc_named_const(NULL, 1, "sdp_fmtp_test");
osmo_init_logging2(ctx, &log_info);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
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);
test_get_val();
test_get_int();
return 0;
}

View File

@@ -1,14 +0,0 @@
--- test_get_val()
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'foo') rc=true val='123' ok
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'bar') rc=true val='success' ok
osmo_sdp_fmtp_get_val('foo=123;bar=success;baz=456', 'baz') rc=true val='456' ok
--- test_get_val() DONE
--- test_get_int()
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'foo') rc=123 ok
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'bar') rc=-1 ok
osmo_sdp_fmtp_get_int('foo=123;bar=success;baz=456', 'baz') rc=456 ok
--- test_get_int() DONE

View File

@@ -1,764 +0,0 @@
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/sdp/sdp_msg.h>
void *test_ctx = NULL;
static void report_callback(const void *ptr, int depth, int max_depth, int is_ref, void *priv)
{
const char *name = talloc_get_name(ptr);
printf(" |%*s%3zu %s\n", depth, "", talloc_total_blocks(ptr), name);
}
/* Print a talloc report that is reproducible for test output verification. It contains no pointer addresses. */
#define report(CTX) _report(CTX, #CTX)
static void _report(void *ctx, const char *label)
{
fflush(stdout);
fflush(stderr);
printf("%s\n", label);
talloc_report_depth_cb(ctx, 0, 100, report_callback, NULL);
fflush(stdout);
}
static void dump_sdp(const char *str, const char *prefix)
{
while (str && *str) {
const char *line_end = str;
while (*line_end && *line_end != '\r' && *line_end != '\n')
line_end++;
while (*line_end == '\r' || *line_end == '\n')
line_end++;
printf("%s%s\n", prefix, osmo_escape_str(str, line_end - str));
str = line_end;
}
}
struct sdp_msg_test_data {
const char *sdp_input;
const char *expect_sdp_str;
};
static const struct sdp_msg_test_data sdp_msg_tests[] = {
{
"v=0\r\n"
"o=- 5628250 5628250 IN IP4 192.168.11.121\r\n"
"s=-\r\n"
"c=IN IP4 192.168.11.121\r\n"
"t=0 0\r\n"
"m=audio 10020 RTP/AVP 18 0 2 4 8 96 97 98 100 101\r\n"
"a=rtpmap:18 G729/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:2 G726-32/8000\r\n"
"a=rtpmap:4 G723/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:96 G726-40/8000\r\n"
"a=rtpmap:97 G726-24/8000\r\n"
"a=rtpmap:98 G726-16/8000\r\n"
"a=rtpmap:100 NSE/8000\r\n"
"a=fmtp:100 192-193\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"a=fmtp:101 0-15\r\n"
"a=ptime:20\r\n"
"a=sendrecv\r\n"
,
},
{
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.151\r\n"
"t=0 0\r\n"
"m=audio 16398 RTP/AVP 98\r\n"
"a=rtpmap:98 AMR/8000\r\n"
"a=fmtp:98 octet-align=1; mode-set=4\r\n"
"a=ptime:20\r\n"
"a=rtcp:16399 IN IP4 192.168.11.151\r\n"
,
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.151\r\n"
"t=0 0\r\n"
"m=audio 16398 RTP/AVP 98\r\n"
"a=rtpmap:98 AMR/8000\r\n"
"a=fmtp:98 octet-align=1; mode-set=4\r\n"
"a=ptime:20\r\n"
/* The rtcp line is dropped, not supported yet */
},
{
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.140\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
"a=rtpmap:18 G729/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:4 G723/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"a=fmtp:101 0-15\r\n"
"a=sendrecv\r\n"
"a=rtcp:30437\r\n"
"a=ptime:20\r\n"
,
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.140\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
"a=rtpmap:18 G729/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:4 G723/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"a=fmtp:101 0-15\r\n"
/* a=sendrecv ends up further below */
/* The rtcp line is dropped, not supported yet */
"a=ptime:20\r\n"
"a=sendrecv\r\n"
,
},
{
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.140\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
"a=rtpmap:18 G729/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:4 G723/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"a=fmtp:101 0-15\r\n"
"a=recvonly\r\n"
"a=rtcp:30437\r\n"
"a=ptime:20\r\n"
,
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.140\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
"a=rtpmap:18 G729/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:4 G723/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"a=fmtp:101 0-15\r\n"
/* a=recvonly ends up further below */
/* The rtcp line is dropped, not supported yet */
"a=ptime:20\r\n"
"a=recvonly\r\n"
,
},
{
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.140\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
"a=rtpmap:18 G729/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:4 G723/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"a=fmtp:101 0-15\r\n"
"a=ptime:20\r\n"
"a=sendonly\r\n"
,
},
{
"v=0\r\n"
"o=FooBar 1565090289 1565090290 IN IP4 192.168.11.151\r\n"
"s=FooBar\r\n"
"c=IN IP4 192.168.11.140\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 18 0 4 8 101\r\n"
"a=rtpmap:18 G729/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:4 G723/8000\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:101 telephone-event/8000\r\n"
"a=fmtp:101 0-15\r\n"
"a=ptime:20\r\n"
"a=inactive\r\n"
,
},
};
static void test_parse_and_compose(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
bool ok = true;
printf("\n\n%s\n", __func__);
for (i = 0; i < ARRAY_SIZE(sdp_msg_tests); i++) {
const struct sdp_msg_test_data *t = &sdp_msg_tests[i];
struct osmo_sdp_msg *sdp_msg;
char str[1024];
const char *expect;
struct osmo_sdp_err err;
printf("\n[%d]\n", i);
dump_sdp(t->sdp_input, "sdp input: ");
sdp_msg = osmo_sdp_msg_decode(ctx, t->sdp_input, &err);
if (err.rc) {
printf("ERROR: %s at %s\n", strerror(abs(err.rc)),
osmo_quote_cstr_c(print_ctx, err.at_input_str, err.at_input_str_len));
ok = false;
}
printf("parsed SDP message %s\n", osmo_sdp_msg_to_str_c(print_ctx, sdp_msg, false));
osmo_sdp_msg_encode_buf(str, sizeof(str), sdp_msg);
dump_sdp(str, "osmo_sdp_msg_encode_buf: ");
expect = t->expect_sdp_str ? : t->sdp_input;
if (strcmp(str, expect)) {
int j;
ok = false;
printf("ERROR:\n");
dump_sdp(expect, "expect: ");
for (j = 0; expect[j]; j++) {
if (expect[j] != str[j]) {
printf("ERROR at position %d, at:\n", j);
dump_sdp(str + j, " mismatch: ");
break;
}
}
} else
printf("[%d] ok\n", i);
report(ctx);
printf("talloc_free(sdp_msg)\n");
talloc_free(sdp_msg);
report(ctx);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak\n");
talloc_free_children(ctx);
}
printf("\n");
}
OSMO_ASSERT(ok);
talloc_free(ctx);
talloc_free(print_ctx);
}
struct intersect_test_data {
const char *descr;
const char *sdp_msg_a;
const char *sdp_msg_b;
const char *expect_intersection;
};
#define SDP_1 \
"v=0\r\n" \
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
"s=GSM Call\r\n" \
"c=IN IP4 23.42.23.42\r\n" \
"t=0 0\r\n" \
"m=audio 30436 RTP/AVP 112 3 111 110\r\n" \
"a=rtpmap:112 AMR/8000\r\n" \
"a=fmtp:112 octet-align=1\r\n" \
"a=rtpmap:3 GSM/8000\r\n" \
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
"a=rtpmap:110 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
#define SDP_2 \
"v=0\r\n" \
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
"s=GSM Call\r\n" \
"c=IN IP4 23.42.23.42\r\n" \
"t=0 0\r\n" \
"m=audio 30436 RTP/AVP 112 110\r\n" \
"a=rtpmap:112 AMR/8000\r\n" \
"a=fmtp:112 octet-align=1\r\n" \
"a=rtpmap:110 GSM-EFR/8000\r\n" \
"a=ptime:20\r\n"
#define SDP_3 \
"v=0\r\n" \
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
"s=GSM Call\r\n" \
"c=IN IP4 23.42.23.42\r\n" \
"t=0 0\r\n" \
"m=audio 30436 RTP/AVP 3 111 123\r\n" \
"a=rtpmap:3 GSM/8000\r\n" \
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
"a=rtpmap:123 FOO/8000\r\n" \
"a=ptime:20\r\n"
#define SDP_4 \
"v=0\r\n" \
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
"s=GSM Call\r\n" \
"c=IN IP4 23.42.23.42\r\n" \
"t=0 0\r\n" \
"m=audio 30436 RTP/AVP 3 111\r\n" \
"a=rtpmap:3 GSM/8000\r\n" \
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
"a=ptime:20\r\n"
#define SDP_5 \
"v=0\r\n" \
"o=libosmo-sdp 0 0 IN IP4 0.0.0.0\r\n" \
"s=GSM Call\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
"m=audio 0 RTP/AVP 112 113 110 3 111\r\n" \
"a=rtpmap:112 AMR/8000\r\n" \
"a=fmtp:112 octet-align=1;mode-set=0,1,2,3\r\n" \
"a=rtpmap:113 AMR-WB/8000\r\n" \
"a=fmtp:113 octet-align=1\r\n" \
"a=rtpmap:110 GSM-EFR/8000\r\n" \
"a=rtpmap:3 GSM/8000\r\n" \
"a=rtpmap:111 GSM-HR-08/8000\r\n" \
"a=ptime:20\r\n"
static const struct intersect_test_data intersect_tests[] = {
{
"identical codecs lead to no change"
,
SDP_1
,
"c=IN IP4 5.6.7.8\r\n" \
"m=audio 12345 RTP/AVP 112 3 111 110\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
,
SDP_1
},
{
"identical codecs in different order also lead to no change"
,
SDP_1
,
"c=IN IP4 5.6.7.8\r\n" \
"m=audio 12345 RTP/AVP 3 110 111 112\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
,
SDP_1
},
{
"identical codecs with mismatching payload type numbers also lead to no change"
,
SDP_1
,
"c=IN IP4 5.6.7.8\r\n" \
"m=audio 12345 RTP/AVP 96 97 98 99\r\n"
"a=rtpmap:96 GSM/8000\r\n"
"a=rtpmap:97 GSM-EFR/8000\r\n"
"a=rtpmap:98 GSM-HR-08/8000\r\n"
"a=rtpmap:99 AMR/8000\r\n"
"a=fmtp:99 octet-align=1\r\n"
,
SDP_1
},
{
"identical codecs plus some extra codecs also lead to no change"
,
SDP_1
,
"c=IN IP4 5.6.7.8\r\n" \
"m=audio 12345 RTP/AVP 8 0 96 97 98 99\r\n"
"a=rtpmap:8 PCMA/8000\r\n"
"a=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:96 GSM/8000\r\n"
"a=rtpmap:97 GSM-EFR/8000\r\n"
"a=rtpmap:98 GSM-HR-08/8000\r\n"
"a=rtpmap:99 AMR/8000\r\n"
"a=fmtp:99 octet-align=1\r\n"
,
SDP_1
},
{
"some codecs removed",
SDP_1,
SDP_2,
SDP_2,
},
{
"other codecs removed",
SDP_1,
SDP_3,
SDP_4,
},
{
"all codecs removed",
SDP_1
,
"s=empty"
,
"v=0\r\n" \
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n" \
"s=GSM Call\r\n" \
"c=IN IP4 23.42.23.42\r\n" \
"t=0 0\r\n" \
"m=audio 30436 RTP/AVP\r\n" \
"a=ptime:20\r\n"
},
{
"some real world test case",
SDP_5, SDP_5, SDP_5,
},
};
static const char *sdp_msg_logstr(const struct osmo_sdp_msg *sdp_msg)
{
static char buf[1024];
osmo_sdp_msg_encode_buf(buf, sizeof(buf), sdp_msg);
return buf;
}
static void test_intersect(void)
{
int i;
bool ok = true;
void *ctx = talloc_named_const(test_ctx, 0, __func__);
printf("\n\n%s\n", __func__);
for (i = 0; i < ARRAY_SIZE(intersect_tests); i++) {
const struct intersect_test_data *t = &intersect_tests[i];
struct osmo_sdp_msg *sdp_msg_a = NULL;
struct osmo_sdp_msg *sdp_msg_b = NULL;
char str[1024];
printf("\n[%d] %s\n", i, t->descr);
dump_sdp(t->sdp_msg_a, "SDP A: ");
dump_sdp(t->sdp_msg_b, " SDP B: ");
sdp_msg_a = osmo_sdp_msg_decode(ctx, t->sdp_msg_a, NULL);
if (!sdp_msg_a) {
printf("ERROR parsing SDP A\n");
break;
}
dump_sdp(sdp_msg_logstr(sdp_msg_a), "parsed SDP A: ");
sdp_msg_b = osmo_sdp_msg_decode(ctx, t->sdp_msg_b, NULL);
if (!sdp_msg_b) {
printf("ERROR parsing SDP B\n");
break;
}
dump_sdp(sdp_msg_logstr(sdp_msg_b), "parsed SDP B: ");
osmo_sdp_codec_list_intersection(sdp_msg_a->codecs, sdp_msg_b->codecs,
&osmo_sdp_codec_cmp_equivalent,
false);
osmo_sdp_msg_encode_buf(str, sizeof(str), sdp_msg_a);
dump_sdp(str, "intersection(a,b): ");
if (strcmp(str, t->expect_intersection)) {
int j;
ok = false;
printf("ERROR:\n");
dump_sdp(t->expect_intersection, "expect_intersection: ");
for (j = 0; t->expect_intersection[j]; j++) {
if (t->expect_intersection[j] != str[j]) {
printf("ERROR at position %d, at:\n", j);
dump_sdp(str + j, " mismatch: ");
break;
}
}
} else
printf("[%d] ok\n", i);
report(ctx);
printf("talloc_free(sdp_msg_a)\n");
talloc_free(sdp_msg_a);
report(ctx);
printf("talloc_free(sdp_msg_b)\n");
talloc_free(sdp_msg_b);
report(ctx);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak\n");
talloc_free_children(ctx);
}
printf("\n");
}
OSMO_ASSERT(ok);
talloc_free(ctx);
}
struct sdp_select_test_data {
const char *sdp;
const struct osmo_sdp_codec_cmp_flags *cmpf;
const struct osmo_sdp_codec select;
const char *expect_sdp;
};
static const struct osmo_sdp_codec_cmp_flags pt_only = { .payload_type = true };
static const struct sdp_select_test_data sdp_select_tests[] = {
{
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=ptime:20\r\n"
,
&pt_only,
{ .payload_type = 112, },
NULL
},
{
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=ptime:20\r\n"
,
&pt_only,
{ .payload_type = 3, },
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 3 112 111 110\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=ptime:20\r\n"
},
{
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=ptime:20\r\n"
,
&pt_only,
{ .payload_type = 111, },
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 111 112 3 110\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=ptime:20\r\n"
},
{
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 112 3 111 110\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=ptime:20\r\n"
,
&pt_only,
{ .payload_type = 110, },
"v=0\r\n"
"o=libosmo-sdp 0 0 IN IP4 23.42.23.42\r\n"
"s=GSM Call\r\n"
"c=IN IP4 23.42.23.42\r\n"
"t=0 0\r\n"
"m=audio 30436 RTP/AVP 110 112 3 111\r\n"
"a=rtpmap:110 GSM-EFR/8000\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1\r\n"
"a=rtpmap:3 GSM/8000\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=ptime:20\r\n"
},
};
static void test_select(void)
{
int i;
bool ok = true;
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
printf("\n\n%s\n", __func__);
for (i = 0; i < ARRAY_SIZE(sdp_select_tests); i++) {
const struct sdp_select_test_data *t = &sdp_select_tests[i];
struct osmo_sdp_msg *sdp_msg;
char buf[1024];
const char *expect_sdp;
printf("\n[%d]\n", i);
sdp_msg = osmo_sdp_msg_decode(ctx, t->sdp, NULL);
if (!sdp_msg) {
printf("ERROR parsing SDP\n");
break;
}
printf("SDP: %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, sdp_msg->codecs, false));
printf("Select: %s\n", osmo_sdp_codec_to_str_c(print_ctx, &t->select));
osmo_sdp_codec_list_move_to_first(sdp_msg->codecs, &t->select, t->cmpf);
printf("SDP: %s\n", osmo_sdp_codec_list_to_str_c(print_ctx, sdp_msg->codecs, false));
osmo_sdp_msg_encode_buf(buf, sizeof(buf), sdp_msg);
expect_sdp = t->expect_sdp ? : t->sdp;
if (strcmp(buf, expect_sdp)) {
int j;
ok = false;
printf("ERROR:\n");
dump_sdp(buf, "selection result: ");
dump_sdp(expect_sdp, "expect result: ");
for (j = 0; expect_sdp[j]; j++) {
if (expect_sdp[j] != buf[j]) {
printf("ERROR at position %d, at:\n", j);
dump_sdp(buf + j, " mismatch: ");
break;
}
}
} else
printf("[%d] ok\n", i);
report(ctx);
printf("talloc_free(sdp_msg)\n");
talloc_free(sdp_msg);
report(ctx);
if (talloc_total_blocks(ctx) != 1) {
printf("ERROR: memleak\n");
talloc_free_children(ctx);
}
printf("\n");
talloc_free_children(print_ctx);
}
OSMO_ASSERT(ok);
talloc_free(ctx);
talloc_free(print_ctx);
}
struct my_obj {
struct osmo_sdp_msg *sdp_msg;
};
static struct my_obj *my_obj_alloc(void *ctx)
{
struct my_obj *o = talloc_zero(ctx, struct my_obj);
return o;
}
static void test_obj_members(void)
{
void *ctx = talloc_named_const(test_ctx, 0, __func__);
void *print_ctx = talloc_named_const(test_ctx, 0, "print");
int i;
struct my_obj *o;
printf("\n\n--- %s()\n", __func__);
o = my_obj_alloc(ctx);
o->sdp_msg = osmo_sdp_msg_alloc(o);
printf("o->sdp_msg = '%s'\n", osmo_sdp_msg_encode_c(print_ctx, o->sdp_msg));
report(ctx);
const struct osmo_sdp_codec all_codecs[] = {
{ .payload_type = 112, .encoding_name = "AMR", .rate = 8000, .fmtp = "octet-align=1;mode-set=0,2,4" },
{ .payload_type = 3, .encoding_name = "GSM", .rate = 8000 },
{ .payload_type = 111, .encoding_name = "GSM-HR-08", .rate = 8000 },
};
for (i = 0; i < ARRAY_SIZE(all_codecs); i++)
osmo_sdp_codec_list_add(o->sdp_msg->codecs, &all_codecs[i], false, false);
printf("o->sdp_msg = '%s'\n", osmo_sdp_msg_encode_c(print_ctx, o->sdp_msg));
report(ctx);
printf("talloc_free(o)\n");
talloc_free(o);
report(ctx);
talloc_free(ctx);
talloc_free(print_ctx);
}
typedef void (*test_func_t)(void);
static const test_func_t test_func[] = {
test_parse_and_compose,
test_intersect,
test_select,
test_obj_members,
};
int main(void)
{
int i;
test_ctx = talloc_named_const(NULL, 0, "sdp_codec_test");
for (i = 0; i < ARRAY_SIZE(test_func); i++) {
test_func[i]();
if (talloc_total_blocks(test_ctx) != 1) {
talloc_report_full(test_ctx, stderr);
printf("ERROR after test %d: memory leak\n", i);
return -1;
}
}
talloc_free(test_ctx);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -13,24 +13,3 @@ AT_KEYWORDS([mgcp])
cat $abs_srcdir/mgcp/mgcp_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
AT_CLEANUP
AT_SETUP([sdp_fmtp])
AT_KEYWORDS([sdp_fmtp])
cat $abs_srcdir/sdp/sdp_fmtp_test.ok > expout
cat $abs_srcdir/sdp/sdp_fmtp_test.err > experr
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_fmtp_test], [], [expout], [experr])
AT_CLEANUP
AT_SETUP([sdp_codec])
AT_KEYWORDS([sdp_codec])
cat $abs_srcdir/sdp/sdp_codec_test.ok > expout
cat $abs_srcdir/sdp/sdp_codec_test.err > experr
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_codec_test], [], [expout], [experr])
AT_CLEANUP
AT_SETUP([sdp_msg])
AT_KEYWORDS([sdp_msg])
cat $abs_srcdir/sdp/sdp_msg_test.ok > expout
cat $abs_srcdir/sdp/sdp_msg_test.err > experr
AT_CHECK([$abs_top_builddir/tests/sdp/sdp_msg_test], [], [expout], [experr])
AT_CLEANUP