mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-11-02 13:03:33 +00:00
Compare commits
46 Commits
neels/drop
...
neels/m_au
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23140d6f94 | ||
|
|
36c823eef1 | ||
|
|
fa393fa1e9 | ||
|
|
e0fc37a437 | ||
|
|
4aa2b0e35b | ||
|
|
e01f6e7f33 | ||
|
|
388b48d14e | ||
|
|
352b967d1b | ||
|
|
ed4da25f2f | ||
|
|
5abda312ed | ||
|
|
75862d3131 | ||
|
|
8733542b13 | ||
|
|
d7aac20cc1 | ||
|
|
8b5361412c | ||
|
|
3a971ba0d1 | ||
|
|
c4b90354ca | ||
|
|
28fd236044 | ||
|
|
17b5701f19 | ||
|
|
9bea6eb78c | ||
|
|
fd57bd5f6f | ||
|
|
d0dbda4106 | ||
|
|
35a738214c | ||
|
|
afbe872045 | ||
|
|
909be19285 | ||
|
|
8ee08c6b78 | ||
|
|
c053e07336 | ||
|
|
2b30cbdfa8 | ||
|
|
c9df010416 | ||
|
|
3a3e1b57c6 | ||
|
|
186d4f1a0b | ||
|
|
cc2f7937ed | ||
|
|
960c031a67 | ||
|
|
4993452561 | ||
|
|
43eed63b09 | ||
|
|
7a6d9c2f72 | ||
|
|
f3715dc0d3 | ||
|
|
a68a863b9d | ||
|
|
18717f5247 | ||
|
|
677b5396cb | ||
|
|
5d0e07112f | ||
|
|
a5acaa68db | ||
|
|
7787a3aeae | ||
|
|
dd1ddf74fc | ||
|
|
5e101c9fed | ||
|
|
f6db465082 | ||
|
|
a7e6fbc220 |
@@ -24,7 +24,7 @@ pkgconfig_DATA = \
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
contrib/osmo-mgw.spec.in \
|
||||
README.md \
|
||||
debian \
|
||||
git-version-gen \
|
||||
osmoappdesc.py \
|
||||
|
||||
24
README
24
README
@@ -1,24 +0,0 @@
|
||||
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
Normal file
102
README.md
Normal file
@@ -0,0 +1,102 @@
|
||||
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.
|
||||
17
TODO-RELEASE
17
TODO-RELEASE
@@ -24,3 +24,20 @@
|
||||
# If any interfaces have been removed or changed since the last public release, a=0.
|
||||
#
|
||||
#library what description / commit summary line
|
||||
libosmo-mgcp-client remove public API These public API items have not been called by any of our osmo-programs
|
||||
for many years. Any sane caller of libosmo-mgcp-client should use the
|
||||
higher level osmo_mgcpc_* API instead. Move these to a private header:
|
||||
struct mgcp_response_head
|
||||
struct mgcp_response
|
||||
struct mgcp_msg
|
||||
mgcp_response_parse_params()
|
||||
mgcp_client_tx()
|
||||
mgcp_client_cancel()
|
||||
mgcp_msg_gen()
|
||||
mgcp_msg_trans_id()
|
||||
libosmo-mgcp-client deprecate public API New code should no longer use codecs[], instead use ptmap[].codec. There
|
||||
is backwards compat code that moves codecs[] entries, if any, over to
|
||||
ptmap[], so callers may migrate at own leisure.
|
||||
osmo-mgw remove cfg Remove VTY config item 'sdp audio fmtp-extra' (see OS#6313)
|
||||
libosmocore bump_dep; workaround Bump libosmocore version dependency after I68328adb952ca8833ba047cb3b49ccc6f8a1f1b5
|
||||
has been merged to libosmocore.git; then remove my_msgb_copy_c wrapper function.
|
||||
|
||||
@@ -204,5 +204,4 @@ AC_OUTPUT(
|
||||
doc/manuals/Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
contrib/osmo-mgw.spec
|
||||
Makefile)
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
#
|
||||
# spec file for package osmo-mgw
|
||||
#
|
||||
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
# upon. The license for this file, and modifications and additions to the
|
||||
# file, is the same license as for the pristine package itself (unless the
|
||||
# license for the pristine package is not an Open Source License, in which
|
||||
# case the license is the MIT License). An "Open Source License" is a
|
||||
# license that conforms to the Open Source Definition (Version 1.9)
|
||||
# published by the Open Source Initiative.
|
||||
|
||||
# Please submit bugfixes or comments via http://bugs.opensuse.org/
|
||||
#
|
||||
|
||||
Name: osmo-mgw
|
||||
Version: @VERSION@
|
||||
Release: 0
|
||||
Summary: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
||||
License: AGPL-3.0-or-later AND GPL-2.0-or-later
|
||||
Group: Hardware/Mobile
|
||||
URL: https://osmocom.org/projects/osmo-mgw
|
||||
Source: %{name}-%{version}.tar.xz
|
||||
BuildRequires: automake >= 1.9
|
||||
BuildRequires: libtool >= 2
|
||||
BuildRequires: pkgconfig >= 0.20
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
BuildRequires: pkgconfig(libosmo-netif) >= 1.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.
|
||||
|
||||
%package -n libosmo-mgcp-devel
|
||||
Summary: Development files for Osmocom's Media Gateway server library
|
||||
Group: Development/Libraries/C and C++
|
||||
|
||||
%description -n libosmo-mgcp-devel
|
||||
Osmocom's Media Gateway Control Protocol server library.
|
||||
|
||||
This subpackage contains libraries and header files for developing
|
||||
applications that want to make use of libosmo-mgcp.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
echo "%{version}" >.tarball-version
|
||||
autoreconf -fi
|
||||
%configure \
|
||||
--disable-static \
|
||||
--docdir=%{_docdir}/%{name} \
|
||||
--with-systemdsystemunitdir=%{_unitdir}
|
||||
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
%make_install
|
||||
find %{buildroot} -type f -name "*.la" -delete -print
|
||||
|
||||
%check
|
||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
|
||||
%post -n libosmo-mgcp-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
|
||||
|
||||
%files -n libosmo-mgcp-devel
|
||||
%dir %{_includedir}/osmocom
|
||||
%dir %{_includedir}/osmocom/mgcp
|
||||
%{_includedir}/osmocom/mgcp/*.h
|
||||
|
||||
%changelog
|
||||
@@ -9,8 +9,11 @@ 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
Executable file
38
debian/postinst
vendored
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/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#
|
||||
@@ -14,15 +14,12 @@ e1_input
|
||||
e1_line 0 port 0
|
||||
mgcp
|
||||
bind ip 127.0.0.1
|
||||
rtp port-range 4002 16000
|
||||
rtp port-range 4002 16001
|
||||
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
|
||||
|
||||
@@ -11,15 +11,12 @@ log stderr
|
||||
|
||||
mgcp
|
||||
bind ip 127.0.0.1
|
||||
rtp port-range 4002 16000
|
||||
rtp port-range 4002 16001
|
||||
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
|
||||
|
||||
@@ -8,6 +8,9 @@ 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 \
|
||||
$(NULL)
|
||||
|
||||
noinst_HEADERS = \
|
||||
osmocom/mgcp/mgcp.h \
|
||||
osmocom/mgcp/mgcp_common.h \
|
||||
osmocom/mgcp/osmux.h \
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#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>
|
||||
@@ -70,12 +71,10 @@ typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
|
||||
/**
|
||||
* Return:
|
||||
* < 0 in case no audio was processed
|
||||
* >= 0 in case audio was processed. The remaining payload
|
||||
* length will be returned.
|
||||
* >= 0 in case audio was processed.
|
||||
*/
|
||||
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size);
|
||||
struct mgcp_rtp_end *dst_end, struct msgb *msg);
|
||||
|
||||
struct mgcp_conn_rtp;
|
||||
|
||||
@@ -138,8 +137,6 @@ struct mgcp_config {
|
||||
mgcp_processing rtp_processing_cb;
|
||||
mgcp_processing_setup setup_rtp_processing_cb;
|
||||
|
||||
mgcp_get_format get_net_downlink_format_cb;
|
||||
|
||||
struct osmo_wqueue gw_fd;
|
||||
|
||||
struct mgcp_port_range net_ports;
|
||||
@@ -208,6 +205,5 @@ 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, 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);
|
||||
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);
|
||||
|
||||
@@ -68,11 +68,11 @@ struct mgcp_codec_param {
|
||||
/* Ensure that the msg->l2h is NUL terminated. */
|
||||
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
||||
{
|
||||
unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */
|
||||
unsigned char *tail = msg->tail; /* char after l2 data */
|
||||
if (tail[-1] == '\0')
|
||||
/* nothing to do */;
|
||||
else if (msgb_tailroom(msg) > 0)
|
||||
tail[0] = '\0';
|
||||
msgb_put_u8(msg, (uint8_t)'\0');
|
||||
else if (tail[-1] == '\r' || tail[-1] == '\n')
|
||||
tail[-1] = '\0';
|
||||
else {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/osmo_io.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
|
||||
@@ -108,7 +109,6 @@ struct mgcp_rtp_end {
|
||||
int frames_per_packet;
|
||||
uint32_t packet_duration_ms;
|
||||
int maximum_packet_time; /* -1: not set */
|
||||
char *fmtp_extra;
|
||||
/* are we transmitting packets (true) or dropping (false) outbound packets */
|
||||
bool output_enabled;
|
||||
/* FIXME: This parameter can be set + printed, but is nowhere used! */
|
||||
@@ -121,8 +121,8 @@ struct mgcp_rtp_end {
|
||||
bool rfc5993_hr_convert;
|
||||
|
||||
/* Each end has a separate socket for RTP and RTCP */
|
||||
struct osmo_fd rtp;
|
||||
struct osmo_fd rtcp;
|
||||
struct osmo_io_fd *rtp;
|
||||
struct osmo_io_fd *rtcp;
|
||||
|
||||
/* local UDP port number of the RTP socket; RTCP is +1 */
|
||||
int local_port;
|
||||
@@ -160,8 +160,7 @@ 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,
|
||||
char *data, int *len, int buf_size);
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct msgb *msg);
|
||||
|
||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst,
|
||||
@@ -181,7 +180,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(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg);
|
||||
void forward_data_tap(struct osmo_io_fd *iofd, 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);
|
||||
|
||||
@@ -27,7 +27,6 @@ struct mgcp_trunk {
|
||||
unsigned int trunk_nr;
|
||||
enum mgcp_trunk_type trunk_type;
|
||||
|
||||
char *audio_fmtp_extra;
|
||||
int audio_send_ptime;
|
||||
int audio_send_name;
|
||||
|
||||
|
||||
@@ -79,27 +79,7 @@ struct ptmap {
|
||||
unsigned int pt;
|
||||
};
|
||||
|
||||
struct mgcp_response_head {
|
||||
int response_code;
|
||||
mgcp_trans_id_t trans_id;
|
||||
char comment[MGCP_COMMENT_MAXLEN];
|
||||
char conn_id[MGCP_CONN_ID_MAXLEN];
|
||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
bool x_osmo_osmux_use;
|
||||
uint8_t x_osmo_osmux_cid;
|
||||
};
|
||||
|
||||
struct mgcp_response {
|
||||
char *body;
|
||||
struct mgcp_response_head head;
|
||||
uint16_t audio_port;
|
||||
char audio_ip[INET6_ADDRSTRLEN];
|
||||
unsigned int ptime;
|
||||
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_len;
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
};
|
||||
int ptmap_cmp(const struct ptmap *a, const struct ptmap *b);
|
||||
|
||||
enum mgcp_verb {
|
||||
MGCP_VERB_CRCX,
|
||||
@@ -109,37 +89,6 @@ enum mgcp_verb {
|
||||
MGCP_VERB_RSIP,
|
||||
};
|
||||
|
||||
#define MGCP_MSG_PRESENCE_ENDPOINT 0x0001
|
||||
#define MGCP_MSG_PRESENCE_CALL_ID 0x0002
|
||||
#define MGCP_MSG_PRESENCE_CONN_ID 0x0004
|
||||
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
|
||||
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
|
||||
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
|
||||
#define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000
|
||||
#define MGCP_MSG_PRESENCE_X_OSMO_IGN 0x8000
|
||||
|
||||
struct mgcp_msg {
|
||||
enum mgcp_verb verb;
|
||||
/* See MGCP_MSG_PRESENCE_* constants */
|
||||
uint32_t presence;
|
||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
unsigned int call_id;
|
||||
char *conn_id;
|
||||
uint16_t audio_port;
|
||||
char *audio_ip;
|
||||
enum mgcp_connection_mode conn_mode;
|
||||
unsigned int ptime;
|
||||
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_len;
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
uint32_t x_osmo_ign;
|
||||
bool x_osmo_osmux_use;
|
||||
int x_osmo_osmux_cid; /* -1 is wildcard */
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
};
|
||||
|
||||
struct mgcp_client_conf *mgcp_client_conf_alloc(void *ctx);
|
||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf) OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use mgcp_client_conf_alloc() (or even better, switch to the mgcp_client_pool API!)");
|
||||
void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf);
|
||||
@@ -161,20 +110,8 @@ const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp);
|
||||
const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts,
|
||||
uint8_t rate, uint8_t offset);
|
||||
|
||||
/* Invoked when an MGCP response is received or sending failed. When the
|
||||
* response is passed as NULL, this indicates failure during transmission. */
|
||||
typedef void (* mgcp_response_cb_t )(struct mgcp_response *response, void *priv);
|
||||
int mgcp_response_parse_params(struct mgcp_response *r);
|
||||
|
||||
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
mgcp_response_cb_t response_cb, void *priv);
|
||||
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
|
||||
|
||||
enum mgcp_connection_mode;
|
||||
|
||||
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
|
||||
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
|
||||
|
||||
extern const struct value_string mgcp_client_connection_mode_strs[];
|
||||
static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
|
||||
{
|
||||
|
||||
@@ -29,11 +29,11 @@ struct mgcp_conn_peer {
|
||||
/*! RTP packetization interval (optional) */
|
||||
unsigned int ptime;
|
||||
|
||||
/*! RTP codec list (optional) */
|
||||
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
|
||||
|
||||
/*! Number of codecs in RTP codec list (optional) */
|
||||
unsigned int codecs_len;
|
||||
/*! Deprecated. Use only ptmap[].codec in new code. */
|
||||
enum mgcp_codecs codecs[MGCP_MAX_CODECS]
|
||||
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[i].codec instead");
|
||||
unsigned int codecs_len
|
||||
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[] and ptmap_len instead");
|
||||
|
||||
/*! RTP payload type map (optional, only needed when payload types are
|
||||
* used that differ from what IANA/3GPP defines) */
|
||||
@@ -64,11 +64,15 @@ struct mgcp_conn_peer {
|
||||
};
|
||||
|
||||
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);
|
||||
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
|
||||
void mgcp_conn_delete(struct osmo_fsm_inst *fi);
|
||||
uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
|
||||
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_alloc() and osmo_mgcpc_ep_ci_add() instead");
|
||||
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
|
||||
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci_request() instead");
|
||||
void mgcp_conn_delete(struct osmo_fsm_inst *fi)
|
||||
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci_dlcx() instead");
|
||||
|
||||
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
|
||||
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi)
|
||||
OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci.mgcp_ci_str instead");
|
||||
struct mgcp_client *mgcp_conn_get_client(struct osmo_fsm_inst *fi);
|
||||
|
||||
const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/osmo_io.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_wqueue wq;
|
||||
struct osmo_io_fd *iofd;
|
||||
mgcp_trans_id_t next_trans_id;
|
||||
struct llist_head responses_pending;
|
||||
struct mgcp_client_pool_member *pool_member;
|
||||
@@ -22,6 +22,30 @@ struct mgcp_client {
|
||||
bool conn_up;
|
||||
};
|
||||
|
||||
struct mgcp_response_head {
|
||||
int response_code;
|
||||
mgcp_trans_id_t trans_id;
|
||||
char comment[MGCP_COMMENT_MAXLEN];
|
||||
char conn_id[MGCP_CONN_ID_MAXLEN];
|
||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
bool x_osmo_osmux_use;
|
||||
uint8_t x_osmo_osmux_cid;
|
||||
};
|
||||
|
||||
struct mgcp_response {
|
||||
char *body;
|
||||
struct mgcp_response_head head;
|
||||
uint16_t audio_port;
|
||||
char audio_ip[INET6_ADDRSTRLEN];
|
||||
unsigned int ptime;
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
};
|
||||
|
||||
/* Invoked when an MGCP response is received or sending failed. When the
|
||||
* response is passed as NULL, this indicates failure during transmission. */
|
||||
typedef void (*mgcp_response_cb_t)(struct mgcp_response *response, void *priv);
|
||||
|
||||
struct mgcp_response_pending {
|
||||
struct llist_head entry;
|
||||
|
||||
@@ -37,3 +61,41 @@ struct mgcp_response_pending * mgcp_client_pending_add(
|
||||
mgcp_trans_id_t trans_id,
|
||||
mgcp_response_cb_t response_cb,
|
||||
void *priv);
|
||||
|
||||
#define MGCP_MSG_PRESENCE_ENDPOINT 0x0001
|
||||
#define MGCP_MSG_PRESENCE_CALL_ID 0x0002
|
||||
#define MGCP_MSG_PRESENCE_CONN_ID 0x0004
|
||||
#define MGCP_MSG_PRESENCE_AUDIO_IP 0x0008
|
||||
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
|
||||
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
|
||||
#define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000
|
||||
#define MGCP_MSG_PRESENCE_X_OSMO_IGN 0x8000
|
||||
|
||||
struct mgcp_msg {
|
||||
enum mgcp_verb verb;
|
||||
/* See MGCP_MSG_PRESENCE_* constants */
|
||||
uint32_t presence;
|
||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
unsigned int call_id;
|
||||
char *conn_id;
|
||||
uint16_t audio_port;
|
||||
char *audio_ip;
|
||||
enum mgcp_connection_mode conn_mode;
|
||||
unsigned int ptime;
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
uint32_t x_osmo_ign;
|
||||
bool x_osmo_osmux_use;
|
||||
int x_osmo_osmux_cid; /* -1 is wildcard */
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
};
|
||||
|
||||
int mgcp_response_parse_params(struct mgcp_response *r);
|
||||
|
||||
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
mgcp_response_cb_t response_cb, void *priv);
|
||||
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
|
||||
|
||||
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
|
||||
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
|
||||
|
||||
@@ -302,7 +302,7 @@ static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
|
||||
char *pt_str;
|
||||
char *pt_end;
|
||||
unsigned long int pt;
|
||||
unsigned int count = 0;
|
||||
unsigned int ptmap_len;
|
||||
unsigned int i;
|
||||
|
||||
/* Extract port information */
|
||||
@@ -316,40 +316,58 @@ static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
|
||||
if (!line)
|
||||
goto exit;
|
||||
|
||||
/* Clear any previous entries before writing over r->ptmap */
|
||||
r->ptmap_len = 0;
|
||||
/* Keep a local ptmap_len to show only the full list after parsing succeeded in whole. */
|
||||
ptmap_len = 0;
|
||||
|
||||
pt_str = strtok(line, " ");
|
||||
while (1) {
|
||||
/* Do not allow excessive payload types */
|
||||
if (count >= ARRAY_SIZE(r->codecs))
|
||||
goto response_parse_failure_pt;
|
||||
|
||||
pt_str = strtok(NULL, " ");
|
||||
if (!pt_str)
|
||||
break;
|
||||
errno = 0;
|
||||
pt = strtoul(pt_str, &pt_end, 0);
|
||||
if ((errno == ERANGE && pt == ULONG_MAX) || (errno && !pt) ||
|
||||
pt_str == pt_end)
|
||||
pt_str == pt_end) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "SDP: cannot parse payload type number from '%s'\n", pt_str);
|
||||
goto response_parse_failure_pt;
|
||||
}
|
||||
|
||||
if (pt >> 7) /* PT is 7 bit field, higher values not allowed */
|
||||
/* PT is 7 bit field, higher values not allowed */
|
||||
if (pt >> 7) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "SDP: payload type number out of range: %lu > 127\n", pt);
|
||||
goto response_parse_failure_pt;
|
||||
}
|
||||
|
||||
/* Do not allow duplicate payload types */
|
||||
for (i = 0; i < count; i++)
|
||||
if (r->codecs[i] == pt)
|
||||
for (i = 0; i < ptmap_len; i++) {
|
||||
if (r->ptmap[i].pt == pt) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "SDP: payload type number %lu listed twice\n", pt);
|
||||
goto response_parse_failure_pt;
|
||||
}
|
||||
}
|
||||
|
||||
/* Note: The payload type we store may not necessarly match
|
||||
* the codec types we have defined in enum mgcp_codecs. To
|
||||
* ensure that the end result only contains codec types which
|
||||
* match enum mgcp_codecs, we will go through afterwards and
|
||||
* remap the affected entries with the inrofmation we learn
|
||||
* from rtpmap */
|
||||
r->codecs[count] = pt;
|
||||
count++;
|
||||
/* Do not allow excessive payload types */
|
||||
if (ptmap_len >= ARRAY_SIZE(r->ptmap)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"SDP: can parse only up to %zu payload type numbers\n", ARRAY_SIZE(r->ptmap));
|
||||
goto response_parse_failure_pt;
|
||||
}
|
||||
|
||||
/* Some payload type numbers imply a specific codec. For those, using the PT number as enum mgcp_codecs
|
||||
* yields the correct result. If no more specific information on the codec follows in "a=rtpmap:N"
|
||||
* lines, then this default number takes over. This only applies for PT below the dynamic range (<96). */
|
||||
if (pt < 96)
|
||||
r->ptmap[ptmap_len].codec = pt;
|
||||
else
|
||||
r->ptmap[ptmap_len].codec = -1;
|
||||
r->ptmap[ptmap_len].pt = pt;
|
||||
ptmap_len++;
|
||||
}
|
||||
|
||||
r->codecs_len = count;
|
||||
/* Parsing succeeded, publish all entries. */
|
||||
r->ptmap_len = ptmap_len;
|
||||
|
||||
exit:
|
||||
return 0;
|
||||
@@ -365,10 +383,11 @@ response_parse_failure_pt:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
|
||||
/* Parse an 'a=...' parameter */
|
||||
static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *line)
|
||||
{
|
||||
unsigned int pt;
|
||||
unsigned int i;
|
||||
char codec_resp[64];
|
||||
int rc;
|
||||
|
||||
@@ -387,18 +406,39 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
|
||||
"Failed to parse SDP parameter, invalid rtpmap: %s\n", osmo_quote_str(line, -1));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r->ptmap_len >= ARRAY_SIZE(r->ptmap)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "No more space in ptmap array (len=%u)\n", r->ptmap_len);
|
||||
return -ENOSPC;
|
||||
}
|
||||
rc = map_str_to_codec(codec_resp);
|
||||
if (rc < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse SDP parameter, can't parse codec in rtpmap: %s\n", osmo_quote_str(line, -1));
|
||||
return -EINVAL;
|
||||
}
|
||||
r->ptmap[r->ptmap_len].pt = pt;
|
||||
r->ptmap[r->ptmap_len].codec = rc;
|
||||
|
||||
/* Earlier, a line like "m=audio 16002 RTP/AVP 98 112 3" established the desired order of payloads, now
|
||||
* enrich it with actual codec information provided by "a=rtpmap:..." entries.
|
||||
* For each, find the entry with the right pt number and add the info there. */
|
||||
|
||||
for (i = 0; i < r->ptmap_len; i++) {
|
||||
if (r->ptmap[i].pt != pt)
|
||||
continue;
|
||||
r->ptmap[i].codec = rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* No entry was found. This is an error in the MGCP protocol, but let's just add another entry
|
||||
* anyway, to not make it look like it was never there. */
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"error in MGCP message: 'a=rtpmap:%u' has no matching entry in 'm=audio ... %u'\n",
|
||||
pt, pt);
|
||||
if (r->ptmap_len >= ARRAY_SIZE(r->ptmap)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"cannot parse all codecs: can only store up to %zu rtpmap entries.\n",
|
||||
ARRAY_SIZE(r->ptmap));
|
||||
return -ENOSPC;
|
||||
}
|
||||
r->ptmap[r->ptmap_len] = (struct ptmap){
|
||||
.pt = pt,
|
||||
.codec = rc,
|
||||
};
|
||||
r->ptmap_len++;
|
||||
}
|
||||
|
||||
@@ -508,7 +548,6 @@ int mgcp_response_parse_params(struct mgcp_response *r)
|
||||
int rc;
|
||||
char *data;
|
||||
char *data_ptr;
|
||||
int i;
|
||||
|
||||
/* Since this functions performs a destructive parsing, we create a
|
||||
* local copy of the body data */
|
||||
@@ -553,10 +592,6 @@ int mgcp_response_parse_params(struct mgcp_response *r)
|
||||
}
|
||||
}
|
||||
|
||||
/* See also note in mgcp_parse_audio_port_pt() */
|
||||
for (i = 0; i < r->codecs_len; i++)
|
||||
r->codecs[i] = map_pt_to_codec(r->ptmap, r->ptmap_len, r->codecs[i]);
|
||||
|
||||
rc = 0;
|
||||
exit:
|
||||
talloc_free(data);
|
||||
@@ -693,7 +728,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_do_read that reads from the socket connected to the MGCP gateway. This
|
||||
* mgcp_read_cb 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)
|
||||
@@ -746,54 +781,54 @@ error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int mgcp_do_read(struct osmo_fd *fd)
|
||||
static void mgcp_read_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg)
|
||||
{
|
||||
struct mgcp_client *mgcp = fd->data;
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
struct mgcp_client *mgcp = osmo_iofd_get_data(iofd);
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "mgcp_from_gw");
|
||||
if (!msg) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed to allocate MGCP message.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = read(fd->fd, msg->data, 4096 - 128);
|
||||
if (ret <= 0) {
|
||||
if (res <= 0) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed to read: %s: %d='%s'\n",
|
||||
osmo_sock_get_name2(fd->fd), errno, strerror(errno));
|
||||
osmo_iofd_get_name(iofd), res, strerror(res));
|
||||
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
msg->l2h = msgb_put(msg, ret);
|
||||
ret = mgcp_client_rx(mgcp, msg);
|
||||
msg->l2h = msg->head;
|
||||
mgcp_client_rx(mgcp, msg);
|
||||
talloc_free(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
|
||||
static int mgcp_do_write(struct mgcp_client *mgcp, struct msgb *msg)
|
||||
{
|
||||
int ret;
|
||||
struct mgcp_client *mgcp = fd->data;
|
||||
|
||||
LOGPMGW(mgcp, LOGL_DEBUG, "Tx MGCP: %s: len=%u '%s'...\n",
|
||||
osmo_sock_get_name2(fd->fd), msg->len,
|
||||
osmo_iofd_get_name(mgcp->iofd), msg->len,
|
||||
osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
|
||||
|
||||
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)));
|
||||
ret = osmo_iofd_write_msgb(mgcp->iofd, msg);
|
||||
if (ret < 0)
|
||||
msgb_free(msg);
|
||||
|
||||
/* 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];
|
||||
@@ -905,11 +940,6 @@ 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);
|
||||
@@ -917,6 +947,11 @@ 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. */
|
||||
@@ -932,19 +967,28 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
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_close_fd;
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
LOGPMGW(mgcp, LOGL_INFO, "MGW connection: %s\n", osmo_sock_get_name2(mgcp->wq.bfd.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);
|
||||
|
||||
/* If configured, send a DLCX message to the endpoints that are configured to
|
||||
* be reset on startup. Usually this is a wildcarded endpoint. */
|
||||
@@ -970,9 +1014,10 @@ 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(mgcp->wq.bfd.fd);
|
||||
mgcp->wq.bfd.fd = -1;
|
||||
close(rc);
|
||||
error_free:
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -989,8 +1034,6 @@ 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;
|
||||
@@ -1001,13 +1044,9 @@ void mgcp_client_disconnect(struct mgcp_client *mgcp)
|
||||
osmo_timer_del(&mgcp->keepalive_tx_timer);
|
||||
mgcp->conn_up = false;
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
/*! Get the IP-Aaddress of the associated MGW as string.
|
||||
@@ -1161,10 +1200,9 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
goto mgcp_tx_error;
|
||||
}
|
||||
|
||||
rc = osmo_wqueue_enqueue(&mgcp->wq, msg);
|
||||
rc = mgcp_do_write(mgcp, 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",
|
||||
@@ -1233,7 +1271,6 @@ static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
|
||||
{
|
||||
unsigned int i;
|
||||
const char *codec;
|
||||
unsigned int pt;
|
||||
|
||||
#define MSGB_PRINTF_OR_RET(FMT, ARGS...) do { \
|
||||
if (msgb_printf(msg, FMT, ##ARGS) != 0) { \
|
||||
@@ -1247,11 +1284,10 @@ static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
|
||||
if (mgcp_msg->ptime)
|
||||
MSGB_PRINTF_OR_RET(" p:%u,", mgcp_msg->ptime);
|
||||
|
||||
if (mgcp_msg->codecs_len) {
|
||||
if (mgcp_msg->ptmap_len) {
|
||||
MSGB_PRINTF_OR_RET(" a:");
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
pt = mgcp_msg->codecs[i];
|
||||
codec = get_value_string_or_null(osmo_mgcpc_codec_names, pt);
|
||||
for (i = 0; i < mgcp_msg->ptmap_len; i++) {
|
||||
codec = get_value_string_or_null(osmo_mgcpc_codec_names, mgcp_msg->ptmap[i].codec);
|
||||
|
||||
/* Note: Use codec descriptors from enum mgcp_codecs
|
||||
* in mgcp_client only! */
|
||||
@@ -1259,7 +1295,7 @@ static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
|
||||
return -EINVAL;
|
||||
|
||||
MSGB_PRINTF_OR_RET("%s", extract_codec_name(codec));
|
||||
if (i < mgcp_msg->codecs_len - 1)
|
||||
if (i < mgcp_msg->ptmap_len - 1)
|
||||
MSGB_PRINTF_OR_RET(";");
|
||||
}
|
||||
MSGB_PRINTF_OR_RET(",");
|
||||
@@ -1279,6 +1315,7 @@ 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) { \
|
||||
@@ -1302,9 +1339,6 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
|
||||
local_ip_family = osmo_ip_str_type(local_ip);
|
||||
if (local_ip_family == AF_UNSPEC)
|
||||
return -EINVAL;
|
||||
audio_ip_family = osmo_ip_str_type(mgcp_msg->audio_ip);
|
||||
if (audio_ip_family == AF_UNSPEC)
|
||||
return -EINVAL;
|
||||
|
||||
/* Add owner/creator (SDP) */
|
||||
MSGB_PRINTF_OR_RET("o=- %x 23 IN IP%c %s\r\n", mgcp_msg->call_id,
|
||||
@@ -1314,41 +1348,47 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
|
||||
/* Add session name (none) */
|
||||
MSGB_PRINTF_OR_RET("s=-\r\n");
|
||||
|
||||
/* Add RTP address and port */
|
||||
if (mgcp_msg->audio_port == 0) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Invalid port number, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
/* Add RTP address */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP) {
|
||||
audio_ip_family = osmo_ip_str_type(mgcp_msg->audio_ip);
|
||||
if (audio_ip_family == AF_UNSPEC)
|
||||
return -EINVAL;
|
||||
if (strlen(mgcp_msg->audio_ip) <= 0) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Empty ip address, can not generate MGCP message\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
MSGB_PRINTF_OR_RET("c=IN IP%c %s\r\n",
|
||||
audio_ip_family == AF_INET6 ? '6' : '4',
|
||||
mgcp_msg->audio_ip);
|
||||
}
|
||||
if (strlen(mgcp_msg->audio_ip) <= 0) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Empty ip address, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
MSGB_PRINTF_OR_RET("c=IN IP%c %s\r\n",
|
||||
audio_ip_family == AF_INET6 ? '6' : '4',
|
||||
mgcp_msg->audio_ip);
|
||||
|
||||
/* Add time description, active time (SDP) */
|
||||
MSGB_PRINTF_OR_RET("t=0 0\r\n");
|
||||
|
||||
MSGB_PRINTF_OR_RET("m=audio %u RTP/AVP", mgcp_msg->audio_port);
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
||||
MSGB_PRINTF_OR_RET(" %u", pt);
|
||||
|
||||
/* 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) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Invalid port number, can not generate MGCP message\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
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) */
|
||||
if (mgcp_msg->param_present) {
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
for (i = 0; i < mgcp_msg->ptmap_len; i++) {
|
||||
/* The following is only applicable for AMR */
|
||||
if (mgcp_msg->codecs[i] != CODEC_AMR_8000_1 && mgcp_msg->codecs[i] != CODEC_AMRWB_16000_1)
|
||||
continue;
|
||||
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
||||
if (mgcp_msg->ptmap[i].codec != CODEC_AMR_8000_1
|
||||
&& mgcp_msg->ptmap[i].codec != CODEC_AMRWB_16000_1)
|
||||
continue;
|
||||
pt = mgcp_msg->ptmap[i].pt;
|
||||
if (mgcp_msg->param.amr_octet_aligned_present && mgcp_msg->param.amr_octet_aligned)
|
||||
MSGB_PRINTF_OR_RET("a=fmtp:%u octet-align=1\r\n", pt);
|
||||
else if (mgcp_msg->param.amr_octet_aligned_present && !mgcp_msg->param.amr_octet_aligned)
|
||||
@@ -1356,14 +1396,14 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
||||
for (i = 0; i < mgcp_msg->ptmap_len; i++) {
|
||||
pt = mgcp_msg->ptmap[i].pt;
|
||||
|
||||
/* Note: Only dynamic payload type from the range 96-127
|
||||
* require to be explained further via rtpmap. All others
|
||||
* are implcitly definedby the number in m=audio */
|
||||
if (pt >= 96 && pt <= 127) {
|
||||
codec = get_value_string_or_null(osmo_mgcpc_codec_names, mgcp_msg->codecs[i]);
|
||||
codec = get_value_string_or_null(osmo_mgcpc_codec_names, mgcp_msg->ptmap[i].codec);
|
||||
|
||||
/* Note: Use codec descriptors from enum mgcp_codecs
|
||||
* in mgcp_client only! */
|
||||
@@ -1470,10 +1510,10 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
MSGB_PRINTF_OR_RET("I: %s\r\n", mgcp_msg->conn_id);
|
||||
}
|
||||
|
||||
/* Using SDP makes sense when a valid IP/Port combination is specified,
|
||||
/* Using SDP makes sense when a valid IP or Port is specified,
|
||||
* if we do not know this information yet, we fall back to LCO */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
|
||||
&& mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT)
|
||||
|| mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT)
|
||||
use_sdp = true;
|
||||
|
||||
/* Add local connection options (LCO) */
|
||||
@@ -1571,3 +1611,20 @@ const char *mgcp_client_name(const struct mgcp_client *mgcp)
|
||||
else
|
||||
return mgcp_client_endpoint_domain(mgcp);
|
||||
}
|
||||
|
||||
/*! Return typical cmp result, comparing a to b.
|
||||
* Return 0 if a == b, -1 if a < b, 1 if a > b; comparing all members of ptmap in turn. */
|
||||
int ptmap_cmp(const struct ptmap *a, const struct ptmap *b)
|
||||
{
|
||||
int rc;
|
||||
if (a == b)
|
||||
return 0;
|
||||
if (!a)
|
||||
return -1;
|
||||
if (!b)
|
||||
return 1;
|
||||
rc = OSMO_CMP(a->codec, b->codec);
|
||||
if (rc)
|
||||
return rc;
|
||||
return OSMO_CMP(a->pt, b->pt);
|
||||
}
|
||||
|
||||
@@ -533,10 +533,42 @@ static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data)
|
||||
mgcp_conn_peer_name(ci->got_port_info? &ci->rtp_info : NULL),
|
||||
ci->notify.fi ? "" : " (not sending a notification)");
|
||||
|
||||
/* Below ordering is a delicate decision:
|
||||
*
|
||||
* We want to
|
||||
* - emit the resulting event to ci->notify.fi,
|
||||
* - check whether we want to tx the next pending MGCP message.
|
||||
* Both these steps may terminate (=deallocate) the ep.
|
||||
* So whichever one goes first may cause a use-after-free in the other.
|
||||
*
|
||||
* When dispatching the FSM event, we don't get an rc indicating dealloc of the FSM -- it may deallocate and we
|
||||
* cannot tell. The common mechanism for that is osmo_fsm_set_dealloc_ctx(OTC_SELECT) and query the still
|
||||
* allocated FSM state after termination (here we would check 'if (ci->ep != NULL)'), but we cannot assume the
|
||||
* caller has actually set up an osmo_fsm_set_dealloc_ctx(). At time of writing, e.g. osmo-hnbgw does not use
|
||||
* it.
|
||||
*
|
||||
* In osmo_mgcpc_ep_fsm_check_state_chg_after_response(), we do get an rc: false means FSM has terminated.
|
||||
* On termination, the ep emits a term event to the FSM's parent.
|
||||
* That may cause the notify.fi to be terminated in turn, depending on how the caller set things up.
|
||||
* So: we cannot store notify.fi before, then call osmo_mgcpc_ep_fsm_check_state_chg_after_response(), and then
|
||||
* emit the event, because notify.fi may have deallocated. We cannot look up whether
|
||||
* osmo_mgcpc_ep_cancel_notify() has been called, because ci may have deallocated along with ci->ep.
|
||||
*
|
||||
* We have to skip emitting below success event in case the ep is now terminated.
|
||||
* - It may be the final DLCX OK: not a problem, osmo_mgcpc_ep_ci_dlcx() has no notify args on purpose, so we do
|
||||
* make all callers not set a notify event for DLCX by design. notify.fi should always be NULL when the final
|
||||
* DLCX OK terminates the local endpoint state.
|
||||
* - It may also be sudden termination due to a bad problem, in which case we shouldn't emit success.
|
||||
* The osmo_fsm_inst.parent_term_event should suffice as feedback to the caller.
|
||||
*/
|
||||
|
||||
if (osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi) == false) {
|
||||
/* false means, the ci->ep has been terminated. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (ci->notify.fi)
|
||||
osmo_fsm_inst_dispatch(ci->notify.fi, ci->notify.success, ci->notify.data);
|
||||
|
||||
osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi);
|
||||
}
|
||||
|
||||
/*! Return the MGW's local RTP port information for this connection, i.e. the local port that MGW is receiving on, as
|
||||
@@ -686,11 +718,11 @@ void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
|
||||
osmo_strlcpy(cleared_ci.mgcp_ci_str, ci->mgcp_ci_str, sizeof(cleared_ci.mgcp_ci_str));
|
||||
*ci = cleared_ci;
|
||||
|
||||
LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify.fi));
|
||||
|
||||
if (verb_info)
|
||||
ci->verb_info = *verb_info;
|
||||
|
||||
LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify.fi));
|
||||
|
||||
if (ep->endpoint[0]) {
|
||||
if (ci->verb_info.endpoint[0] && strcmp(ci->verb_info.endpoint, ep->endpoint))
|
||||
LOG_CI(ci, LOGL_ERROR,
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_fsm.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
@@ -114,12 +115,10 @@ static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info
|
||||
.call_id = info->call_id,
|
||||
.conn_mode = MGCP_CONN_RECV_ONLY,
|
||||
.ptime = info->ptime,
|
||||
.codecs_len = info->codecs_len,
|
||||
.ptmap_len = info->ptmap_len,
|
||||
.param_present = info->param_present
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg->endpoint, info->endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
memcpy(mgcp_msg->codecs, info->codecs, sizeof(mgcp_msg->codecs));
|
||||
memcpy(mgcp_msg->ptmap, info->ptmap, sizeof(mgcp_msg->ptmap));
|
||||
memcpy(&mgcp_msg->param, &info->param, sizeof(mgcp_msg->param));
|
||||
|
||||
@@ -136,10 +135,19 @@ static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info
|
||||
|
||||
static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
|
||||
{
|
||||
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT;
|
||||
mgcp_msg->audio_ip = info->addr;
|
||||
mgcp_msg->audio_port = info->port;
|
||||
mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
|
||||
bool ip_is_set = info->addr[0] != '\0' &&
|
||||
strncmp(info->addr, "::", sizeof(info->addr)) != 0 &&
|
||||
strncmp(info->addr, "0.0.0.0", sizeof(info->addr)) != 0;
|
||||
if (ip_is_set) {
|
||||
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP;
|
||||
mgcp_msg->audio_ip = info->addr;
|
||||
}
|
||||
if (info->port) {
|
||||
mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_PORT;
|
||||
mgcp_msg->audio_port = info->port;
|
||||
}
|
||||
if (ip_is_set && info->port)
|
||||
mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
|
||||
}
|
||||
|
||||
static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer)
|
||||
@@ -163,12 +171,10 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
.audio_ip = mgcp_ctx->conn_peer_local.addr,
|
||||
.audio_port = mgcp_ctx->conn_peer_local.port,
|
||||
.ptime = mgcp_ctx->conn_peer_local.ptime,
|
||||
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len,
|
||||
.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len,
|
||||
.param_present = mgcp_ctx->conn_peer_local.param_present
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
|
||||
memcpy(mgcp_msg.ptmap, mgcp_ctx->conn_peer_local.ptmap, sizeof(mgcp_msg.ptmap));
|
||||
memcpy(&mgcp_msg.param, &mgcp_ctx->conn_peer_local.param, sizeof(mgcp_ctx->conn_peer_local.param));
|
||||
|
||||
@@ -221,8 +227,7 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
mgcp_ctx->conn_peer_local.endpoint);
|
||||
|
||||
make_crcx_msg(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
if (mgcp_ctx->conn_peer_local.port)
|
||||
add_audio(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
add_audio(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
|
||||
msg = mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
@@ -619,6 +624,72 @@ static struct osmo_fsm fsm_mgcp_client = {
|
||||
.log_subsys = DLMGCP,
|
||||
};
|
||||
|
||||
/* Provide backwards compat for deprecated conn_peer->codecs[]: when the caller passes in an mgcp_conn_peer instance
|
||||
* that has codecs[] set, apply it to ptmap[] instead. */
|
||||
static void mgcp_conn_peer_compat(struct mgcp_conn_peer *conn_peer)
|
||||
{
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
|
||||
if (!conn_peer->codecs_len)
|
||||
return;
|
||||
|
||||
/* Before dropping codecs[], codecs[] would indicate the order in which the codecs should appear in SDP. ptmap[]
|
||||
* would indicate payload type numbers when not using a default payload type number (may omit entries).
|
||||
* Now, ptmap[] just indicates both at the same time; codecs[] should be empty, and ptmap[] lists all codecs.
|
||||
* So if any codecs[] are present, recreate ptmap[] in the order of codecs[]. */
|
||||
|
||||
ptmap_len = 0;
|
||||
for (int i = 0; i < conn_peer->codecs_len; i++) {
|
||||
enum mgcp_codecs codec = conn_peer->codecs[i];
|
||||
struct ptmap *found = NULL;
|
||||
|
||||
/* Look up whether a specific pt was indicated for this codec */
|
||||
for (int p = 0; p < conn_peer->ptmap_len; p++) {
|
||||
if (conn_peer->ptmap[p].codec != codec)
|
||||
continue;
|
||||
found = &conn_peer->ptmap[p];
|
||||
break;
|
||||
}
|
||||
if (found) {
|
||||
ptmap[ptmap_len] = *found;
|
||||
} else {
|
||||
ptmap[ptmap_len] = (struct ptmap){
|
||||
.codec = codec,
|
||||
/* some enum mgcp_codecs correspond to their standard PT nr, so for compat: */
|
||||
.pt = codec,
|
||||
};
|
||||
}
|
||||
ptmap_len++;
|
||||
}
|
||||
|
||||
/* Are there any entries in the old ptmap that were omitted by codecs[]? */
|
||||
for (int p = 0; p < conn_peer->ptmap_len; p++) {
|
||||
bool exists = false;
|
||||
for (int i = 0; i < ptmap_len; i++) {
|
||||
if (ptmap_cmp(&ptmap[i], &conn_peer->ptmap[p]))
|
||||
continue;
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (exists)
|
||||
continue;
|
||||
|
||||
if (ptmap_len >= ARRAY_SIZE(ptmap))
|
||||
break;
|
||||
|
||||
/* Not present yet, add it to the end */
|
||||
ptmap[ptmap_len] = conn_peer->ptmap[p];
|
||||
ptmap_len++;
|
||||
}
|
||||
|
||||
/* Use the new ptmap[], and clear out legacy codecs[]. */
|
||||
memcpy(conn_peer->ptmap, ptmap, sizeof(conn_peer->ptmap));
|
||||
conn_peer->ptmap_len = ptmap_len;
|
||||
conn_peer->codecs_len = 0;
|
||||
}
|
||||
|
||||
/*! allocate FSM, and create a new connection on the MGW.
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \param[in] parent_fi Parent FSM instance.
|
||||
@@ -633,6 +704,7 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct in6_addr ip_test;
|
||||
|
||||
mgcp_conn_peer_compat(conn_peer);
|
||||
|
||||
OSMO_ASSERT(parent_fi);
|
||||
OSMO_ASSERT(mgcp);
|
||||
@@ -672,6 +744,8 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
|
||||
struct mgcp_ctx *mgcp_ctx = fi->priv;
|
||||
struct in6_addr ip_test;
|
||||
|
||||
mgcp_conn_peer_compat(conn_peer);
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
OSMO_ASSERT(conn_peer);
|
||||
|
||||
|
||||
@@ -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->wq.bfd.fd != -1) { /* UDP MGCP socket connected */
|
||||
if (mgcp->iofd) { /* UDP MGCP socket connected */
|
||||
if (mgcp->actual.keepalive.req_interval_sec > 0) {
|
||||
/* Re-schedule: */
|
||||
osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0);
|
||||
@@ -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->wq.bfd.fd != -1) { /* UDP MGCP socket connected */
|
||||
if (mgcp->iofd) { /* UDP MGCP socket connected */
|
||||
if (mgcp->actual.keepalive.timeout_sec > 0) {
|
||||
/* Re-schedule: */
|
||||
osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0);
|
||||
@@ -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->wq.bfd.fd != -1 ? "connected" : "disconnected",
|
||||
cli && cli->iofd ? "connected" : "disconnected",
|
||||
cli && cli->conn_up ?
|
||||
((cli->actual.keepalive.timeout_sec > 0) ? "UP" : "MAYBE") :
|
||||
"DOWN",
|
||||
|
||||
@@ -431,13 +431,13 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn
|
||||
* of a match set this codec on both connections. This would be an ideal selection since no codec conversion would be
|
||||
* required. */
|
||||
for (i = 0; i < conn_src->end.codecs_assigned; i++) {
|
||||
struct mgcp_rtp_codec *codec_conn_dst = mgcp_codec_find_same(conn_dst, &conn_src->end.codecs[i]);
|
||||
struct mgcp_rtp_codec *codec_conn_src = &conn_src->end.codecs[i];
|
||||
struct mgcp_rtp_codec *codec_conn_dst = mgcp_codec_find_same(conn_dst, codec_conn_src);
|
||||
if (codec_conn_dst) {
|
||||
/* We found the a codec that is exactly the same (same codec, same payload format etc.) on both
|
||||
* sides. We now set this codec on both connections. */
|
||||
conn_dst->end.codec = codec_conn_dst;
|
||||
conn_src->end.codec = mgcp_codec_find_same(conn_src, codec_conn_dst);
|
||||
OSMO_ASSERT(conn_src->end.codec);
|
||||
conn_src->end.codec = codec_conn_src;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -445,18 +445,27 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn
|
||||
/* In case we could not find a codec that is exactly the same, let's at least try to find a codec that we are able
|
||||
* to convert. */
|
||||
for (i = 0; i < conn_src->end.codecs_assigned; i++) {
|
||||
struct mgcp_rtp_codec *codec_conn_dst = codec_find_convertible(conn_dst, &conn_src->end.codecs[i]);
|
||||
struct mgcp_rtp_codec *codec_conn_src = &conn_src->end.codecs[i];
|
||||
struct mgcp_rtp_codec *codec_conn_dst = codec_find_convertible(conn_dst, codec_conn_src);
|
||||
if (codec_conn_dst) {
|
||||
/* We found the a codec that we are able to convert on both sides. We now set this codec on both
|
||||
* connections. */
|
||||
/* We found the a codec that we can convert to. Set each side to its codec. */
|
||||
conn_dst->end.codec = codec_conn_dst;
|
||||
conn_src->end.codec = codec_find_convertible(conn_src, codec_conn_dst);
|
||||
OSMO_ASSERT(conn_src->end.codec);
|
||||
conn_src->end.codec = codec_conn_src;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
if (conn_dst->end.codecs_assigned)
|
||||
conn_dst->end.codec = &conn_dst->end.codecs[0];
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (conn_src->end.codecs_assigned)
|
||||
conn_src->end.codec = &conn_src->end.codecs[0];
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if the codec has a specific AMR mode (octet-aligned or bandwith-efficient) set. */
|
||||
|
||||
@@ -106,12 +106,10 @@ 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.fd = -1;
|
||||
end->rtcp.fd = -1;
|
||||
end->rtp = NULL;
|
||||
end->rtcp = NULL;
|
||||
memset(&end->addr, 0, sizeof(end->addr));
|
||||
end->rtcp_port = 0;
|
||||
talloc_free(end->fmtp_extra);
|
||||
end->fmtp_extra = NULL;
|
||||
|
||||
/* Set default values */
|
||||
end->frames_per_packet = 0; /* unknown */
|
||||
|
||||
@@ -301,7 +301,6 @@ 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));
|
||||
|
||||
@@ -311,7 +311,6 @@ 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;
|
||||
}
|
||||
|
||||
@@ -469,7 +468,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 len;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(conn_src);
|
||||
OSMO_ASSERT(conn_dst);
|
||||
@@ -506,6 +505,7 @@ 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,19 +513,17 @@ 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.fd, &conn_src->tap_out,
|
||||
msg);
|
||||
forward_data_tap(rtp_end->rtp, &conn_src->tap_out, msg);
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, (char *)hdr, buflen);
|
||||
rc = mgcp_udp_send(rtp_end->rtp, &rtp_end->addr, (char *)hdr, buflen);
|
||||
|
||||
if (len <= 0)
|
||||
return len;
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_PACKETS_TX_CTR, 1);
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
|
||||
rtp_state->alt_rtp_tx_sequence++;
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, buflen);
|
||||
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Received TNL primitive from IuUP layer FSM, transmit it further down to the
|
||||
@@ -640,7 +638,7 @@ free_ret:
|
||||
}
|
||||
|
||||
/* Build IuUP RNL Data primitive from msg containing an incoming RTP pkt from
|
||||
* peer and send it down the IuUP layer towards the destination as IuUP/RTP: */
|
||||
* peer and send it down the IuUP layer towards the destination as IuUP/RTP. Takes ownership of msg. */
|
||||
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;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
/*
|
||||
* (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
|
||||
@@ -70,6 +71,18 @@ 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)
|
||||
@@ -404,15 +417,12 @@ 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] pointer to buffer with voice data.
|
||||
* \param[in] voice data length.
|
||||
* \param[in] maximum size of caller provided voice data buffer.
|
||||
* \param[in,out] msg message bufffer containing data. Function might change length.
|
||||
* \returns ignores input parameters, return always 0. */
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size)
|
||||
struct msgb *msg)
|
||||
{
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "transcoding disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -429,18 +439,6 @@ int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
const struct mgcp_rtp_codec **codec,
|
||||
const char **fmtp_extra,
|
||||
struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "conn:%s using format defaults\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
|
||||
*codec = conn->end.codec;
|
||||
*fmtp_extra = conn->end.fmtp_extra;
|
||||
}
|
||||
|
||||
void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state, const uint16_t seq,
|
||||
const int32_t transit, const uint32_t ssrc,
|
||||
@@ -797,16 +795,18 @@ 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(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
|
||||
void forward_data_tap(struct osmo_io_fd *iofd, struct mgcp_rtp_tap *tap, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!tap->enabled)
|
||||
return;
|
||||
|
||||
rc = sendto(fd, msgb_data(msg), msgb_length(msg), 0, (struct sockaddr *)&tap->forward,
|
||||
sizeof(tap->forward));
|
||||
struct msgb *msg2 = msgb_copy(msg, "RTP TAP Tx");
|
||||
if (!msg2)
|
||||
return;
|
||||
|
||||
rc = osmo_iofd_sendto_msgb(iofd, msg2, 0, &tap->forward);
|
||||
if (rc < 0)
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"Forwarding tapped (debug) voice data failed.\n");
|
||||
@@ -834,32 +834,27 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
|
||||
{
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
|
||||
if (osmo_sockaddr_is_any(&conn->end.addr) != 0) {
|
||||
switch (conn->conn->mode) {
|
||||
case MGCP_CONN_LOOPBACK:
|
||||
/* HACK: for IuUP, we want to reply with an IuUP Initialization ACK upon the first RTP
|
||||
* message received. We currently hackishly accomplish that by putting the endpoint in
|
||||
* loopback mode and patching over the looped back RTP message to make it look like an
|
||||
* ack. We don't know the femto cell's IP address and port until the RAB Assignment
|
||||
* Response is received, but the nano3G expects an IuUP Initialization Ack before it even
|
||||
* sends the RAB Assignment Response. Hence, if the remote address is 0.0.0.0 and the
|
||||
* MGCP port is in loopback mode, allow looping back the packet to any source. */
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"In loopback mode and remote address not set:"
|
||||
" allowing data from address: %s\n",
|
||||
osmo_sockaddr_ntop(&addr->u.sa, ipbuf));
|
||||
return 0;
|
||||
|
||||
default:
|
||||
/* Receiving early media before the endpoint is configured. Instead of logging
|
||||
* this as an error that occurs on every call, keep it more low profile to not
|
||||
* confuse humans with expected errors. */
|
||||
if (osmo_sockaddr_is_any(&conn->end.addr) != 0 ||
|
||||
osmo_sockaddr_port(&conn->end.addr.u.sa) == 0) {
|
||||
if (mgcp_conn_rtp_is_iuup(conn) && !conn->iuup.configured) {
|
||||
/* Allow IuUP Initialization to get through even if we don't have a remote address set yet.
|
||||
* This is needed because hNodeB doesn't announce its IuUP remote IP addr to the MGCP client
|
||||
* (RAB Assignment Response at HNBGW) until it has gone through IuUP Initialization against
|
||||
* this MGW here. Hence the MGW may not yet know the remote IuUP address and port at the time
|
||||
* of receiving IuUP Initialization from the hNodeB.
|
||||
*/
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_INFO,
|
||||
"Rx RTP from %s, but remote address not set:"
|
||||
" dropping early media\n",
|
||||
osmo_sockaddr_ntop(&addr->u.sa, ipbuf));
|
||||
return -1;
|
||||
"Rx RTP from %s: allowing unknown src for IuUP Initialization\n",
|
||||
osmo_sockaddr_to_str(addr));
|
||||
return 0;
|
||||
}
|
||||
/* Receiving early media before the endpoint is configured. Instead of logging
|
||||
* this as an error that occurs on every call, keep it more low profile to not
|
||||
* confuse humans with expected errors. */
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_INFO,
|
||||
"Rx RTP from %s, but remote address not set: dropping early media\n",
|
||||
osmo_sockaddr_to_str(addr));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Note: Check if the inbound RTP data comes from the same host to
|
||||
@@ -871,11 +866,8 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
|
||||
memcmp(&conn->end.addr.u.sin6.sin6_addr, &addr->u.sin6.sin6_addr,
|
||||
sizeof(struct in6_addr)))) {
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"data from wrong address: %s, ",
|
||||
osmo_sockaddr_ntop(&addr->u.sa, ipbuf));
|
||||
LOGPC(DRTP, LOGL_ERROR, "expected: %s\n",
|
||||
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR, "packet tossed\n");
|
||||
"data from wrong src %s, expected IP Address %s. Packet tossed.\n",
|
||||
osmo_sockaddr_to_str(addr), osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -886,12 +878,9 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
|
||||
if (osmo_sockaddr_port(&conn->end.addr.u.sa) != osmo_sockaddr_port(&addr->u.sa) &&
|
||||
ntohs(conn->end.rtcp_port) != osmo_sockaddr_port(&addr->u.sa)) {
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"data from wrong source port: %d, ",
|
||||
osmo_sockaddr_port(&addr->u.sa));
|
||||
LOGPC(DRTP, LOGL_ERROR,
|
||||
"expected: %d for RTP or %d for RTCP\n",
|
||||
osmo_sockaddr_port(&conn->end.addr.u.sa), ntohs(conn->end.rtcp_port));
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR, "packet tossed\n");
|
||||
"data from wrong src %s, expected port: %u for RTP or %u for RTCP. Packet tossed.\n",
|
||||
osmo_sockaddr_to_str(addr), osmo_sockaddr_port(&conn->end.addr.u.sa),
|
||||
ntohs(conn->end.rtcp_port));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -989,7 +978,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.
|
||||
/*! Dispatch msg bridged from the sister conn in the endpoint. Takes ownership of msgb.
|
||||
* \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,
|
||||
@@ -1011,8 +1000,10 @@ 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)
|
||||
if (check_rtp_destin(conn_dst) != 0) {
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Depending on the RTP connection type, deliver the RTP packet to the
|
||||
* destination connection. */
|
||||
@@ -1047,39 +1038,46 @@ 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 udp packet.
|
||||
* \param[in] fd associated file descriptor.
|
||||
/*! 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.
|
||||
* \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 bytes sent, -1 on error. */
|
||||
int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len)
|
||||
* \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)
|
||||
{
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
size_t addr_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);
|
||||
|
||||
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);
|
||||
return mgcp_udp_send_msg(iofd, addr, msg);
|
||||
}
|
||||
|
||||
/*! 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 bytes sent, -1 on error. */
|
||||
* \returns 0 in case of success, -1 on error. */
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
int rc;
|
||||
@@ -1101,8 +1099,7 @@ 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.fd, &conn->end.addr,
|
||||
rtp_dummy_payload, sizeof(rtp_dummy_payload));
|
||||
rc = mgcp_udp_send(conn->end.rtp, &conn->end.addr, rtp_dummy_payload, sizeof(rtp_dummy_payload));
|
||||
|
||||
if (rc == -1)
|
||||
goto failed;
|
||||
@@ -1113,10 +1110,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.fd, &rtcp_addr,
|
||||
rc = mgcp_udp_send(conn->end.rtcp, &rtcp_addr,
|
||||
rtp_dummy_payload, sizeof(rtp_dummy_payload));
|
||||
|
||||
if (rc >= 0)
|
||||
if (rc == 0)
|
||||
return rc;
|
||||
|
||||
failed:
|
||||
@@ -1127,7 +1124,7 @@ failed:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! Send RTP/RTCP data to a specified destination connection.
|
||||
/*! Send RTP/RTCP data to a specified destination connection. Takes ownership of msg.
|
||||
* \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).
|
||||
@@ -1166,6 +1163,7 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -1191,70 +1189,66 @@ 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);
|
||||
|
||||
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;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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)) {
|
||||
rc = amr_oa_bwe_convert(endp, msg,
|
||||
conn_dst->end.codec->param.amr_octet_aligned);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion (target=%s)\n",
|
||||
conn_dst->end.codec->param.amr_octet_aligned ? "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;
|
||||
}
|
||||
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)) {
|
||||
rc = amr_oa_bwe_convert(endp, msg, conn_dst->end.codec->param.amr_octet_aligned);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion (target=%s)\n",
|
||||
conn_dst->end.codec->param.amr_octet_aligned ? "octet-aligned" : "bandwidth-efficient");
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
} 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.fd, &conn_src->tap_out,
|
||||
msg);
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data_tap(rtp_end->rtp, &conn_src->tap_out, msg);
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr,
|
||||
(char *)msgb_data(msg), msgb_length(msg));
|
||||
len = msgb_length(msg);
|
||||
|
||||
if (len <= 0)
|
||||
return len;
|
||||
rc = mgcp_udp_send_msg(rtp_end->rtp, &rtp_end->addr, msg);
|
||||
if (rc < 0) {
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
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++;
|
||||
|
||||
nbytes += len;
|
||||
buflen = cont;
|
||||
} while (buflen > 0);
|
||||
return nbytes;
|
||||
return 0;
|
||||
} 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);
|
||||
@@ -1265,19 +1259,54 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
osmo_sockaddr_port(&rtcp_addr.u.sa)
|
||||
);
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtcp.fd, &rtcp_addr,
|
||||
(char *)msgb_data(msg), msgb_length(msg));
|
||||
len = msgb_length(msg);
|
||||
|
||||
rc = mgcp_udp_send_msg(rtp_end->rtcp, &rtcp_addr, msg);
|
||||
if (rc < 0) {
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
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 len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1335,23 +1364,44 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* If the mode is "confecho", send RTP back to the sender. */
|
||||
if (conn->mode == MGCP_CONN_CONFECHO)
|
||||
rc = mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
|
||||
/* 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. */
|
||||
|
||||
/* 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;
|
||||
/* 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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
/* as we only sent copies in the previous llist_for_each_entry() loop, we must free the
|
||||
* original one */
|
||||
msgb_free(msg);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1423,7 +1473,7 @@ void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *con
|
||||
}
|
||||
|
||||
/* Handle incoming RTP data from NET */
|
||||
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
static void rtp_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *saddr)
|
||||
{
|
||||
/* NOTE: This is a generic implementation. RTP data is received. In
|
||||
* case of loopback the data is just sent back to its origin. All
|
||||
@@ -1434,49 +1484,34 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
|
||||
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 *)fd->data;
|
||||
conn_src = (struct mgcp_conn_rtp *) osmo_iofd_get_data(iofd);
|
||||
OSMO_ASSERT(conn_src);
|
||||
endp = conn_src->conn->endp;
|
||||
OSMO_ASSERT(endp);
|
||||
msg = msgb_alloc_c(endp->trunk, RTP_BUF_SIZE, "RTP-rx");
|
||||
|
||||
proto = (fd == &conn_src->end.rtp)? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
|
||||
proto = (iofd == conn_src->end.rtp) ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
|
||||
|
||||
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;
|
||||
if (res <= 0) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(-res));
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
msgb_put(msg, ret);
|
||||
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s\n",
|
||||
proto == MGCP_PROTO_RTP ? "RTP" : "RTCP",
|
||||
msgb_length(msg), osmo_sockaddr_ntop(&addr.u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&addr.u.sa));
|
||||
msgb_length(msg), osmo_sockaddr_to_str(saddr));
|
||||
|
||||
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 */
|
||||
rc = -1;
|
||||
goto out;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (mgcp_is_rtp_dummy_payload(msg)) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx dummy packet (dropped)\n");
|
||||
rc = 0;
|
||||
goto out;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* Since the msgb remains owned and freed by this function, the msg ctx data struct can just be on the stack and
|
||||
@@ -1485,7 +1520,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
*mc = (struct osmo_rtp_msg_ctx){
|
||||
.proto = proto,
|
||||
.conn_src = conn_src,
|
||||
.from_addr = &addr,
|
||||
.from_addr = (struct osmo_sockaddr *) saddr,
|
||||
};
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "msg ctx: %d %p %s\n",
|
||||
mc->proto, mc->conn_src,
|
||||
@@ -1500,16 +1535,17 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
/* 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(fd->fd, &conn_src->tap_in, msg);
|
||||
forward_data_tap(iofd, &conn_src->tap_in, msg);
|
||||
|
||||
rc = rx_rtp(msg);
|
||||
rx_rtp(msg);
|
||||
return;
|
||||
|
||||
out:
|
||||
out_free:
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Note: This function is able to handle RTP and RTCP */
|
||||
/* 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. */
|
||||
static int rx_rtp(struct msgb *msg)
|
||||
{
|
||||
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
|
||||
@@ -1522,22 +1558,23 @@ 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))
|
||||
return -1;
|
||||
goto out_free;
|
||||
|
||||
/* Handle AMR frame format conversion (octet-aligned vs. bandwith-efficient) */
|
||||
if (mc->proto == MGCP_PROTO_RTP &&
|
||||
mgcp_codec_amr_align_mode_is_indicated(conn_src->end.codec)) {
|
||||
if (mc->proto == MGCP_PROTO_RTP
|
||||
&& conn_src->end.codec
|
||||
&& mgcp_codec_amr_align_mode_is_indicated(conn_src->end.codec)) {
|
||||
/* Make sure that the incoming AMR frame format matches the frame format that the call agent has
|
||||
* communicated via SDP when the connection was created/modfied. */
|
||||
int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
|
||||
if (oa < 0)
|
||||
return -1;
|
||||
goto out_free;
|
||||
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned) {
|
||||
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), conn_src->end.codec->param.amr_octet_aligned, oa);
|
||||
return -1;
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1546,17 +1583,36 @@ 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 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)
|
||||
* \returns file descriptor on success, -1 on ERROR. */
|
||||
int mgcp_create_bind(const char *source_addr, int port, uint8_t dscp, uint8_t prio)
|
||||
{
|
||||
int rc;
|
||||
|
||||
@@ -1564,47 +1620,50 @@ int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint
|
||||
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:%i).\n",
|
||||
LOGP(DRTP, LOGL_ERROR, "failed to bind UDP port (%s:%d).\n",
|
||||
source_addr, port);
|
||||
return -1;
|
||||
}
|
||||
fd->fd = rc;
|
||||
LOGP(DRTP, LOGL_DEBUG, "created socket + bound UDP port (%s:%i).\n", source_addr, port);
|
||||
LOGP(DRTP, LOGL_DEBUG, "created socket + bound UDP port (%s:%d).\n", source_addr, port);
|
||||
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 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) */
|
||||
|
||||
if (mgcp_create_bind(source_addr, &rtp_end->rtp, rtp_end->local_port,
|
||||
cfg->endp_dscp, cfg->endp_priority) != 0) {
|
||||
rc = mgcp_create_bind(source_addr, rtp_end->local_port, cfg->endp_dscp, cfg->endp_priority);
|
||||
if (rc < 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;
|
||||
|
||||
if (mgcp_create_bind(source_addr, &rtp_end->rtcp, rtp_end->local_port + 1,
|
||||
cfg->endp_dscp, cfg->endp_priority) != 0) {
|
||||
rc = mgcp_create_bind(source_addr, rtp_end->local_port + 1, cfg->endp_dscp, cfg->endp_priority);
|
||||
if (rc < 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_fd_register(&rtp_end->rtp) != 0) {
|
||||
if (osmo_iofd_register(rtp_end->rtp, rtp_fd) < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to register RTP port %d\n",
|
||||
rtp_end->local_port);
|
||||
goto cleanup2;
|
||||
}
|
||||
|
||||
if (osmo_fd_register(&rtp_end->rtcp) != 0) {
|
||||
if (osmo_iofd_register(rtp_end->rtcp, rtcp_fd) != 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to register RTCP port %d\n",
|
||||
rtp_end->local_port + 1);
|
||||
@@ -1614,13 +1673,11 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
|
||||
return 0;
|
||||
|
||||
cleanup3:
|
||||
osmo_fd_unregister(&rtp_end->rtp);
|
||||
osmo_iofd_unregister(rtp_end->rtp);
|
||||
cleanup2:
|
||||
close(rtp_end->rtcp.fd);
|
||||
rtp_end->rtcp.fd = -1;
|
||||
close(rtcp_fd);
|
||||
cleanup1:
|
||||
close(rtp_end->rtp.fd);
|
||||
rtp_end->rtp.fd = -1;
|
||||
close(rtp_fd);
|
||||
cleanup0:
|
||||
return -1;
|
||||
}
|
||||
@@ -1639,7 +1696,8 @@ 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.fd != -1 || end->rtcp.fd != -1) {
|
||||
if ((end->rtp && osmo_iofd_get_fd(end->rtp) != -1) ||
|
||||
(end->rtcp && osmo_iofd_get_fd(end->rtcp) != -1)) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "%u was already bound on conn:%s\n",
|
||||
rtp_port, mgcp_conn_dump(conn->conn));
|
||||
|
||||
@@ -1652,8 +1710,18 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
}
|
||||
|
||||
end->local_port = rtp_port;
|
||||
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);
|
||||
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 */
|
||||
|
||||
return bind_rtp(endp->trunk->cfg, conn->end.local_addr, end, endp);
|
||||
}
|
||||
@@ -1662,15 +1730,13 @@ 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.fd != -1) {
|
||||
osmo_fd_unregister(&end->rtp);
|
||||
close(end->rtp.fd);
|
||||
end->rtp.fd = -1;
|
||||
if (end->rtp) {
|
||||
osmo_iofd_free(end->rtp);
|
||||
end->rtp = NULL;
|
||||
}
|
||||
|
||||
if (end->rtcp.fd != -1) {
|
||||
osmo_fd_unregister(&end->rtcp);
|
||||
close(end->rtcp.fd);
|
||||
end->rtcp.fd = -1;
|
||||
if (end->rtcp) {
|
||||
osmo_iofd_free(end->rtcp);
|
||||
end->rtcp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* (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
|
||||
@@ -13,9 +14,11 @@
|
||||
#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>
|
||||
@@ -30,8 +33,8 @@
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
|
||||
static struct osmo_fd osmux_fd_v4;
|
||||
static struct osmo_fd osmux_fd_v6;
|
||||
static struct osmo_io_fd *osmux_fd_v4;
|
||||
static struct osmo_io_fd *osmux_fd_v6;
|
||||
|
||||
static LLIST_HEAD(osmux_handle_list);
|
||||
|
||||
@@ -76,34 +79,31 @@ 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;
|
||||
socklen_t dest_len;
|
||||
int rc, fd;
|
||||
struct mgcp_trunk *trunk = (struct mgcp_trunk *)osmux_fd_v4.data;
|
||||
int rc;
|
||||
struct osmo_io_fd *iofd;
|
||||
struct mgcp_trunk *trunk = (struct mgcp_trunk *) osmo_iofd_get_data(osmux_fd_v4);
|
||||
struct rate_ctr_group *all_osmux_stats = trunk->ratectr.all_osmux_conn_stats;
|
||||
|
||||
switch (handle->rem_addr.u.sa.sa_family) {
|
||||
case AF_INET6:
|
||||
dest_len = sizeof(handle->rem_addr.u.sin6);
|
||||
fd = osmux_fd_v6.fd;
|
||||
iofd = osmux_fd_v6;
|
||||
break;
|
||||
case AF_INET:
|
||||
default:
|
||||
dest_len = sizeof(handle->rem_addr.u.sin);
|
||||
fd = osmux_fd_v4.fd;
|
||||
iofd = osmux_fd_v4;
|
||||
break;
|
||||
}
|
||||
rc = sendto(fd, batch_msg->data, batch_msg->len, 0,
|
||||
(struct sockaddr *)&handle->rem_addr.u.sa, dest_len);
|
||||
rc = osmo_iofd_sendto_msgb(iofd, batch_msg, 0, &handle->rem_addr);
|
||||
if (rc < 0) {
|
||||
char errbuf[129];
|
||||
strerror_r(errno, errbuf, sizeof(errbuf));
|
||||
strerror_r(-rc, 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.
|
||||
/*! send RTP packet through OSMUX connection. Takes ownership of msg.
|
||||
* \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,22 +222,19 @@ 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, msg2, true) < 0) {
|
||||
if (amr_oa_bwe_convert(conn->conn->endp, msg, 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, msg2, conn->osmux.remote_cid)) > 0) {
|
||||
while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.remote_cid)) > 0) {
|
||||
/* batch full, build and deliver it */
|
||||
osmux_xfrm_input_deliver(conn->osmux.in);
|
||||
}
|
||||
@@ -245,7 +242,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(msg2) - sizeof(struct rtp_hdr));
|
||||
rtpconn_osmux_rate_ctr_add(conn, OSMUX_AMR_OCTETS_TX_CTR, msgb_length(msg) - sizeof(struct rtp_hdr));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -325,29 +322,7 @@ static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data)
|
||||
};
|
||||
|
||||
endp->type->dispatch_rtp_cb(msg);
|
||||
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;
|
||||
/* dispatch_rtp_cb() has taken ownership of the msgb */
|
||||
}
|
||||
|
||||
/* To be called every time some AMR data is received on a connection
|
||||
@@ -445,22 +420,16 @@ out:
|
||||
}
|
||||
|
||||
#define osmux_chunk_length(msg, rem) ((rem) - (msg)->len)
|
||||
static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
static void osmux_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *rem_addr)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmux_hdr *osmuxh;
|
||||
struct osmo_sockaddr rem_addr;
|
||||
uint32_t rem;
|
||||
struct mgcp_trunk *trunk = ofd->data;
|
||||
struct mgcp_trunk *trunk = osmo_iofd_get_data(iofd);
|
||||
struct rate_ctr_group *all_rtp_stats = trunk->ratectr.all_osmux_conn_stats;
|
||||
uint32_t rem;
|
||||
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,
|
||||
@@ -470,14 +439,16 @@ static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
}
|
||||
|
||||
/* Catch legacy dummy message and process them separately: */
|
||||
if (msg->len == 2 && msg->data[0] == MGCP_DUMMY_LOAD)
|
||||
return osmux_handle_legacy_dummy(trunk, &rem_addr, msg);
|
||||
if (msg->len == 2 && msg->data[0] == MGCP_DUMMY_LOAD) {
|
||||
osmux_handle_legacy_dummy(trunk, rem_addr, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
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",
|
||||
@@ -485,7 +456,7 @@ static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
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);
|
||||
@@ -499,58 +470,94 @@ 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;
|
||||
int ret, fd;
|
||||
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));
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
if (cfg->osmux.local_addr_v4) {
|
||||
ret = mgcp_create_bind(cfg->osmux.local_addr_v4, &osmux_fd_v4, cfg->osmux.local_port,
|
||||
ret = mgcp_create_bind(cfg->osmux.local_addr_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);
|
||||
return ret;
|
||||
goto out_free_v4;
|
||||
}
|
||||
fd = ret;
|
||||
|
||||
ret = osmo_fd_register(&osmux_fd_v4);
|
||||
ret = osmo_iofd_register(osmux_fd_v4, fd);
|
||||
if (ret < 0) {
|
||||
LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv4 socket %s\n",
|
||||
osmo_sock_get_name2(osmux_fd_v4.fd));
|
||||
return ret;
|
||||
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_INFO, "OSMUX IPv4 socket listening on %s\n",
|
||||
osmo_sock_get_name2(osmux_fd_v4.fd));
|
||||
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv4 socket listening on %s\n", osmo_sock_get_name2(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, &osmux_fd_v6, cfg->osmux.local_port,
|
||||
ret = mgcp_create_bind(cfg->osmux.local_addr_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);
|
||||
return ret;
|
||||
goto out_free_v6;
|
||||
}
|
||||
fd = ret;
|
||||
|
||||
ret = osmo_fd_register(&osmux_fd_v6);
|
||||
ret = osmo_iofd_register(osmux_fd_v6, fd);
|
||||
if (ret < 0) {
|
||||
LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv6 socket %s\n",
|
||||
osmo_sock_get_name2(osmux_fd_v6.fd));
|
||||
return ret;
|
||||
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_INFO, "OSMUX IPv6 socket listening on %s\n",
|
||||
osmo_sock_get_name2(osmux_fd_v6.fd));
|
||||
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv6 socket listening on %s\n", osmo_sock_get_name2(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.
|
||||
@@ -688,7 +695,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 bytes sent, -1 on error */
|
||||
* \returns 0 in case of success, -1 on error */
|
||||
int osmux_send_dummy(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
@@ -716,7 +723,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.fd, &conn->end.addr, (char *)osmuxh, buf_len);
|
||||
return mgcp_udp_send(osmux_fd_v4, &conn->end.addr, (char *)osmuxh, buf_len);
|
||||
}
|
||||
|
||||
/* Keeps track of locally allocated Osmux circuit ID. +7 to round up to 8 bit boundary. */
|
||||
|
||||
@@ -1076,9 +1076,6 @@ mgcp_header_done:
|
||||
rc = mgcp_conn_iuup_init(conn);
|
||||
}
|
||||
|
||||
conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
|
||||
trunk->audio_fmtp_extra);
|
||||
|
||||
if (pdata->cfg->force_ptime) {
|
||||
conn->end.packet_duration_ms = pdata->cfg->force_ptime;
|
||||
conn->end.force_output_ptime = 1;
|
||||
@@ -1712,8 +1709,6 @@ struct mgcp_config *mgcp_config_alloc(void)
|
||||
cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
|
||||
cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
|
||||
|
||||
cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
|
||||
|
||||
INIT_LLIST_HEAD(&cfg->trunks);
|
||||
|
||||
/* Allocate virtual trunk */
|
||||
|
||||
@@ -488,34 +488,10 @@ static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_
|
||||
}
|
||||
|
||||
/* Add fmtp strings to sdp payload */
|
||||
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len,
|
||||
const char *fmtp_extra)
|
||||
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
|
||||
{
|
||||
unsigned int i;
|
||||
int rc;
|
||||
int fmtp_extra_pt = -1;
|
||||
char *fmtp_extra_pars = "";
|
||||
|
||||
/* When no fmtp parameters ara available but an fmtp extra string
|
||||
* is configured, just add the fmtp extra string */
|
||||
if (fmtp_params_len == 0 && fmtp_extra) {
|
||||
return msgb_printf(sdp, "%s\r\n", fmtp_extra);
|
||||
}
|
||||
|
||||
/* When there is fmtp extra configured we dissect it in order to drop
|
||||
* in the configured extra parameters at the right place when
|
||||
* generating the fmtp strings. */
|
||||
if (fmtp_extra) {
|
||||
if (sscanf(fmtp_extra, "a=fmtp:%d ", &fmtp_extra_pt) != 1)
|
||||
fmtp_extra_pt = -1;
|
||||
|
||||
fmtp_extra_pars = strstr(fmtp_extra, " ");
|
||||
|
||||
if (!fmtp_extra_pars)
|
||||
fmtp_extra_pars = "";
|
||||
else
|
||||
fmtp_extra_pars++;
|
||||
}
|
||||
|
||||
for (i = 0; i < fmtp_params_len; i++) {
|
||||
rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
|
||||
@@ -532,13 +508,6 @@ static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsign
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Append extra parameters from fmtp extra */
|
||||
if (fmtp_params[i].payload_type == fmtp_extra_pt) {
|
||||
rc = msgb_printf(sdp, " %s", fmtp_extra_pars);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = msgb_printf(sdp, "\r\n");
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
@@ -558,7 +527,6 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
const char *addr)
|
||||
{
|
||||
const struct mgcp_rtp_codec *codec;
|
||||
const char *fmtp_extra;
|
||||
const char *audio_name;
|
||||
int payload_type;
|
||||
struct sdp_fmtp_param fmtp_param;
|
||||
@@ -574,10 +542,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
OSMO_ASSERT(sdp);
|
||||
OSMO_ASSERT(addr);
|
||||
|
||||
/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
|
||||
endp->trunk->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
|
||||
&codec, &fmtp_extra,
|
||||
(struct mgcp_conn_rtp *)conn);
|
||||
codec = conn->end.codec;
|
||||
|
||||
audio_name = codec->audio_name;
|
||||
payload_type = codec->payload_type;
|
||||
@@ -619,7 +584,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
fmtp_params[0] = fmtp_param;
|
||||
fmtp_params_len = 1;
|
||||
}
|
||||
rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra);
|
||||
rc = add_fmtp(sdp, fmtp_params, fmtp_params_len);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
}
|
||||
|
||||
@@ -111,9 +111,6 @@ static int config_write_mgcp(struct vty *vty)
|
||||
VTY_NEWLINE);
|
||||
} else
|
||||
vty_out(vty, " no rtp-patch%s", VTY_NEWLINE);
|
||||
if (trunk->audio_fmtp_extra)
|
||||
vty_out(vty, " sdp audio fmtp-extra %s%s",
|
||||
trunk->audio_fmtp_extra, VTY_NEWLINE);
|
||||
vty_out(vty, " %ssdp audio-payload send-ptime%s",
|
||||
trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE);
|
||||
vty_out(vty, " %ssdp audio-payload send-name%s",
|
||||
@@ -187,7 +184,7 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
|
||||
" Payload Type: %d Rate: %u Channels: %d %s"
|
||||
" Frame Duration: %u Frame Denominator: %u%s"
|
||||
" FPP: %d Packet Duration: %u%s"
|
||||
" FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"
|
||||
" Audio-Name: %s Sub-Type: %s%s"
|
||||
" Output-Enabled: %d Force-PTIME: %d%s",
|
||||
tx_packets->current, tx_bytes->current, VTY_NEWLINE,
|
||||
rx_packets->current, rx_bytes->current, VTY_NEWLINE,
|
||||
@@ -198,7 +195,7 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
|
||||
codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
|
||||
codec->frame_duration_num, codec->frame_duration_den,
|
||||
VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms,
|
||||
VTY_NEWLINE, end->fmtp_extra, codec->audio_name,
|
||||
VTY_NEWLINE, codec->audio_name,
|
||||
codec->subtype_name, VTY_NEWLINE, end->output_enabled,
|
||||
end->force_output_ptime, VTY_NEWLINE);
|
||||
if (mgcp_conn_rtp_is_osmux(conn)) {
|
||||
@@ -681,21 +678,15 @@ DEFUN_USRATTR(cfg_mgcp_no_rtp_force_ptime,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_USRATTR(cfg_mgcp_sdp_fmtp_extra,
|
||||
cfg_mgcp_sdp_fmtp_extra_cmd,
|
||||
X(MGW_CMD_ATTR_NEWCONN),
|
||||
"sdp audio fmtp-extra .NAME",
|
||||
"Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
|
||||
"Extra Information\n")
|
||||
{
|
||||
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
OSMO_ASSERT(trunk);
|
||||
char *txt = argv_concat(argv, argc, 0);
|
||||
if (!txt)
|
||||
return CMD_WARNING;
|
||||
#define SDP_STR "SDP File related options\n"
|
||||
#define AUDIO_STR "Audio payload options\n"
|
||||
|
||||
osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt);
|
||||
talloc_free(txt);
|
||||
DEFUN_DEPRECATED(cfg_mgcp_sdp_fmtp_extra,
|
||||
cfg_mgcp_sdp_fmtp_extra_cmd,
|
||||
"sdp audio fmtp-extra .NAME",
|
||||
SDP_STR AUDIO_STR "Deprecated, without effect since osmo-mgw v1.13\n" "Deprecated, without effect\n")
|
||||
{
|
||||
vty_out(vty, "%% deprecated: the config option 'sdp audio fmtp-extra' has been removed.%s", VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -715,8 +706,6 @@ DEFUN_DEPRECATED(cfg_mgcp_no_allow_transcoding,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define SDP_STR "SDP File related options\n"
|
||||
#define AUDIO_STR "Audio payload options\n"
|
||||
DEFUN_DEPRECATED(cfg_mgcp_sdp_payload_number,
|
||||
cfg_mgcp_sdp_payload_number_cmd,
|
||||
"sdp audio-payload number <0-255>",
|
||||
@@ -1062,28 +1051,17 @@ static int config_write_trunk(struct vty *vty)
|
||||
VTY_NEWLINE);
|
||||
} else
|
||||
vty_out(vty, " no rtp-patch%s", VTY_NEWLINE);
|
||||
if (trunk->audio_fmtp_extra)
|
||||
vty_out(vty, " sdp audio fmtp-extra %s%s",
|
||||
trunk->audio_fmtp_extra, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_USRATTR(cfg_trunk_sdp_fmtp_extra,
|
||||
DEFUN_DEPRECATED(cfg_trunk_sdp_fmtp_extra,
|
||||
cfg_trunk_sdp_fmtp_extra_cmd,
|
||||
X(MGW_CMD_ATTR_NEWCONN),
|
||||
"sdp audio fmtp-extra .NAME",
|
||||
"Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
|
||||
"Extra Information\n")
|
||||
SDP_STR AUDIO_STR "Deprecated, without effect since osmo-mgw v1.13\n" "Deprecated, without effect\n")
|
||||
{
|
||||
struct mgcp_trunk *trunk = vty->index;
|
||||
char *txt = argv_concat(argv, argc, 0);
|
||||
if (!txt)
|
||||
return CMD_WARNING;
|
||||
|
||||
osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt);
|
||||
talloc_free(txt);
|
||||
vty_out(vty, "%% deprecated: the config option 'sdp audio fmtp-extra' has been removed.%s", VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -280,6 +280,31 @@ 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",
|
||||
@@ -328,6 +353,8 @@ 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);
|
||||
|
||||
@@ -45,3 +45,6 @@ mgcp_test_LDADD = \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
-lm \
|
||||
$(NULL)
|
||||
|
||||
update_exp:
|
||||
$(builddir)/mgcp_test >$(srcdir)/mgcp_test.ok
|
||||
|
||||
@@ -127,7 +127,6 @@ 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=fmtp:126 0/1/2\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define MDCX4_ADDR0000 \
|
||||
@@ -336,7 +335,6 @@ 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=fmtp:126 0/1/2\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define CRCX_ZYN \
|
||||
@@ -388,6 +386,56 @@ static void test_strline(void)
|
||||
"a=rtpmap:97 GSM-EFR/8000\r\n" \
|
||||
"a=ptime:40\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:40\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:40\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:40\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:40\r\n"
|
||||
|
||||
#define DLCX \
|
||||
"DLCX 7 1@mgw MGCP 1.0\r\n" \
|
||||
"I: %s\r\n" \
|
||||
@@ -586,7 +634,6 @@ struct mgcp_test {
|
||||
const char *req;
|
||||
const char *exp_resp;
|
||||
int ptype;
|
||||
const char *extra_fmtp;
|
||||
};
|
||||
|
||||
static const struct mgcp_test tests[] = {
|
||||
@@ -614,10 +661,9 @@ static const struct mgcp_test tests[] = {
|
||||
{"RQNT1", RQNT, RQNT1_RET},
|
||||
{"RQNT2", RQNT2, RQNT2_RET},
|
||||
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
|
||||
{"CRCX", CRCX, CRCX_FMTP_RET, 97,.extra_fmtp = "a=fmtp:126 0/1/2"},
|
||||
{"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE,.extra_fmtp =
|
||||
"a=fmtp:126 0/1/2"},
|
||||
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
|
||||
{"CRCX", CRCX, CRCX_FMTP_RET, 97},
|
||||
{"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE},
|
||||
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
|
||||
{"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
|
||||
{"CRCX", CRCX_X_OSMO_IGN, CRCX_X_OSMO_IGN_RET, 97},
|
||||
{"MDCX_TOO_LONG_CI", MDCX_TOO_LONG_CI, MDCX_TOO_LONG_CI_RET},
|
||||
@@ -627,6 +673,8 @@ 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},
|
||||
};
|
||||
|
||||
static const struct mgcp_test retransmit[] = {
|
||||
@@ -657,12 +705,13 @@ static struct msgb *create_msg(const char *str, const char *conn_id)
|
||||
|
||||
static int dummy_packets = 0;
|
||||
/* override and forward */
|
||||
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
|
||||
const struct sockaddr *dest_addr, socklen_t addrlen)
|
||||
int osmo_iofd_sendto_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int flags, const struct osmo_sockaddr *addr)
|
||||
{
|
||||
uint32_t dest_host =
|
||||
htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr);
|
||||
int dest_port = htons(((struct sockaddr_in *)dest_addr)->sin_port);
|
||||
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);
|
||||
|
||||
if (len == sizeof(rtp_dummy_payload)
|
||||
&& memcmp(buf, rtp_dummy_payload, sizeof(rtp_dummy_payload)) == 0) {
|
||||
@@ -676,6 +725,8 @@ ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
|
||||
OSMO_ASSERT(dest_host);
|
||||
OSMO_ASSERT(dest_port);
|
||||
|
||||
msgb_free(msg);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -831,9 +882,6 @@ static void test_messages(void)
|
||||
|
||||
dummy_packets = 0;
|
||||
|
||||
osmo_talloc_replace_string(cfg, &trunk->audio_fmtp_extra,
|
||||
t->extra_fmtp);
|
||||
|
||||
inp = create_msg(t->req, last_conn_id);
|
||||
msg = mgcp_handle_message(cfg, inp);
|
||||
msgb_free(inp);
|
||||
@@ -2144,7 +2192,7 @@ static bool codec_decision(struct mgcp_conn_rtp *conn, unsigned int index_conn_s
|
||||
static void test_mgcp_codec_decide(void)
|
||||
{
|
||||
int i;
|
||||
bool ok = true;
|
||||
bool ok_all = true;
|
||||
printf("\nTesting mgcp_codec_find_convertible()\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_mgcp_codec_find_convertible_cases); i++) {
|
||||
@@ -2153,6 +2201,7 @@ static void test_mgcp_codec_decide(void)
|
||||
int rc;
|
||||
int conn_i;
|
||||
int c;
|
||||
bool ok = true;
|
||||
|
||||
printf("#%d: %s\n", i, t->descr);
|
||||
|
||||
@@ -2193,9 +2242,12 @@ static void test_mgcp_codec_decide(void)
|
||||
printf(" ===> SUCCESS: codec decision as expected!\n");
|
||||
else
|
||||
printf(" ===> FAIL: unexpected codec decision!\n");
|
||||
|
||||
if (!ok)
|
||||
ok_all = false;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(ok);
|
||||
OSMO_ASSERT(ok_all);
|
||||
}
|
||||
|
||||
void test_conn_id_matching(void)
|
||||
|
||||
@@ -599,6 +599,48 @@ using message as statically defined for comparison
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
Testing RQNT_NULL
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
RQNT 186908782 null@mgw MGCP 1.0
|
||||
X: B244F267488
|
||||
S: D/9
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message as statically defined for comparison
|
||||
Response matches our expectations.
|
||||
(response does not contain a connection id)
|
||||
|
||||
================================================
|
||||
Testing CRCX_PORT_0
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 3 1@mgw MGCP 1.0
|
||||
m: recvonly
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 0 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
a=ptime:40
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
|
||||
================================================
|
||||
Testing CRCX_PORT_0_IUFP
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 4 1@mgw MGCP 1.0
|
||||
m: recvonly
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
|
||||
@@ -107,9 +107,6 @@ void test_response_cb(struct mgcp_response *response, void *priv)
|
||||
printf(" audio_port = %u\n", response->audio_port);
|
||||
printf(" audio_ip = %s\n", response->audio_ip);
|
||||
printf(" ptime = %u\n", response->ptime);
|
||||
printf(" codecs_len = %u\n", response->codecs_len);
|
||||
for(i=0;i<response->codecs_len;i++)
|
||||
printf(" codecs[%u] = %u\n", i, response->codecs[i]);
|
||||
printf(" ptmap_len = %u\n", response->ptmap_len);
|
||||
for(i=0;i<response->ptmap_len;i++) {
|
||||
printf(" ptmap[%u].codec = %u\n", i, response->ptmap[i].codec);
|
||||
@@ -149,12 +146,11 @@ void test_mgcp_msg(void)
|
||||
.conn_id = "11",
|
||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||
.ptime = 20,
|
||||
.codecs[0] = CODEC_GSM_8000_1,
|
||||
.codecs[1] = CODEC_AMR_8000_1,
|
||||
.codecs[2] = CODEC_GSMEFR_8000_1,
|
||||
.codecs_len = 1,
|
||||
.ptmap[0].codec = CODEC_GSMEFR_8000_1,
|
||||
.ptmap[0].pt = 96,
|
||||
.ptmap = {
|
||||
{ .codec = CODEC_GSM_8000_1, .pt = CODEC_GSM_8000_1 },
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = CODEC_AMR_8000_1 },
|
||||
{ .codec = CODEC_GSMEFR_8000_1, .pt = 96 },
|
||||
},
|
||||
.ptmap_len = 1,
|
||||
.x_osmo_ign = MGCP_X_OSMO_IGN_CALLID,
|
||||
.x_osmo_osmux_cid = -1, /* wildcard */
|
||||
@@ -179,9 +175,9 @@ void test_mgcp_msg(void)
|
||||
mgcp_msg.presence =
|
||||
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
|
||||
mgcp_msg.codecs_len = 2;
|
||||
mgcp_msg.ptmap_len = 2;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
mgcp_msg.ptmap_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated CRCX message (three codecs, one with custom pt):\n");
|
||||
@@ -189,9 +185,9 @@ void test_mgcp_msg(void)
|
||||
mgcp_msg.presence =
|
||||
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
|
||||
mgcp_msg.codecs_len = 3;
|
||||
mgcp_msg.ptmap_len = 3;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
mgcp_msg.ptmap_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated MDCX message:\n");
|
||||
@@ -209,9 +205,9 @@ void test_mgcp_msg(void)
|
||||
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
|
||||
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
|
||||
mgcp_msg.codecs_len = 2;
|
||||
mgcp_msg.ptmap_len = 2;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
mgcp_msg.ptmap_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated MDCX message (three codecs, one with custom pt):\n");
|
||||
@@ -220,9 +216,9 @@ void test_mgcp_msg(void)
|
||||
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
|
||||
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
|
||||
mgcp_msg.codecs_len = 3;
|
||||
mgcp_msg.ptmap_len = 3;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
mgcp_msg.ptmap_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated DLCX message:\n");
|
||||
@@ -330,8 +326,10 @@ void test_mgcp_client_cancel(void)
|
||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
|
||||
| MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE),
|
||||
.ptime = 20,
|
||||
.codecs[0] = CODEC_AMR_8000_1,
|
||||
.codecs_len = 1
|
||||
.ptmap = {
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = CODEC_AMR_8000_1 },
|
||||
},
|
||||
.ptmap_len = 1
|
||||
};
|
||||
|
||||
printf("\n%s():\n", __func__);
|
||||
@@ -531,7 +529,7 @@ void test_sdp_section_start(void)
|
||||
OSMO_ASSERT(!failures);
|
||||
}
|
||||
|
||||
static void test_map_pt_to_codec(void)
|
||||
static void test_map_str_to_codec(void)
|
||||
{
|
||||
/* Full form */
|
||||
OSMO_ASSERT(map_str_to_codec("PCMU/8000/1") == CODEC_PCMU_8000_1);
|
||||
@@ -675,6 +673,214 @@ void test_mgcp_client_e1_epname(void)
|
||||
OSMO_ASSERT(epname == NULL);
|
||||
}
|
||||
|
||||
struct parse_response_test {
|
||||
const char *body;
|
||||
int expect_rc;
|
||||
struct mgcp_response expect_params;
|
||||
};
|
||||
|
||||
static struct parse_response_test parse_response_tests[] = {
|
||||
{
|
||||
.body = "200 2 OK\r\n"
|
||||
"I: foo\r\n"
|
||||
"\r\n"
|
||||
"v=0\r\n"
|
||||
"o=- name 23 IN IP4 0.0.0.0\r\n"
|
||||
"s=-\r\n"
|
||||
"c=IN IP4 1.2.3.4\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 23 RTP/AVP 112 3\r\n" /* <-- implicit: 3 = GSM-FR */
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=ptime:20\r\n",
|
||||
.expect_rc = 0,
|
||||
.expect_params = {
|
||||
.audio_port = 23,
|
||||
.audio_ip = "1.2.3.4",
|
||||
.ptmap = {
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = 112 },
|
||||
{ .codec = CODEC_GSM_8000_1, .pt = 3 },
|
||||
},
|
||||
.ptmap_len = 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
.body = "200 2 OK\r\n"
|
||||
"I: foo\r\n"
|
||||
"\r\n"
|
||||
"v=0\r\n"
|
||||
"o=- name 23 IN IP4 0.0.0.0\r\n"
|
||||
"s=-\r\n"
|
||||
"c=IN IP4 1.2.3.4\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 23 RTP/AVP 112 3\r\n"
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=rtpmap:3 GSM/8000\r\n" /* 3 == GSM-FR implicitly, is an explicit entry a problem? */
|
||||
"a=ptime:20\r\n",
|
||||
.expect_rc = 0,
|
||||
.expect_params = {
|
||||
.audio_port = 23,
|
||||
.audio_ip = "1.2.3.4",
|
||||
.ptmap = {
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = 112 },
|
||||
{ .codec = CODEC_GSM_8000_1, .pt = 3 }, /* no, not a problem */
|
||||
},
|
||||
.ptmap_len = 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
.body = "200 2 OK\r\n"
|
||||
"I: foo\r\n"
|
||||
"\r\n"
|
||||
"v=0\r\n"
|
||||
"o=- name 23 IN IP4 0.0.0.0\r\n"
|
||||
"s=-\r\n"
|
||||
"c=IN IP4 1.2.3.4\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 23 RTP/AVP 3\r\n" /* <-- 112 is missing here. Will it still appear? */
|
||||
"a=rtpmap:112 AMR/8000\r\n"
|
||||
"a=ptime:20\r\n",
|
||||
.expect_rc = 0,
|
||||
.expect_params = {
|
||||
.audio_port = 23,
|
||||
.audio_ip = "1.2.3.4",
|
||||
.ptmap = {
|
||||
{ .codec = CODEC_GSM_8000_1, .pt = 3 },
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = 112 }, /* <-- yes, it was added to the end. */
|
||||
},
|
||||
.ptmap_len = 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
/* test MGCP_MAX_CODECS */
|
||||
.body = "200 2 OK\r\n"
|
||||
"I: foo\r\n"
|
||||
"\r\n"
|
||||
"v=0\r\n"
|
||||
"o=- name 23 IN IP4 0.0.0.0\r\n"
|
||||
"s=-\r\n"
|
||||
"c=IN IP4 1.2.3.4\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110\r\n" /* <-- 10 codecs max */
|
||||
"a=rtpmap:101 AMR/8000\r\n"
|
||||
"a=rtpmap:102 AMR/8000\r\n"
|
||||
"a=rtpmap:103 AMR/8000\r\n"
|
||||
"a=rtpmap:104 AMR/8000\r\n"
|
||||
"a=rtpmap:105 AMR/8000\r\n"
|
||||
"a=rtpmap:106 AMR/8000\r\n"
|
||||
"a=rtpmap:107 AMR/8000\r\n"
|
||||
"a=rtpmap:108 AMR/8000\r\n"
|
||||
"a=rtpmap:109 AMR/8000\r\n"
|
||||
"a=rtpmap:110 AMR/8000\r\n"
|
||||
"a=ptime:20\r\n",
|
||||
.expect_rc = 0,
|
||||
.expect_params = {
|
||||
.audio_port = 23,
|
||||
.audio_ip = "1.2.3.4",
|
||||
.ptmap = {
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = 101 },
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = 102 },
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = 103 },
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = 104 },
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = 105 },
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = 106 },
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = 107 },
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = 108 },
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = 109 },
|
||||
{ .codec = CODEC_AMR_8000_1, .pt = 110 },
|
||||
},
|
||||
.ptmap_len = 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
/* test MGCP_MAX_CODECS */
|
||||
.body = "200 2 OK\r\n"
|
||||
"I: foo\r\n"
|
||||
"\r\n"
|
||||
"v=0\r\n"
|
||||
"o=- name 23 IN IP4 0.0.0.0\r\n"
|
||||
"s=-\r\n"
|
||||
"c=IN IP4 1.2.3.4\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110 3\r\n" /* <-- 11 > MGCP_MAX_CODECS */
|
||||
"a=rtpmap:101 AMR/8000\r\n"
|
||||
"a=rtpmap:102 AMR/8000\r\n"
|
||||
"a=rtpmap:103 AMR/8000\r\n"
|
||||
"a=rtpmap:104 AMR/8000\r\n"
|
||||
"a=rtpmap:105 AMR/8000\r\n"
|
||||
"a=rtpmap:106 AMR/8000\r\n"
|
||||
"a=rtpmap:107 AMR/8000\r\n"
|
||||
"a=rtpmap:108 AMR/8000\r\n"
|
||||
"a=rtpmap:109 AMR/8000\r\n"
|
||||
"a=rtpmap:110 AMR/8000\r\n"
|
||||
"a=ptime:20\r\n",
|
||||
.expect_rc = -EINVAL,
|
||||
},
|
||||
};
|
||||
|
||||
static void test_parse_response(void)
|
||||
{
|
||||
int i;
|
||||
int failures = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(parse_response_tests); i++) {
|
||||
int rc;
|
||||
struct parse_response_test *t = &parse_response_tests[i];
|
||||
struct mgcp_response *r = talloc_zero(ctx, struct mgcp_response);
|
||||
int p;
|
||||
|
||||
r->body = talloc_strdup(r, t->body);
|
||||
|
||||
//printf("\n%s() test [%d]:\n", __func__, i);
|
||||
fprintf(stderr, "\n%s() test [%d]:\n", __func__, i);
|
||||
fprintf(stderr, "body: \"%s\"\n", osmo_escape_str(r->body, -1));
|
||||
|
||||
rc = mgcp_response_parse_params(r);
|
||||
|
||||
fprintf(stderr, "got rc=%d\n", rc);
|
||||
if (rc != t->expect_rc) {
|
||||
fprintf(stderr, "FAIL: Expected rc=%d\n", t->expect_rc);
|
||||
failures++;
|
||||
}
|
||||
if (rc) {
|
||||
talloc_free(r);
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(stderr, "got audio_ip=\"%s\"\n", r->audio_ip);
|
||||
if (strcmp(r->audio_ip, t->expect_params.audio_ip)) {
|
||||
fprintf(stderr, "FAIL: Expected audio_ip=\"%s\"\n", t->expect_params.audio_ip);
|
||||
failures++;
|
||||
}
|
||||
fprintf(stderr, "got audio_port=%u\n", r->audio_port);
|
||||
if (r->audio_port != t->expect_params.audio_port) {
|
||||
fprintf(stderr, "FAIL: Expected audio_port=%u\n", t->expect_params.audio_port);
|
||||
failures++;
|
||||
}
|
||||
|
||||
for (p = 0; p < r->ptmap_len; p++) {
|
||||
struct ptmap *got = &r->ptmap[p];
|
||||
struct ptmap *expect = NULL;
|
||||
fprintf(stderr, " %d %s\n", got->pt, osmo_mgcpc_codec_name(got->codec));
|
||||
if (p >= t->expect_params.ptmap_len) {
|
||||
fprintf(stderr, " - ERROR: too many codec entries\n");
|
||||
failures++;
|
||||
continue;
|
||||
}
|
||||
expect = &t->expect_params.ptmap[p];
|
||||
if (ptmap_cmp(got, expect)) {
|
||||
fprintf(stderr, " - ERROR: expected: %d %s\n",
|
||||
expect->pt, osmo_mgcpc_codec_name(expect->codec));
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(r);
|
||||
}
|
||||
|
||||
OSMO_ASSERT(!failures);
|
||||
}
|
||||
|
||||
|
||||
static const struct log_info_cat log_categories[] = {
|
||||
};
|
||||
|
||||
@@ -703,9 +909,11 @@ int main(int argc, char **argv)
|
||||
test_mgcp_client_cancel();
|
||||
test_sdp_section_start();
|
||||
test_map_codec_to_pt_and_map_pt_to_codec();
|
||||
test_map_pt_to_codec();
|
||||
test_map_str_to_codec();
|
||||
test_mgcp_client_e1_epname();
|
||||
|
||||
test_parse_response();
|
||||
|
||||
printf("Done\n");
|
||||
fprintf(stderr, "Done\n");
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
@@ -137,4 +137,51 @@ DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw),
|
||||
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!
|
||||
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-0/su8-2@mgw), E1-timeslot number (0) is invalid!
|
||||
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-64/su8-2@mgw), E1-timeslot number (64) is invalid!
|
||||
|
||||
test_parse_response() test [0]:
|
||||
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 112 3\r\na=rtpmap:112 AMR/8000\r\na=ptime:20\r\n"
|
||||
got rc=0
|
||||
got audio_ip="1.2.3.4"
|
||||
got audio_port=23
|
||||
112 AMR/8000/1
|
||||
3 GSM/8000/1
|
||||
|
||||
test_parse_response() test [1]:
|
||||
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 112 3\r\na=rtpmap:112 AMR/8000\r\na=rtpmap:3 GSM/8000\r\na=ptime:20\r\n"
|
||||
got rc=0
|
||||
got audio_ip="1.2.3.4"
|
||||
got audio_port=23
|
||||
112 AMR/8000/1
|
||||
3 GSM/8000/1
|
||||
|
||||
test_parse_response() test [2]:
|
||||
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 3\r\na=rtpmap:112 AMR/8000\r\na=ptime:20\r\n"
|
||||
DLMGCP error in MGCP message: 'a=rtpmap:112' has no matching entry in 'm=audio ... 112'
|
||||
got rc=0
|
||||
got audio_ip="1.2.3.4"
|
||||
got audio_port=23
|
||||
3 GSM/8000/1
|
||||
112 AMR/8000/1
|
||||
|
||||
test_parse_response() test [3]:
|
||||
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110\r\na=rtpmap:101 AMR/8000\r\na=rtpmap:102 AMR/8000\r\na=rtpmap:103 AMR/8000\r\na=rtpmap:104 AMR/8"
|
||||
got rc=0
|
||||
got audio_ip="1.2.3.4"
|
||||
got audio_port=23
|
||||
101 AMR/8000/1
|
||||
102 AMR/8000/1
|
||||
103 AMR/8000/1
|
||||
104 AMR/8000/1
|
||||
105 AMR/8000/1
|
||||
106 AMR/8000/1
|
||||
107 AMR/8000/1
|
||||
108 AMR/8000/1
|
||||
109 AMR/8000/1
|
||||
110 AMR/8000/1
|
||||
|
||||
test_parse_response() test [4]:
|
||||
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110 3\r\na=rtpmap:101 AMR/8000\r\na=rtpmap:102 AMR/8000\r\na=rtpmap:103 AMR/8000\r\na=rtpmap:104 AMR"
|
||||
DLMGCP SDP: can parse only up to 10 payload type numbers
|
||||
DLMGCP Failed to parse SDP parameter payload types (RTP/AVP)
|
||||
got rc=-22
|
||||
Done
|
||||
|
||||
Reference in New Issue
Block a user