Compare commits

..

28 Commits

Author SHA1 Message Date
Neels Hofmeyr
c3a7b6b085 fix SDP codecs lists returned in OK messages
So far, osmo-mgw picked a single codec per side and returned only that
entry in the SDP part of MGCP 'OK' responses.

Instead, return all of the codec entries that were successfully
assigned.

The difference is shown in mgcp_test.c: before, only one of the codecs
made it through to the 'OK' reponse. Now, all of them are reflected
properly.

To clients that only ever assign a single codec, there is no change in
behavior.

Simplify the code: collapse these functions
  add_rtpmap()
  add_fmtp()
  add_audio()
  codec related part from mgcp_write_response_sdp()
and have only
  add_codecs()
add_codecs() simply iterates all assigned codecs to output the 'm=audio'
line as well as any 'a=rtpmap' and 'a=fmtp' lines.

Also helping to simplify is that we recently dropped the 'fmtp-extra'
feature in Icee0cd1f5a751fa760d5a9deca29089e78e7eb93

Related: OS#6293
Change-Id: Ib197ae30a4e732e7dc36700ba8b5841806ea12f7
2023-12-23 06:59:35 +01:00
Neels Hofmeyr
850a7c3b5a mgcp_test.c: test responses for multiple payloads
There are tests with multiple payloads in test_multiple_codec(), but
they do not verify the responses that osmo-mgw returns. Just add a
couple of entries to test_messages().

Change-Id: I604b81d9887eb63dab584a524b8f4eeb821e2c78
2023-12-23 06:59:35 +01:00
Neels Hofmeyr
ae6042e773 dbg: show conns on CRCX/MDCX
Change-Id: I8f50edfc6100ccb88fe9c35592be820f76bd256b
2023-12-23 06:59:35 +01:00
Neels Hofmeyr
5d9036176d mgcp_conn_dump(): include assigned codecs
Change-Id: I6c9234467e26e2b7e6a41ecf079cc0799f6b5dac
2023-12-23 06:59:35 +01:00
Neels Hofmeyr
553b123a97 mgcp_test.c: fflush
Change-Id: I6a30aeda4c139b52982faeabb3492991a8ff88ac
2023-12-23 06:59:35 +01:00
Neels Hofmeyr
cb4a296a21 mgcp_test.c: tweak output, comment
Change-Id: If5187d1d79fba78e19800822a5964d1c08c2fcfa
2023-12-23 06:59:25 +01:00
Neels Hofmeyr
7f158600ce dbg: log MGCP retransmission
Change-Id: I3920364a7573411c8d6a1d64721064f720ae1280
2023-12-23 06:10:23 +01:00
Neels Hofmeyr
ca3d664d08 dbg: mgcp_codec_decide(): log codecs
Change-Id: Ic24184d99a042970ac8e314bb020276fd2bc55c5
2023-12-23 06:10:17 +01:00
Neels Hofmeyr
146a370412 mgcp_test.c: show all DLMGCP logging
Change-Id: I7fde39bb368c0f2926b1147753fa5d925c354a21
2023-12-23 06:03:37 +01:00
Neels Hofmeyr
1cbf8495c7 drop cfg 'sdp audio fmtp-extra'
There is considerable code complexity in place for this ancient hack.

It dates back to 5ea1bc77a3
"
mgcp: Allow to freely control the a=fmtp line for experiments

In case of AMR one can specify the available codecs out-of-band. Allow
to configure this line statically in the configuration file.
"

Looking in mgcp_test.c output, the fmtp-extra tests do not even make
sense: they result in fmtp for pt=126 being added, even though there is
no payload type 126 listed in the SDP...

Related: OS#6313
Change-Id: Icee0cd1f5a751fa760d5a9deca29089e78e7eb93
2023-12-23 04:00:10 +01:00
Neels Hofmeyr
7ee24139a5 drop get_net_downlink_format_cb
It seems to be a remnant from early openbsc_mgcp. There is only the
default implementation for this callback and it simply returns two
pointers. Simplify that.

Change-Id: I18dfd44c931540caf4ac360c08ed10e5f65b2165
2023-12-23 03:58:19 +01:00
Neels Hofmeyr
febbf7d337 tests/mgcp: add update_exp target
Change-Id: I1ea7e881fe13429762bf31507d8d23fe58e241b3
2023-12-23 03:57:55 +01:00
Neels Hofmeyr
fc17517e9e tweak DEBUG log
Printing the debug log line a little later will include the MGCP verb
information.

Change-Id: Icb230cf4d623cdbc4ab52bd52d2a72525c0168c7
2023-12-22 05:34:43 +01:00
Neels Hofmeyr
d2634027cf mgcp_test: fix false negatives in test output
If one test fails, do not print failure for all following tests as well.

Change-Id: I196880b4b34a672ef45042c25f89bc1684363567
2023-12-22 05:34:43 +01:00
Neels Hofmeyr
5fbe450e64 mgw: do not fail MGCP on codec mismatch
Before this patch, when an CRCX+MDCX wants to set a codec list that has
no match with the codecs for the other conn of that same endpoint,
osmo-mgw returns an MGCP "FAIL" response.

When a client wants to change the codec, it has to do that one RTP port
at a time. So osmo-mgw *must* allow to configure an MGCP conn with a
codec choice that mismatches the other conn.

This is crucial to allow codec negotiation in osmo-msc: if MO has
already assigned a specific codec, and later wants to re-assign to the
codec that MT has chosen, the codec needs to be changed at osmo-mgw.

This patch is the minimal fix required to get re-assignment to a
different codec to work (via osmo-msc). There is more work to be done
about this bit of code in osmo-mgw, but keep that to a separate patch.

In detail, before this patch, we fail both
- when a side has no codecs,
- or when there is no single match between codecs of the two sides of
  the endpoint.
Remove only the second condition; after this patch, still fail when a
side has no codecs -- this allows mgcp_test.c to still pass.

Related: OS#6293
Related: osmo-msc I8760feaa8598047369ef8c3ab2673013bac8ac8a
Change-Id: I3d1163fe622bdd7dc42a485f796072524ab39db9
2023-12-22 05:34:43 +01:00
Neels Hofmeyr
6fe0ed2bb4 mgcp_codec_decide: remove redundant lookup
We already did a lookup from conn_src[i] and found a matching
codec_conn_dst, no need to do another reverse lookup to end up at the
same conn_src[i] codec.

Change-Id: Iecc7f22c551fd17b23db434fdb177266407d2621
2023-12-22 05:34:43 +01:00
Neels Hofmeyr
0b4d5c0d82 mgcp-client: MGCP response: pass fmtp to caller
When receiving MGCP responses, so far libosmo-mgcp-client completely
ignored a=fmtp: parameters (like 'octet-align'). Add fmtp parsing to
pass the fmtp string to the caller as-is.

Since the responses so far never included the octet_aligned flags, do
not bother to parse fmtp to populate the legacy items. New callers
should use the fmtp string.

Change-Id: If8ca5c3880cad9e41b80e9d1c821439b0d7b7e23
2023-12-22 05:34:43 +01:00
Neels Hofmeyr
e220fa10d3 mgw, client: add fmtp string to ptmap: allow all possible fmtp
Remove the limit of having only one AMR octet-aligned fmtp parameter per
MGCP message. Instead allow any arbitrary fmtp options, one per every
codec.

Deprecate all use of struct mgcp_codec_param. Instead, store and pass
plain fmtp strings.

Provide legacy shims that still act correctly for any callers that may
pass the old struct mgcp_codec_param. (I'm not sure if we need to keep
this, but we can always drop it in another patch.)

Adjust one mgcp_test.c: instead of returning only the octet-aligned
parameter, now osmo-mgw keeps and returns all the fmtp parameters that
the user provided. So add the missing "mode-change-capability".

Related: OS#6171
Related: osmo-msc Ief9225c9bcf7525a9a0a07c282ffb8cc0d092186
Change-Id: If58590bda8627519ff07e0b6f43aa47a274f052b
2023-12-22 05:34:43 +01:00
Neels Hofmeyr
527a45bc67 add fmtp.h
Upcoming patches will add handling of arbitrary ftmp strings to
osmo-mgw as well as libosmo-mgcp-client
(If58590bda8627519ff07e0b6f43aa47a274f052b).

Add generic API for handling ftmp strings. The primary intended user is
osmo-mgw, but this is also generally useful to libosmo-mgcp-client
callers for parsing the received fmtp.

We discussed that fmtp.[hc] should be published in libosmo-mgcp-client,
but also built within osmo-mgw. Hence:
- put fmtp.h and .c in libosmo-mgcp-client/
- in osmo-mgw code, use #include <mgcp_client/fmtp.h> and build the
  src/libosmo-mgcp-client/fmtp.c also into libosmo-mgcp.a.

(This is currently the quickest solution and without side effects since
fmtp.c is fully self-contained; an alternative would be adding a
not-installed libmgcp-common.a within the build tree)

Change-Id: I3eaea353dbd98c19212199b564342d0ac16cbc07
2023-12-22 05:34:43 +01:00
Neels Hofmeyr
7365685f47 build: move mgcp/*.h to noinst_HEADERS, drop RPM libosmo-mgcp-devel
We only install the mgcp_client/ headers.

Related: OS#6300
Change-Id: Ie0f79222bd1702097c12193dcf7a0462805cfc4a
2023-12-22 03:04:53 +01:00
Philipp Maier
3f940e5812 mgcp_client_fsm: explain member param in struct mgcp_conn_peer better
The struct member param specifies additional codec parameters. Let's
improve its explaination.

Change-Id: Iea4dc1e72fccaa464ce503fae88b5d8a867b1d19
Related: OS#6171
2023-12-22 03:04:53 +01:00
Philipp Maier
ce37944ce4 mgcp_client_fsm: allocate struct mgcp_conn_peer dynamically
The struct mgcp_conn_peer is allocated statically at the moment. This is
an ABI compatibility in case the struct changes.

Add an alloc function for this struct that API users can use, and
indicate its use in a comment on struct mgcp_conn_peer.

It is not necessary to dynamically allocate the mgcp_conn_peer struct
within libosmo-mgcp-client, because the problem arises only when ABIs
interact. Hence leaving mgcp_client_endpoint_fsm.c's use static.

Related: OS#6171
Change-Id: I523d0fcb020f7d46323c497a4be9ee00d5f242ba
2023-12-22 03:04:53 +01:00
Neels Hofmeyr
77676f2151 drop (now) unused code
Removing the duality of codecs[] and ptmap[] in structs mgcp_msg,
mgcp_response and mgcp_conn_peer has removed the need to "map" from
codec type enum to payload type number. They are stored together now.

Remove functions that are no longer used.
None of our osmocom users of libosmo-mgcp-client call these functions.

Change-Id: I84e5285831397c992af59deee12dea8458d16cc6
2023-12-22 03:04:53 +01:00
Neels Hofmeyr
e8c297248e mgcp_client_test: add test_parse_response()
Change-Id: I842ce65a9a70f313570857b7df53727cc572b9e6
2023-12-22 03:04:45 +01:00
Neels Hofmeyr
3ea1382637 client SDP: more verbose error logging
So far it was pure guess work to find out why a message fails.

Change-Id: Ibc6343db82281789004c140ba98d99e5f6f73d83
2023-12-22 03:03:12 +01:00
Neels Hofmeyr
12d0b04ddc client: allow MGCP_MAX_CODECS entries
So far we allow only MGCP_MAX_CODECS-1 entries, because the parsing exit
condition hits only after the array size check. Instead, check the array
size a bit later, just before actually adding a valid entry.

This is verified to work as expected in upcoming patch
I842ce65a9a70f313570857b7df53727cc572b9e6 that adds a new
mgcp_client_test.c section for this.

Change-Id: I9a28da85e437f118026ea71a5a708e5758fff623
2023-12-22 03:02:40 +01:00
Neels Hofmeyr
b1f7e37847 client: collapse codecs[] and ptmap[]; allow codec variants
codecs[] is an array of enum osmo_mgcp_codecs.
ptmap[] is an array of { enum osmo_mgcp_codecs, unsigned int ptmap }.

MGCP lists first a bunch of payload type numbers and then specifies them
again for details, like the numbers 112, 96, 3 in this example:

 m=audio <port> RTP/AVP 112 96 3
 a=rtpmap:112 AMR/8000
 a=rtpmap:96 VND.3GPP.IUFP/16000
 a=rtpmap:3 GSM-FR/8000

So far we keep these lists in two separate arrays, in both struct
mgcp_response and struct mgcp_msg:
- codecs[], codecs_len stores the 'm=audio' list
- ptmap[], ptmap_len stores the 'a=rtpmap' list (and may omit some
  elements present in codecs[])

These are semantically identical, and the separation means we cannot
have the same codec twice, like AMR with different fmtp variations. It
also leads to checks, conversions and dear hopes of correct ordering.

So let's keep only one list with both codec and payload type number in
it. The 'm=audio' list establishes the order of the pt numbers, and the
'a=rtpmap' list adds codec information to the established entries.

In the internal API structs mgcp_msg and mgcp_response, just drop the
codecs[] entirely.

In public API struct mgcp_conn_peer, keep the codecs[] array, mark it
deprecated, and provide a backwards compat conversion: if any caller
invokes mgcp_conn_create() or mgcp_conn_modify() with codecs[] still
present in the verb_info arg, then move codecs[] entries over to the
ptmap[] array in a sensible way.
(BTW, even mgcp_conn_create() and mgcp_conn_modify() are never called
from outside of libosmo-mgcp-client in any of our osmo-cni programs;
users call osmo_mgcpc_ep_ci_add() and osmo_mgcpc_ep_ci_request(), which
in turn may pass user-provided codecs[] lists on to mgcp_conn_create() or
mgcp_conn_modify().)

Tests for parsing the MGCP response are mostly missing. They will be
added in upcoming patch I842ce65a9a70f313570857b7df53727cc572b9e6,
because they will be using only the new ptmap API.

Related: OS#6171
Change-Id: I798e02c6663376d3d52f4a74fc4b32411ce95bed
2023-12-22 02:56:33 +01:00
Neels Hofmeyr
ffd88eda6e client: deprecate legacy API
Change-Id: I7409907dafbb2fe905fee9bc22d6870056bf3022
2023-12-22 02:53:16 +01:00
65 changed files with 1039 additions and 6335 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

@@ -35,9 +35,8 @@ libosmo-mgcp-client remove public API These public API items have not been calle
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.
libosmo-mgcp-client remove public API Since codecs[] has been deprecated in favor of ptmap[], there is no use
for the following functions; all known callers have been within
osmo-mgw.git:
map_codec_to_pt()
map_pt_to_codec()

View File

@@ -189,11 +189,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 +199,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)

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

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

View File

@@ -9,11 +9,8 @@ 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):

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

@@ -14,12 +14,15 @@ e1_input
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

@@ -11,12 +11,15 @@ log stderr
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

@@ -8,11 +8,7 @@ nobase_include_HEADERS = \
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 \
osmocom/mgcp_client/fmtp.h \
$(NULL)
noinst_HEADERS = \

View File

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

View File

@@ -24,7 +24,6 @@
#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>
@@ -71,10 +70,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;
@@ -205,5 +206,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, const struct osmo_sockaddr *addr, const char *buf, int len);

View File

@@ -4,7 +4,6 @@
#include <stdbool.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/mgcp/mgcp.h>
@@ -124,8 +123,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;
@@ -163,7 +162,8 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
int 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,
@@ -183,7 +183,7 @@ void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *
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

@@ -0,0 +1,29 @@
#pragma once
#include <stddef.h>
#include <stdbool.h>
#define OSMO_SDP_NAME_A "a"
#define OSMO_SDP_NAME_FMTP "fmtp"
#define OSMO_SDP_NAME_AMR_OCTET_ALIGN "octet-align"
#define OSMO_SDP_VAL_AMR_OCTET_ALIGN_0 OSMO_SDP_NAME_AMR_OCTET_ALIGN "=0"
#define OSMO_SDP_VAL_AMR_OCTET_ALIGN_1 OSMO_SDP_NAME_AMR_OCTET_ALIGN "=1"
/* "fmtp:" */
#define OSMO_SDP_PREFIX_FMTP OSMO_SDP_NAME_FMTP ":"
/* "a=fmtp:" */
#define OSMO_SDP_PREFIX_A_FMTP OSMO_SDP_NAME_A "=" OSMO_SDP_PREFIX_FMTP
bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const char *option_name);
int osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int default_value);
/* Some AMR related fmtp parameters as in https://www.rfc-editor.org/rfc/rfc4867#section-8.1 that osmo-mgw needs.*/
bool osmo_sdp_fmtp_amr_is_octet_aligned(const char *fmtp);
/*! To compose AMR related fmtp indicating octet-align.
* Usage:
* printf("%s", OSMO_SDP_AMR_SET_OCTET_ALIGN(oa_flag));
*/
#define OSMO_SDP_AMR_SET_OCTET_ALIGN(VAL) \
((VAL) ? OSMO_SDP_VAL_AMR_OCTET_ALIGN_1 : OSMO_SDP_VAL_AMR_OCTET_ALIGN_0 )

View File

@@ -122,9 +122,5 @@ static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
}
enum mgcp_codecs map_str_to_codec(const char *str);
unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
enum mgcp_codecs codec);
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
unsigned int pt);
const char *mgcp_client_name(const struct mgcp_client *mgcp);

View File

@@ -12,7 +12,11 @@
* When modifiying a connection, the endpoint and call_id members may be left
* unpopulated. The call_id field is ignored in this case. If an endpoint
* identifier is supplied it is checked against the internal state to make
* sure it is correct. */
* sure it is correct.
*
* CAUTION: This struct may be subject to changes and new struct members may
* be added in the future. To prevent memory conflicts it is strongly advised
* to allocate this struct dynamically using mgcp_conn_peer_alloc() */
struct mgcp_conn_peer {
/*! RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
char addr[INET6_ADDRSTRLEN];
@@ -60,10 +64,12 @@ struct mgcp_conn_peer {
/*! 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");
bool param_present;
struct mgcp_codec_param param;
};
struct mgcp_conn_peer *mgcp_conn_peer_alloc(void *ctx);
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");

View File

@@ -1,6 +1,6 @@
#pragma once
#include <osmocom/core/osmo_io.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/timer.h>
#define MSGB_CB_MGCP_TRANS_ID 0
@@ -13,7 +13,7 @@ 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;

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

@@ -15,7 +15,6 @@ AM_CFLAGS = \
# Libraries
SUBDIRS = \
libosmo-sdp \
libosmo-mgcp-client \
libosmo-mgcp \
$(NULL)

View File

@@ -31,6 +31,7 @@ libosmo_mgcp_client_la_SOURCES = \
mgcp_client_fsm.c \
mgcp_client_endpoint_fsm.c \
mgcp_client_pool.c \
fmtp.c \
$(NULL)
libosmo_mgcp_client_la_LDFLAGS = \
@@ -40,7 +41,6 @@ libosmo_mgcp_client_la_LDFLAGS = \
$(NULL)
libosmo_mgcp_client_la_LIBADD = \
$(top_builddir)/src/libosmo-sdp/libosmo-sdp.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(NULL)

View File

@@ -1,5 +1,5 @@
/*
* (C) 2023-2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* (C) 2023-2015 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr
@@ -22,27 +22,12 @@
#include <ctype.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/mgcp_client/fmtp.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)
static const char *fmtp_next_option(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++);
for (; fmtp && *fmtp && *fmtp != ';'; fmtp++);
for (; fmtp && isspace(*fmtp); fmtp++);
return fmtp;
}
@@ -52,14 +37,11 @@ static const char *osmo_sdp_fmtp_next(const char *fmtp)
*
* 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...
* char res[23];
* if (osmo_sdp_fmtp_get_val(res, sizeof(res), fmtp_vals, "mode-set"))
* use_modeset(res);
* else
* use_modeset(MY_DEFAULT_MODESET);
* }
*
* \param[out] val Buffer to write the option's value to.
* \param[in] val_size Space available in val.
@@ -72,7 +54,7 @@ bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const c
const char *pos = fmtp;
const char *end;
int option_name_len = strlen(option_name);
for (; pos && *pos; pos = osmo_sdp_fmtp_next(pos)) {
for (; pos && *pos; pos = fmtp_next_option(pos)) {
if (!osmo_str_startswith(pos, option_name))
continue;
pos += option_name_len;
@@ -85,7 +67,7 @@ bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const c
if (!pos || !*pos)
return false;
end = osmo_sdp_fmtp_end(pos);
end = fmtp_next_option(pos);
OSMO_ASSERT(end);
if (val && val_size)
osmo_strlcpy(val, pos, OSMO_MIN(val_size, end - pos + 1));
@@ -104,16 +86,17 @@ bool osmo_sdp_fmtp_get_val(char *val, size_t val_size, const char *fmtp, const c
* \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)
int osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int 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)) {
int i;
if (osmo_str_to_int(&i, val, 10, 0, 1)) {
/* error parsing number */
LOGP(DLMGCP, LOGL_ERROR, "Invalid number in fmtp parameter '%s': '%s'\n", option_name, val);
return default_value;
}
return i;
@@ -133,61 +116,5 @@ int64_t osmo_sdp_fmtp_get_int(const char *fmtp, const char *option_name, int64_t
*/
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;
return osmo_sdp_fmtp_get_int(fmtp, OSMO_SDP_NAME_AMR_OCTET_ALIGN, 0) == 1;
}

View File

@@ -29,7 +29,6 @@
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
#include <osmocom/sdp/sdp_strings.h>
#include <osmocom/abis/e1_input.h>
@@ -106,94 +105,6 @@ enum mgcp_codecs map_str_to_codec(const char *str)
return -1;
}
/* Check the ptmap for illegal mappings */
static int check_ptmap(const struct ptmap *ptmap)
{
/* Check if there are mappings that leave the IANA assigned dynamic
* payload type range. Under normal conditions such mappings should
* not occur */
/* Its ok to have a 1:1 mapping in the statically defined
* range, this won't hurt */
if (ptmap->codec == ptmap->pt)
return 0;
if (ptmap->codec < 96 || ptmap->codec > 127)
goto error;
if (ptmap->pt < 96 || ptmap->pt > 127)
goto error;
return 0;
error:
LOGP(DLMGCP, LOGL_ERROR,
"ptmap contains illegal mapping: codec=%u maps to pt=%u\n",
ptmap->codec, ptmap->pt);
return -1;
}
/*! Map a codec to a payload type.
* \ptmap[in] payload pointer to payload type map with specified payload types.
* \ptmap[in] ptmap_len length of the payload type map.
* \ptmap[in] codec the codec for which the payload type should be looked up.
* \returns assigned payload type */
unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len,
enum mgcp_codecs codec)
{
unsigned int i;
/*! Note: If the payload type map is empty or the codec is not found
* in the map, then a 1:1 mapping is performed. If the codec falls
* into the statically defined range or if the mapping table isself
* tries to map to the statically defined range, then the mapping
* is also ignored and a 1:1 mapping is performed instead. */
/* we may return the codec directly since enum mgcp_codecs directly
* corresponds to the statically assigned payload types */
if (codec < 96 || codec > 127)
return codec;
for (i = 0; i < ptmap_len; i++) {
/* Skip illegal map entries */
if (check_ptmap(ptmap) == 0 && ptmap->codec == codec)
return ptmap->pt;
ptmap++;
}
/* If nothing is found, do not perform any mapping */
return codec;
}
/*! Map a payload type to a codec.
* \ptmap[in] payload pointer to payload type map with specified payload types.
* \ptmap[in] ptmap_len length of the payload type map.
* \ptmap[in] payload type for which the codec should be looked up.
* \returns codec that corresponds to the specified payload type */
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
unsigned int pt)
{
unsigned int i;
/*! Note: If the payload type map is empty or the payload type is not
* found in the map, then a 1:1 mapping is performed. If the payload
* type falls into the statically defined range or if the mapping
* table isself tries to map to the statically defined range, then
* the mapping is also ignored and a 1:1 mapping is performed
* instead. */
/* See also note in map_codec_to_pt() */
if (pt < 96 || pt > 127)
return pt;
for (i = 0; i < ptmap_len; i++) {
if (check_ptmap(ptmap) == 0 && ptmap->pt == pt)
return ptmap->codec;
ptmap++;
}
/* If nothing is found, do not perform any mapping */
return pt;
}
static void _mgcp_client_conf_init(struct mgcp_client_conf *conf)
{
/* NULL and -1 default to MGCP_CLIENT_*_DEFAULT values */
@@ -392,9 +303,9 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
char codec_resp[256];
int rc;
#define A_PTIME OSMO_SDP_A_PREFIX(OSMO_SDP_STR_PTIME)
#define A_RTPMAP OSMO_SDP_A_PREFIX(OSMO_SDP_STR_RTPMAP)
#define A_FMTP OSMO_SDP_A_PREFIX(OSMO_SDP_STR_FMTP)
#define A_PTIME "a=ptime:"
#define A_RTPMAP "a=rtpmap:"
#define A_FMTP "a=fmtp:"
if (osmo_str_startswith(line, A_PTIME)) {
if (sscanf(line, A_PTIME "%u", &r->ptime) != 1) {
@@ -766,7 +677,7 @@ static struct mgcp_response_pending *mgcp_client_response_pending_get(
/* Feed an MGCP message into the receive processing.
* Parse the head and call any callback registered for the transaction id found
* in the MGCP message. This is normally called directly from the internal
* mgcp_read_cb that reads from the socket connected to the MGCP gateway. This
* mgcp_do_read that reads from the socket connected to the MGCP gateway. This
* function is published mainly to be able to feed data from the test suite.
*/
int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg)
@@ -819,54 +730,55 @@ error:
return rc;
}
static void mgcp_read_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg)
{
struct mgcp_client *mgcp = osmo_iofd_get_data(iofd);
if (res <= 0) {
LOGPMGW(mgcp, LOGL_ERROR, "Failed to read: %s: %d='%s'\n",
osmo_iofd_get_name(iofd), res, strerror(res));
msgb_free(msg);
return;
}
msg->l2h = msg->head;
mgcp_client_rx(mgcp, msg);
talloc_free(msg);
}
static int mgcp_do_write(struct mgcp_client *mgcp, struct msgb *msg)
static int mgcp_do_read(struct osmo_fd *fd)
{
struct mgcp_client *mgcp = fd->data;
struct msgb *msg;
int ret;
msg = msgb_alloc_headroom(4096, 128, "mgcp_from_gw");
if (!msg) {
LOGPMGW(mgcp, LOGL_ERROR, "Failed to allocate MGCP message.\n");
return -1;
}
/* msgb_tailroom() is basically (4096 - 128); -1 is for '\0' */
ret = read(fd->fd, msg->data, msgb_tailroom(msg) - 1);
if (ret <= 0) {
LOGPMGW(mgcp, LOGL_ERROR, "Failed to read: %s: %d='%s'\n",
osmo_sock_get_name2(fd->fd), errno, strerror(errno));
msgb_free(msg);
return -1;
}
msg->l2h = msgb_put(msg, ret);
ret = mgcp_client_rx(mgcp, msg);
talloc_free(msg);
return ret;
}
static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
{
int ret;
struct mgcp_client *mgcp = fd->data;
LOGPMGW(mgcp, LOGL_DEBUG, "Tx MGCP: %s: len=%u '%s'...\n",
osmo_iofd_get_name(mgcp->iofd), msg->len,
osmo_sock_get_name2(fd->fd), msg->len,
osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
ret = osmo_iofd_write_msgb(mgcp->iofd, msg);
if (ret < 0)
msgb_free(msg);
ret = write(fd->fd, msg->data, msg->len);
if (OSMO_UNLIKELY(ret != msg->len))
LOGPMGW(mgcp, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
osmo_sock_get_name2(fd->fd), errno, strerror(errno),
msg->len, osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
/* Re-arm the keepalive Tx timer: */
if (mgcp->actual.keepalive.req_interval_sec > 0)
osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0);
return ret;
}
static void mgcp_write_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg)
{
struct mgcp_client *mgcp = osmo_iofd_get_data(iofd);
if (OSMO_UNLIKELY(res != msg->len)) {
LOGPMGW(mgcp, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
osmo_iofd_get_name(mgcp->iofd), res, strerror(res),
msg->len, osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
}
}
static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgcp, const char *name)
{
static char endpoint[MGCP_ENDPOINT_MAXLEN];
@@ -978,6 +890,11 @@ struct mgcp_client *mgcp_client_init(void *ctx,
if (conf->description)
mgcp->actual.description = talloc_strdup(mgcp, conf->description);
osmo_wqueue_init(&mgcp->wq, 1024);
mgcp->wq.read_cb = mgcp_do_read;
mgcp->wq.write_cb = mgcp_do_write;
osmo_fd_setup(&mgcp->wq.bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, mgcp, 0);
memcpy(&mgcp->actual.keepalive, &conf->keepalive, sizeof(conf->keepalive));
osmo_timer_setup(&mgcp->keepalive_tx_timer, mgcp_client_keepalive_tx_timer_cb, mgcp);
osmo_timer_setup(&mgcp->keepalive_rx_timer, mgcp_client_keepalive_rx_timer_cb, mgcp);
@@ -985,11 +902,6 @@ struct mgcp_client *mgcp_client_init(void *ctx,
return mgcp;
}
static const struct osmo_io_ops mgcp_clnt_ioops = {
.read_cb = mgcp_read_cb,
.write_cb = mgcp_write_cb,
};
/*! Initialize client connection (opens socket)
* \param[in,out] mgcp MGCP client descriptor.
* \returns 0 on success, -EINVAL on error. */
@@ -1005,28 +917,19 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
return -EINVAL;
}
rc = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
rc = osmo_sock_init2_ofd(&mgcp->wq.bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (rc < 0) {
LOGPMGW(mgcp, LOGL_FATAL,
"Failed to initialize socket %s:%u -> %s:%u for MGW: %s\n",
mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port,
mgcp->actual.remote_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.remote_port,
strerror(errno));
goto error_free;
goto error_close_fd;
}
mgcp->iofd = osmo_iofd_setup(mgcp, rc, osmo_sock_get_name2(rc), OSMO_IO_FD_MODE_READ_WRITE,
&mgcp_clnt_ioops, mgcp);
if (!mgcp->iofd)
goto error_close_fd;
LOGPMGW(mgcp, LOGL_INFO, "MGW connection: %s\n", osmo_iofd_get_name(mgcp->iofd));
osmo_iofd_register(mgcp->iofd, -1);
osmo_iofd_set_alloc_info(mgcp->iofd, 4096, 128);
osmo_iofd_set_txqueue_max_length(mgcp->iofd, 1024);
LOGPMGW(mgcp, LOGL_INFO, "MGW connection: %s\n", osmo_sock_get_name2(mgcp->wq.bfd.fd));
/* If configured, send a DLCX message to the endpoints that are configured to
* be reset on startup. Usually this is a wildcarded endpoint. */
@@ -1052,10 +955,9 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0);
return 0;
error_close_fd:
close(rc);
error_free:
close(mgcp->wq.bfd.fd);
mgcp->wq.bfd.fd = -1;
return rc;
}
@@ -1072,6 +974,8 @@ int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports)
* \returns 0 on success, -EINVAL on error. */
void mgcp_client_disconnect(struct mgcp_client *mgcp)
{
struct osmo_wqueue *wq;
if (!mgcp) {
LOGP(DLMGCP, LOGL_FATAL, "MGCP client not initialized properly\n");
return;
@@ -1082,9 +986,13 @@ void mgcp_client_disconnect(struct mgcp_client *mgcp)
osmo_timer_del(&mgcp->keepalive_tx_timer);
mgcp->conn_up = false;
osmo_iofd_txqueue_clear(mgcp->iofd);
LOGPMGW(mgcp, LOGL_INFO, "MGCP association: %s -- closed!\n", osmo_iofd_get_name(mgcp->iofd));
osmo_iofd_free(mgcp->iofd);
wq = &mgcp->wq;
osmo_wqueue_clear(wq);
LOGPMGW(mgcp, LOGL_INFO, "MGCP association: %s -- closed!\n", osmo_sock_get_name2(wq->bfd.fd));
if (osmo_fd_is_registered(&wq->bfd))
osmo_fd_unregister(&wq->bfd);
close(wq->bfd.fd);
wq->bfd.fd = -1;
}
/*! Get the IP-Aaddress of the associated MGW as string.
@@ -1238,9 +1146,10 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
goto mgcp_tx_error;
}
rc = mgcp_do_write(mgcp, msg);
rc = osmo_wqueue_enqueue(&mgcp->wq, msg);
if (rc) {
LOGPMGW(mgcp, LOGL_FATAL, "Could not queue message to MGW\n");
msgb_free(msg);
goto mgcp_tx_error;
} else
LOGPMGW(mgcp, LOGL_DEBUG, "Queued %u bytes for MGW\n",
@@ -1353,7 +1262,6 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
int local_ip_family, audio_ip_family;
const char *codec;
unsigned int pt;
uint16_t audio_port;
#define MSGB_PRINTF_OR_RET(FMT, ARGS...) do { \
if (msgb_printf(msg, FMT, ##ARGS) != 0) { \
@@ -1405,19 +1313,17 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
MSGB_PRINTF_OR_RET("t=0 0\r\n");
/* Add RTP address port and codecs */
audio_port = 0;
if ((mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT)) {
audio_port = mgcp_msg->audio_port;
if (!audio_port) {
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) {
if (mgcp_msg->audio_port == 0) {
LOGPMGW(mgcp, LOGL_ERROR,
"Invalid port number, can not generate MGCP message\n");
return -EINVAL;
}
MSGB_PRINTF_OR_RET("m=audio %u RTP/AVP", mgcp_msg->audio_port);
for (i = 0; i < mgcp_msg->ptmap_len; i++)
MSGB_PRINTF_OR_RET(" %u", mgcp_msg->ptmap[i].pt);
MSGB_PRINTF_OR_RET("\r\n");
}
MSGB_PRINTF_OR_RET("m=audio %u RTP/AVP", audio_port);
for (i = 0; i < mgcp_msg->ptmap_len; i++)
MSGB_PRINTF_OR_RET(" %u", mgcp_msg->ptmap[i].pt);
MSGB_PRINTF_OR_RET("\r\n");
/* Add optional codec parameters (fmtp) */
for (i = 0; i < mgcp_msg->ptmap_len; i++) {

View File

@@ -624,6 +624,17 @@ static struct osmo_fsm fsm_mgcp_client = {
.log_subsys = DLMGCP,
};
/*! allocate struct to hold the description of an MGCP connection peer.
* \param[in] ctx talloc context.
* \returns newly-allocated and initialized struct mgcp_conn_peer. */
struct mgcp_conn_peer *mgcp_conn_peer_alloc(void *ctx)
{
struct mgcp_conn_peer *peer;
peer = talloc_zero(ctx, struct mgcp_conn_peer);
OSMO_ASSERT(peer);
return peer;
}
/* 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)

View File

@@ -322,7 +322,7 @@ DEFUN(cfg_mgw_mgw_keepalive_req_interval,
/* 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->wq.bfd.fd != -1) { /* 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);
@@ -375,7 +375,7 @@ DEFUN(cfg_mgw_mgw_keepalive_timeout,
/* 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->wq.bfd.fd != -1) { /* 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);
@@ -680,7 +680,7 @@ DEFUN(mgw_show, mgw_show_cmd, "show mgw-pool", SHOW_STR "Display information abo
const struct mgcp_client *cli = pool_member->client;
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->wq.bfd.fd != -1 ? "connected" : "disconnected",
cli && cli->conn_up ?
((cli->actual.keepalive.timeout_sec > 0) ? "UP" : "MAYBE") :
"DOWN",

View File

@@ -19,15 +19,15 @@ AM_LDFLAGS = \
$(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 \
@@ -42,14 +42,5 @@ libosmo_mgcp_la_SOURCES = \
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) \
$(top_srcdir)/src/libosmo-mgcp-client/fmtp.c \
$(NULL)

View File

@@ -24,10 +24,7 @@
#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 <osmocom/mgcp_client/fmtp.h>
#include <errno.h>
/* Helper function to dump codec information of a specified codec to a printable
@@ -297,7 +294,7 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
{
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);
fmtp = OSMO_SDP_AMR_SET_OCTET_ALIGN(param->amr_octet_aligned);
return mgcp_codec_add2(conn, payload_type, audio_name, fmtp);
}
@@ -432,6 +429,26 @@ static struct mgcp_rtp_codec *codec_find_convertible(struct mgcp_conn_rtp *conn,
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst)
{
unsigned int i;
if (log_check_level(DLMGCP, LOGL_DEBUG)) {
LOGP(DLMGCP, LOGL_DEBUG, "%s(): src.codecs_assigned=%d dst.codecs_assigned=%d\n",
__func__,
conn_src ? conn_src->end.codecs_assigned : 0,
conn_dst ? conn_dst->end.codecs_assigned : 0);
if (conn_src) {
for (i = 0; i < conn_src->end.codecs_assigned; i++) {
struct mgcp_rtp_codec *c = &conn_src->end.codecs[i];
LOGP(DLMGCP, LOGL_DEBUG, "src.codecs[%d]: %d %s %s %s\n",
i, c->payload_type, c->audio_name, c->subtype_name, c->fmtp);
}
}
if (conn_dst) {
for (i = 0; i < conn_dst->end.codecs_assigned; i++) {
struct mgcp_rtp_codec *c = &conn_dst->end.codecs[i];
LOGP(DLMGCP, LOGL_DEBUG, "dst.codecs[%d]: %d %s %s %s\n",
i, c->payload_type, c->audio_name, c->subtype_name, c->fmtp);
}
}
}
/* 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:
@@ -473,6 +490,7 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn
}
}
LOGP(DLMGCP, LOGL_ERROR, "no matching codec found\n");
if (conn_dst->end.codecs_assigned)
conn_dst->end.codec = &conn_dst->end.codecs[0];
else
@@ -495,7 +513,7 @@ bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
}
/* 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 osmo_sdp_fmtp_get_val(NULL, 0, codec->fmtp, OSMO_SDP_NAME_AMR_OCTET_ALIGN);
}
/* Find the payload type number configured for a specific codec by SDP.

View File

@@ -106,8 +106,8 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
/* backpointer to the generic part of the connection */
conn->u.rtp.conn = conn;
end->rtp = NULL;
end->rtcp = NULL;
end->rtp.fd = -1;
end->rtcp.fd = -1;
memset(&end->addr, 0, sizeof(end->addr));
end->rtcp_port = 0;
@@ -357,6 +357,7 @@ 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) };
int i;
if (!conn)
return "NULL";
@@ -380,6 +381,11 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
break;
}
for (i = 0; i < conn->u.rtp.end.codecs_assigned; i++) {
struct mgcp_rtp_codec *c = &conn->u.rtp.end.codecs[i];
OSMO_STRBUF_PRINTF(sb, " %s#%d%s", c->subtype_name, c->payload_type, c->fmtp);
}
OSMO_STRBUF_PRINTF(sb, ")");
break;

View File

@@ -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));

View File

@@ -311,6 +311,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;
}
@@ -468,7 +469,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);
@@ -505,7 +506,6 @@ 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",
@@ -513,17 +513,19 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
osmo_sockaddr_port(&rtp_end->addr.u.sa), 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, (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
@@ -638,7 +640,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;

View File

@@ -4,7 +4,6 @@
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2013-2024 by sysmocom - s.f.m.c. GmbH
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -71,18 +70,6 @@ void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *
rtpconn_rate_ctr_add(conn_rtp, endp, id, 1);
}
/* wrapper around libosmocore msgb_copy_c, which [at least before libosmocore.git Change-Id
* I68328adb952ca8833ba047cb3b49ccc6f8a1f1b5] doesn't copy the cb */
static inline struct msgb *mgw_msgb_copy_c(void *ctx, struct msgb *msg, const char *name)
{
struct msgb *msg2 = msgb_copy_c(ctx, msg, name);
if (OSMO_UNLIKELY(!msg2))
return NULL;
memcpy(msg2->cb, msg->cb, sizeof(msg2->cb));
return msg2;
}
static int rx_rtp(struct msgb *msg);
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end)
@@ -417,12 +404,15 @@ static int align_rtp_timestamp_offset(const struct mgcp_endpoint *endp,
/*! dummy callback to disable transcoding (see also cfg->rtp_processing_cb).
* \param[in] associated endpoint.
* \param[in] destination RTP end.
* \param[in,out] msg message bufffer containing data. Function might change length.
* \param[in,out] pointer to buffer with voice data.
* \param[in] voice data length.
* \param[in] maximum size of caller provided voice data buffer.
* \returns ignores input parameters, return always 0. */
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
struct msgb *msg)
char *data, int *len, int buf_size)
{
LOGPENDP(endp, DRTP, LOGL_DEBUG, "transcoding disabled\n");
return 0;
}
@@ -795,18 +785,16 @@ static int amr_oa_check(char *data, int len)
/* Forward data to a debug tap. This is debug function that is intended for
* debugging the voice traffic with tools like gstreamer */
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)
{
int rc;
if (!tap->enabled)
return;
struct msgb *msg2 = msgb_copy(msg, "RTP TAP Tx");
if (!msg2)
return;
rc = sendto(fd, msgb_data(msg), msgb_length(msg), 0, (struct sockaddr *)&tap->forward,
sizeof(tap->forward));
rc = osmo_iofd_sendto_msgb(iofd, msg2, 0, &tap->forward);
if (rc < 0)
LOGP(DRTP, LOGL_ERROR,
"Forwarding tapped (debug) voice data failed.\n");
@@ -978,7 +966,7 @@ static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
return 0;
}
/*! Dispatch msg bridged from the sister conn in the endpoint. Takes ownership of msgb.
/*! Dispatch msg bridged from the sister conn in the endpoint.
* \param[in] conn_dst The destination conn that should handle and transmit the content to
* its peer outside MGW.
* \param[in] msg msgb containing an RTP pkt received by the sister conn in the endpoint,
@@ -1000,10 +988,8 @@ static int mgcp_conn_rtp_dispatch_rtp(struct mgcp_conn_rtp *conn_dst, struct msg
/* Before we try to deliver the packet, we check if the destination
* port and IP-Address make sense at all. If not, we will be unable
* to deliver the packet. */
if (check_rtp_destin(conn_dst) != 0) {
msgb_free(msg);
if (check_rtp_destin(conn_dst) != 0)
return -1;
}
/* Depending on the RTP connection type, deliver the RTP packet to the
* destination connection. */
@@ -1038,46 +1024,39 @@ static int mgcp_conn_rtp_dispatch_rtp(struct mgcp_conn_rtp *conn_dst, struct msg
* be discarded, this should not happen, normally the MGCP type
* should be properly set */
LOGPENDP(endp, DRTP, LOGL_ERROR, "bad MGCP type -- data discarded!\n");
msgb_free(msg);
return -1;
}
/*! send message buffer via udp socket. If it succeeds, it takes ownership of the msgb and internally calls
* msgb_free() after the aynchronous sendto() completes. In case of error, the msgb is still owned by the
* caller and must be free'd accordingly.
* \param[in] iofd associated file descriptor.
* \param[in] addr destination ip-address.
* \param[in] msg message buffer that holds the data to be send.
* \returns 0 in case of success (takes msgb ownership), -1 on error (doesn't take msgb ownership). */
static int mgcp_udp_send_msg(struct osmo_io_fd *iofd, const struct osmo_sockaddr *addr, struct msgb *msg)
{
LOGP(DRTP, LOGL_DEBUG, "sending %d bytes length packet to %s ...\n", msgb_length(msg),
osmo_sockaddr_to_str(addr));
return osmo_iofd_sendto_msgb(iofd, msg, 0, addr);
}
/*! send udp packet from raw buffer/length.
* \param[in] iofd associated file descriptor.
/*! send udp packet.
* \param[in] fd associated file descriptor.
* \param[in] addr destination ip-address.
* \param[in] buf buffer that holds the data to be send.
* \param[in] len length of the data to be sent.
* \returns 0 in case of success, -1 on error. */
int mgcp_udp_send(struct osmo_io_fd *iofd, const struct osmo_sockaddr *addr, const char *buf, int len)
* \returns bytes sent, -1 on error. */
int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len)
{
struct msgb *msg = msgb_alloc_c(iofd, len, "mgcp_udp_send");
if (!msg)
return -ENOMEM;
memcpy(msg->tail, buf, len);
msgb_put(msg, len);
char ipbuf[INET6_ADDRSTRLEN];
size_t addr_len;
return mgcp_udp_send_msg(iofd, addr, msg);
LOGP(DRTP, LOGL_DEBUG,
"sending %i bytes length packet to %s:%u ...\n", len,
osmo_sockaddr_ntop(&addr->u.sa, ipbuf),
osmo_sockaddr_port(&addr->u.sa));
if (addr->u.sa.sa_family == AF_INET6) {
addr_len = sizeof(addr->u.sin6);
} else {
addr_len = sizeof(addr->u.sin);
}
return sendto(fd, buf, len, 0, &addr->u.sa, addr_len);
}
/*! send RTP dummy packet (to keep NAT connection open).
* \param[in] endp mcgp endpoint that holds the RTP connection.
* \param[in] conn associated RTP connection.
* \returns 0 in case of success, -1 on error. */
* \returns bytes sent, -1 on error. */
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
int rc;
@@ -1099,7 +1078,8 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
if (mgcp_conn_rtp_is_iuup(conn))
rc = mgcp_conn_iuup_send_dummy(conn);
else
rc = mgcp_udp_send(conn->end.rtp, &conn->end.addr, rtp_dummy_payload, sizeof(rtp_dummy_payload));
rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
rtp_dummy_payload, sizeof(rtp_dummy_payload));
if (rc == -1)
goto failed;
@@ -1110,10 +1090,10 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
was_rtcp = 1;
rtcp_addr = conn->end.addr;
osmo_sockaddr_set_port(&rtcp_addr.u.sa, ntohs(conn->end.rtcp_port));
rc = mgcp_udp_send(conn->end.rtcp, &rtcp_addr,
rc = mgcp_udp_send(conn->end.rtcp.fd, &rtcp_addr,
rtp_dummy_payload, sizeof(rtp_dummy_payload));
if (rc == 0)
if (rc >= 0)
return rc;
failed:
@@ -1124,7 +1104,7 @@ failed:
return -1;
}
/*! Send RTP/RTCP data to a specified destination connection. Takes ownership of msg.
/*! Send RTP/RTCP data to a specified destination connection.
* \param[in] endp associated endpoint (for configuration, logging).
* \param[in] is_rtp flag to specify if the packet is of type RTP or RTCP.
* \param[in] addr spoofed source address (set to NULL to disable).
@@ -1163,7 +1143,6 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
if (is_rtp && !mgcp_conn_rtp_is_iuup(conn_src)) {
if (mgcp_patch_pt(conn_dst, msg) < 0) {
LOGPENDP(endp, DRTP, LOGL_NOTICE, "unable to patch payload type RTP packet, discarding...\n");
msgb_free(msg);
return -EINVAL;
}
}
@@ -1189,67 +1168,70 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
);
} else if (is_rtp) {
int cont;
int nbytes = 0;
int buflen = msgb_length(msg);
/* Make sure we have a valid RTP header, in cases where no RTP
* header is present, we will generate one. */
gen_rtp_header(msg, rtp_end, rtp_state);
/* Run transcoder */
rc = endp->trunk->cfg->rtp_processing_cb(endp, rtp_end, msg);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error %d during transcoding\n", rc);
msgb_free(msg);
return rc;
}
do {
/* Run transcoder */
cont = endp->trunk->cfg->rtp_processing_cb(endp, rtp_end, (char *)msgb_data(msg), &buflen, RTP_BUF_SIZE);
if (cont < 0)
break;
if (addr)
mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, msg);
if (addr)
mgcp_patch_and_count(endp, rtp_state, rtp_end,
addr, msg);
if (mgcp_conn_rtp_is_iuup(conn_dst) || mgcp_conn_rtp_is_iuup(conn_src)) {
/* the iuup code will correctly transform to the correct AMR mode */
} else if (mgcp_codec_amr_align_mode_is_indicated(conn_dst->end.codec)) {
bool oa = mgcp_codec_amr_is_octet_aligned(conn_dst->end.codec);
rc = amr_oa_bwe_convert(endp, msg, oa);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion (target=%s)\n",
oa ? "octet-aligned" : "bandwidth-efficient");
msgb_free(msg);
return rc;
if (mgcp_conn_rtp_is_iuup(conn_dst) || mgcp_conn_rtp_is_iuup(conn_src)) {
/* the iuup code will correctly transform to the correct AMR mode */
} else if (mgcp_codec_amr_align_mode_is_indicated(conn_dst->end.codec)) {
bool oa = mgcp_codec_amr_is_octet_aligned(conn_dst->end.codec);
rc = amr_oa_bwe_convert(endp, msg, oa);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion (target=%s)\n",
oa ? "octet-aligned" : "bandwidth-efficient");
break;
}
} else if (rtp_end->rfc5993_hr_convert &&
strcmp(conn_src->end.codec->subtype_name, "GSM-HR-08") == 0) {
rc = rfc5993_hr_convert(endp, msg);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
break;
}
}
} else if (rtp_end->rfc5993_hr_convert &&
strcmp(conn_src->end.codec->subtype_name, "GSM-HR-08") == 0) {
rc = rfc5993_hr_convert(endp, msg);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
msgb_free(msg);
return rc;
}
}
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"process/send 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)
);
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"process/send 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)
);
/* Forward a copy of the RTP data to a debug ip/port */
forward_data_tap(rtp_end->rtp, &conn_src->tap_out, msg);
/* Forward a copy of the RTP data to a debug ip/port */
forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
msg);
len = msgb_length(msg);
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr,
(char *)msgb_data(msg), msgb_length(msg));
rc = mgcp_udp_send_msg(rtp_end->rtp, &rtp_end->addr, msg);
if (rc < 0) {
msgb_free(msg);
return rc;
}
if (len <= 0)
return len;
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
rtp_state->alt_rtp_tx_sequence++;
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
rtp_state->alt_rtp_tx_sequence++;
return 0;
nbytes += len;
buflen = cont;
} while (buflen > 0);
return nbytes;
} else if (!trunk->omit_rtcp) {
struct osmo_sockaddr rtcp_addr = rtp_end->addr;
osmo_sockaddr_set_port(&rtcp_addr.u.sa, rtp_end->rtcp_port);
@@ -1260,54 +1242,19 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
osmo_sockaddr_port(&rtcp_addr.u.sa)
);
len = msgb_length(msg);
rc = mgcp_udp_send_msg(rtp_end->rtcp, &rtcp_addr, msg);
if (rc < 0) {
msgb_free(msg);
return rc;
}
len = mgcp_udp_send(rtp_end->rtcp.fd, &rtcp_addr,
(char *)msgb_data(msg), msgb_length(msg));
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
rtp_state->alt_rtp_tx_sequence++;
return 0;
return len;
}
msgb_free(msg);
return 0;
}
/*! determine if there's only a single recipient in endp for data received via conn_src.
* The function returns NULL in case there is no recipient, or in case there are multiple recipients.
* \param endp The MGCP endpoint whose connections to analyze
* \param conn_src The source MGCP connection [which shall not count in results]
* \returns recipient donnection if there is only one; NULL in case there are multiple */
static struct mgcp_conn *rtpbridge_get_only_recipient(struct mgcp_endpoint *endp, struct mgcp_conn *conn_src)
{
struct mgcp_conn *conn_ret = NULL;
struct mgcp_conn *conn_dst;
llist_for_each_entry(conn_dst, &endp->conns, entry) {
if (conn_dst == conn_src)
continue;
switch (conn_dst->mode) {
case MGCP_CONN_SEND_ONLY:
case MGCP_CONN_RECV_SEND:
case MGCP_CONN_CONFECHO:
if (conn_ret)
return NULL;
conn_ret = conn_dst;
break;
default:
break;
}
}
return conn_ret;
}
/*! Dispatch incoming RTP packet to opposite RTP connection.
* \param[in] msg Message buffer to bridge, coming from source connection.
* msg shall contain "struct osmo_rtp_msg_ctx *" attached in
@@ -1365,44 +1312,23 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
return rc;
}
/* All the use cases above are 1:1 where we have one source msgb and we're sending that to one
* destination. msgb ownership had been passed to the respective _*dospatch_rtp() function.
* In the cases below, we actually [can] have multiple recipients, so we copy the original msgb
* for each of the recipients. */
/* If the mode is "confecho", send RTP back to the sender. */
if (conn->mode == MGCP_CONN_CONFECHO) {
struct msgb *msg2 = mgw_msgb_copy_c(conn, msg, "RTP confecho");
if (OSMO_LIKELY(msg2))
rc = mgcp_conn_rtp_dispatch_rtp(conn_src, msg2);
}
if (conn->mode == MGCP_CONN_CONFECHO)
rc = mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
conn_dst = rtpbridge_get_only_recipient(endp, conn);
if (OSMO_LIKELY(conn_dst)) {
/* we only have a single recipient and cann hence send the original msgb without copying */
rc = mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
} else {
/* Dispatch RTP packet to all other connection(s) that send audio. */
llist_for_each_entry(conn_dst, &endp->conns, entry) {
struct msgb *msg2;
if (conn_dst == conn)
continue;
switch (conn_dst->mode) {
case MGCP_CONN_SEND_ONLY:
case MGCP_CONN_RECV_SEND:
case MGCP_CONN_CONFECHO:
/* we have multiple recipients and must make copies for each recipient */
msg2 = mgw_msgb_copy_c(conn_dst, msg, "RTP Tx copy");
if (OSMO_LIKELY(msg2))
rc = mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg2);
break;
default:
break;
}
/* Dispatch RTP packet to all other connection(s) that send audio. */
llist_for_each_entry(conn_dst, &endp->conns, entry) {
if (conn_dst == conn)
continue;
switch (conn_dst->mode) {
case MGCP_CONN_SEND_ONLY:
case MGCP_CONN_RECV_SEND:
case MGCP_CONN_CONFECHO:
rc = mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
break;
default:
break;
}
/* as we only sent copies in the previous llist_for_each_entry() loop, we must free the
* original one */
msgb_free(msg);
}
return rc;
}
@@ -1474,7 +1400,7 @@ void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *con
}
/* Handle incoming RTP data from NET */
static void rtp_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *saddr)
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
{
/* NOTE: This is a generic implementation. RTP data is received. In
* case of loopback the data is just sent back to its origin. All
@@ -1485,34 +1411,49 @@ static void rtp_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg,
struct mgcp_conn_rtp *conn_src;
struct mgcp_endpoint *endp;
struct osmo_sockaddr addr;
socklen_t slen = sizeof(addr);
char ipbuf[INET6_ADDRSTRLEN];
int ret;
enum rtp_proto proto;
struct osmo_rtp_msg_ctx *mc;
struct msgb *msg;
int rc;
conn_src = (struct mgcp_conn_rtp *) osmo_iofd_get_data(iofd);
conn_src = (struct mgcp_conn_rtp *)fd->data;
OSMO_ASSERT(conn_src);
endp = conn_src->conn->endp;
OSMO_ASSERT(endp);
msg = msgb_alloc_c(endp->trunk, RTP_BUF_SIZE, "RTP-rx");
proto = (iofd == conn_src->end.rtp) ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
proto = (fd == &conn_src->end.rtp)? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
if (res <= 0) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(-res));
goto out_free;
ret = recvfrom(fd->fd, msgb_data(msg), msg->data_len, 0, (struct sockaddr *)&addr.u.sa, &slen);
if (ret <= 0) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(errno));
rc = -1;
goto out;
}
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s\n",
msgb_put(msg, ret);
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
proto == MGCP_PROTO_RTP ? "RTP" : "RTCP",
msgb_length(msg), osmo_sockaddr_to_str(saddr));
msgb_length(msg), osmo_sockaddr_ntop(&addr.u.sa, ipbuf),
osmo_sockaddr_port(&addr.u.sa));
if ((proto == MGCP_PROTO_RTP && check_rtp(conn_src, msg))
|| (proto == MGCP_PROTO_RTCP && check_rtcp(conn_src, msg))) {
/* Logging happened in the two check_ functions */
goto out_free;
rc = -1;
goto out;
}
if (mgcp_is_rtp_dummy_payload(msg)) {
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx dummy packet (dropped)\n");
goto out_free;
rc = 0;
goto out;
}
/* Since the msgb remains owned and freed by this function, the msg ctx data struct can just be on the stack and
@@ -1521,7 +1462,7 @@ static void rtp_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg,
*mc = (struct osmo_rtp_msg_ctx){
.proto = proto,
.conn_src = conn_src,
.from_addr = (struct osmo_sockaddr *) saddr,
.from_addr = &addr,
};
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "msg ctx: %d %p %s\n",
mc->proto, mc->conn_src,
@@ -1536,17 +1477,16 @@ static void rtp_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg,
/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
/* Forward a copy of the RTP data to a debug ip/port */
forward_data_tap(iofd, &conn_src->tap_in, msg);
forward_data_tap(fd->fd, &conn_src->tap_in, msg);
rx_rtp(msg);
return;
rc = rx_rtp(msg);
out_free:
out:
msgb_free(msg);
return rc;
}
/* Note: This function is able to handle RTP and RTCP. msgb ownership is transferred, so this function or its
* downstream consumers must make sure to [eventually] free the msgb. */
/* Note: This function is able to handle RTP and RTCP */
static int rx_rtp(struct msgb *msg)
{
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
@@ -1559,7 +1499,7 @@ static int rx_rtp(struct msgb *msg)
/* Check if the origin of the RTP packet seems plausible */
if (!trunk->rtp_accept_all && check_rtp_origin(conn_src, from_addr))
goto out_free;
return -1;
/* Handle AMR frame format conversion (octet-aligned vs. bandwith-efficient) */
if (mc->proto == MGCP_PROTO_RTP
@@ -1570,14 +1510,14 @@ static int rx_rtp(struct msgb *msg)
* communicated via SDP when the connection was created/modfied. */
int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
if (oa < 0)
goto out_free;
return -1;
src_oa = mgcp_codec_amr_is_octet_aligned(conn_src->end.codec);
if (((bool)oa) != src_oa) {
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
"rx_rtp(%u bytes): Expected RTP AMR octet-aligned=%u but got octet-aligned=%u."
" check the config of your call-agent!\n",
msgb_length(msg), src_oa, oa);
goto out_free;
return -1;
}
}
@@ -1586,36 +1526,17 @@ static int rx_rtp(struct msgb *msg)
/* Execute endpoint specific implementation that handles the
* dispatching of the RTP data */
return conn->endp->type->dispatch_rtp_cb(msg);
out_free:
msgb_free(msg);
return -1;
}
static void rtp_sendto_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *daddr)
{
/* nothing; osmo_io takes care of msgb_free */
if (res < 0) {
struct mgcp_conn_rtp *conn_rtp = (struct mgcp_conn_rtp *) osmo_iofd_get_data(iofd);
int priv_nr = osmo_iofd_get_priv_nr(iofd);
char errbuf[129];
strerror_r(-res, errbuf, sizeof(errbuf));
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "%s sendto(%s) failed: %s\n", priv_nr ? "RTCP" : "RTP",
osmo_sockaddr_to_str(daddr), errbuf);
}
}
static const struct osmo_io_ops rtp_ioops = {
.recvfrom_cb = rtp_recvfrom_cb,
.sendto_cb = rtp_sendto_cb,
};
/*! bind RTP port to osmo_fd.
* \param[in] source_addr source (local) address to bind on.
* \param[in] fd associated file descriptor.
* \param[in] port to bind on.
* \param[in] dscp IP DSCP value to use.
* \param[in] prio socket priority to use.
* \returns file descriptor on success, -1 on ERROR. */
int mgcp_create_bind(const char *source_addr, int port, uint8_t dscp, uint8_t prio)
* \returns 0 on success, -1 on ERROR. */
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint8_t dscp,
uint8_t prio)
{
int rc;
@@ -1623,50 +1544,47 @@ int mgcp_create_bind(const char *source_addr, int port, uint8_t dscp, uint8_t pr
NULL, 0, OSMO_SOCK_F_BIND | OSMO_SOCK_F_DSCP(dscp) |
OSMO_SOCK_F_PRIO(prio));
if (rc < 0) {
LOGP(DRTP, LOGL_ERROR, "failed to bind UDP port (%s:%d).\n",
LOGP(DRTP, LOGL_ERROR, "failed to bind UDP port (%s:%i).\n",
source_addr, port);
return -1;
}
LOGP(DRTP, LOGL_DEBUG, "created socket + bound UDP port (%s:%d).\n", source_addr, port);
fd->fd = rc;
LOGP(DRTP, LOGL_DEBUG, "created socket + bound UDP port (%s:%i).\n", source_addr, port);
return rc;
return 0;
}
/* Bind RTP and RTCP port (helper function for mgcp_bind_net_rtp_port()) */
static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
struct mgcp_rtp_end *rtp_end, struct mgcp_endpoint *endp)
{
int rc, rtp_fd, rtcp_fd;
/* NOTE: The port that is used for RTCP is the RTP port incremented by one
* (e.g. RTP-Port = 16000 ==> RTCP-Port = 16001) */
rc = mgcp_create_bind(source_addr, rtp_end->local_port, cfg->endp_dscp, cfg->endp_priority);
if (rc < 0) {
if (mgcp_create_bind(source_addr, &rtp_end->rtp, rtp_end->local_port,
cfg->endp_dscp, cfg->endp_priority) != 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"failed to create RTP port: %s:%d\n",
source_addr, rtp_end->local_port);
goto cleanup0;
}
rtp_fd = rc;
rc = mgcp_create_bind(source_addr, rtp_end->local_port + 1, cfg->endp_dscp, cfg->endp_priority);
if (rc < 0) {
if (mgcp_create_bind(source_addr, &rtp_end->rtcp, rtp_end->local_port + 1,
cfg->endp_dscp, cfg->endp_priority) != 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"failed to create RTCP port: %s:%d\n",
source_addr, rtp_end->local_port + 1);
goto cleanup1;
}
rtcp_fd = rc;
if (osmo_iofd_register(rtp_end->rtp, rtp_fd) < 0) {
if (osmo_fd_register(&rtp_end->rtp) != 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"failed to register RTP port %d\n",
rtp_end->local_port);
goto cleanup2;
}
if (osmo_iofd_register(rtp_end->rtcp, rtcp_fd) != 0) {
if (osmo_fd_register(&rtp_end->rtcp) != 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"failed to register RTCP port %d\n",
rtp_end->local_port + 1);
@@ -1676,11 +1594,13 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
return 0;
cleanup3:
osmo_iofd_unregister(rtp_end->rtp);
osmo_fd_unregister(&rtp_end->rtp);
cleanup2:
close(rtcp_fd);
close(rtp_end->rtcp.fd);
rtp_end->rtcp.fd = -1;
cleanup1:
close(rtp_fd);
close(rtp_end->rtp.fd);
rtp_end->rtp.fd = -1;
cleanup0:
return -1;
}
@@ -1699,8 +1619,7 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
snprintf(name, sizeof(name), "%s-%s", conn->conn->name, conn->conn->id);
end = &conn->end;
if ((end->rtp && osmo_iofd_get_fd(end->rtp) != -1) ||
(end->rtcp && osmo_iofd_get_fd(end->rtcp) != -1)) {
if (end->rtp.fd != -1 || end->rtcp.fd != -1) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "%u was already bound on conn:%s\n",
rtp_port, mgcp_conn_dump(conn->conn));
@@ -1713,18 +1632,8 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
}
end->local_port = rtp_port;
end->rtp = osmo_iofd_setup(conn->conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn);
if (!end->rtp)
return -EIO;
osmo_iofd_set_alloc_info(end->rtp, RTP_BUF_SIZE, 0);
end->rtcp = osmo_iofd_setup(conn->conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn);
if (!end->rtcp) {
osmo_iofd_free(end->rtp);
end->rtp = NULL;
return -EIO;
}
osmo_iofd_set_alloc_info(end->rtcp, RTP_BUF_SIZE, 0);
osmo_iofd_set_priv_nr(end->rtcp, 1); /* we use priv_nr as identifier for RTCP */
osmo_fd_setup(&end->rtp, -1, OSMO_FD_READ, rtp_data_net, conn, 0);
osmo_fd_setup(&end->rtcp, -1, OSMO_FD_READ, rtp_data_net, conn, 0);
return bind_rtp(endp->trunk->cfg, conn->end.local_addr, end, endp);
}
@@ -1733,13 +1642,15 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
* \param[in] end RTP end */
void mgcp_free_rtp_port(struct mgcp_rtp_end *end)
{
if (end->rtp) {
osmo_iofd_free(end->rtp);
end->rtp = NULL;
if (end->rtp.fd != -1) {
osmo_fd_unregister(&end->rtp);
close(end->rtp.fd);
end->rtp.fd = -1;
}
if (end->rtcp) {
osmo_iofd_free(end->rtcp);
end->rtcp = NULL;
if (end->rtcp.fd != -1) {
osmo_fd_unregister(&end->rtcp);
close(end->rtcp.fd);
end->rtcp.fd = -1;
}
}

View File

@@ -1,7 +1,6 @@
/*
* (C) 2012-2013 by Pablo Neira Ayuso <pablo@gnumonks.org>
* (C) 2012-2013 by On Waves ehf <http://www.on-waves.com>
* (C) 2013-2024 by sysmocom - s.f.m.c. GmbH
* All rights not specifically granted under this license are reserved.
*
* This program is free software; you can redistribute it and/or modify it
@@ -14,11 +13,9 @@
#include <string.h> /* for memcpy */
#include <stdlib.h> /* for abs */
#include <inttypes.h> /* for PRIu64 */
#include <unistd.h> /* for PRIu64 */
#include <netinet/in.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/osmo_io.h>
#include <osmocom/core/talloc.h>
#include <osmocom/netif/osmux.h>
@@ -33,8 +30,8 @@
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
static struct osmo_io_fd *osmux_fd_v4;
static struct osmo_io_fd *osmux_fd_v6;
static struct osmo_fd osmux_fd_v4;
static struct osmo_fd osmux_fd_v6;
static LLIST_HEAD(osmux_handle_list);
@@ -79,31 +76,34 @@ static void rtpconn_osmux_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, int id)
static void osmux_deliver_cb(struct msgb *batch_msg, void *data)
{
struct osmux_handle *handle = data;
int rc;
struct osmo_io_fd *iofd;
struct mgcp_trunk *trunk = (struct mgcp_trunk *) osmo_iofd_get_data(osmux_fd_v4);
socklen_t dest_len;
int rc, fd;
struct mgcp_trunk *trunk = (struct mgcp_trunk *)osmux_fd_v4.data;
struct rate_ctr_group *all_osmux_stats = trunk->ratectr.all_osmux_conn_stats;
switch (handle->rem_addr.u.sa.sa_family) {
case AF_INET6:
iofd = osmux_fd_v6;
dest_len = sizeof(handle->rem_addr.u.sin6);
fd = osmux_fd_v6.fd;
break;
case AF_INET:
default:
iofd = osmux_fd_v4;
dest_len = sizeof(handle->rem_addr.u.sin);
fd = osmux_fd_v4.fd;
break;
}
rc = osmo_iofd_sendto_msgb(iofd, batch_msg, 0, &handle->rem_addr);
rc = sendto(fd, batch_msg->data, batch_msg->len, 0,
(struct sockaddr *)&handle->rem_addr.u.sa, dest_len);
if (rc < 0) {
char errbuf[129];
strerror_r(-rc, errbuf, sizeof(errbuf));
strerror_r(errno, errbuf, sizeof(errbuf));
LOGP(DOSMUX, LOGL_NOTICE, "osmux sendto(%s) failed: %s\n",
osmo_sockaddr_to_str(&handle->rem_addr), errbuf);
rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_DROPPED_PACKETS_CTR));
msgb_free(batch_msg);
} else {
rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_PACKETS_TX_CTR));
}
msgb_free(batch_msg);
}
/* Lookup existing OSMUX handle for specified destination address. */
@@ -204,17 +204,17 @@ osmux_handle_find_or_create(const struct mgcp_trunk *trunk, const struct osmo_so
return h->in;
}
/*! send RTP packet through OSMUX connection. Takes ownership of msg.
/*! send RTP packet through OSMUX connection.
* \param[in] conn associated RTP connection
* \param[in] msg msgb containing an RTP AMR packet
* \returns 0 on success, -1 on ERROR */
int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
{
int ret;
struct msgb *msg2;
if (!conn->end.output_enabled) {
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
msgb_free(msg);
return -1;
}
@@ -222,19 +222,22 @@ int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
LOGPCONN(conn->conn, DOSMUX, LOGL_INFO, "forwarding RTP to Osmux conn not yet enabled, dropping (cid=%d)\n",
conn->osmux.remote_cid);
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
msgb_free(msg);
return -1;
}
/* msg is not owned by us and will be freed by the caller stack upon return: */
msg2 = msgb_copy_c(conn->conn, msg, "osmux-rtp-send");
if (!msg2)
return -1;
/* Osmux implementation works with AMR OA only, make sure we convert to it if needed: */
if (amr_oa_bwe_convert(conn->conn->endp, msg, true) < 0) {
if (amr_oa_bwe_convert(conn->conn->endp, msg2, true) < 0) {
LOGPCONN(conn->conn, DOSMUX, LOGL_ERROR,
"Error converting to AMR octet-aligned mode\n");
msgb_free(msg);
return -1;
}
while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.remote_cid)) > 0) {
while ((ret = osmux_xfrm_input(conn->osmux.in, msg2, conn->osmux.remote_cid)) > 0) {
/* batch full, build and deliver it */
osmux_xfrm_input_deliver(conn->osmux.in);
}
@@ -242,7 +245,7 @@ int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
} else {
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_CTR);
rtpconn_osmux_rate_ctr_add(conn, OSMUX_AMR_OCTETS_TX_CTR, msgb_length(msg) - sizeof(struct rtp_hdr));
rtpconn_osmux_rate_ctr_add(conn, OSMUX_AMR_OCTETS_TX_CTR, msgb_length(msg2) - sizeof(struct rtp_hdr));
}
return 0;
}
@@ -322,7 +325,29 @@ static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data)
};
endp->type->dispatch_rtp_cb(msg);
/* dispatch_rtp_cb() has taken ownership of the msgb */
msgb_free(msg);
}
static struct msgb *osmux_recv(struct osmo_fd *ofd, struct osmo_sockaddr *addr)
{
struct msgb *msg;
socklen_t slen = sizeof(addr->u.sas);
int ret;
msg = msgb_alloc(4096, "OSMUX");
if (!msg) {
LOGP(DOSMUX, LOGL_ERROR, "cannot allocate message\n");
return NULL;
}
ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0, &addr->u.sa, &slen);
if (ret <= 0) {
msgb_free(msg);
LOGP(DOSMUX, LOGL_ERROR, "cannot receive message\n");
return NULL;
}
msgb_put(msg, ret);
return msg;
}
/* To be called every time some AMR data is received on a connection
@@ -420,16 +445,22 @@ out:
}
#define osmux_chunk_length(msg, rem) ((rem) - (msg)->len)
static void osmux_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *rem_addr)
static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
struct mgcp_trunk *trunk = osmo_iofd_get_data(iofd);
struct rate_ctr_group *all_rtp_stats = trunk->ratectr.all_osmux_conn_stats;
struct osmo_sockaddr rem_addr;
uint32_t rem;
struct mgcp_trunk *trunk = ofd->data;
struct rate_ctr_group *all_rtp_stats = trunk->ratectr.all_osmux_conn_stats;
char addr_str[64];
msg = osmux_recv(ofd, &rem_addr);
if (!msg)
return -1;
rate_ctr_inc(rate_ctr_group_get_ctr(all_rtp_stats, OSMUX_PACKETS_RX_CTR));
osmo_sockaddr_to_str_buf(addr_str, sizeof(addr_str), rem_addr);
osmo_sockaddr_to_str_buf(addr_str, sizeof(addr_str), &rem_addr);
if (trunk->cfg->osmux.usage == OSMUX_USAGE_OFF) {
LOGP(DOSMUX, LOGL_ERROR,
@@ -439,16 +470,14 @@ static void osmux_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg
}
/* Catch legacy dummy message and process them separately: */
if (msg->len == 2 && msg->data[0] == MGCP_DUMMY_LOAD) {
osmux_handle_legacy_dummy(trunk, rem_addr, msg);
return;
}
if (msg->len == 2 && msg->data[0] == MGCP_DUMMY_LOAD)
return osmux_handle_legacy_dummy(trunk, &rem_addr, msg);
rem = msg->len;
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
struct mgcp_conn_rtp *conn_src;
conn_src = osmux_conn_lookup(trunk, osmuxh->circuit_id,
rem_addr);
&rem_addr);
if (!conn_src) {
LOGP(DOSMUX, LOGL_DEBUG,
"Cannot find a src conn for %s CID=%d\n",
@@ -456,7 +485,7 @@ static void osmux_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg
goto next;
}
if (conn_osmux_event_data_received(conn_src, rem_addr) < 0)
if (conn_osmux_event_data_received(conn_src, &rem_addr) < 0)
goto next;
mgcp_conn_watchdog_kick(conn_src->conn);
@@ -470,94 +499,58 @@ next:
}
out:
msgb_free(msg);
return 0;
}
static void osmux_sendto_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *rem_addr)
{
/* nothing; osmo_io takes care of msgb_free */
if (res < 0) {
struct mgcp_trunk *trunk = (struct mgcp_trunk *) osmo_iofd_get_data(iofd);
struct rate_ctr_group *all_osmux_stats = trunk->ratectr.all_osmux_conn_stats;
char errbuf[129];
strerror_r(-res, errbuf, sizeof(errbuf));
LOGP(DOSMUX, LOGL_NOTICE, "osmux sendto(%s) failed: %s\n", osmo_sockaddr_to_str(rem_addr), errbuf);
rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_DROPPED_PACKETS_CTR));
}
}
static const struct osmo_io_ops osmux_ioops = {
.recvfrom_cb = osmux_recvfrom_cb,
.sendto_cb = osmux_sendto_cb,
};
int osmux_init(struct mgcp_trunk *trunk)
{
int ret, fd;
int ret;
struct mgcp_config *cfg = trunk->cfg;
/* So far we only support running on one trunk: */
OSMO_ASSERT(trunk == mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID));
osmux_fd_v4 = osmo_iofd_setup(trunk, -1, "osmux_fd_v4", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &osmux_ioops, trunk);
if (!osmux_fd_v4)
goto out;
osmo_iofd_set_alloc_info(osmux_fd_v4, 4096, 0);
osmo_fd_setup(&osmux_fd_v4, -1, OSMO_FD_READ, osmux_read_fd_cb, trunk, 0);
osmo_fd_setup(&osmux_fd_v6, -1, OSMO_FD_READ, osmux_read_fd_cb, trunk, 0);
if (cfg->osmux.local_addr_v4) {
ret = mgcp_create_bind(cfg->osmux.local_addr_v4, cfg->osmux.local_port,
ret = mgcp_create_bind(cfg->osmux.local_addr_v4, &osmux_fd_v4, cfg->osmux.local_port,
cfg->endp_dscp, cfg->endp_priority);
if (ret < 0) {
LOGP(DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv4 socket to %s:%u\n",
cfg->osmux.local_addr_v4, cfg->osmux.local_port);
goto out_free_v4;
return ret;
}
fd = ret;
ret = osmo_iofd_register(osmux_fd_v4, fd);
ret = osmo_fd_register(&osmux_fd_v4);
if (ret < 0) {
LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv4 socket %s\n", osmo_sock_get_name2(fd));
close(fd);
goto out_free_v4;
LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv4 socket %s\n",
osmo_sock_get_name2(osmux_fd_v4.fd));
return ret;
}
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv4 socket listening on %s\n", osmo_sock_get_name2(fd));
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv4 socket listening on %s\n",
osmo_sock_get_name2(osmux_fd_v4.fd));
}
osmux_fd_v6 = osmo_iofd_setup(trunk, -1, "osmux_fd_v6", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &osmux_ioops, trunk);
if (!osmux_fd_v6)
goto out_free_v4;
osmo_iofd_set_alloc_info(osmux_fd_v6, 4096, 0);
if (cfg->osmux.local_addr_v6) {
ret = mgcp_create_bind(cfg->osmux.local_addr_v6, cfg->osmux.local_port,
ret = mgcp_create_bind(cfg->osmux.local_addr_v6, &osmux_fd_v6, cfg->osmux.local_port,
cfg->endp_dscp, cfg->endp_priority);
if (ret < 0) {
LOGP(DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv6 socket to [%s]:%u\n",
cfg->osmux.local_addr_v6, cfg->osmux.local_port);
goto out_free_v6;
return ret;
}
fd = ret;
ret = osmo_iofd_register(osmux_fd_v6, fd);
ret = osmo_fd_register(&osmux_fd_v6);
if (ret < 0) {
LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv6 socket %s\n", osmo_sock_get_name2(fd));
close(fd);
goto out_free_v6;
LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv6 socket %s\n",
osmo_sock_get_name2(osmux_fd_v6.fd));
return ret;
}
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv6 socket listening on %s\n", osmo_sock_get_name2(fd));
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv6 socket listening on %s\n",
osmo_sock_get_name2(osmux_fd_v6.fd));
}
cfg->osmux.initialized = true;
return 0;
out_free_v6:
/* osmo_iofd_free performs unregister + close */
osmo_iofd_free(osmux_fd_v6);
osmux_fd_v6 = NULL;
out_free_v4:
/* osmo_iofd_free performs unregister + close */
osmo_iofd_free(osmux_fd_v4);
osmux_fd_v4 = NULL;
out:
return -1;
}
/*! relase OSXMUX cid, that had been allocated to this connection.
@@ -695,7 +688,7 @@ void conn_osmux_disable(struct mgcp_conn_rtp *conn)
/*! send RTP dummy packet to OSMUX connection port.
* \param[in] conn associated RTP connection
* \returns 0 in case of success, -1 on error */
* \returns bytes sent, -1 on error */
int osmux_send_dummy(struct mgcp_conn_rtp *conn)
{
char ipbuf[INET6_ADDRSTRLEN];
@@ -723,7 +716,7 @@ int osmux_send_dummy(struct mgcp_conn_rtp *conn)
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf),
osmo_sockaddr_port(&conn->end.addr.u.sa), conn->osmux.remote_cid);
return mgcp_udp_send(osmux_fd_v4, &conn->end.addr, (char *)osmuxh, buf_len);
return mgcp_udp_send(osmux_fd_v4.fd, &conn->end.addr, (char *)osmuxh, buf_len);
}
/* Keeps track of locally allocated Osmux circuit ID. +7 to round up to 8 bit boundary. */

View File

@@ -397,6 +397,23 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
rq.null_endp = mgcp_endp_is_null(pdata.epname);
if (!rq.null_endp)
rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
if (rq.endp) {
struct mgcp_conn *c;
int count = 0;
llist_for_each_entry(c, &rq.endp->conns, entry) {
LOGP(DLMGCP, LOGL_DEBUG,
"%s: endp=%s conn %s\n",
rq.name,
rq.endp->name,
mgcp_conn_dump(c));
count++;
}
if (!count)
LOGP(DLMGCP, LOGL_DEBUG,
"%s: endp=%s no conns\n",
rq.name,
rq.endp->name);
}
rq.mgcp_cause = rc;
if (!rq.endp) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
@@ -429,6 +446,7 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
/* Check if we have to retransmit a response from a previous transaction */
if (pdata.trans && rq.endp->last_trans && strcmp(rq.endp->last_trans, pdata.trans) == 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
LOGP(DLMGCP, LOGL_DEBUG, "%s: retransmission\n", rq.name);
return create_retransmission_response(rq.endp);
}
}
@@ -1083,6 +1101,17 @@ mgcp_header_done:
mgcp_rtp_end_config(endp, 0, &conn->end);
/* check connection mode setting */
if (conn->conn->mode != MGCP_CONN_LOOPBACK
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
&& osmo_sockaddr_port(&conn->end.addr.u.sa) == 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 */
if (mgcp_get_local_addr(conn->end.local_addr, conn) < 0) {
@@ -1285,6 +1314,17 @@ mgcp_header_done:
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) {

View File

@@ -34,9 +34,7 @@
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/sdp/fmtp.h>
#include <osmocom/sdp/sdp_strings.h>
#include <osmocom/mgcp_client/fmtp.h>
#include <errno.h>
#include <stdlib.h>
@@ -420,35 +418,34 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
}
/* Add rtpmap string to the sdp payload, but only when the payload type falls
* into the dynamic payload type range */
static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name)
{
int rc;
if (payload_type >= 96 && payload_type <= 127) {
if (!audio_name)
return -EINVAL;
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", payload_type, audio_name);
if (rc < 0)
return -EINVAL;
}
return 0;
}
/* Add audio strings to sdp payload */
static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
/* Add all codecs related lines to SDP payload */
static int add_codecs(struct msgb *sdp, const struct mgcp_conn_rtp *conn)
{
int rc;
unsigned int i;
int local_port;
struct mgcp_trunk *trunk = conn->conn->endp->trunk;
if (!conn->end.codecs_assigned)
return 0;
/* Compose 'm=audio 1234 RTP/AVP 112 96 3' line, with
* - local RTP port
* - a list of all assigned payload type numbers
*/
if (mgcp_conn_rtp_is_osmux(conn))
local_port = trunk->cfg->osmux.local_port;
else
local_port = conn->end.local_port;
rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
if (rc < 0)
return -EINVAL;
for (i = 0; i < payload_types_len; i++) {
rc = msgb_printf(sdp, " %d", payload_types[i]);
for (i = 0; i < conn->end.codecs_assigned; i++) {
const struct mgcp_rtp_codec *c = &conn->end.codecs[i];
rc = msgb_printf(sdp, " %d", c->payload_type);
if (rc < 0)
return -EINVAL;
}
@@ -457,28 +454,45 @@ static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_
if (rc < 0)
return -EINVAL;
return 0;
}
/* Compose 'a=rtpmap:N FOO' lines for codecs in above list that require it.
* e.g. GSM-FR is implicitly defined by payload type number 3, so it is enough to list 3 above;
* AMR needs a line like 'a=rtpmap:112 AMR/8000/1' in addition to listing 112 above.
*/
for (i = 0; i < conn->end.codecs_assigned; i++) {
const struct mgcp_rtp_codec *c = &conn->end.codecs[i];
if (!c->audio_name[0])
continue;
/* Add fmtp strings to sdp payload */
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
{
unsigned int i;
int rc;
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;
/* Dynamic payload type numbers need explicit rtpmap defining the codec by "subtype name" like "AMR" or
* "GSM-HR-08". Others are defined implicitly, like GSM-FR by payload type number 3.
*
* Also, if the trunk is configured as "no sdp audio-payload send-name", omit all rtpmap lines.
*/
if (c->payload_type >= 96 && c->payload_type <= 127
&& trunk->audio_send_name) {
if (msgb_printf(sdp, "a=rtpmap:%d %s\r\n", c->payload_type, c->audio_name) < 0)
return -EINVAL;
}
rc = msgb_printf(sdp, "\r\n");
/* Compose 'a=fmtp:N foo=bar' line if fmtp is defined for this codec.
* e.g. AMR has fmtp like 'octet-align=1', 'mode-set=0,2,4,7'.
*/
if (c->fmtp[0]) {
if (msgb_printf(sdp, OSMO_SDP_PREFIX_A_FMTP "%d %s\r\n", c->payload_type, c->fmtp) < 0)
return -EINVAL;
}
else if (c->param_present) {
/* Legacy */
if (msgb_printf(sdp, OSMO_SDP_PREFIX_A_FMTP "%d %s\r\n", c->payload_type,
OSMO_SDP_AMR_SET_OCTET_ALIGN(c->param.amr_octet_aligned))
< 0)
return -EINVAL;
}
}
if (conn->end.packet_duration_ms > 0 && conn->conn->endp->trunk->audio_send_ptime) {
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
conn->end.packet_duration_ms);
if (rc < 0)
return -EINVAL;
}
@@ -496,14 +510,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
const char *addr)
{
const struct mgcp_rtp_codec *codec;
const char *audio_name;
int payload_type;
int rc;
int payload_types[1];
int local_port;
struct sdp_fmtp_param fmtp_params[1];
unsigned int fmtp_params_len = 0;
bool addr_is_v6;
OSMO_ASSERT(endp);
@@ -511,11 +518,6 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
OSMO_ASSERT(sdp);
OSMO_ASSERT(addr);
codec = conn->end.codec;
audio_name = codec->audio_name;
payload_type = codec->payload_type;
addr_is_v6 = osmo_ip_str_type(addr) == AF_INET6;
rc = msgb_printf(sdp,
@@ -530,53 +532,14 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
if (rc < 0)
goto buffer_too_small;
if (payload_type >= 0) {
payload_types[0] = payload_type;
if (mgcp_conn_rtp_is_osmux(conn))
local_port = endp->trunk->cfg->osmux.local_port;
else
local_port = conn->end.local_port;
rc = add_audio(sdp, payload_types, 1, local_port);
if (rc < 0)
goto buffer_too_small;
if (endp->trunk->audio_send_name) {
rc = add_rtpmap(sdp, payload_type, audio_name);
if (rc < 0)
goto buffer_too_small;
}
if (codec->fmtp[0]) {
fmtp_params[0] = (struct sdp_fmtp_param){
.payload_type = payload_type,
.fmtp = codec->fmtp,
};
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);
if (rc < 0)
goto buffer_too_small;
}
if (conn->end.packet_duration_ms > 0 && endp->trunk->audio_send_ptime) {
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
conn->end.packet_duration_ms);
if (rc < 0)
goto buffer_too_small;
}
/* Add all codecs related SDP lines */
rc = add_codecs(sdp, conn);
if (rc < 0)
goto buffer_too_small;
return 0;
buffer_too_small:
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP message too large for buffer\n");
return -1;
}

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,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

@@ -280,31 +280,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",
@@ -353,8 +328,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);

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.

View File

@@ -34,7 +34,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) \

View File

@@ -103,7 +103,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 +115,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 +127,40 @@ 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=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 +172,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 +184,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"
@@ -306,7 +297,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 +310,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 +322,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 +335,17 @@ 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=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 +371,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 +384,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" \
@@ -538,7 +441,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 +456,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 +471,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 +486,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" \
@@ -672,7 +575,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_NULL_RET "502 2 FAIL\r\n"
@@ -720,11 +623,139 @@ static const struct mgcp_test tests[] = {
{"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},
{
"CRCX_EXPLICIT_EP",
/* CRCX for a new endpoint 8@mgw, not using the '*@mgw' wildcard */
"CRCX 101 rtpbridge/8@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 1.2.3.4\r\n"
"m=audio 1234 RTP/AVP 112\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n",
"200 101 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 112\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n"
},
{
"CRCX_TWO_PAYLOADS_1",
"CRCX 102 rtpbridge/*@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 1.2.3.4\r\n"
"m=audio 1234 RTP/AVP 112 3\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n",
"200 102 OK\r\n"
"Z: rtpbridge/2@mgw\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 112 3\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n"
},
{
"CRCX_TWO_PAYLOADS_2",
"CRCX 103 rtpbridge/2@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 1.2.3.4\r\n"
"m=audio 1234 RTP/AVP 3 112\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n",
"200 103 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 16018 RTP/AVP 3 112\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=ptime:20\r\n"
},
{
"CRCX_THREE_PAYLOADS_1",
"CRCX 104 rtpbridge/*@mgw MGCP 1.0\r\n"
"m: recvonly\r\n"
"C: 4\r\n"
"L: p:20\r\n"
"\r\n"
"v=0\r\n"
"c=IN IP4 1.2.3.4\r\n"
"m=audio 1234 RTP/AVP 112 3 111\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1;mode-set=0,2,4,7\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=ptime:20\r\n",
"200 104 OK\r\n"
"Z: rtpbridge/3@mgw\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 16020 RTP/AVP 112 3 111\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1;mode-set=0,2,4,7\r\n"
"a=rtpmap:111 GSM-HR-08/8000\r\n"
"a=ptime:20\r\n"
},
{
"CRCX_THREE_PAYLOADS_2",
"CRCX 105 rtpbridge/3@mgw MGCP 1.0\r\n"
"m: recvonly\r\n"
"C: 4\r\n"
"L: p:20\r\n"
"\r\n"
"v=0\r\n"
"c=IN IP4 1.2.3.4\r\n"
"m=audio 1234 RTP/AVP 3 112 113\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1;mode-set=0,2,4,7\r\n"
"a=rtpmap:113 AMR/8000\r\n"
"a=fmtp:113 octet-align=1;mode-set=0,2,4\r\n"
"a=ptime:20\r\n",
"200 105 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 16022 RTP/AVP 3 112 113\r\n"
"a=rtpmap:112 AMR/8000\r\n"
"a=fmtp:112 octet-align=1;mode-set=0,2,4,7\r\n"
"a=rtpmap:113 AMR/8000\r\n"
"a=fmtp:113 octet-align=1;mode-set=0,2,4\r\n"
"a=ptime:20\r\n"
},
};
static const struct mgcp_test retransmit[] = {
@@ -755,13 +786,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 +805,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;
}
@@ -928,7 +956,7 @@ static void test_messages(void)
struct msgb *msg;
printf("\n================================================\n");
printf("Testing %s\n", t->name);
printf("Testing %s() %s\n", __func__, t->name);
dummy_packets = 0;
@@ -1033,6 +1061,9 @@ static void test_messages(void)
/* Reset them again for next test */
conn->end.codec->payload_type = PTYPE_NONE;
}
fflush(stdout);
fflush(stderr);
}
mgcp_endpoints_release(trunk);
@@ -1061,7 +1092,7 @@ static void test_retransmission(void)
struct msgb *msg;
printf("\n================================================\n");
printf("Testing %s\n", t->name);
printf("Testing %s() %s\n", __func__, t->name);
inp = create_msg(t->req, last_conn_id);
msg = mgcp_handle_message(cfg, inp);
@@ -1091,6 +1122,9 @@ static void test_retransmission(void)
OSMO_ASSERT(false);
}
msgb_free(msg);
fflush(stdout);
fflush(stderr);
}
mgcp_endpoints_release(trunk);
@@ -1719,6 +1753,7 @@ static void test_no_cycle(void)
talloc_free(cfg);
}
/* Set audio_send_name=0 and verify that a=rtpmap: entries are omitted. */
static void test_no_name(void)
{
struct mgcp_trunk *trunk;
@@ -2437,6 +2472,13 @@ int main(int argc, char **argv)
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
osmo_init_logging2(ctx, &log_info);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
log_set_print_level(osmo_stderr_target, 1);
log_set_print_category(osmo_stderr_target, 1);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 1);
log_set_category_filter(osmo_stderr_target, DLMGCP, true, LOGL_DEBUG);
test_strline();
test_values();

View File

@@ -13,7 +13,7 @@ line: ''
line: ''
================================================
Testing AUEP1
Testing test_messages() AUEP1
creating message from statically defined input:
---------8<---------
AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0
@@ -25,7 +25,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing AUEP2
Testing test_messages() AUEP2
creating message from statically defined input:
---------8<---------
AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0
@@ -37,7 +37,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing MDCX1
Testing test_messages() MDCX1
creating message from statically defined input:
---------8<---------
MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0
@@ -49,7 +49,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing MDCX2
Testing test_messages() MDCX2
creating message from statically defined input:
---------8<---------
MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0
@@ -61,7 +61,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing CRCX
Testing test_messages() CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
@@ -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:
@@ -83,7 +83,7 @@ Response matches our expectations.
Dummy packets: 2
================================================
Testing MDCX3
Testing test_messages() MDCX3
creating message from statically defined input:
---------8<---------
MDCX 18983215 1@mgw MGCP 1.0
@@ -97,46 +97,44 @@ Response matches our expectations.
Dummy packets: 2
================================================
Testing MDCX4_ADDR000
Testing test_messages() 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
================================================
Testing test_messages() 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
@@ -146,22 +144,21 @@ Response matches our expectations.
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
Testing MDCX4_PT1
================================================
Testing test_messages() 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
@@ -171,22 +168,21 @@ Response matches our expectations.
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
Testing MDCX4_PT2
Dummy packets: 2
================================================
Testing test_messages() 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
@@ -196,22 +192,21 @@ Response matches our expectations.
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
Testing MDCX4_PT3
(response contains a connection id)
Dummy packets: 2
================================================
M: sendrecv
C: 2
Testing test_messages() 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
@@ -221,22 +216,21 @@ Response matches our expectations.
---------8<---------
checking response:
using message with patched conn_id for comparison
Testing MDCX4_PT4
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
m: sendrecv
c: 2
================================================
Testing test_messages() 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
@@ -246,22 +240,21 @@ Response matches our expectations.
---------8<---------
checking response:
Testing MDCX4_SO
using message with patched conn_id for comparison
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
M: sendonly
C: 2
================================================
Testing test_messages() 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
@@ -270,12 +263,11 @@ Response matches our expectations.
a=rtpmap:99 AMR/8000
a=ptime:40
Testing MDCX4_RO
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
M: recvonly
C: 2
(response contains a connection id)
================================================
Testing test_messages() MDCX4_RO
@@ -287,7 +279,7 @@ Response matches our expectations.
I: %s
L: p:20, a:AMR, nt:IN
Testing DLCX
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
@@ -301,18 +293,12 @@ Response matches our expectations.
DLCX 7 1@mgw MGCP 1.0
I: %s
C: 2
Testing CRCX_ZYN
---------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 test_messages() CRCX_ZYN
@@ -321,7 +307,7 @@ Response matches our expectations.
CRCX 2 1@mgw MGCP 1.0
M: recvonly
C: 2
Testing EMPTY
v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
@@ -329,7 +315,7 @@ creating message from statically defined input:
---------8<---------
checking response:
using message with patched conn_id for comparison
Testing SHORT1
Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
@@ -341,7 +327,7 @@ Response matches our expectations.
---------8<---------
Testing SHORT2
================================================
Testing test_messages() SHORT1
creating message from statically defined input:
---------8<---------
@@ -352,7 +338,7 @@ Response matches our expectations.
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
Testing SHORT3
================================================
Testing test_messages() SHORT2
creating message from statically defined input:
@@ -363,7 +349,7 @@ Response matches our expectations.
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
Testing SHORT4
================================================
Testing test_messages() SHORT3
creating message from statically defined input:
@@ -374,7 +360,7 @@ Response matches our expectations.
using message as statically defined for comparison
Response matches our expectations.
(response does not contain a connection id)
Testing RQNT1
================================================
Testing test_messages() SHORT4
creating message from statically defined input:
@@ -388,7 +374,7 @@ Response matches our expectations.
================================================
Testing test_messages() RQNT1
Testing RQNT2
creating message from statically defined input:
---------8<---------
RQNT 186908780 1@mgw MGCP 1.0
X: B244F267488
@@ -402,7 +388,7 @@ Response matches our expectations.
================================================
Testing test_messages() RQNT2
Testing DLCX
creating message from statically defined input:
---------8<---------
RQNT 186908781 1@mgw MGCP 1.0
X: ADD4F26746F
@@ -416,7 +402,7 @@ Response matches our expectations.
================================================
Testing test_messages() DLCX
Testing CRCX
creating message from statically defined input:
---------8<---------
DLCX 7 1@mgw MGCP 1.0
I: %s
@@ -428,7 +414,7 @@ v=0
Response matches our expectations.
(response does not contain a connection id)
a=ptime:20
================================================
Testing test_messages() CRCX
creating message from statically defined input:
---------8<---------
@@ -438,7 +424,7 @@ Response matches our expectations.
L: p:20
v=0
Testing MDCX3
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:40
@@ -452,7 +438,7 @@ Response matches our expectations.
================================================
Testing test_messages() MDCX3
Testing DLCX
creating message from statically defined input:
---------8<---------
MDCX 18983215 1@mgw MGCP 1.0
I: %s
@@ -466,7 +452,7 @@ Response matches our expectations.
================================================
Testing test_messages() DLCX
Testing CRCX
creating message from statically defined input:
---------8<---------
DLCX 7 1@mgw MGCP 1.0
I: %s
@@ -480,7 +466,7 @@ Response matches our expectations.
================================================
Testing test_messages() CRCX
Testing CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 6@mgw MGCP 1.0
M: recvonly
@@ -493,7 +479,7 @@ v=0
(response contains a connection id)
================================================
a=ptime:20
Testing test_messages() CRCX
creating message from statically defined input:
---------8<---------
CRCX 2 1@mgw MGCP 1.0
@@ -503,7 +489,7 @@ Response matches our expectations.
X-Osmo-IGN: C foo
v=0
Testing MDCX_TOO_LONG_CI
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
a=ptime:40
@@ -516,7 +502,7 @@ Response matches our expectations.
Dummy packets: 2
================================================
Testing CRCX
Testing test_messages() MDCX_TOO_LONG_CI
creating message from statically defined input:
---------8<---------
MDCX 18983224 1@mgw MGCP 1.0
@@ -540,7 +526,7 @@ Response matches our expectations.
v=0
c=IN IP4 123.12.12.123
Testing AUEP_NULL
m=audio 5904 RTP/AVP 111
a=rtpmap:111 AMR/8000/1
a=ptime:20
a=fmtp:111 mode-change-capability=2; octet-align=1
@@ -552,7 +538,7 @@ Response matches our expectations.
(response contains a connection id)
Dummy packets: 2
Testing CRCX_NULL
================================================
Testing test_messages() AUEP_NULL
creating message from statically defined input:
---------8<---------
@@ -564,7 +550,7 @@ v=0
Response matches our expectations.
(response does not contain a connection id)
a=ptime:20
================================================
Testing test_messages() CRCX_NULL
creating message from statically defined input:
---------8<---------
@@ -573,7 +559,7 @@ Response matches our expectations.
C: 2
L: p:20
Testing MDCX_NULL
v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
@@ -586,7 +572,7 @@ Response matches our expectations.
(response does not contain a connection id)
================================================
Testing DLCX_NULL
Testing test_messages() MDCX_NULL
creating message from statically defined input:
---------8<---------
MDCX 9 null@mgw MGCP 1.0
@@ -600,7 +586,7 @@ Response matches our expectations.
================================================
Testing test_messages() DLCX_NULL
Testing RQNT_NULL
creating message from statically defined input:
---------8<---------
DLCX 8 null@mgw MGCP 1.0
I: %s
@@ -614,18 +600,18 @@ Response matches our expectations.
================================================
Testing test_messages() RQNT_NULL
Testing CRCX_PORT_0
creating message from statically defined input:
---------8<---------
RQNT 186908782 null@mgw MGCP 1.0
CRCX 3 1@mgw MGCP 1.0
X: B244F267488
S: D/9
---------8<---------
checking response:
using message as statically defined for comparison
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
Response matches our expectations.
(response does not contain a connection id)
================================================
Testing test_messages() CRCX_EXPLICIT_EP
creating message from statically defined input:
@@ -633,20 +619,21 @@ checking response:
CRCX 101 rtpbridge/8@mgw MGCP 1.0
m: recvonly
C: 2
L: p:20
v=0
Testing CRCX_PORT_0_IUFP
c=IN IP4 1.2.3.4
m=audio 1234 RTP/AVP 112
a=rtpmap:112 AMR/8000
CRCX 4 1@mgw MGCP 1.0
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
(response contains a connection id)
Dummy packets: 2
================================================
Testing test_messages() CRCX_TWO_PAYLOADS_1
creating message from statically defined input:
@@ -654,20 +641,21 @@ checking response:
CRCX 102 rtpbridge/*@mgw MGCP 1.0
m: recvonly
C: 2
L: p:20
v=0
Testing CRCX_PORT_0_IUFP_SENDRECV
c=IN IP4 1.2.3.4
m=audio 1234 RTP/AVP 112 3
a=rtpmap:112 AMR/8000
CRCX 4 1@mgw MGCP 1.0
M: sendrecv
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
(response contains a connection id)
Dummy packets: 2
================================================
Testing test_messages() CRCX_TWO_PAYLOADS_2
creating message from statically defined input:
@@ -675,20 +663,23 @@ checking response:
CRCX 103 rtpbridge/2@mgw MGCP 1.0
m: recvonly
C: 2
L: p:20
v=0
Testing CRCX_PORT_0_IUFP_SENDRECV2
c=IN IP4 1.2.3.4
m=audio 1234 RTP/AVP 3 112
a=rtpmap:112 AMR/8000
CRCX 4 1@mgw MGCP 1.0
C: 2
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
c=IN IP4 123.12.12.123
a=sendrecv
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
(response contains a connection id)
Dummy packets: 2
================================================
Testing test_messages() CRCX_THREE_PAYLOADS_1
creating message from statically defined input:
---------8<---------
CRCX 104 rtpbridge/*@mgw MGCP 1.0
@@ -696,19 +687,24 @@ checking response:
C: 4
L: p:20
v=0
c=IN IP4 1.2.3.4
m=audio 1234 RTP/AVP 112 3 111
Testing CRCX_PORT_0_IUFP_SENDRECV3
a=rtpmap:112 AMR/8000
a=fmtp:112 octet-align=1;mode-set=0,2,4,7
a=rtpmap:111 GSM-HR-08/8000
CRCX 4 1@mgw MGCP 1.0
C: 2
a=ptime:20
---------8<---------
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
c=IN IP4 123.12.12.123
m=audio 0 RTP/AVP 96
a=rtpmap:96 VND.3GPP.IUFP/16000
(response contains a connection id)
Dummy packets: 2
================================================
Testing test_messages() CRCX_THREE_PAYLOADS_2
creating message from statically defined input:
---------8<---------
CRCX 105 rtpbridge/3@mgw MGCP 1.0
m: recvonly
@@ -716,9 +712,10 @@ checking response:
L: p:20
v=0
c=IN IP4 1.2.3.4
m=audio 1234 RTP/AVP 3 112 113
a=rtpmap:112 AMR/8000
Testing CRCX
a=fmtp:112 octet-align=1;mode-set=0,2,4,7
a=rtpmap:113 AMR/8000
a=fmtp:113 octet-align=1;mode-set=0,2,4
a=ptime:20
@@ -730,7 +727,7 @@ v=0
(response contains a connection id)
Dummy packets: 2
a=ptime:20
================================================
Testing test_retransmission() CRCX
creating message from statically defined input:
---------8<---------
@@ -748,7 +745,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<---------
@@ -756,7 +753,7 @@ using message with patched conn_id for comparison
m: recvonly
C: 2
L: p:20
Testing RQNT1
v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
@@ -780,7 +777,7 @@ using message as statically defined for comparison
checking response:
using message as statically defined for comparison
Response matches our expectations.
Testing RQNT2
Re-transmitting RQNT1
creating message from statically defined input:
---------8<---------
RQNT 186908780 1@mgw MGCP 1.0
@@ -804,7 +801,7 @@ using message as statically defined for comparison
checking response:
using message as statically defined for comparison
Response matches our expectations.
Testing MDCX3
Re-transmitting RQNT2
creating message from statically defined input:
---------8<---------
RQNT 186908781 1@mgw MGCP 1.0
@@ -826,7 +823,7 @@ using message with patched conn_id for comparison
---------8<---------
checking response:
using message with patched conn_id for comparison
Testing DLCX
Response matches our expectations.
Re-transmitting MDCX3
creating message from statically defined input:
---------8<---------
@@ -860,7 +857,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 +1311,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 +1328,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 +1345,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 +1362,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 +1444,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<---------

View File

@@ -571,57 +571,6 @@ static void test_map_str_to_codec(void)
OSMO_ASSERT(map_str_to_codec("AMR-WB####################################################################################################################") == -1);
}
static void test_map_codec_to_pt_and_map_pt_to_codec(void)
{
struct ptmap ptmap[10];
unsigned int ptmap_len;
unsigned int i;
ptmap[0].codec = CODEC_GSMEFR_8000_1;
ptmap[0].pt = 96;
ptmap[1].codec = CODEC_GSMHR_8000_1;
ptmap[1].pt = 97;
ptmap[2].codec = CODEC_AMR_8000_1;
ptmap[2].pt = 98;
ptmap[3].codec = CODEC_AMRWB_16000_1;
ptmap[3].pt = 99;
ptmap_len = 4;
/* Mappings that are covered by the table */
for (i = 0; i < ptmap_len; i++)
printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
for (i = 0; i < ptmap_len; i++)
printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
printf("\n");
/* Map some codecs/payload types from the static range, result must
* always be a 1:1 mapping */
printf(" %u => %u\n", CODEC_PCMU_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMU_8000_1));
printf(" %u => %u\n", CODEC_GSM_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_GSM_8000_1));
printf(" %u => %u\n", CODEC_PCMA_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMA_8000_1));
printf(" %u => %u\n", CODEC_G729_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_G729_8000_1));
printf(" %u <= %u\n", CODEC_PCMU_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMU_8000_1));
printf(" %u <= %u\n", CODEC_GSM_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_GSM_8000_1));
printf(" %u <= %u\n", CODEC_PCMA_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMA_8000_1));
printf(" %u <= %u\n", CODEC_G729_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_G729_8000_1));
printf("\n");
/* Try to do mappings from statically defined range to danymic range and vice versa. This
* is illegal and should result into a 1:1 mapping */
ptmap[3].codec = CODEC_AMRWB_16000_1;
ptmap[3].pt = 2;
ptmap[4].codec = CODEC_PCMU_8000_1;
ptmap[4].pt = 100;
ptmap_len = 5;
/* Apply all mappings again, the illegal ones we defined should result into 1:1 mappings */
for (i = 0; i < ptmap_len; i++)
printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
for (i = 0; i < ptmap_len; i++)
printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
printf("\n");
}
void test_mgcp_client_e1_epname(void)
{
char *epname;
@@ -908,7 +857,6 @@ int main(int argc, char **argv)
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_mgcp_client_e1_epname();

View File

@@ -128,10 +128,6 @@ test_sdp_section_start() test [18]:
body: "some mgcp header data\r\nand header params\r\n\r\nc=IN IP4 \r\n"
DLMGCP Failed to parse MGCP response header (audio ip)
got rc=-22
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw), rate(128)/offset(0) combination is invalid!
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!

View File

@@ -181,35 +181,6 @@ test_sdp_section_start() test [16]:
test_sdp_section_start() test [17]:
test_sdp_section_start() test [18]:
110 => 96
111 => 97
112 => 98
113 => 99
96 <= 110
97 <= 111
98 <= 112
99 <= 113
0 => 0
3 => 3
8 => 8
18 => 18
0 <= 0
3 <= 3
8 <= 8
18 <= 18
110 => 96
111 => 97
112 => 98
113 => 113
0 => 0
96 <= 110
97 <= 111
98 <= 112
2 <= 2
100 <= 100
ds/e1-1/s-15/su64-0@mgw
ds/e1-2/s-14/su32-0@mgw
ds/e1-3/s-13/su32-4@mgw

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