mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 16:14:03 +00:00
Compare commits
74 Commits
osmith/fix
...
pmaier/mgw
Author | SHA1 | Date | |
---|---|---|---|
|
5a71251229 | ||
|
113141d4f9 | ||
|
993ea6be7a | ||
|
889fe7f203 | ||
|
efd09d0b77 | ||
|
ffbc618a13 | ||
|
563ffc51b5 | ||
|
51b42ff2e1 | ||
|
7a755be4c2 | ||
|
06ea49159e | ||
|
869b21c869 | ||
|
37a808c5a5 | ||
|
24b8c0ce92 | ||
|
fe67e094ad | ||
|
fbcf39976e | ||
|
4863591c23 | ||
|
827da2c867 | ||
|
efe1571b7b | ||
|
6521fc0695 | ||
|
f5531845bc | ||
|
13fae78b32 | ||
|
b7d395d83a | ||
|
6fbbeec064 | ||
|
0ffa3bdc45 | ||
|
ca8639dc05 | ||
|
8d6a193c1a | ||
|
58a1ba85c7 | ||
|
b0c05aa3a9 | ||
|
0996a1e4ae | ||
|
7e9ddc9904 | ||
|
04bbb9de3e | ||
|
bea56788cb | ||
|
48bcc2ee12 | ||
|
080935a8c7 | ||
|
a466f57007 | ||
|
41f77d8018 | ||
|
e674345e29 | ||
|
3f8139c55f | ||
|
98c09b3f30 | ||
|
7462b95829 | ||
|
7a64182f9a | ||
|
d19de2ee80 | ||
|
08eb9352ab | ||
|
0b79d21c7f | ||
|
bce5f29265 | ||
|
3d5a2dd19f | ||
|
7f90ddb519 | ||
|
c66ab2c4c3 | ||
|
f53796c1fe | ||
|
14b27a8893 | ||
|
21be42abed | ||
|
2d681fd84c | ||
|
c39b1bffec | ||
|
62612e8575 | ||
|
af932ce3bc | ||
|
265b0a8045 | ||
|
74390c521e | ||
|
abfb858f0c | ||
|
6500d72aaf | ||
|
d6877eb8f9 | ||
|
ebb9bf3f12 | ||
|
61cf9bb5f1 | ||
|
a7152e055a | ||
|
63866009e2 | ||
|
dab89af070 | ||
|
eebbf2b1fc | ||
|
e885bc5c24 | ||
|
173dc129fc | ||
|
3abced8d64 | ||
|
a48ff4a738 | ||
|
6c92f9d83e | ||
|
9852e22101 | ||
|
b141cccbfb | ||
|
ec45068972 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -61,3 +61,5 @@ doc/manuals/generated/
|
||||
doc/manuals/osmomsc-usermanual.xml
|
||||
doc/manuals/common
|
||||
doc/manuals/build
|
||||
|
||||
contrib/osmo-mgw.spec
|
||||
|
10
Makefile.am
10
Makefile.am
@@ -9,10 +9,10 @@ AM_CPPFLAGS = \
|
||||
$(NULL)
|
||||
|
||||
SUBDIRS = \
|
||||
doc \
|
||||
include \
|
||||
src \
|
||||
tests \
|
||||
doc \
|
||||
contrib \
|
||||
$(NULL)
|
||||
|
||||
@@ -22,7 +22,13 @@ pkgconfig_DATA = \
|
||||
$(NULL)
|
||||
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = git-version-gen osmoappdesc.py .version
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
contrib/osmo-mgw.spec.in \
|
||||
debian \
|
||||
git-version-gen \
|
||||
osmoappdesc.py \
|
||||
$(NULL)
|
||||
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
18
configure.ac
18
configure.ac
@@ -22,6 +22,11 @@ AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
LT_INIT
|
||||
|
||||
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
|
||||
AS_CASE(["$LD"],[*clang*],
|
||||
[AS_CASE(["${host_os}"],
|
||||
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
|
||||
|
||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
|
||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||
@@ -38,11 +43,17 @@ dnl checks for libraries
|
||||
AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
|
||||
AC_SUBST(LIBRARY_DL)
|
||||
|
||||
AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
|
||||
AC_SUBST(LIBRARY_DLSYM)
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.0.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.0.0)
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 0.6.0)
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
@@ -200,4 +211,5 @@ AC_OUTPUT(
|
||||
doc/manuals/Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
contrib/osmo-mgw.spec
|
||||
Makefile)
|
||||
|
134
contrib/osmo-mgw.spec.in
Normal file
134
contrib/osmo-mgw.spec.in
Normal file
@@ -0,0 +1,134 @@
|
||||
#
|
||||
# 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) >= 0.6.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.0.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.1.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.0.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.0.0
|
||||
BuildRequires: pkgconfig(libosmocoding) >= 1.0.0
|
||||
%{?systemd_requires}
|
||||
|
||||
%description
|
||||
OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks.
|
||||
|
||||
%package -n libosmo-mgcp-client6
|
||||
Summary: Osmocom's Media Gateway Control Protocol client library
|
||||
Group: System/Libraries
|
||||
|
||||
%description -n libosmo-mgcp-client6
|
||||
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-client6 = %{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-client6 -p /sbin/ldconfig
|
||||
%postun -n libosmo-mgcp-client6 -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
|
||||
%{_bindir}/osmo-mgw
|
||||
%{_unitdir}/osmo-mgw.service
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg
|
||||
|
||||
%files -n libosmo-mgcp-client6
|
||||
%{_libdir}/libosmo-mgcp-client.so.6*
|
||||
|
||||
%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
|
42
debian/changelog
vendored
42
debian/changelog
vendored
@@ -1,3 +1,45 @@
|
||||
osmo-mgw (1.7.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* rename codecs_cmp() to codecs_same()
|
||||
* mgcp_codec: constify 'param' arg
|
||||
* fix crashes: don't assert on incoming RTP packet size
|
||||
* mgcp_send(): stop looping on conversion error
|
||||
* mgcp_codec: split codec_free() off of codec_init()
|
||||
* fix memleak: actually free strings in mgcp_codec_reset_all()
|
||||
* mgcp_test: extend / rewrite test_mgcp_codec_pt_translate()
|
||||
* test_mgcp_codec_pt_translate(): more tests
|
||||
* differentiate AMR octet-aligned=0 vs =1
|
||||
* ptmap: implicitly match '/8000' and '/8000/1'
|
||||
* mgcp_codec: codec_set(): log about all possible errors
|
||||
* mgcp_codec_add: fix audio_name size check
|
||||
* explicitly free codecs in mgcp_rtp_conn_cleanup()
|
||||
* tweak mgcp_parse_audio_ptime_rtpmap()
|
||||
* SDP: store all ptmap entries
|
||||
* mgcp_client_fsm cleanup: Do not assert on DLCX failure
|
||||
* clear pending requests on MGCP failure
|
||||
* client: endp fsm: add notify struct, prep for cancel-notify
|
||||
* client: endp fsm: clear ci[] before dispatching DLCX success
|
||||
* client: endp fsm: allow cancelling a notify event
|
||||
* client: endp fsm: add osmo_mgcpc_ep_ci_ep()
|
||||
* accept MGCP without SDP
|
||||
* fix use-after-free: require new fsm deferred dealloc, check for term
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* mgcp_test: Correctly release all endpoints allocated
|
||||
* mgw: Allocate mgcp_conn instance under tcfg->endpoints
|
||||
|
||||
[ Harald Welte ]
|
||||
* manual: Fix copy+paste error
|
||||
* mgcp_client: Check for osmo_fsm_register() error return value
|
||||
* Move fsm_mgcp_client regstration to __attribute__((contructor))
|
||||
* exit(2) on unsupported positional arguments on command line
|
||||
|
||||
[ Oliver Smith ]
|
||||
* osmoappdesc.py: switch to python 3
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 03 Jan 2020 13:35:09 +0100
|
||||
|
||||
osmo-mgw (1.6.0) unstable; urgency=medium
|
||||
|
||||
[ Oliver Smith ]
|
||||
|
@@ -4,7 +4,7 @@
|
||||
mgcp
|
||||
bind ip 127.0.0.1
|
||||
rtp port-range 4002 16000
|
||||
rtp bind-ip 10.9.1.122
|
||||
rtp bind-ip 127.0.0.1
|
||||
rtp ip-probing
|
||||
rtp ip-tos 184
|
||||
bind port 2427
|
||||
|
@@ -13,6 +13,11 @@ if BUILD_MANUALS
|
||||
VTY_REFERENCE = osmomgw-vty-reference.xml
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
|
||||
|
||||
BUILT_REFERENCE_XML = $(builddir)/vty/mgw_vty_reference.xml
|
||||
$(builddir)/vty/mgw_vty_reference.xml: $(top_builddir)/src/osmo-mgw/osmo-mgw
|
||||
mkdir -p $(builddir)/vty
|
||||
$(top_builddir)/src/osmo-mgw/osmo-mgw --vty-ref-xml > $@
|
||||
|
||||
OSMO_REPOSITORY = osmo-mgw
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
|
||||
endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,6 @@ nobase_include_HEADERS = \
|
||||
osmocom/mgcp_client/mgcp_client_fsm.h \
|
||||
osmocom/mgcp/mgcp.h \
|
||||
osmocom/mgcp/mgcp_common.h \
|
||||
osmocom/mgcp/mgcp_internal.h \
|
||||
osmocom/mgcp/osmux.h \
|
||||
$(NULL)
|
||||
|
||||
|
@@ -6,5 +6,11 @@ noinst_HEADERS = \
|
||||
mgcp_endp.h \
|
||||
mgcp_sdp.h \
|
||||
mgcp_codec.h \
|
||||
mgcp_ctrl.h \
|
||||
mgcp_trunk.h \
|
||||
debug.h \
|
||||
mgcp_ratectr.h \
|
||||
mgcp_e1.h \
|
||||
mgcp_network.h \
|
||||
mgcp_protocol.h \
|
||||
$(NULL)
|
||||
|
@@ -29,6 +29,7 @@
|
||||
/* Debug Areas of the code */
|
||||
enum {
|
||||
DRTP,
|
||||
DE1,
|
||||
Debug_LastEntry,
|
||||
};
|
||||
|
||||
|
@@ -34,6 +34,8 @@
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "mgcp_ratectr.h"
|
||||
|
||||
#define RTP_PORT_DEFAULT_RANGE_START 16002
|
||||
#define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64
|
||||
|
||||
@@ -42,7 +44,7 @@
|
||||
*/
|
||||
struct mgcp_endpoint;
|
||||
struct mgcp_config;
|
||||
struct mgcp_trunk_config;
|
||||
struct mgcp_trunk;
|
||||
struct mgcp_rtp_end;
|
||||
|
||||
#define MGCP_ENDP_CRCX 1
|
||||
@@ -59,10 +61,9 @@ struct mgcp_rtp_end;
|
||||
#define MGCP_POLICY_REJECT 5
|
||||
#define MGCP_POLICY_DEFER 6
|
||||
|
||||
typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint);
|
||||
typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state);
|
||||
typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id);
|
||||
typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
|
||||
typedef int (*mgcp_change)(struct mgcp_endpoint *endp, int state);
|
||||
typedef int (*mgcp_policy)(struct mgcp_endpoint *endp, int state, const char *transaction_id);
|
||||
typedef int (*mgcp_reset)(struct mgcp_trunk *cfg);
|
||||
typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
|
||||
|
||||
/**
|
||||
@@ -119,104 +120,6 @@ struct mgcp_port_range {
|
||||
#define MGCP_KEEPALIVE_ONCE (-1)
|
||||
#define MGCP_KEEPALIVE_NEVER 0
|
||||
|
||||
/* Global MCGP CRCX related rate counters */
|
||||
enum {
|
||||
MGCP_CRCX_SUCCESS,
|
||||
MGCP_CRCX_FAIL_BAD_ACTION,
|
||||
MGCP_CRCX_FAIL_UNHANDLED_PARAM,
|
||||
MGCP_CRCX_FAIL_MISSING_CALLID,
|
||||
MGCP_CRCX_FAIL_INVALID_MODE,
|
||||
MGCP_CRCX_FAIL_LIMIT_EXCEEDED,
|
||||
MGCP_CRCX_FAIL_UNKNOWN_CALLID,
|
||||
MGCP_CRCX_FAIL_ALLOC_CONN,
|
||||
MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC,
|
||||
MGCP_CRCX_FAIL_START_RTP,
|
||||
MGCP_CRCX_FAIL_REJECTED_BY_POLICY,
|
||||
MGCP_CRCX_FAIL_NO_OSMUX,
|
||||
MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS,
|
||||
MGCP_CRCX_FAIL_CODEC_NEGOTIATION,
|
||||
MGCP_CRCX_FAIL_BIND_PORT,
|
||||
};
|
||||
|
||||
/* Global MCGP MDCX related rate counters */
|
||||
enum {
|
||||
MGCP_MDCX_SUCCESS,
|
||||
MGCP_MDCX_FAIL_WILDCARD,
|
||||
MGCP_MDCX_FAIL_NO_CONN,
|
||||
MGCP_MDCX_FAIL_INVALID_CALLID,
|
||||
MGCP_MDCX_FAIL_INVALID_CONNID,
|
||||
MGCP_MDCX_FAIL_UNHANDLED_PARAM,
|
||||
MGCP_MDCX_FAIL_NO_CONNID,
|
||||
MGCP_MDCX_FAIL_CONN_NOT_FOUND,
|
||||
MGCP_MDCX_FAIL_INVALID_MODE,
|
||||
MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS,
|
||||
MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
|
||||
MGCP_MDCX_FAIL_START_RTP,
|
||||
MGCP_MDCX_FAIL_REJECTED_BY_POLICY,
|
||||
MGCP_MDCX_DEFERRED_BY_POLICY
|
||||
};
|
||||
|
||||
/* Global MCGP DLCX related rate counters */
|
||||
enum {
|
||||
MGCP_DLCX_SUCCESS,
|
||||
MGCP_DLCX_FAIL_WILDCARD,
|
||||
MGCP_DLCX_FAIL_NO_CONN,
|
||||
MGCP_DLCX_FAIL_INVALID_CALLID,
|
||||
MGCP_DLCX_FAIL_INVALID_CONNID,
|
||||
MGCP_DLCX_FAIL_UNHANDLED_PARAM,
|
||||
MGCP_DLCX_FAIL_REJECTED_BY_POLICY,
|
||||
MGCP_DLCX_DEFERRED_BY_POLICY,
|
||||
};
|
||||
|
||||
struct mgcp_trunk_config {
|
||||
struct llist_head entry;
|
||||
|
||||
struct mgcp_config *cfg;
|
||||
|
||||
int trunk_nr;
|
||||
int trunk_type;
|
||||
|
||||
char *audio_fmtp_extra;
|
||||
char *audio_name;
|
||||
int audio_payload;
|
||||
int audio_send_ptime;
|
||||
int audio_send_name;
|
||||
int audio_loop;
|
||||
|
||||
int no_audio_transcoding;
|
||||
|
||||
int omit_rtcp;
|
||||
int keepalive_interval;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* 0: don't, 1: once */
|
||||
int force_aligned_timing;
|
||||
bool rfc5993_hr_convert;
|
||||
|
||||
/* spec handling */
|
||||
int force_realloc;
|
||||
|
||||
/* timer */
|
||||
struct osmo_timer_list keepalive_timer;
|
||||
|
||||
/* When set, incoming RTP packets are not filtered
|
||||
* when ports and ip-address do not match (debug) */
|
||||
int rtp_accept_all;
|
||||
|
||||
unsigned int number_endpoints;
|
||||
int vty_number_endpoints;
|
||||
struct mgcp_endpoint *endpoints;
|
||||
|
||||
/* Rate counter group which contains stats for processed CRCX commands. */
|
||||
struct rate_ctr_group *mgcp_crcx_ctr_group;
|
||||
/* Rate counter group which contains stats for processed MDCX commands. */
|
||||
struct rate_ctr_group *mgcp_mdcx_ctr_group;
|
||||
/* Rate counter group which contains stats for processed DLCX commands. */
|
||||
struct rate_ctr_group *mgcp_dlcx_ctr_group;
|
||||
/* Rate counter group which aggregates stats of individual RTP connections. */
|
||||
struct rate_ctr_group *all_rtp_conn_stats;
|
||||
};
|
||||
|
||||
enum mgcp_role {
|
||||
MGCP_BSC = 0,
|
||||
MGCP_BSC_NAT,
|
||||
@@ -244,14 +147,12 @@ struct mgcp_config {
|
||||
mgcp_change change_cb;
|
||||
mgcp_policy policy_cb;
|
||||
mgcp_reset reset_cb;
|
||||
mgcp_realloc realloc_cb;
|
||||
mgcp_rqnt rqnt_cb;
|
||||
void *data;
|
||||
|
||||
uint32_t last_call_id;
|
||||
|
||||
/* trunk handling */
|
||||
struct mgcp_trunk_config trunk;
|
||||
/* list holding the trunks */
|
||||
struct llist_head trunks;
|
||||
|
||||
enum mgcp_role role;
|
||||
@@ -279,6 +180,13 @@ struct mgcp_config {
|
||||
|
||||
/* time after which inactive connections (CIs) get closed */
|
||||
int conn_timeout;
|
||||
|
||||
/* osmocom CTRL interface */
|
||||
struct ctrl_handle *ctrl;
|
||||
|
||||
/* global rate counters to measure the MGWs overall performance and
|
||||
* health */
|
||||
struct mgcp_ratectr_global ratectr;
|
||||
};
|
||||
|
||||
/* config management */
|
||||
@@ -286,8 +194,7 @@ struct mgcp_config *mgcp_config_alloc(void);
|
||||
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
|
||||
enum mgcp_role role);
|
||||
int mgcp_vty_init(void);
|
||||
int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg);
|
||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
|
||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval);
|
||||
|
||||
/*
|
||||
* format helper functions
|
||||
@@ -295,7 +202,7 @@ void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
|
||||
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
|
||||
|
||||
|
||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint);
|
||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp);
|
||||
int mgcp_send_reset_all(struct mgcp_config *cfg);
|
||||
|
||||
|
||||
|
@@ -1,7 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
|
||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
|
||||
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
|
||||
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
|
||||
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
|
||||
|
||||
#define PTYPE_UNDEFINED (-1)
|
||||
|
||||
struct mgcp_conn_rtp;
|
||||
|
||||
void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
|
||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param);
|
||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
|
||||
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
|
||||
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
|
||||
const char *subtype_name, unsigned int match_nr);
|
||||
|
@@ -100,6 +100,10 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
||||
/* A prefix to denote the virtual trunk (RTP on both ends) */
|
||||
#define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/"
|
||||
|
||||
/* A prefix to denote the e1 trunk
|
||||
* (see also RFC3435 section E.2) */
|
||||
#define MGCP_ENDPOINT_PREFIX_E1_TRUNK "ds/e1-"
|
||||
|
||||
/* Maximal number of payload types / codecs that can be negotiated via SDP at
|
||||
* at once. */
|
||||
#define MGCP_MAX_CODECS 10
|
||||
|
@@ -23,11 +23,114 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_network.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define LOGPCONN(conn, cat, level, fmt, args...) \
|
||||
LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
|
||||
(conn)->id, \
|
||||
## args)
|
||||
|
||||
#define LOG_CONN(conn, level, fmt, args...) \
|
||||
LOGP(DRTP, level, "(%s I:%s) " fmt, \
|
||||
(conn)->endp ? (conn)->endp->name : "none", (conn)->id, ## args)
|
||||
|
||||
#define LOG_CONN_RTP(conn_rtp, level, fmt, args...) \
|
||||
LOG_CONN((conn_rtp)->conn, level, fmt, ## args)
|
||||
|
||||
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
|
||||
enum mgcp_conn_rtp_type {
|
||||
MGCP_RTP_DEFAULT = 0,
|
||||
MGCP_OSMUX_BSC,
|
||||
MGCP_OSMUX_BSC_NAT,
|
||||
};
|
||||
|
||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
||||
* contains a useful connection description (currently only RTP) */
|
||||
enum mgcp_conn_type {
|
||||
MGCP_CONN_TYPE_RTP,
|
||||
};
|
||||
|
||||
/* MGCP connection (RTP) */
|
||||
struct mgcp_conn_rtp {
|
||||
|
||||
/* Backpointer to conn struct */
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
/* Specific connection type */
|
||||
enum mgcp_conn_rtp_type type;
|
||||
|
||||
/* Port status */
|
||||
struct mgcp_rtp_end end;
|
||||
|
||||
/* Sequence bits */
|
||||
struct mgcp_rtp_state state;
|
||||
|
||||
/* taps for the rtp connection; one per direction */
|
||||
struct mgcp_rtp_tap tap_in;
|
||||
struct mgcp_rtp_tap tap_out;
|
||||
|
||||
/* Osmux states (optional) */
|
||||
struct {
|
||||
/* Osmux state: disabled, activating, active */
|
||||
enum osmux_state state;
|
||||
/* Is cid holding valid data? is it allocated from pool? */
|
||||
bool cid_allocated;
|
||||
/* Allocated Osmux circuit ID for this conn */
|
||||
uint8_t cid;
|
||||
/* handle to batch messages */
|
||||
struct osmux_in_handle *in;
|
||||
/* handle to unbatch messages */
|
||||
struct osmux_out_handle out;
|
||||
/* statistics */
|
||||
struct {
|
||||
uint32_t chunks;
|
||||
uint32_t octets;
|
||||
} stats;
|
||||
} osmux;
|
||||
|
||||
struct rate_ctr_group *rate_ctr_group;
|
||||
};
|
||||
|
||||
/*! MGCP connection (untyped) */
|
||||
struct mgcp_conn {
|
||||
/*! list head */
|
||||
struct llist_head entry;
|
||||
|
||||
/*! Backpointer to the endpoint where the conn belongs to */
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
/*! type of the connection (union) */
|
||||
enum mgcp_conn_type type;
|
||||
|
||||
/*! mode of the connection */
|
||||
enum mgcp_connection_mode mode;
|
||||
|
||||
/*! copy of the mode to restore the original setting (VTY) */
|
||||
enum mgcp_connection_mode mode_orig;
|
||||
|
||||
/*! connection id to identify the connection */
|
||||
char id[MGCP_CONN_ID_MAXLEN];
|
||||
|
||||
/*! human readable name (vty, logging) */
|
||||
char name[256];
|
||||
|
||||
/*! activity tracker (for cleaning up inactive connections) */
|
||||
struct osmo_timer_list watchdog;
|
||||
|
||||
/*! union with connection description */
|
||||
union {
|
||||
struct mgcp_conn_rtp rtp;
|
||||
} u;
|
||||
|
||||
/*! pointer to optional private data */
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/* RTP connection related counters */
|
||||
enum {
|
||||
IN_STREAM_ERR_TSTMP_CTR,
|
||||
@@ -68,6 +171,11 @@ static const struct rate_ctr_desc all_rtp_conn_rate_ctr_desc[] = {
|
||||
[RTP_NUM_CONNECTIONS] = {"all_rtp:num_closed_conns", "Total number of rtp connections closed."}
|
||||
};
|
||||
|
||||
/* Was conn configured to handle Osmux? */
|
||||
static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) {
|
||||
return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT;
|
||||
}
|
||||
|
||||
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
enum mgcp_conn_type type, char *name);
|
||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
|
||||
@@ -78,3 +186,5 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
|
||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
|
||||
char *mgcp_conn_dump(struct mgcp_conn *conn);
|
||||
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);
|
||||
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp);
|
||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);
|
||||
|
24
include/osmocom/mgcp/mgcp_ctrl.h
Normal file
24
include/osmocom/mgcp/mgcp_ctrl.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
|
||||
const char *bind_addr, uint16_t port);
|
||||
|
28
include/osmocom/mgcp/mgcp_e1.h
Normal file
28
include/osmocom/mgcp/mgcp_e1.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
/* A 64k timeslot on an E1 line can be subdevied into the following
|
||||
* subslot combinations:
|
||||
*
|
||||
* subslot: offset:
|
||||
* [ ][ ][ 16k ][8k_subslot] 0
|
||||
* [ ][ 32k ][_subslot__][8k_subslot] 1
|
||||
* [ ][ subslot ][ 16k ][8k_subslot] 2
|
||||
* [ 64k ][__________][_subslot__][8k_subslot] 3
|
||||
* [ timeslot ][ ][ 16k ][8k_subslot] 4
|
||||
* [ ][ 32K ][_subslot__][8k_subslot] 5
|
||||
* [ ][ subslot ][ 16k ][8k_subslot] 6
|
||||
* [ ][ ][ subslot ][8k_subslot] 7
|
||||
*
|
||||
* Since overlapping assignment of subslots is not possible there is a limited
|
||||
* set of subslot assignments possible. The e1_rates array lists the possible
|
||||
* assignments as depicted above. Also each subslot assignment comes along with
|
||||
* a bit offset in the E1 bitstream. The e1_offsets arrays lists the bit
|
||||
* offsets. */
|
||||
static const uint8_t e1_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
|
||||
static const uint8_t e1_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
int mgcp_e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr);
|
||||
int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs);
|
||||
void mgcp_e1_endp_update(struct mgcp_endpoint *endp);
|
||||
void mgcp_e1_endp_release(struct mgcp_endpoint *endp);
|
||||
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg);
|
@@ -23,15 +23,35 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/i460_mux.h>
|
||||
|
||||
struct sockaddr_in;
|
||||
struct mgcp_conn;
|
||||
struct mgcp_conn_rtp;
|
||||
struct mgcp_endpoint;
|
||||
|
||||
/* Callback type for RTP dispatcher functions
|
||||
(e.g mgcp_dispatch_rtp_bridge_cb, see below) */
|
||||
typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in *addr,
|
||||
char *buf, unsigned int buf_size,
|
||||
struct mgcp_conn *conn);
|
||||
/* Number of E1 subslots (different variants, not all useable at the same time) */
|
||||
#define MGCP_ENDP_E1_SUBSLOTS 15
|
||||
|
||||
#define LOGPENDP(endp, cat, level, fmt, args...) \
|
||||
LOGP(cat, level, "endpoint:%s " fmt, \
|
||||
endp ? endp->name : "none", \
|
||||
## args)
|
||||
|
||||
struct osmo_rtp_msg_ctx {
|
||||
int proto;
|
||||
struct mgcp_conn_rtp *conn_src;
|
||||
struct sockaddr_in *from_addr;
|
||||
};
|
||||
|
||||
#define OSMO_RTP_MSG_CTX(MSGB) ((struct osmo_rtp_msg_ctx*)(MSGB)->cb)
|
||||
|
||||
osmo_static_assert(sizeof(((struct msgb*)0)->cb) >= sizeof(struct osmo_rtp_msg_ctx), osmo_rtp_msg_ctx_fits_in_msgb_cb);
|
||||
|
||||
/* Callback type for RTP dispatcher functions (e.g mgcp_dispatch_rtp_bridge_cb, see below).
|
||||
* The OSMO_RTP_MSG_CTX() should be set appropriately on the msg. */
|
||||
typedef int (*mgcp_dispatch_rtp_cb) (struct msgb *msg);
|
||||
|
||||
/* Callback type for endpoint specific cleanup actions. This function
|
||||
* is automatically executed when a connection is freed (see mgcp_conn_free()
|
||||
@@ -55,6 +75,7 @@ struct mgcp_endpoint_type {
|
||||
/*! MGCP endpoint typeset */
|
||||
struct mgcp_endpoint_typeset {
|
||||
struct mgcp_endpoint_type rtp;
|
||||
struct mgcp_endpoint_type e1;
|
||||
};
|
||||
|
||||
/*! static MGCP endpoint typeset (pre-initalized, read-only) */
|
||||
@@ -63,6 +84,9 @@ extern const struct mgcp_endpoint_typeset ep_typeset;
|
||||
/*! MGCP endpoint model */
|
||||
struct mgcp_endpoint {
|
||||
|
||||
/*! Unique endpoint name, used for addressing via MGCP */
|
||||
char *name;
|
||||
|
||||
/*! Call identifier string (as supplied by the call agant) */
|
||||
char *callid;
|
||||
|
||||
@@ -75,8 +99,8 @@ struct mgcp_endpoint {
|
||||
/*! Backpointer to the MGW configuration */
|
||||
struct mgcp_config *cfg;
|
||||
|
||||
/*! Backpointer to the Trunk specific configuration */
|
||||
struct mgcp_trunk_config *tcfg;
|
||||
/*! Backpointer to the trunk this endpoint belongs to */
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
/*! Endpoint properties (see above) */
|
||||
const struct mgcp_endpoint_type *type;
|
||||
@@ -93,9 +117,25 @@ struct mgcp_endpoint {
|
||||
|
||||
/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */
|
||||
uint32_t x_osmo_ign;
|
||||
|
||||
/* E1 specific */
|
||||
struct {
|
||||
struct osmo_i460_schan_desc scd;
|
||||
struct osmo_i460_subchan *schan;
|
||||
struct osmo_fsm_inst *trau_sync_fi;
|
||||
struct osmo_trau2rtp_state *trau_rtp_st;
|
||||
uint8_t last_amr_ft;
|
||||
struct mgcp_rtp_codec *last_codec;
|
||||
} e1;
|
||||
|
||||
};
|
||||
|
||||
/*! Extract endpoint number for a given endpoint */
|
||||
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
|
||||
|
||||
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int index);
|
||||
void mgcp_endp_release(struct mgcp_endpoint *endp);
|
||||
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid);
|
||||
void mgcp_endp_update(struct mgcp_endpoint *endp);
|
||||
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
|
||||
const struct mgcp_trunk *trunk);
|
||||
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
|
||||
struct mgcp_config *cfg);
|
||||
bool mgcp_endp_avail(struct mgcp_endpoint *endp);
|
||||
|
@@ -1,352 +0,0 @@
|
||||
/* MGCP Private Data */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/counter.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
|
||||
#define CI_UNUSED 0
|
||||
|
||||
/* FIXME: This this is only needed to compile the currently
|
||||
* broken OSMUX support. Remove when fixed */
|
||||
#define CONN_ID_BTS "0"
|
||||
#define CONN_ID_NET "1"
|
||||
|
||||
enum mgcp_trunk_type {
|
||||
MGCP_TRUNK_VIRTUAL,
|
||||
MGCP_TRUNK_E1,
|
||||
};
|
||||
|
||||
struct mgcp_rtp_stream_state {
|
||||
uint32_t ssrc;
|
||||
uint16_t last_seq;
|
||||
uint32_t last_timestamp;
|
||||
struct rate_ctr *err_ts_ctr;
|
||||
int32_t last_tsdelta;
|
||||
uint32_t last_arrival_time;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_state {
|
||||
/* has this state structure been initialized? */
|
||||
int initialized;
|
||||
|
||||
struct {
|
||||
/* are we patching the SSRC value? */
|
||||
int patch_ssrc;
|
||||
/* original SSRC (to which we shall patch any different SSRC) */
|
||||
uint32_t orig_ssrc;
|
||||
/* offset to apply on the sequence number */
|
||||
int seq_offset;
|
||||
/* offset to apply on the timestamp number */
|
||||
int32_t timestamp_offset;
|
||||
} patch;
|
||||
|
||||
/* duration of a packet (FIXME: in which unit?) */
|
||||
uint32_t packet_duration;
|
||||
|
||||
struct mgcp_rtp_stream_state in_stream;
|
||||
struct mgcp_rtp_stream_state out_stream;
|
||||
|
||||
/* jitter and packet loss calculation */
|
||||
struct {
|
||||
int initialized;
|
||||
uint16_t base_seq;
|
||||
uint16_t max_seq;
|
||||
uint32_t ssrc;
|
||||
uint32_t jitter;
|
||||
int32_t transit;
|
||||
int cycles;
|
||||
} stats;
|
||||
|
||||
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
|
||||
};
|
||||
|
||||
struct mgcp_rtp_codec {
|
||||
uint32_t rate;
|
||||
int channels;
|
||||
uint32_t frame_duration_num;
|
||||
uint32_t frame_duration_den;
|
||||
|
||||
int payload_type;
|
||||
char *audio_name;
|
||||
char *subtype_name;
|
||||
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
};
|
||||
|
||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
|
||||
struct mgcp_rtp_end {
|
||||
/* local IP address of the RTP socket */
|
||||
struct in_addr addr;
|
||||
|
||||
/* in network byte order */
|
||||
int rtp_port, rtcp_port;
|
||||
|
||||
/* currently selected audio codec */
|
||||
struct mgcp_rtp_codec *codec;
|
||||
|
||||
/* array with assigned audio codecs to choose from (SDP) */
|
||||
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
|
||||
|
||||
/* number of assigned audio codecs (SDP) */
|
||||
unsigned int codecs_assigned;
|
||||
|
||||
/* per endpoint data */
|
||||
int frames_per_packet;
|
||||
uint32_t packet_duration_ms;
|
||||
int maximum_packet_time; /* -1: not set */
|
||||
char *fmtp_extra;
|
||||
/* are we transmitting packets (1) or dropping (0) outbound packets */
|
||||
int output_enabled;
|
||||
/* FIXME: This parameter can be set + printed, but is nowhere used! */
|
||||
int force_output_ptime;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
|
||||
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
|
||||
int force_aligned_timing;
|
||||
bool rfc5993_hr_convert;
|
||||
|
||||
/* Each end has a separate socket for RTP and RTCP */
|
||||
struct osmo_fd rtp;
|
||||
struct osmo_fd rtcp;
|
||||
|
||||
/* local UDP port number of the RTP socket; RTCP is +1 */
|
||||
int local_port;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_tap {
|
||||
/* is this tap active (1) or not (0) */
|
||||
int enabled;
|
||||
/* IP/port to which we're forwarding the tapped data */
|
||||
struct sockaddr_in forward;
|
||||
};
|
||||
|
||||
struct mgcp_lco {
|
||||
char *string;
|
||||
char *codec;
|
||||
int pkt_period_min; /* time in ms */
|
||||
int pkt_period_max; /* time in ms */
|
||||
};
|
||||
|
||||
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
|
||||
enum mgcp_conn_rtp_type {
|
||||
MGCP_RTP_DEFAULT = 0,
|
||||
MGCP_OSMUX_BSC,
|
||||
MGCP_OSMUX_BSC_NAT,
|
||||
};
|
||||
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
struct mgcp_conn;
|
||||
|
||||
/* MGCP connection (RTP) */
|
||||
struct mgcp_conn_rtp {
|
||||
|
||||
/* Backpointer to conn struct */
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
/* Specific connection type */
|
||||
enum mgcp_conn_rtp_type type;
|
||||
|
||||
/* Port status */
|
||||
struct mgcp_rtp_end end;
|
||||
|
||||
/* Sequence bits */
|
||||
struct mgcp_rtp_state state;
|
||||
|
||||
/* taps for the rtp connection; one per direction */
|
||||
struct mgcp_rtp_tap tap_in;
|
||||
struct mgcp_rtp_tap tap_out;
|
||||
|
||||
/* Osmux states (optional) */
|
||||
struct {
|
||||
/* Osmux state: disabled, activating, active */
|
||||
enum osmux_state state;
|
||||
/* Is cid holding valid data? is it allocated from pool? */
|
||||
bool cid_allocated;
|
||||
/* Allocated Osmux circuit ID for this conn */
|
||||
uint8_t cid;
|
||||
/* handle to batch messages */
|
||||
struct osmux_in_handle *in;
|
||||
/* handle to unbatch messages */
|
||||
struct osmux_out_handle out;
|
||||
/* statistics */
|
||||
struct {
|
||||
uint32_t chunks;
|
||||
uint32_t octets;
|
||||
} stats;
|
||||
} osmux;
|
||||
|
||||
struct rate_ctr_group *rate_ctr_group;
|
||||
};
|
||||
|
||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
||||
* contains a useful connection description (currently only RTP) */
|
||||
enum mgcp_conn_type {
|
||||
MGCP_CONN_TYPE_RTP,
|
||||
};
|
||||
|
||||
/*! MGCP connection (untyped) */
|
||||
struct mgcp_conn {
|
||||
/*! list head */
|
||||
struct llist_head entry;
|
||||
|
||||
/*! Backpointer to the endpoint where the conn belongs to */
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
/*! type of the connection (union) */
|
||||
enum mgcp_conn_type type;
|
||||
|
||||
/*! mode of the connection */
|
||||
enum mgcp_connection_mode mode;
|
||||
|
||||
/*! copy of the mode to restore the original setting (VTY) */
|
||||
enum mgcp_connection_mode mode_orig;
|
||||
|
||||
/*! connection id to identify the connection */
|
||||
char id[MGCP_CONN_ID_MAXLEN];
|
||||
|
||||
/*! human readable name (vty, logging) */
|
||||
char name[256];
|
||||
|
||||
/*! activity tracker (for cleaning up inactive connections) */
|
||||
struct osmo_timer_list watchdog;
|
||||
|
||||
/*! union with connection description */
|
||||
union {
|
||||
struct mgcp_conn_rtp rtp;
|
||||
} u;
|
||||
|
||||
/*! pointer to optional private data */
|
||||
void *priv;
|
||||
};
|
||||
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
|
||||
struct mgcp_endpoint_type;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Internal structure while parsing a request
|
||||
*/
|
||||
struct mgcp_parse_data {
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_endpoint *endp;
|
||||
char *trans;
|
||||
char *save;
|
||||
};
|
||||
|
||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
char *buf, int rc, struct mgcp_conn_rtp *conn_src,
|
||||
struct mgcp_conn_rtp *conn_dst);
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
|
||||
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
unsigned int buf_size, struct mgcp_conn *conn);
|
||||
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
|
||||
|
||||
/* For transcoding we need to manage an in and an output that are connected */
|
||||
static inline int endp_back_channel(int endpoint)
|
||||
{
|
||||
return endpoint + 60;
|
||||
}
|
||||
|
||||
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
|
||||
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
|
||||
|
||||
char *get_lco_identifier(const char *options);
|
||||
int check_local_cx_options(void *ctx, const char *options);
|
||||
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
|
||||
struct mgcp_rtp_end *rtp);
|
||||
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *rtp);
|
||||
|
||||
/* payload processing default functions */
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size);
|
||||
|
||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst,
|
||||
struct mgcp_conn_rtp *conn_src);
|
||||
|
||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
const struct mgcp_rtp_codec **codec,
|
||||
const char **fmtp_extra,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
|
||||
/* internal RTP Annex A counting */
|
||||
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
|
||||
const uint16_t seq, const int32_t transit,
|
||||
const uint32_t ssrc);
|
||||
|
||||
int mgcp_set_ip_tos(int fd, int tos);
|
||||
|
||||
/* Was conn configured to handle Osmux? */
|
||||
static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) {
|
||||
return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT;
|
||||
}
|
||||
|
||||
enum {
|
||||
MGCP_DEST_NET = 0,
|
||||
MGCP_DEST_BTS,
|
||||
};
|
||||
|
||||
|
||||
#define MGCP_DUMMY_LOAD 0x23
|
||||
|
||||
|
||||
/**
|
||||
* SDP related information
|
||||
*/
|
||||
/* Assume audio frame length of 20ms */
|
||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
|
||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
|
||||
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
|
||||
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
|
||||
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
|
||||
|
||||
#define PTYPE_UNDEFINED (-1)
|
||||
|
||||
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
|
||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);
|
||||
|
||||
#define LOGPENDP(endp, cat, level, fmt, args...) \
|
||||
LOGP(cat, level, "endpoint:0x%x " fmt, \
|
||||
endp ? ENDPOINT_NUMBER(endp) : -1, \
|
||||
## args)
|
||||
|
||||
#define LOGPCONN(conn, cat, level, fmt, args...) \
|
||||
LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
|
||||
(conn)->id, \
|
||||
## args)
|
163
include/osmocom/mgcp/mgcp_network.h
Normal file
163
include/osmocom/mgcp/mgcp_network.h
Normal file
@@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
|
||||
#define MGCP_DUMMY_LOAD 0x23
|
||||
#define RTP_BUF_SIZE 4096
|
||||
|
||||
struct mgcp_rtp_stream_state {
|
||||
uint32_t ssrc;
|
||||
uint16_t last_seq;
|
||||
uint32_t last_timestamp;
|
||||
struct rate_ctr *err_ts_ctr;
|
||||
int32_t last_tsdelta;
|
||||
uint32_t last_arrival_time;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_state {
|
||||
/* has this state structure been initialized? */
|
||||
int initialized;
|
||||
|
||||
struct {
|
||||
/* are we patching the SSRC value? */
|
||||
int patch_ssrc;
|
||||
/* original SSRC (to which we shall patch any different SSRC) */
|
||||
uint32_t orig_ssrc;
|
||||
/* offset to apply on the sequence number */
|
||||
int seq_offset;
|
||||
/* offset to apply on the timestamp number */
|
||||
int32_t timestamp_offset;
|
||||
} patch;
|
||||
|
||||
/* duration of a packet (FIXME: in which unit?) */
|
||||
uint32_t packet_duration;
|
||||
|
||||
/* Note: These states are not continuously updated, they serve as an
|
||||
* information source to patch certain values in the RTP header. Do
|
||||
* not use this state if constantly updated data about the RTP stream
|
||||
* is needed. (see also mgcp_patch_and_count() */
|
||||
struct mgcp_rtp_stream_state in_stream;
|
||||
struct mgcp_rtp_stream_state out_stream;
|
||||
|
||||
/* jitter and packet loss calculation */
|
||||
struct {
|
||||
int initialized;
|
||||
uint16_t base_seq;
|
||||
uint16_t max_seq;
|
||||
uint32_t ssrc;
|
||||
uint32_t jitter;
|
||||
int32_t transit;
|
||||
int cycles;
|
||||
} stats;
|
||||
|
||||
/* Alternative values for RTP tx, in case no sufficient header
|
||||
* information is available so the header needs to be generated
|
||||
* locally (when just forwarding packets, the header of incoming
|
||||
* data is just re-used) */
|
||||
uint16_t alt_rtp_tx_sequence;
|
||||
uint32_t alt_rtp_tx_ssrc;
|
||||
|
||||
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
|
||||
};
|
||||
|
||||
struct mgcp_rtp_codec {
|
||||
uint32_t rate;
|
||||
int channels;
|
||||
uint32_t frame_duration_num;
|
||||
uint32_t frame_duration_den;
|
||||
|
||||
int payload_type;
|
||||
char *audio_name;
|
||||
char *subtype_name;
|
||||
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
};
|
||||
|
||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
|
||||
struct mgcp_rtp_end {
|
||||
/* local IP address of the RTP socket */
|
||||
struct in_addr addr;
|
||||
|
||||
/* in network byte order */
|
||||
int rtp_port, rtcp_port;
|
||||
|
||||
/* currently selected audio codec */
|
||||
struct mgcp_rtp_codec *codec;
|
||||
|
||||
/* array with assigned audio codecs to choose from (SDP) */
|
||||
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
|
||||
|
||||
/* number of assigned audio codecs (SDP) */
|
||||
unsigned int codecs_assigned;
|
||||
|
||||
/* per endpoint data */
|
||||
int frames_per_packet;
|
||||
uint32_t packet_duration_ms;
|
||||
int maximum_packet_time; /* -1: not set */
|
||||
char *fmtp_extra;
|
||||
/* are we transmitting packets (1) or dropping (0) outbound packets */
|
||||
int output_enabled;
|
||||
/* FIXME: This parameter can be set + printed, but is nowhere used! */
|
||||
int force_output_ptime;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
|
||||
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
|
||||
int force_aligned_timing;
|
||||
bool rfc5993_hr_convert;
|
||||
|
||||
/* Each end has a separate socket for RTP and RTCP */
|
||||
struct osmo_fd rtp;
|
||||
struct osmo_fd rtcp;
|
||||
|
||||
/* local UDP port number of the RTP socket; RTCP is +1 */
|
||||
int local_port;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_tap {
|
||||
/* is this tap active (1) or not (0) */
|
||||
int enabled;
|
||||
/* IP/port to which we're forwarding the tapped data */
|
||||
struct sockaddr_in forward;
|
||||
};
|
||||
|
||||
struct mgcp_conn;
|
||||
|
||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
struct msgb *msg, struct mgcp_conn_rtp *conn_src,
|
||||
struct mgcp_conn_rtp *conn_dst);
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
|
||||
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
|
||||
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
int mgcp_dispatch_e1_bridge_cb(struct msgb *msg);
|
||||
void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
|
||||
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *rtp_end,
|
||||
struct sockaddr_in *addr, struct msgb *msg);
|
||||
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
|
||||
int mgcp_set_ip_tos(int fd, int tos);
|
||||
|
||||
/* payload processing default functions */
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
|
||||
char *data, int *len, int buf_size);
|
||||
|
||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst,
|
||||
struct mgcp_conn_rtp *conn_src);
|
||||
|
||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
const struct mgcp_rtp_codec **codec,
|
||||
const char **fmtp_extra,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
|
||||
/* internal RTP Annex A counting */
|
||||
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
|
||||
const uint16_t seq, const int32_t transit,
|
||||
const uint32_t ssrc);
|
27
include/osmocom/mgcp/mgcp_protocol.h
Normal file
27
include/osmocom/mgcp/mgcp_protocol.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
/* Internal structure while parsing a request */
|
||||
struct mgcp_parse_data {
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_endpoint *endp;
|
||||
char *trans;
|
||||
char *save;
|
||||
};
|
||||
|
||||
/* Local connection options */
|
||||
struct mgcp_lco {
|
||||
char *string;
|
||||
char *codec;
|
||||
int pkt_period_min; /* time in ms */
|
||||
int pkt_period_max; /* time in ms */
|
||||
};
|
||||
|
||||
char *get_lco_identifier(const char *options);
|
||||
int check_local_cx_options(void *ctx, const char *options);
|
||||
|
||||
struct mgcp_rtp_end;
|
||||
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
|
||||
struct mgcp_rtp_end *rtp);
|
||||
|
||||
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *rtp);
|
95
include/osmocom/mgcp/mgcp_ratectr.h
Normal file
95
include/osmocom/mgcp/mgcp_ratectr.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
/* Global MCGP general rate counters */
|
||||
enum {
|
||||
MGCP_GENERAL_RX_MSGS_TOTAL,
|
||||
MGCP_GENERAL_RX_MSGS_RETRANSMITTED,
|
||||
MGCP_GENERAL_RX_MSGS_HANDLED,
|
||||
MGCP_GENERAL_RX_MSGS_UNHANDLED,
|
||||
MGCP_GENERAL_RX_FAIL_MSG_PARSE,
|
||||
MGCP_GENERAL_RX_FAIL_NO_ENDPOINT,
|
||||
};
|
||||
|
||||
/* Trunk-global MCGP CRCX related rate counters */
|
||||
enum {
|
||||
MGCP_CRCX_SUCCESS,
|
||||
MGCP_CRCX_FAIL_BAD_ACTION,
|
||||
MGCP_CRCX_FAIL_UNHANDLED_PARAM,
|
||||
MGCP_CRCX_FAIL_MISSING_CALLID,
|
||||
MGCP_CRCX_FAIL_INVALID_MODE,
|
||||
MGCP_CRCX_FAIL_LIMIT_EXCEEDED,
|
||||
MGCP_CRCX_FAIL_UNKNOWN_CALLID,
|
||||
MGCP_CRCX_FAIL_ALLOC_CONN,
|
||||
MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC,
|
||||
MGCP_CRCX_FAIL_START_RTP,
|
||||
MGCP_CRCX_FAIL_REJECTED_BY_POLICY,
|
||||
MGCP_CRCX_FAIL_NO_OSMUX,
|
||||
MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS,
|
||||
MGCP_CRCX_FAIL_CODEC_NEGOTIATION,
|
||||
MGCP_CRCX_FAIL_BIND_PORT,
|
||||
MGCP_CRCX_FAIL_AVAIL,
|
||||
MGCP_CRCX_FAIL_CLAIM,
|
||||
};
|
||||
|
||||
/* Trunk-global MCGP MDCX related rate counters */
|
||||
enum {
|
||||
MGCP_MDCX_SUCCESS,
|
||||
MGCP_MDCX_FAIL_WILDCARD,
|
||||
MGCP_MDCX_FAIL_NO_CONN,
|
||||
MGCP_MDCX_FAIL_INVALID_CALLID,
|
||||
MGCP_MDCX_FAIL_INVALID_CONNID,
|
||||
MGCP_MDCX_FAIL_UNHANDLED_PARAM,
|
||||
MGCP_MDCX_FAIL_NO_CONNID,
|
||||
MGCP_MDCX_FAIL_CONN_NOT_FOUND,
|
||||
MGCP_MDCX_FAIL_INVALID_MODE,
|
||||
MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS,
|
||||
MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
|
||||
MGCP_MDCX_FAIL_START_RTP,
|
||||
MGCP_MDCX_FAIL_REJECTED_BY_POLICY,
|
||||
MGCP_MDCX_DEFERRED_BY_POLICY,
|
||||
MGCP_MDCX_FAIL_AVAIL,
|
||||
};
|
||||
|
||||
/* Trunk-global MCGP DLCX related rate counters */
|
||||
enum {
|
||||
MGCP_DLCX_SUCCESS,
|
||||
MGCP_DLCX_FAIL_WILDCARD,
|
||||
MGCP_DLCX_FAIL_NO_CONN,
|
||||
MGCP_DLCX_FAIL_INVALID_CALLID,
|
||||
MGCP_DLCX_FAIL_INVALID_CONNID,
|
||||
MGCP_DLCX_FAIL_UNHANDLED_PARAM,
|
||||
MGCP_DLCX_FAIL_REJECTED_BY_POLICY,
|
||||
MGCP_DLCX_DEFERRED_BY_POLICY,
|
||||
MGCP_DLCX_FAIL_AVAIL,
|
||||
};
|
||||
|
||||
/* Trunk-global E1 related counters */
|
||||
enum {
|
||||
E1_I460_TRAU_RX_FAIL_CTR,
|
||||
E1_I460_TRAU_TX_FAIL_CTR,
|
||||
E1_I460_TRAU_MUX_EMPTY_CTR,
|
||||
};
|
||||
|
||||
/* NOTE: When adding counters, also the dump_ratectr_* routines in vty.c must be updated. */
|
||||
|
||||
struct mgcp_ratectr_global {
|
||||
/* Rate counter group which contains stats for generic MGCP events. */
|
||||
struct rate_ctr_group *mgcp_general_ctr_group;
|
||||
};
|
||||
|
||||
struct mgcp_ratectr_trunk {
|
||||
/* Rate counter group which contains stats for processed CRCX commands. */
|
||||
struct rate_ctr_group *mgcp_crcx_ctr_group;
|
||||
/* Rate counter group which contains stats for processed MDCX commands. */
|
||||
struct rate_ctr_group *mgcp_mdcx_ctr_group;
|
||||
/* Rate counter group which contains stats for processed DLCX commands. */
|
||||
struct rate_ctr_group *mgcp_dlcx_ctr_group;
|
||||
/* Rate counter group which aggregates stats of individual RTP connections. */
|
||||
struct rate_ctr_group *all_rtp_conn_stats;
|
||||
/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */
|
||||
struct rate_ctr_group *e1_stats;
|
||||
};
|
||||
|
||||
int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr);
|
||||
int mgcp_ratectr_trunk_alloc(void *ctx, struct mgcp_ratectr_trunk *ratectr);
|
@@ -24,7 +24,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn);
|
||||
|
80
include/osmocom/mgcp/mgcp_trunk.h
Normal file
80
include/osmocom/mgcp/mgcp_trunk.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/gsm/i460_mux.h>
|
||||
|
||||
#define LOGPTRUNK(trunk, cat, level, fmt, args...) \
|
||||
LOGP(cat, level, "trunk:%u " fmt, \
|
||||
trunk ? trunk->trunk_nr : 0, \
|
||||
## args)
|
||||
|
||||
|
||||
enum mgcp_trunk_type {
|
||||
MGCP_TRUNK_VIRTUAL,
|
||||
MGCP_TRUNK_E1,
|
||||
};
|
||||
|
||||
struct mgcp_trunk {
|
||||
struct llist_head entry;
|
||||
|
||||
struct mgcp_config *cfg;
|
||||
|
||||
int trunk_nr;
|
||||
enum mgcp_trunk_type trunk_type;
|
||||
|
||||
char *audio_fmtp_extra;
|
||||
int audio_send_ptime;
|
||||
int audio_send_name;
|
||||
int audio_loop;
|
||||
|
||||
int no_audio_transcoding;
|
||||
|
||||
int omit_rtcp;
|
||||
int keepalive_interval;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* 0: don't, 1: once */
|
||||
int force_aligned_timing;
|
||||
bool rfc5993_hr_convert;
|
||||
|
||||
/* spec handling */
|
||||
int force_realloc;
|
||||
|
||||
/* timer */
|
||||
struct osmo_timer_list keepalive_timer;
|
||||
|
||||
/* When set, incoming RTP packets are not filtered
|
||||
* when ports and ip-address do not match (debug) */
|
||||
int rtp_accept_all;
|
||||
|
||||
unsigned int number_endpoints;
|
||||
struct mgcp_endpoint **endpoints;
|
||||
|
||||
/* global rate counters to measure the trunks overall performance and health */
|
||||
struct mgcp_ratectr_trunk ratectr;
|
||||
|
||||
union {
|
||||
/* Virtual trunk specific */
|
||||
struct {
|
||||
unsigned int vty_number_endpoints;
|
||||
} v;
|
||||
/* E1 specific */
|
||||
struct {
|
||||
unsigned int vty_line_nr;
|
||||
struct e1inp_line *line;
|
||||
bool ts_in_use[31];
|
||||
struct osmo_i460_timeslot i460_ts[31];
|
||||
} e1;
|
||||
};
|
||||
};
|
||||
|
||||
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
|
||||
int mgcp_trunk_equip(struct mgcp_trunk *trunk);
|
||||
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr);
|
||||
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname);
|
||||
int e1_trunk_nr_from_epname(const char *epname);
|
||||
struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num);
|
||||
|
||||
/* The virtual trunk is always created on trunk id 0 for historical reasons,
|
||||
* use this define constant as ID when allocating a virtual trunk. Other
|
||||
* trunks may be assigned with arbritrary id numbers */
|
||||
#define MGCP_VIRT_TRUNK_ID 0
|
@@ -137,6 +137,8 @@ uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp);
|
||||
|
||||
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp);
|
||||
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp);
|
||||
const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts,
|
||||
uint8_t rate, uint8_t offset);
|
||||
|
||||
/* Invoked when an MGCP response is received or sending failed. When the
|
||||
* response is passed as NULL, this indicates failure during transmission. */
|
||||
|
@@ -20,7 +20,7 @@ AM_LDFLAGS = \
|
||||
|
||||
# This is not at all related to the release version, but a range of supported
|
||||
# API versions. Read TODO_RELEASE in the source tree's root!
|
||||
MGCP_CLIENT_LIBVERSION=6:0:0
|
||||
MGCP_CLIENT_LIBVERSION=7:0:1
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp-client.la \
|
||||
|
@@ -735,6 +735,8 @@ struct mgcp_client *mgcp_client_init(void *ctx,
|
||||
struct mgcp_client *mgcp;
|
||||
|
||||
mgcp = talloc_zero(ctx, struct mgcp_client);
|
||||
if (!mgcp)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&mgcp->responses_pending);
|
||||
INIT_LLIST_HEAD(&mgcp->inuse_endpoints);
|
||||
@@ -828,8 +830,8 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
|
||||
inet_aton(mgcp->actual.remote_addr, &addr.sin_addr);
|
||||
mgcp->remote_addr = htonl(addr.sin_addr.s_addr);
|
||||
|
||||
osmo_wqueue_init(wq, 10);
|
||||
wq->bfd.when = BSC_FD_READ;
|
||||
osmo_wqueue_init(wq, 1024);
|
||||
wq->bfd.when = OSMO_FD_READ;
|
||||
wq->bfd.data = mgcp;
|
||||
wq->read_cb = mgcp_do_read;
|
||||
wq->write_cb = mgcp_do_write;
|
||||
@@ -892,11 +894,66 @@ static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgc
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
/*! Compose endpoint name for a wildcarded request to the virtual trunk
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \returns string containing the endpoint name (e.g. rtpbridge\*@mgw) */
|
||||
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
|
||||
{
|
||||
return _mgcp_client_name_append_domain(mgcp, "rtpbridge/*");
|
||||
}
|
||||
|
||||
/*! Compose endpoint name for an E1 endpoint.
|
||||
* \param[in] ctx talloc context.
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \param[in] trunk_id id number of the E1 trunk (1-64).
|
||||
* \param[in] ts timeslot on the E1 trunk (1-31).
|
||||
* \param[in] rate bitrate used on the E1 trunk (e.g 16 for 16kbit).
|
||||
* \param[in] offset bit offset of the E1 subslot (e.g. 4 for the third 16k subslot).
|
||||
* \returns string containing the endpoint name (e.g. ds/e1-1/s-1/su16-4). */
|
||||
const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts,
|
||||
uint8_t rate, uint8_t offset)
|
||||
{
|
||||
/* See also comment in libosmo-mgcp, mgcp_client.c, gen_e1_epname() */
|
||||
const uint8_t valid_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 };
|
||||
const uint8_t valid_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
uint8_t i;
|
||||
bool rate_offs_valid = false;
|
||||
char *epname;
|
||||
|
||||
epname =
|
||||
talloc_asprintf(ctx, "ds/e1-%u/s-%u/su%u-%u@%s", trunk_id, ts, rate, offset,
|
||||
mgcp_client_endpoint_domain(mgcp));
|
||||
if (!epname) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP e1-endpoint name!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if the supplied rate/offset pair resembles a valid combination */
|
||||
for (i = 0; i < sizeof(valid_rates); i++) {
|
||||
if (valid_rates[i] == rate && valid_offsets[i] == offset)
|
||||
rate_offs_valid = true;
|
||||
}
|
||||
if (!rate_offs_valid) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot compose MGCP e1-endpoint name (%s), rate(%u)/offset(%u) combination is invalid!\n", epname,
|
||||
rate, offset);
|
||||
talloc_free(epname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* An E1 line has a maximum of 32 timeslots, while the first (ts=0) is
|
||||
* reserverd for framing and alignment, so we can not use it here. */
|
||||
if (ts == 0 || ts > 31) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot compose MGCP e1-endpoint name (%s), E1-timeslot number (%u) is invalid!\n", epname, ts);
|
||||
talloc_free(epname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return epname;
|
||||
}
|
||||
|
||||
struct mgcp_response_pending * mgcp_client_pending_add(
|
||||
struct mgcp_client *mgcp,
|
||||
mgcp_trans_id_t trans_id,
|
||||
@@ -906,6 +963,9 @@ struct mgcp_response_pending * mgcp_client_pending_add(
|
||||
struct mgcp_response_pending *pending;
|
||||
|
||||
pending = talloc_zero(mgcp, struct mgcp_response_pending);
|
||||
if (!pending)
|
||||
return NULL;
|
||||
|
||||
pending->trans_id = trans_id;
|
||||
pending->response_cb = response_cb;
|
||||
pending->priv = priv;
|
||||
@@ -925,7 +985,7 @@ struct mgcp_response_pending * mgcp_client_pending_add(
|
||||
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
mgcp_response_cb_t response_cb, void *priv)
|
||||
{
|
||||
struct mgcp_response_pending *pending;
|
||||
struct mgcp_response_pending *pending = NULL;
|
||||
mgcp_trans_id_t trans_id;
|
||||
int rc;
|
||||
|
||||
@@ -937,7 +997,14 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pending = mgcp_client_pending_add(mgcp, trans_id, response_cb, priv);
|
||||
/* Do not allocate a dummy 'mgcp_response_pending' entry */
|
||||
if (response_cb != NULL) {
|
||||
pending = mgcp_client_pending_add(mgcp, trans_id, response_cb, priv);
|
||||
if (!pending) {
|
||||
talloc_free(msg);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
if (msgb_l2len(msg) > 4096) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
@@ -959,9 +1026,13 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
return 0;
|
||||
|
||||
mgcp_tx_error:
|
||||
if (!pending)
|
||||
return rc;
|
||||
/* Dequeue pending response, it's going to be free()d */
|
||||
llist_del(&pending->entry);
|
||||
/* Pass NULL to response cb to indicate an error */
|
||||
mgcp_client_handle_response(mgcp, pending, NULL);
|
||||
return -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! Cancel a pending transaction.
|
||||
@@ -1206,6 +1277,7 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
"Endpoint name (%s) lacks separator (@), can not generate MGCP message\n",
|
||||
mgcp_msg->endpoint);
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc += msgb_printf(msg, " %s", mgcp_msg->endpoint);
|
||||
|
@@ -10,6 +10,8 @@ AM_CFLAGS = \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(LIBOSMOABIS_CFLAGS) \
|
||||
$(LIBOSMOTRAU_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -18,6 +20,8 @@ AM_LDFLAGS = \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMOTRAU_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -40,4 +44,8 @@ libosmo_mgcp_a_SOURCES = \
|
||||
mgcp_conn.c \
|
||||
mgcp_stat.c \
|
||||
mgcp_endp.c \
|
||||
mgcp_trunk.c \
|
||||
mgcp_ctrl.c \
|
||||
mgcp_ratectr.c \
|
||||
mgcp_e1.c \
|
||||
$(NULL)
|
||||
|
@@ -17,8 +17,13 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Helper function to dump codec information of a specified codec to a printable
|
||||
@@ -279,36 +284,16 @@ error:
|
||||
* Helper function for mgcp_codec_decide() */
|
||||
static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
char codec_name[64];
|
||||
|
||||
/* A codec name must be set, if not, this might mean that the codec
|
||||
* (payload type) that was assigned is unknown to us so we must stop
|
||||
* here. */
|
||||
if (!codec->subtype_name)
|
||||
return false;
|
||||
|
||||
/* We now extract the codec_name (letters before the /, e.g. "GSM"
|
||||
* from the audio name that is stored in the trunk configuration.
|
||||
* We do not compare to the full audio_name because we expect that
|
||||
* "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the
|
||||
* audio name of the codec is set to "GSM" */
|
||||
if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
|
||||
return false;
|
||||
/* FIXME: implement meaningful checks to make sure that the given codec
|
||||
* is compatible with the given endpoint */
|
||||
|
||||
/* Finally we check if the subtype_name we have generated from the
|
||||
* audio_name in the trunc struct patches the codec_name of the
|
||||
* given codec */
|
||||
if (strcasecmp(codec_name, codec->subtype_name) == 0)
|
||||
return true;
|
||||
|
||||
/* FIXME: It is questinable that the method to pick a compatible
|
||||
* codec can work properly. Since this useses tcfg->audio_name, as
|
||||
* a reference, which is set to "AMR/8000" permanently.
|
||||
* tcfg->audio_name must be updated by the first connection that
|
||||
* has been made on an endpoint, so that the second connection
|
||||
* can make a meaningful decision here */
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*! Decide for one suitable codec
|
||||
@@ -335,7 +320,7 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
|
||||
for (i = 0; i < rtp->codecs_assigned; i++) {
|
||||
/* When no transcoding is available, avoid codecs that would
|
||||
* require transcoding. */
|
||||
if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
|
||||
if (endp->trunk->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
|
||||
rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
|
||||
continue;
|
||||
@@ -451,3 +436,28 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
|
||||
|
||||
return codec_dst->payload_type;
|
||||
}
|
||||
|
||||
/* Find the payload type number configured for a specific codec by SDP.
|
||||
* For example, IuUP gets assigned a payload type number, and the endpoint needs to translate that to the number
|
||||
* assigned to "AMR" on the other conn (by a=rtpmap:N).
|
||||
* \param conn The side of an endpoint to get the payload type number for (to translate the payload type number to).
|
||||
* \param subtype_name SDP codec name without parameters (e.g. "AMR").
|
||||
* \param match_nr Index for the match found, first being match_nr == 0. Iterate all matches by calling multiple times
|
||||
* with incrementing match_nr.
|
||||
* \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found.
|
||||
*/
|
||||
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
|
||||
const char *subtype_name, unsigned int match_nr)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < conn->end.codecs_assigned; i++) {
|
||||
if (!strcmp(conn->end.codecs[i].subtype_name, subtype_name)) {
|
||||
if (match_nr) {
|
||||
match_nr--;
|
||||
continue;
|
||||
}
|
||||
return &conn->end.codecs[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@@ -22,9 +22,11 @@
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_network.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
@@ -254,12 +256,10 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
aggregate_rtp_conn_stats(struct mgcp_trunk_config *trunk, struct mgcp_conn_rtp *conn_rtp)
|
||||
static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
struct rate_ctr_group *all_stats = trunk->all_rtp_conn_stats;
|
||||
struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
|
||||
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
|
||||
int i;
|
||||
|
||||
if (all_stats == NULL || conn_stats == NULL)
|
||||
return;
|
||||
@@ -269,8 +269,11 @@ aggregate_rtp_conn_stats(struct mgcp_trunk_config *trunk, struct mgcp_conn_rtp *
|
||||
* All other counters in both counter groups correspond to each other. */
|
||||
OSMO_ASSERT(conn_stats->desc->num_ctr + 1 == all_stats->desc->num_ctr);
|
||||
|
||||
for (i = 0; i < conn_stats->desc->num_ctr; i++)
|
||||
rate_ctr_add(&all_stats->ctr[i], conn_stats->ctr[i].current);
|
||||
/* all other counters are [now] updated in real-time */
|
||||
rate_ctr_add(&all_stats->ctr[IN_STREAM_ERR_TSTMP_CTR],
|
||||
conn_stats->ctr[IN_STREAM_ERR_TSTMP_CTR].current);
|
||||
rate_ctr_add(&all_stats->ctr[OUT_STREAM_ERR_TSTMP_CTR],
|
||||
conn_stats->ctr[OUT_STREAM_ERR_TSTMP_CTR].current);
|
||||
|
||||
rate_ctr_inc(&all_stats->ctr[RTP_NUM_CONNECTIONS]);
|
||||
}
|
||||
@@ -294,7 +297,7 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
|
||||
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
aggregate_rtp_conn_stats(endp->tcfg, &conn->u.rtp);
|
||||
aggregate_rtp_conn_stats(endp, &conn->u.rtp);
|
||||
mgcp_rtp_conn_cleanup(&conn->u.rtp);
|
||||
break;
|
||||
default:
|
||||
@@ -395,3 +398,13 @@ struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! get oldest connection in the list.
|
||||
* \param[in] endp associated endpoint */
|
||||
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (llist_empty(&endp->conns))
|
||||
return NULL;
|
||||
|
||||
return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
|
||||
}
|
||||
|
36
src/libosmo-mgcp/mgcp_ctrl.c
Normal file
36
src/libosmo-mgcp/mgcp_ctrl.c
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
|
||||
|
||||
static int mgw_ctrl_node_lookup(void *data, vector vline, int *node_type,
|
||||
void **node_data, int *i)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
|
||||
const char *bind_addr, uint16_t port)
|
||||
{
|
||||
return ctrl_interface_setup_dynip2(cfg, bind_addr, port, mgw_ctrl_node_lookup,
|
||||
_LAST_CTRL_NODE);
|
||||
}
|
690
src/libosmo-mgcp/mgcp_e1.c
Normal file
690
src/libosmo-mgcp/mgcp_e1.c
Normal file
@@ -0,0 +1,690 @@
|
||||
/* E1 traffic handling */
|
||||
|
||||
/*
|
||||
* (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/abis/abis.h>
|
||||
|
||||
#include <osmocom/trau/trau_sync.h>
|
||||
#include <osmocom/trau/trau_frame.h>
|
||||
#include <osmocom/trau/trau_rtp.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/netif/rtp.h>
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
#include <osmocom/mgcp/mgcp_e1.h>
|
||||
#include <osmocom/codec/codec.h>
|
||||
|
||||
#define DEBUG_BITS_MAX 80
|
||||
#define DEBUG_BYTES_MAX 40
|
||||
#define DEBUG_E1_TS 0
|
||||
#define E1_TS_BYTES 160
|
||||
#define E1_TRAU_BITS 320
|
||||
#define E1_TRAU_BITS_MSGB 2048
|
||||
|
||||
static struct mgcp_config *cfg;
|
||||
|
||||
static const struct e1inp_line_ops dummy_e1_line_ops = {
|
||||
.sign_link_up = NULL,
|
||||
.sign_link_down = NULL,
|
||||
.sign_link = NULL,
|
||||
};
|
||||
|
||||
/* EFR idle frame */
|
||||
static const ubit_t idle_tf_efr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
};
|
||||
|
||||
/* FR idle frame */
|
||||
static const ubit_t idle_tf_fr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
};
|
||||
|
||||
/* Idle speech frame, see also GSM 08.60, chapter 3.4 */
|
||||
static const ubit_t idle_tf_spch[] = { 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
};
|
||||
|
||||
/* If the RTP transmission has dropouts for some reason the I.460 TX-Queue may
|
||||
* run empty. In order to make sure that the TRAU frame transmission continues
|
||||
* we generate idle TRAU frames here. */
|
||||
static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_data)
|
||||
{
|
||||
struct mgcp_endpoint *endp = user_data;
|
||||
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
|
||||
struct msgb *msg = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame");
|
||||
uint8_t *ptr;
|
||||
const uint8_t *ptr_ft;
|
||||
enum osmo_trau_frame_type ft;
|
||||
|
||||
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_MUX_EMPTY_CTR]);
|
||||
|
||||
/* Choose an appropiate idle frame type */
|
||||
ft = endp->e1.trau_rtp_st->type;
|
||||
switch (ft) {
|
||||
case OSMO_TRAU16_FT_FR:
|
||||
ptr_ft = idle_tf_fr;
|
||||
break;
|
||||
case OSMO_TRAU16_FT_EFR:
|
||||
ptr_ft = idle_tf_efr;
|
||||
break;
|
||||
default:
|
||||
/* FIXME: What about 8k subslots and AMR frames? */
|
||||
ptr_ft = idle_tf_spch;
|
||||
}
|
||||
|
||||
/* Put the replacement into a message buffer and enqueue it into the
|
||||
* I.460 multiplexer */
|
||||
ptr = msgb_put(msg, E1_TRAU_BITS);
|
||||
memcpy(ptr, ptr_ft, E1_TRAU_BITS);
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-IDLE-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg),
|
||||
osmo_ubit_dump(msgb_data(msg), msgb_length(msg) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg)));
|
||||
osmo_i460_mux_enqueue(endp->e1.schan, msg);
|
||||
}
|
||||
|
||||
/* called by I.460 de-multeiplexer; feed output of I.460 demux into TRAU frame sync */
|
||||
static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits,
|
||||
unsigned int num_bits)
|
||||
{
|
||||
struct mgcp_endpoint *endp = user_data;
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u bits from subslot: %s...\n", num_bits,
|
||||
osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
|
||||
|
||||
OSMO_ASSERT(endp->e1.trau_sync_fi);
|
||||
osmo_trau_sync_rx_ubits(endp->e1.trau_sync_fi, bits, num_bits);
|
||||
}
|
||||
|
||||
/* called for each synchronized TRAU frame received; decode frame + convert to RTP
|
||||
* (the resulting frame will be prepended with an all-zero (12-byte) rtp header) */
|
||||
static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc(RTP_BUF_SIZE, "RTP-rx-from-E1");
|
||||
unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
|
||||
struct mgcp_endpoint *endp = user_data;
|
||||
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
|
||||
struct mgcp_conn *conn_dst;
|
||||
struct osmo_trau_frame fr;
|
||||
int rc;
|
||||
|
||||
if (!bits || num_bits == 0)
|
||||
goto skip;
|
||||
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u TRAU frame bits from E1 subslot: %s...\n",
|
||||
num_bits, osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
|
||||
|
||||
/* Decode TRAU frame */
|
||||
switch (endp->e1.scd.rate) {
|
||||
case OSMO_I460_RATE_8k:
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 8k trau frame...\n");
|
||||
rc = osmo_trau_frame_decode_8k(&fr, bits, OSMO_TRAU_DIR_UL);
|
||||
break;
|
||||
case OSMO_I460_RATE_16k:
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 16k trau frame...\n");
|
||||
rc = osmo_trau_frame_decode_16k(&fr, bits, OSMO_TRAU_DIR_UL);
|
||||
break;
|
||||
default:
|
||||
/* TRAU frames only exist in 8K or 16K subslots. */
|
||||
OSMO_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
if (rc != 0) {
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to decode trau frame\n");
|
||||
goto skip;
|
||||
}
|
||||
|
||||
/* Check if the payload type is supported and what the expected lenth
|
||||
* of the RTP payload will be. */
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoded trau frame type: %s\n",
|
||||
osmo_trau_frame_type_name(fr.type));
|
||||
|
||||
/* Convert decoded trau frame to RTP frame */
|
||||
struct osmo_trau2rtp_state t2rs = {
|
||||
.type = fr.type,
|
||||
};
|
||||
rc = osmo_trau2rtp(msgb_data(msg) + rtp_hdr_len, msg->data_len - rtp_hdr_len, &fr, &t2rs);
|
||||
if (rc <= 0) {
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to convert trau frame to RTP audio\n");
|
||||
goto skip;
|
||||
}
|
||||
msgb_put(msg, rtp_hdr_len + rc);
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: encoded %u bytes of RTP audio: %s\n", rc,
|
||||
osmo_hexdump(msgb_data(msg) + rtp_hdr_len, msgb_length(msg) - rtp_hdr_len));
|
||||
|
||||
/* Forward RTP data to IP */
|
||||
conn_dst = llist_first_entry(&endp->conns, struct mgcp_conn, entry);
|
||||
if (!conn_dst) {
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG,
|
||||
"E1-I.460-RX: unable to forward RTP audio data from E1: no connection to forward an incoming RTP packet to\n");
|
||||
goto skip;
|
||||
}
|
||||
OSMO_ASSERT(conn_dst->type == MGCP_CONN_TYPE_RTP);
|
||||
|
||||
mgcp_send(endp, 1, NULL, msg, &conn_dst->u.rtp, &conn_dst->u.rtp);
|
||||
|
||||
msgb_free(msg);
|
||||
return;
|
||||
skip:
|
||||
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_RX_FAIL_CTR]);
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Function to handle outgoing E1 traffic */
|
||||
static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc(E1_TS_BYTES, "E1-TX-timeslot-bytes");
|
||||
uint8_t *ptr;
|
||||
|
||||
/* Get E1 frame from I.460 multiplexer */
|
||||
ptr = msgb_put(msg, E1_TS_BYTES);
|
||||
osmo_i460_mux_out(&trunk->e1.i460_ts[ts->num - 1], ptr, E1_TS_BYTES);
|
||||
|
||||
#if DEBUG_E1_TS == 1
|
||||
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-TX: (ts:%u) sending %u bytes: %s...\n", ts->num, msgb_length(msg),
|
||||
osmo_hexdump_nospc(msgb_data(msg),
|
||||
msgb_length(msg) > DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
|
||||
#endif
|
||||
/* Hand data over to the E1 stack */
|
||||
msgb_enqueue(&ts->raw.tx_queue, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Callback function to handle incoming E1 traffic */
|
||||
static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
|
||||
{
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
/* Find associated trunk */
|
||||
trunk = mgcp_trunk_by_line_num(cfg, ts->line->num);
|
||||
if (!trunk) {
|
||||
LOGP(DE1, LOGL_DEBUG, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if the incoming data looks sane */
|
||||
if (msgb_length(msg) != E1_TS_BYTES) {
|
||||
LOGPTRUNK(trunk, DE1, LOGL_NOTICE,
|
||||
"E1-RX: (ts:%u) expected length is %u, actual length is %u!\n", ts->num, E1_TS_BYTES,
|
||||
msgb_length(msg));
|
||||
}
|
||||
#if DEBUG_E1_TS == 1
|
||||
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-RX: (ts:%u) receiving %u bytes: %s...\n", ts->num,
|
||||
msgb_length(msg), osmo_hexdump_nospc(msgb_data(msg),
|
||||
msgb_length(msg) >
|
||||
DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
|
||||
#endif
|
||||
|
||||
/* Hand data over to the I.460 demultiplexer. */
|
||||
osmo_i460_demux_in(&trunk->e1.i460_ts[ts->num - 1], msgb_data(msg), msgb_length(msg));
|
||||
|
||||
/* Trigger sending of pending E1 traffic */
|
||||
e1_send(ts, trunk);
|
||||
}
|
||||
|
||||
/*! Find an endpoint by its name on a specified trunk.
|
||||
* \param[in] trunk trunk configuration.
|
||||
* \param[in] ts_nr E1 timeslot number.
|
||||
* \returns -EINVAL on failure, 0 on success. */
|
||||
int mgcp_e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
|
||||
{
|
||||
/*! Each timeslot needs only to be configured once. The Timeslot then
|
||||
* stays open and permanently receives data. It is then up to the
|
||||
* I.460 demultiplexer to add/remove subchannels as needed. It is
|
||||
* allowed to call this function multiple times since we check if the
|
||||
* timeslot is already configured. */
|
||||
|
||||
struct e1inp_line *e1_line;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
|
||||
cfg = trunk->cfg;
|
||||
|
||||
if (trunk->e1.ts_in_use[ts_nr - 1]) {
|
||||
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u already set up, skipping...\n", ts_nr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get E1 line */
|
||||
if (!trunk->e1.line) {
|
||||
e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
|
||||
if (!e1_line) {
|
||||
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "no such E1 line %u - check VTY config!\n",
|
||||
trunk->e1.vty_line_nr);
|
||||
return -EINVAL;
|
||||
}
|
||||
e1inp_line_bind_ops(e1_line, &dummy_e1_line_ops);
|
||||
} else
|
||||
e1_line = trunk->e1.line;
|
||||
if (!e1_line)
|
||||
return -EINVAL;
|
||||
|
||||
/* Configure E1 timeslot */
|
||||
rc = e1inp_ts_config_raw(&e1_line->ts[ts_nr - 1], e1_line, e1_recv_cb);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
e1inp_line_update(e1_line);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1 timeslot %u set up successfully.\n", ts_nr);
|
||||
trunk->e1.ts_in_use[ts_nr - 1] = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Determine a suitable TRAU frame type for a given codec */
|
||||
static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
|
||||
uint8_t amr_ft, struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (strcmp(sdp_subtype_name, "GSM") == 0)
|
||||
return OSMO_TRAU16_FT_FR;
|
||||
else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
|
||||
return OSMO_TRAU16_FT_EFR;
|
||||
else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
|
||||
return OSMO_TRAU16_FT_HR;
|
||||
else if (strcmp(sdp_subtype_name, "AMR") == 0) {
|
||||
if (i460_rate == OSMO_I460_RATE_8k) {
|
||||
switch (amr_ft) {
|
||||
case AMR_4_75:
|
||||
case AMR_5_15:
|
||||
case AMR_5_90:
|
||||
return OSMO_TRAU8_AMR_LOW;
|
||||
case AMR_6_70:
|
||||
return OSMO_TRAU8_AMR_6k7;
|
||||
case AMR_7_40:
|
||||
return OSMO_TRAU8_AMR_7k4;
|
||||
default:
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR,
|
||||
"E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
|
||||
return OSMO_TRAU_FT_NONE;
|
||||
}
|
||||
}
|
||||
return OSMO_TRAU16_FT_AMR;
|
||||
} else {
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
|
||||
sdp_subtype_name);
|
||||
return OSMO_TRAU_FT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine a suitable TRAU frame type for a given codec */
|
||||
static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name, enum osmo_i460_rate i460_rate,
|
||||
uint8_t amr_ft, struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (strcmp(sdp_subtype_name, "GSM") == 0)
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0)
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0)
|
||||
return OSMO_TRAU_SYNCP_8_HR;
|
||||
else if (strcmp(sdp_subtype_name, "AMR") == 0) {
|
||||
if (i460_rate == OSMO_I460_RATE_8k) {
|
||||
switch (amr_ft) {
|
||||
case AMR_4_75:
|
||||
case AMR_5_15:
|
||||
case AMR_5_90:
|
||||
return OSMO_TRAU_SYNCP_8_AMR_LOW;
|
||||
case AMR_6_70:
|
||||
return OSMO_TRAU_SYNCP_8_AMR_6K7;
|
||||
case AMR_7_40:
|
||||
return OSMO_TRAU_SYNCP_8_AMR_7K4;
|
||||
default:
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR,
|
||||
"E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft);
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
}
|
||||
}
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
} else {
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n",
|
||||
sdp_subtype_name);
|
||||
return OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find out if a given TRAU frame type is AMR */
|
||||
static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
|
||||
{
|
||||
|
||||
switch (ft) {
|
||||
case OSMO_TRAU16_FT_AMR:
|
||||
case OSMO_TRAU8_AMR_LOW:
|
||||
case OSMO_TRAU8_AMR_6k7:
|
||||
case OSMO_TRAU8_AMR_7k4:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* !Equip E1 endpoint with I.460 mux resources.
|
||||
* \param[in] endp endpoint to equip
|
||||
* \param[in] ts E1 timeslot number.
|
||||
* \param[in] ss E1 subslot number.
|
||||
* \param[in] offset E1 bit offset.
|
||||
* \returns 0 on success, -EINVAL on error. */
|
||||
int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs)
|
||||
{
|
||||
int rc;
|
||||
enum osmo_tray_sync_pat_id sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
|
||||
OSMO_ASSERT(ts != 0);
|
||||
OSMO_ASSERT(ts != 0xFF);
|
||||
OSMO_ASSERT(ss != 0xFF);
|
||||
OSMO_ASSERT(offs != 0xFF);
|
||||
|
||||
memset(&endp->e1, 0, sizeof(endp->e1));
|
||||
|
||||
endp->e1.last_amr_ft = AMR_4_75;
|
||||
|
||||
/* Set up E1 line / timeslot */
|
||||
rc = mgcp_e1_init(endp->trunk, ts);
|
||||
if (rc != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Set up I.460 mux */
|
||||
switch (e1_rates[ss]) {
|
||||
case 64:
|
||||
endp->e1.scd.rate = OSMO_I460_RATE_64k;
|
||||
endp->e1.scd.demux.num_bits = 160 * 8;
|
||||
break;
|
||||
case 32:
|
||||
endp->e1.scd.rate = OSMO_I460_RATE_32k;
|
||||
endp->e1.scd.demux.num_bits = 80 * 8;
|
||||
break;
|
||||
case 16:
|
||||
endp->e1.scd.rate = OSMO_I460_RATE_16k;
|
||||
endp->e1.scd.demux.num_bits = 40 * 8;
|
||||
sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR;
|
||||
break;
|
||||
case 8:
|
||||
endp->e1.scd.rate = OSMO_I460_RATE_8k;
|
||||
endp->e1.scd.demux.num_bits = 20 * 8;
|
||||
sync_pat_id = OSMO_TRAU_SYNCP_8_HR;
|
||||
break;
|
||||
}
|
||||
endp->e1.scd.bit_offset = offs;
|
||||
endp->e1.scd.demux.out_cb_bits = e1_i460_demux_bits_cb;
|
||||
endp->e1.scd.demux.out_cb_bytes = NULL;
|
||||
endp->e1.scd.demux.user_data = endp;
|
||||
endp->e1.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb;
|
||||
endp->e1.scd.mux.user_data = endp;
|
||||
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "adding I.460 subchannel: ts=%u, bit_offset=%u, rate=%uk, num_bits=%lu\n", ts,
|
||||
offs, e1_rates[ss], endp->e1.scd.demux.num_bits);
|
||||
endp->e1.schan = osmo_i460_subchan_add(endp, &endp->trunk->e1.i460_ts[ts - 1], &endp->e1.scd);
|
||||
if (!endp->e1.schan) {
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 subchannel: failed!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (endp->e1.scd.rate == OSMO_I460_RATE_16k || endp->e1.scd.rate == OSMO_I460_RATE_8k) {
|
||||
/* TRAU frames are only specified for 16k and 8k subslots. For all other subslot
|
||||
* types the concept of TRAU frames does not apply. However, at the moment this
|
||||
* is the only format we currently support in osmo-mgw */
|
||||
endp->e1.trau_sync_fi = osmo_trau_sync_alloc(endp, "trau-sync", sync_frame_out_cb, sync_pat_id, endp);
|
||||
if (!endp->e1.trau_sync_fi) {
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 TRAU frame sync: failed!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
endp->e1.trau_rtp_st = talloc_zero(endp->e1.trau_sync_fi, struct osmo_trau2rtp_state);
|
||||
endp->e1.trau_rtp_st->type = OSMO_TRAU_FT_NONE;
|
||||
} else {
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR,
|
||||
"osmo-mgw currently only supports 16K and 8K subslots (TRAU frames)!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Update E1 related parameters (codec and sync pattern).
|
||||
* \param[in] endp endpoint to update. */
|
||||
void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
struct mgcp_rtp_codec *codec;
|
||||
enum osmo_tray_sync_pat_id sync_pat_id;
|
||||
|
||||
/* In order to determine the codec, find the oldest connection on
|
||||
* the endpoint and use its codec information. Normally on an E1
|
||||
* endpoint no more than one connection should exist. */
|
||||
conn = mgcp_conn_get_oldest(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
codec = conn->u.rtp.end.codec;
|
||||
OSMO_ASSERT(codec);
|
||||
|
||||
/* Update codec information */
|
||||
endp->e1.trau_rtp_st->type =
|
||||
determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
|
||||
endp->e1.last_codec = codec;
|
||||
|
||||
/* Update sync pattern */
|
||||
sync_pat_id = determine_trau_sync_pat(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
|
||||
osmo_trau_sync_set_pat(endp->e1.trau_sync_fi, sync_pat_id);
|
||||
}
|
||||
|
||||
/*! Remove E1 resources from endpoint
|
||||
* \param[in] endp endpoint to release. */
|
||||
void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
|
||||
{
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
|
||||
|
||||
if (endp->e1.schan)
|
||||
osmo_i460_subchan_del(endp->e1.schan);
|
||||
if (endp->e1.trau_rtp_st)
|
||||
talloc_free(endp->e1.trau_rtp_st);
|
||||
if (endp->e1.trau_sync_fi)
|
||||
osmo_fsm_inst_term(endp->e1.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
|
||||
memset(&endp->e1, 0, sizeof(endp->e1));
|
||||
}
|
||||
|
||||
/*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit.
|
||||
* \param[in] endp related endpoint (does not take ownership).
|
||||
* \param[in] codec configuration.
|
||||
* \param[in] msg RTP message buffer (including RTP header).
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg)
|
||||
{
|
||||
struct msgb *msg_tf = msgb_alloc(E1_TRAU_BITS_MSGB, "E1-I.460-TX-TRAU-frame");
|
||||
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
|
||||
unsigned int rtp_hdr_len = sizeof(struct rtp_hdr);
|
||||
struct osmo_trau_frame tf;
|
||||
uint8_t amr_ft;
|
||||
int rc;
|
||||
|
||||
/* Extract AMR frame type from AMR head (if AMR is used) */
|
||||
if (tf_type_is_amr(endp->e1.trau_rtp_st->type))
|
||||
amr_ft = (msgb_data(msg)[rtp_hdr_len + 1] >> 3) & 0xf;
|
||||
else
|
||||
amr_ft = 0xff;
|
||||
|
||||
/* Adapt TRAU frame type on codec changes */
|
||||
OSMO_ASSERT(endp->e1.last_codec);
|
||||
if (codec != endp->e1.last_codec || (amr_ft != 0xff && amr_ft != endp->e1.last_amr_ft)) {
|
||||
endp->e1.trau_rtp_st->type =
|
||||
determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, amr_ft, endp);
|
||||
endp->e1.last_codec = codec;
|
||||
endp->e1.last_amr_ft = amr_ft;
|
||||
}
|
||||
if (endp->e1.trau_rtp_st->type == OSMO_TRAU_FT_NONE)
|
||||
goto skip;
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: using trau frame type for encoding: %s\n",
|
||||
osmo_trau_frame_type_name(endp->e1.trau_rtp_st->type));
|
||||
|
||||
/* Convert from RTP to TRAU format */
|
||||
msg->l2h = msgb_data(msg) + rtp_hdr_len;
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: decoding %u bytes of RTP audio to TRAU format: %s\n",
|
||||
msgb_length(msg), osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
|
||||
memset(&tf, 0, sizeof(tf));
|
||||
tf.dir = OSMO_TRAU_DIR_DL;
|
||||
rc = osmo_rtp2trau(&tf, msgb_l2(msg), msgb_l2len(msg), endp->e1.trau_rtp_st);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG,
|
||||
"E1-I.460-TX: failed to decode from RTP payload format to TRAU format\n");
|
||||
goto skip;
|
||||
}
|
||||
rc = osmo_trau_frame_encode(msgb_data(msg_tf), msg_tf->data_len, &tf);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: failed to encode TRAU frame\n");
|
||||
goto skip;
|
||||
}
|
||||
msgb_put(msg_tf, rc);
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg_tf),
|
||||
osmo_ubit_dump(msgb_data(msg_tf),
|
||||
msgb_length(msg_tf) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg_tf)));
|
||||
|
||||
/* Enqueue data to I.460 multiplexer */
|
||||
OSMO_ASSERT(endp->e1.schan);
|
||||
OSMO_ASSERT(endp->e1.trau_sync_fi);
|
||||
|
||||
osmo_i460_mux_enqueue(endp->e1.schan, msg_tf);
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: %u bits of audio enqued for E1 tx\n", msgb_length(msg_tf));
|
||||
|
||||
return 0;
|
||||
skip:
|
||||
rate_ctr_inc(&rate_ctrs->ctr[E1_I460_TRAU_TX_FAIL_CTR]);
|
||||
msgb_free(msg_tf);
|
||||
return -1;
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
/* Endpoint types */
|
||||
|
||||
/*
|
||||
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
@@ -21,17 +21,95 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_e1.h>
|
||||
|
||||
#define E1_TIMESLOTS 32
|
||||
#define E1_RATE_MAX 64
|
||||
#define E1_OFFS_MAX 8
|
||||
|
||||
/* Endpoint typeset definition */
|
||||
const struct mgcp_endpoint_typeset ep_typeset = {
|
||||
/* Specify endpoint properties for RTP endpoint */
|
||||
.rtp.max_conns = 2,
|
||||
.rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
|
||||
.rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb
|
||||
.rtp = {
|
||||
.max_conns = 2,
|
||||
.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
|
||||
.cleanup_cb = mgcp_cleanup_rtp_bridge_cb,
|
||||
},
|
||||
/* Specify endpoint properties for E1 endpoint */
|
||||
.e1 = {
|
||||
.max_conns = 1,
|
||||
.dispatch_rtp_cb = mgcp_dispatch_e1_bridge_cb,
|
||||
.cleanup_cb = mgcp_cleanup_e1_bridge_cb,
|
||||
},
|
||||
};
|
||||
|
||||
/* Generate virtual endpoint name from given parameters */
|
||||
static char *gen_virtual_epname(void *ctx, const char *domain,
|
||||
unsigned int index)
|
||||
{
|
||||
return talloc_asprintf(ctx, "%s%x@%s",
|
||||
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, index, domain);
|
||||
}
|
||||
|
||||
/* Generate E1 endpoint name from given numeric parameters */
|
||||
static char *gen_e1_epname(void *ctx, const char *domain, uint8_t trunk_nr,
|
||||
uint8_t ts_nr, uint8_t ss_nr)
|
||||
{
|
||||
unsigned int rate;
|
||||
unsigned int offset;
|
||||
|
||||
OSMO_ASSERT(ss_nr < sizeof(e1_rates));
|
||||
|
||||
rate = e1_rates[ss_nr];
|
||||
offset = e1_offsets[ss_nr];
|
||||
|
||||
return talloc_asprintf(ctx, "%s%u/s-%u/su%u-%u@%s",
|
||||
MGCP_ENDPOINT_PREFIX_E1_TRUNK, trunk_nr, ts_nr,
|
||||
rate, offset, domain);
|
||||
}
|
||||
|
||||
/*! allocate an endpoint and set default values.
|
||||
* \param[in] trunk configuration.
|
||||
* \param[in] name endpoint index.
|
||||
* \returns endpoint on success, NULL on failure. */
|
||||
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
|
||||
unsigned int index)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
endp = talloc_zero(trunk->endpoints, struct mgcp_endpoint);
|
||||
if (!endp)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&endp->conns);
|
||||
endp->cfg = trunk->cfg;
|
||||
endp->trunk = trunk;
|
||||
|
||||
switch (trunk->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
endp->type = &ep_typeset.rtp;
|
||||
endp->name = gen_virtual_epname(endp, trunk->cfg->domain, index);
|
||||
break;
|
||||
case MGCP_TRUNK_E1:
|
||||
endp->type = &ep_typeset.e1;
|
||||
endp->name = gen_e1_epname(endp, trunk->cfg->domain,
|
||||
trunk->trunk_nr,
|
||||
index / MGCP_ENDP_E1_SUBSLOTS, index % MGCP_ENDP_E1_SUBSLOTS);
|
||||
break;
|
||||
default:
|
||||
osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
|
||||
trunk->trunk_type, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
return endp;
|
||||
}
|
||||
|
||||
/*! release endpoint, all open connections are closed.
|
||||
* \param[in] endp endpoint to release */
|
||||
void mgcp_endp_release(struct mgcp_endpoint *endp)
|
||||
@@ -52,4 +130,539 @@ void mgcp_endp_release(struct mgcp_endpoint *endp)
|
||||
talloc_free(endp->local_options.codec);
|
||||
endp->local_options.codec = NULL;
|
||||
endp->wildcarded_req = false;
|
||||
|
||||
if (endp->trunk->trunk_type == MGCP_TRUNK_E1)
|
||||
mgcp_e1_endp_release(endp);
|
||||
}
|
||||
|
||||
/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
|
||||
* "ds/e1-") and write the epname without the prefix back to the memory
|
||||
* pointed at by epname. (per trunk the prefix is the same for all endpoints,
|
||||
* so no ambiguity is introduced) */
|
||||
static void chop_epname_prefix(char *epname, const struct mgcp_trunk *trunk)
|
||||
{
|
||||
size_t prefix_len;
|
||||
switch (trunk->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
|
||||
if (strncmp
|
||||
(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
|
||||
prefix_len) == 0)
|
||||
memmove(epname, epname + prefix_len,
|
||||
strlen(epname) - prefix_len + 1);
|
||||
return;
|
||||
case MGCP_TRUNK_E1:
|
||||
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
|
||||
if (strncmp
|
||||
(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
|
||||
prefix_len) == 0)
|
||||
memmove(epname, epname + prefix_len,
|
||||
strlen(epname) - prefix_len + 1);
|
||||
return;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the endpoint name contains a suffix (e.g. "@mgw") and truncate
|
||||
* epname by writing a '\0' char where the suffix starts. */
|
||||
static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
|
||||
{
|
||||
char *suffix_begin;
|
||||
|
||||
/* Endpoints on the virtual trunk may have a domain name that is
|
||||
* followed after an @ character, this can be chopped off. All
|
||||
* other supported trunk types do not have any suffixes that may
|
||||
* be chopped off */
|
||||
if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
|
||||
suffix_begin = strchr(epname, '@');
|
||||
if (!suffix_begin)
|
||||
return;
|
||||
*suffix_begin = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert all characters in epname to lowercase and strip trunk prefix and
|
||||
* endpoint name suffix (domain name) from epname. The result is written to
|
||||
* to the memory pointed at by epname_stripped. The expected size of the
|
||||
* result is either equal or lower then the length of the input string
|
||||
* (epname) */
|
||||
static void strip_epname(char *epname_stripped, const char *epname,
|
||||
const struct mgcp_trunk *trunk)
|
||||
{
|
||||
osmo_str_tolower_buf(epname_stripped, MGCP_ENDPOINT_MAXLEN, epname);
|
||||
chop_epname_prefix(epname_stripped, trunk);
|
||||
chop_epname_suffix(epname_stripped, trunk);
|
||||
}
|
||||
|
||||
/* Go through the trunk and find a random free (no active calls) endpoint,
|
||||
* this function is called when a wildcarded request is carried out, which
|
||||
* means that it is up to the MGW to choose a random free endpoint. */
|
||||
static struct mgcp_endpoint *find_free_endpoint(const struct mgcp_trunk *trunk)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < trunk->number_endpoints; i++) {
|
||||
endp = trunk->endpoints[i];
|
||||
/* A free endpoint must not serve a call already and it must
|
||||
* be available. */
|
||||
if (endp->callid == NULL && mgcp_endp_avail(endp))
|
||||
return endp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find an endpoint specified by its name. If the endpoint can not be found,
|
||||
* return NULL */
|
||||
static struct mgcp_endpoint *find_specific_endpoint(const char *epname,
|
||||
const struct mgcp_trunk *trunk)
|
||||
{
|
||||
char epname_stripped[MGCP_ENDPOINT_MAXLEN];
|
||||
char epname_stripped_endp[MGCP_ENDPOINT_MAXLEN];
|
||||
struct mgcp_endpoint *endp;
|
||||
unsigned int i;
|
||||
|
||||
/* Strip irrelevant information from the endpoint name */
|
||||
strip_epname(epname_stripped, epname, trunk);
|
||||
|
||||
for (i = 0; i < trunk->number_endpoints; i++) {
|
||||
endp = trunk->endpoints[i];
|
||||
strip_epname(epname_stripped_endp, endp->name, trunk);
|
||||
if (strcmp(epname_stripped_endp, epname_stripped) == 0)
|
||||
return endp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Find an endpoint by its name on a specified trunk.
|
||||
* \param[out] cause pointer to store cause code, can be NULL.
|
||||
* \param[in] epname endpoint name to lookup.
|
||||
* \param[in] trunk where the endpoint is located.
|
||||
* \returns endpoint or NULL if endpoint was not found. */
|
||||
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
|
||||
const struct mgcp_trunk *trunk)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
if (cause)
|
||||
*cause = 0;
|
||||
|
||||
/* At the moment we only support a primitive ('*'-only) method of
|
||||
* wildcarded endpoint searches that picks the next free endpoint on
|
||||
* a trunk. */
|
||||
if (strstr(epname, "*")) {
|
||||
endp = find_free_endpoint(trunk);
|
||||
if (endp) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
|
||||
"(trunk:%d) found free endpoint: %s\n",
|
||||
trunk->trunk_nr, endp->name);
|
||||
endp->wildcarded_req = true;
|
||||
return endp;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"(trunk:%d) Not able to find a free endpoint\n",
|
||||
trunk->trunk_nr);
|
||||
if (cause)
|
||||
*cause = -403;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find an endpoint by its name (if wildcarded request is not
|
||||
* applicable) */
|
||||
endp = find_specific_endpoint(epname, trunk);
|
||||
if (endp) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
|
||||
"(trunk:%d) found endpoint: %s\n",
|
||||
trunk->trunk_nr, endp->name);
|
||||
endp->wildcarded_req = false;
|
||||
return endp;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"(trunk:%d) Not able to find specified endpoint: %s\n",
|
||||
trunk->trunk_nr, epname);
|
||||
if (cause)
|
||||
*cause = -500;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if the domain name, which is supplied with the endpoint name
|
||||
* matches the configuration. */
|
||||
static int check_domain_name(const char *epname, struct mgcp_config *cfg)
|
||||
{
|
||||
char *domain_to_check;
|
||||
|
||||
domain_to_check = strstr(epname, "@");
|
||||
if (!domain_to_check) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "missing domain name in endpoint name \"%s\", expecting \"%s\"\n",
|
||||
epname, cfg->domain);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Accept any domain if configured as "*" */
|
||||
if (!strcmp(cfg->domain, "*"))
|
||||
return 0;
|
||||
|
||||
if (strcmp(domain_to_check+1, cfg->domain) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "wrong domain name in endpoint name \"%s\", expecting \"%s\"\n",
|
||||
epname, cfg->domain);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Find an endpoint by its name, search at all trunks.
|
||||
* \param[out] cause, pointer to store cause code, can be NULL.
|
||||
* \param[in] epname, must contain trunk prefix.
|
||||
* \param[in] cfg, mgcp configuration (trunks).
|
||||
* \returns endpoint or NULL if endpoint was not found. */
|
||||
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
|
||||
struct mgcp_config *cfg)
|
||||
{
|
||||
struct mgcp_trunk *trunk;
|
||||
struct mgcp_endpoint *endp;
|
||||
char epname_lc[MGCP_ENDPOINT_MAXLEN];
|
||||
|
||||
osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
|
||||
epname = epname_lc;
|
||||
|
||||
if (cause)
|
||||
*cause = -500;
|
||||
|
||||
/* Identify the trunk where the endpoint is located */
|
||||
trunk = mgcp_trunk_by_name(cfg, epname);
|
||||
if (!trunk)
|
||||
return NULL;
|
||||
|
||||
/* All endpoint names require a domain as suffix */
|
||||
if (check_domain_name(epname, cfg))
|
||||
return NULL;
|
||||
|
||||
/* Identify the endpoint on the trunk */
|
||||
endp = mgcp_endp_by_name_trunk(cause, epname, trunk);
|
||||
if (!endp) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cause)
|
||||
*cause = 0;
|
||||
return endp;
|
||||
}
|
||||
|
||||
/* Get the E1 timeslot number from a given E1 endpoint name
|
||||
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
|
||||
static uint8_t e1_ts_nr_from_epname(const char *epname)
|
||||
{
|
||||
char buf[MGCP_ENDPOINT_MAXLEN + 1];
|
||||
char *save_ptr = NULL;
|
||||
char *buf_ptr = buf;
|
||||
char *token;
|
||||
unsigned long int res = 0;
|
||||
|
||||
strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
|
||||
|
||||
while (1) {
|
||||
token = strtok_r(buf_ptr, "/", &save_ptr);
|
||||
buf_ptr = NULL;
|
||||
if (!token)
|
||||
break;
|
||||
if (strncmp(token, "s-", 2) == 0) {
|
||||
errno = 0;
|
||||
res = strtoul(token + 2, NULL, 10);
|
||||
if (errno == ERANGE || res > E1_TIMESLOTS)
|
||||
return 0xff;
|
||||
return (uint8_t) res;
|
||||
}
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
/* Get the E1 timeslot number from a given E1 endpoint name
|
||||
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
|
||||
static uint8_t e1_rate_from_epname(const char *epname)
|
||||
{
|
||||
char buf[MGCP_ENDPOINT_MAXLEN + 1];
|
||||
char *save_ptr = NULL;
|
||||
char *buf_ptr = buf;
|
||||
char *token;
|
||||
unsigned long int res = 0;
|
||||
unsigned int i;
|
||||
|
||||
strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
|
||||
|
||||
while (1) {
|
||||
token = strtok_r(buf_ptr, "/", &save_ptr);
|
||||
buf_ptr = NULL;
|
||||
if (!token)
|
||||
break;
|
||||
if (strncmp(token, "su", 2) == 0) {
|
||||
errno = 0;
|
||||
res = strtoul(token + 2, NULL, 10);
|
||||
if (errno == ERANGE || res > E1_RATE_MAX)
|
||||
return 0xff;
|
||||
/* Make sure the rate is a valid rate */
|
||||
for (i = 0; i < sizeof(e1_rates); i++) {
|
||||
if (res == e1_rates[i])
|
||||
return (uint8_t) res;
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
/* Get the E1 bitstream offset from a given E1 endpoint name
|
||||
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
|
||||
static uint8_t e1_offs_from_epname(const char *epname)
|
||||
{
|
||||
char buf[MGCP_ENDPOINT_MAXLEN + 1];
|
||||
char *save_ptr = NULL;
|
||||
char *buf_ptr = buf;
|
||||
char *token;
|
||||
unsigned long int res = 0;
|
||||
|
||||
strncpy(buf, epname, MGCP_ENDPOINT_MAXLEN);
|
||||
|
||||
while (1) {
|
||||
token = strtok_r(buf_ptr, "/", &save_ptr);
|
||||
buf_ptr = NULL;
|
||||
if (!token)
|
||||
break;
|
||||
if (strncmp(token, "su", 2) == 0) {
|
||||
token = strstr(token, "-");
|
||||
if (!token)
|
||||
return 0xff;
|
||||
token += 1;
|
||||
errno = 0;
|
||||
res = strtoul(token, NULL, 10);
|
||||
if (errno == ERANGE || res > E1_OFFS_MAX)
|
||||
return 0xff;
|
||||
return (uint8_t) res;
|
||||
}
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
/* Get the E1 subslot number (internal) from a given E1 endpoint name
|
||||
* (e.g. ds/e1-0/s-30/su16-4), returns 0xff on error. */
|
||||
static uint8_t e1_ss_nr_from_epname(const char *epname)
|
||||
{
|
||||
uint8_t rate;
|
||||
uint8_t offs;
|
||||
unsigned int i;
|
||||
|
||||
rate = e1_rate_from_epname(epname);
|
||||
offs = e1_offs_from_epname(epname);
|
||||
|
||||
osmo_static_assert(sizeof(e1_rates) == sizeof(e1_offsets), e1_rates_e1_offsets_size);
|
||||
|
||||
for (i = 0; i < sizeof(e1_rates); i++) {
|
||||
if ((e1_rates[i] == rate) && (e1_offsets[i] == offs))
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
/* Check if the selected E1 endpoint is avalable, which means that none of
|
||||
* the overlapping endpoints are currently serving a call. (if the system
|
||||
* is properly configured such a situation should never ocurr!) */
|
||||
static bool endp_avail_e1(struct mgcp_endpoint *endp)
|
||||
{
|
||||
/* The following map shows the overlapping of the subslots and their
|
||||
* respective rates. The numbers on the right running from top to bottom
|
||||
* are the bit offsets in the whole 64k timeslot. The numbers inside the
|
||||
* boxes symbolize the internal subslot number (array index) and the
|
||||
* rate in the form: i:r where i is the subslot number and r the
|
||||
* respective rate.
|
||||
*
|
||||
* +--------+--------+--------+--------+ 0
|
||||
* | | | | 7:8k |
|
||||
* | | + 3:16k +--------+ 1
|
||||
* | | | | 8:8k |
|
||||
* | | 1:32k +--------+--------+ 2
|
||||
* | | | | 9:8k |
|
||||
* | | + 4:16k +--------+ 3
|
||||
* | | | | 10:8k |
|
||||
* | 0:64k +--------+--------+--------+ 4
|
||||
* | | | | 11:8k |
|
||||
* | | + 5:16k +--------+ 5
|
||||
* | | | | 12:8k |
|
||||
* | | 2:32k +--------+--------+ 6
|
||||
* | | | | 13:8k |
|
||||
* | | + 6:16k +--------+ 7
|
||||
* | | | | 14:8k |
|
||||
* +--------+--------+--------+--------+ 8
|
||||
*
|
||||
* The following array contains tables with the subslot numbers that must be
|
||||
* unused for each subslot. During this test we do not have to check the
|
||||
* endpoint we need to verify, only the overlaps need to be checked. This is
|
||||
* also the reason why the related subslot number is missing from each each
|
||||
* line. */
|
||||
const int8_t interlock_tab[MGCP_ENDP_E1_SUBSLOTS][15] = {
|
||||
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1 },
|
||||
{ 0, 3, 4, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 5, 6, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 1, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 1, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 2, 11, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 2, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } };
|
||||
|
||||
const int8_t *interlock;
|
||||
unsigned int i;
|
||||
uint8_t ts_nr = 0;
|
||||
uint8_t ss_nr = 0;
|
||||
char *epname_check;
|
||||
struct mgcp_endpoint *endp_check;
|
||||
bool available = true;
|
||||
|
||||
/* This function must only be used with E1 type endpoints! */
|
||||
OSMO_ASSERT(endp->trunk->trunk_type == MGCP_TRUNK_E1);
|
||||
|
||||
ts_nr = e1_ts_nr_from_epname(endp->name);
|
||||
ss_nr = e1_ss_nr_from_epname(endp->name);
|
||||
if (ts_nr == 0xff || ss_nr == 0xff) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"cannot check endpoint availability, endpoint name not parseable!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
interlock = interlock_tab[ss_nr];
|
||||
|
||||
for (i = 0; i < sizeof(interlock_tab[0]); i++) {
|
||||
/* Detect row end */
|
||||
if (interlock[i] == -1)
|
||||
break;
|
||||
|
||||
/* Pick overlapping endpoint to check */
|
||||
epname_check = gen_e1_epname(endp, endp->trunk->cfg->domain,
|
||||
endp->trunk->trunk_nr, ts_nr,
|
||||
interlock[i]);
|
||||
endp_check = find_specific_endpoint(epname_check, endp->trunk);
|
||||
if (!endp_check) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"cannot check endpoint availability, overlapping endpoint:%s not found!\n",
|
||||
epname_check);
|
||||
talloc_free(epname_check);
|
||||
continue;
|
||||
}
|
||||
talloc_free(epname_check);
|
||||
|
||||
/* Check if overlapping endpoint currently serves another call
|
||||
* (This is an exceptional situation, that should not occur
|
||||
* in a properly configured environment!) */
|
||||
if (endp_check->callid) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"endpoint unavailable - overlapping endpoint:%s already serves a call!\n",
|
||||
endp_check->name);
|
||||
available = false;
|
||||
}
|
||||
}
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
/*! check if an endpoint is available for any kind of operation.
|
||||
* \param[in] endp endpoint to check.
|
||||
* \returns true if endpoint is avalable, false it is blocked for any reason. */
|
||||
bool mgcp_endp_avail(struct mgcp_endpoint *endp)
|
||||
{
|
||||
switch (endp->trunk->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
/* There are no obstacles that may render a virtual trunk
|
||||
* endpoint unusable, so virtual trunk endpoints are always
|
||||
* available */
|
||||
return true;
|
||||
case MGCP_TRUNK_E1:
|
||||
return endp_avail_e1(endp);
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! claim endpoint, sets callid and activates endpoint, should be called at the
|
||||
* beginning of the CRCX procedure when it is clear that a new call should be
|
||||
* created.
|
||||
* \param[in] endp endpoint to claim.
|
||||
* \param[in] callid that is assingned to this endpoint. */
|
||||
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid)
|
||||
{
|
||||
int rc = 0;
|
||||
uint8_t ts;
|
||||
uint8_t ss;
|
||||
uint8_t offs;
|
||||
|
||||
/* TODO: Make this function more intelligent, it should run the
|
||||
* call id checks we currently have in protocol.c directly here. */
|
||||
|
||||
/* Set the callid, creation of another connection will only be possible
|
||||
* when the callid matches up. (Connections are distinguished by their
|
||||
* connection ids) */
|
||||
endp->callid = talloc_strdup(endp, callid);
|
||||
OSMO_ASSERT(endp->callid);
|
||||
|
||||
/* Allocate resources */
|
||||
switch (endp->trunk->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
/* No additional initaliziation required here, virtual
|
||||
* endpoints will open/close network sockets themselves
|
||||
* on demand. */
|
||||
break;
|
||||
case MGCP_TRUNK_E1:
|
||||
ts = e1_ts_nr_from_epname(endp->name);
|
||||
ss = e1_ss_nr_from_epname(endp->name);
|
||||
offs = e1_offs_from_epname(endp->name);
|
||||
OSMO_ASSERT(ts != 0xFF);
|
||||
OSMO_ASSERT(ts != 0);
|
||||
OSMO_ASSERT(ss != 0xFF);
|
||||
OSMO_ASSERT(offs != 0xFF);
|
||||
rc = mgcp_e1_endp_equip(endp, ts, ss, offs);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
/* Make sure the endpoint is released when claiming the endpoint
|
||||
* failes. */
|
||||
if (rc < 0)
|
||||
mgcp_endp_release(endp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! update endpoint, updates internal endpoint specific data, should be
|
||||
* after when MDCX or CRCX has been executed successuflly.
|
||||
* \param[in] endp endpoint to update. */
|
||||
void mgcp_endp_update(struct mgcp_endpoint *endp)
|
||||
{
|
||||
/* Allocate resources */
|
||||
switch (endp->trunk->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
/* No updating initaliziation required for virtual endpoints. */
|
||||
break;
|
||||
case MGCP_TRUNK_E1:
|
||||
mgcp_e1_endp_update(endp);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
@@ -24,7 +24,9 @@
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
@@ -129,166 +131,6 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We have a null terminated string with the endpoint name here. We only
|
||||
* support two kinds. Simple ones as seen on the BSC level and the ones
|
||||
* seen on the trunk side. (helper function for find_endpoint()) */
|
||||
static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
|
||||
const char *mgcp)
|
||||
{
|
||||
char *rest = NULL;
|
||||
struct mgcp_trunk_config *tcfg;
|
||||
int trunk, endp;
|
||||
struct mgcp_endpoint *endp_ptr;
|
||||
|
||||
trunk = strtoul(mgcp + 6, &rest, 10);
|
||||
if (rest == NULL || rest[0] != '/' || trunk < 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
endp = strtoul(rest + 1, &rest, 10);
|
||||
if (rest == NULL || rest[0] != '@') {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* signalling is on timeslot 1 */
|
||||
if (endp == 1)
|
||||
return NULL;
|
||||
|
||||
tcfg = mgcp_trunk_num(cfg, trunk);
|
||||
if (!tcfg) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n",
|
||||
trunk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!tcfg->endpoints) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Endpoints of trunk %d not allocated.\n", trunk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (endp < 1 || endp >= tcfg->number_endpoints) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n",
|
||||
mgcp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
endp_ptr = &tcfg->endpoints[endp];
|
||||
endp_ptr->wildcarded_req = false;
|
||||
return endp_ptr;
|
||||
}
|
||||
|
||||
/* Find an endpoint that is not in use. Do this by going through the endpoint
|
||||
* array, check the callid. A callid nullpointer indicates that the endpoint
|
||||
* is free */
|
||||
static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
|
||||
unsigned int number_endpoints)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < number_endpoints; i++) {
|
||||
if (endpoints[i].callid == NULL) {
|
||||
endp = &endpoints[i];
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
|
||||
"found free endpoint\n");
|
||||
endp->wildcarded_req = true;
|
||||
return endp;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if the domain name, which is supplied with the endpoint name
|
||||
* matches the configuration. */
|
||||
static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
|
||||
{
|
||||
char *domain_to_check;
|
||||
|
||||
domain_to_check = strstr(mgcp, "@");
|
||||
if (!domain_to_check)
|
||||
return -EINVAL;
|
||||
|
||||
/* Accept any domain if configured as "*" */
|
||||
if (!strcmp(cfg->domain, "*"))
|
||||
return 0;
|
||||
|
||||
if (strcmp(domain_to_check+1, cfg->domain) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s', expecting '%s'\n", mgcp, cfg->domain);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Search the endpoint pool for the endpoint that had been selected via the
|
||||
* MGCP message (helper function for mgcp_analyze_header()) */
|
||||
static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
|
||||
const char *mgcp,
|
||||
int *cause)
|
||||
{
|
||||
char *endptr = NULL;
|
||||
unsigned int gw = INT_MAX;
|
||||
const char *endpoint_number_str;
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
*cause = 0;
|
||||
|
||||
/* Check if the domainname in the request is correct */
|
||||
if (check_domain_name(cfg, mgcp)) {
|
||||
*cause = -500;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if the E1 trunk is requested */
|
||||
if (strncmp(mgcp, "ds/e1", 5) == 0) {
|
||||
endp = find_e1_endpoint(cfg, mgcp);
|
||||
if (!endp)
|
||||
*cause = -500;
|
||||
return endp;
|
||||
}
|
||||
|
||||
/* Check if the virtual trunk is addressed (new, correct way with prefix) */
|
||||
if (strncmp
|
||||
(mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
|
||||
strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) {
|
||||
endpoint_number_str =
|
||||
mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
|
||||
if (endpoint_number_str[0] == '*') {
|
||||
endp = find_free_endpoint(cfg->trunk.endpoints,
|
||||
cfg->trunk.number_endpoints);
|
||||
if (!endp)
|
||||
*cause = -403;
|
||||
return endp;
|
||||
}
|
||||
gw = strtoul(endpoint_number_str, &endptr, 16);
|
||||
if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
|
||||
endp = &cfg->trunk.endpoints[gw];
|
||||
endp->wildcarded_req = false;
|
||||
return endp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Deprecated method without prefix */
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n",
|
||||
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp);
|
||||
gw = strtoul(mgcp, &endptr, 16);
|
||||
if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
|
||||
endp = &cfg->trunk.endpoints[gw];
|
||||
endp->wildcarded_req = false;
|
||||
return endp;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
|
||||
*cause = -500;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Analyze and parse the the hader of an MGCP messeage string.
|
||||
* \param[out] pdata caller provided memory to store the parsing results
|
||||
* \param[in] data mgcp message string
|
||||
@@ -316,7 +158,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
pdata->trans = elem;
|
||||
break;
|
||||
case 1:
|
||||
pdata->endp = find_endpoint(pdata->cfg, elem, &cause);
|
||||
pdata->endp = mgcp_endp_by_name(&cause, elem, pdata->cfg);
|
||||
if (!pdata->endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Unable to find Endpoint `%s'\n", elem);
|
||||
@@ -390,8 +232,8 @@ int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
|
||||
const size_t line_len = strlen(line);
|
||||
if (line[0] != '\0' && line_len < 2) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Wrong MGCP option format: '%s' on 0x%x\n",
|
||||
line, ENDPOINT_NUMBER(endp));
|
||||
"Wrong MGCP option format: '%s' on %s\n",
|
||||
line, endp->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -37,29 +37,49 @@
|
||||
#include <osmocom/netif/amr.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_network.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_stat.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
#include <osmocom/codec/codec.h>
|
||||
#include <osmocom/mgcp/mgcp_e1.h>
|
||||
|
||||
|
||||
#define RTP_SEQ_MOD (1 << 16)
|
||||
#define RTP_MAX_DROPOUT 3000
|
||||
#define RTP_MAX_MISORDER 100
|
||||
#define RTP_BUF_SIZE 4096
|
||||
|
||||
enum {
|
||||
enum rtp_proto {
|
||||
MGCP_PROTO_RTP,
|
||||
MGCP_PROTO_RTCP,
|
||||
};
|
||||
|
||||
static void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
|
||||
int id, int inc)
|
||||
{
|
||||
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
|
||||
struct rate_ctr_group *mgw_stats = endp->trunk->ratectr.all_rtp_conn_stats;
|
||||
|
||||
/* add to both the per-connection and the global stats */
|
||||
rate_ctr_add(&conn_stats->ctr[id], inc);
|
||||
rate_ctr_add(&mgw_stats->ctr[id], inc);
|
||||
}
|
||||
|
||||
static void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, int id)
|
||||
{
|
||||
rtpconn_rate_ctr_add(conn_rtp, endp, id, 1);
|
||||
}
|
||||
|
||||
static int rx_rtp(struct msgb *msg);
|
||||
|
||||
/*! Determine the local rtp bind IP-address.
|
||||
* \param[out] addr caller provided memory to store the resulting IP-Address
|
||||
* \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters
|
||||
* \param[out] addr caller provided memory to store the resulting IP-Address.
|
||||
* \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters.
|
||||
*
|
||||
* The local bind IP-address is automatically selected by probing the
|
||||
* IP-Address of the interface that is pointing towards the remote IP-Address,
|
||||
@@ -128,12 +148,12 @@ static uint32_t get_current_ts(unsigned codec_rate)
|
||||
}
|
||||
|
||||
/*! send udp packet.
|
||||
* \param[in] fd associated file descriptor
|
||||
* \param[in] addr destination ip-address
|
||||
* \param[in] port destination UDP port (network byte order)
|
||||
* \param[in] buf buffer that holds the data to be send
|
||||
* \param[in] len length of the data to be sent
|
||||
* \returns bytes sent, -1 on error */
|
||||
* \param[in] fd associated file descriptor.
|
||||
* \param[in] addr destination ip-address.
|
||||
* \param[in] port destination UDP port (network byte order).
|
||||
* \param[in] buf buffer that holds the data to be send.
|
||||
* \param[in] len length of the data to be sent.
|
||||
* \returns bytes sent, -1 on error. */
|
||||
int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len)
|
||||
{
|
||||
struct sockaddr_in out;
|
||||
@@ -150,9 +170,9 @@ int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len)
|
||||
}
|
||||
|
||||
/*! send RTP dummy packet (to keep NAT connection open).
|
||||
* \param[in] endp mcgp endpoint that holds the RTP connection
|
||||
* \param[in] conn associated RTP connection
|
||||
* \returns bytes sent, -1 on error */
|
||||
* \param[in] endp mcgp endpoint that holds the RTP connection.
|
||||
* \param[in] conn associated RTP connection.
|
||||
* \returns bytes sent, -1 on error. */
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
static char buf[] = { MGCP_DUMMY_LOAD };
|
||||
@@ -171,7 +191,7 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
if (rc == -1)
|
||||
goto failed;
|
||||
|
||||
if (endp->tcfg->omit_rtcp)
|
||||
if (endp->trunk->omit_rtcp)
|
||||
return rc;
|
||||
|
||||
was_rtcp = 1;
|
||||
@@ -378,12 +398,12 @@ static int align_rtp_timestamp_offset(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
|
||||
* \returns ignores input parameters, return always 0 */
|
||||
* \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.
|
||||
* \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)
|
||||
@@ -393,10 +413,10 @@ int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
}
|
||||
|
||||
/*! dummy callback to disable transcoding (see also cfg->setup_rtp_processing_cb).
|
||||
* \param[in] associated endpoint
|
||||
* \param[in] destination RTP connnection
|
||||
* \param[in] source RTP connection
|
||||
* \returns ignores input parameters, return always 0 */
|
||||
* \param[in] associated endpoint.
|
||||
* \param[in] destination RTP connnection.
|
||||
* \param[in] source RTP connection.
|
||||
* \returns ignores input parameters, return always 0. */
|
||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst,
|
||||
struct mgcp_conn_rtp *conn_src)
|
||||
@@ -469,16 +489,19 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
|
||||
* Patch the payload type of an RTP packet so that it uses the payload type
|
||||
* that is valid for the destination connection (conn_dst) */
|
||||
static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
|
||||
struct mgcp_conn_rtp *conn_dst, char *data, int len)
|
||||
struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
|
||||
{
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
uint8_t pt_in;
|
||||
int pt_out;
|
||||
|
||||
if (len < sizeof(struct rtp_hdr))
|
||||
if (msgb_length(msg) < sizeof(struct rtp_hdr)) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
|
||||
msgb_length(msg), sizeof(struct rtp_hdr));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rtp_hdr = (struct rtp_hdr *)data;
|
||||
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
|
||||
|
||||
pt_in = rtp_hdr->payload_type;
|
||||
pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
|
||||
@@ -498,7 +521,7 @@ static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
|
||||
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *rtp_end,
|
||||
struct sockaddr_in *addr, char *data, int len)
|
||||
struct sockaddr_in *addr, struct msgb *msg)
|
||||
{
|
||||
uint32_t arrival_time;
|
||||
int32_t transit;
|
||||
@@ -506,11 +529,12 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
uint32_t timestamp, ssrc;
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
int payload = rtp_end->codec->payload_type;
|
||||
unsigned int len = msgb_length(msg);
|
||||
|
||||
if (len < sizeof(*rtp_hdr))
|
||||
return;
|
||||
|
||||
rtp_hdr = (struct rtp_hdr *)data;
|
||||
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
|
||||
seq = ntohs(rtp_hdr->sequence);
|
||||
timestamp = ntohl(rtp_hdr->timestamp);
|
||||
arrival_time = get_current_ts(rtp_end->codec->rate);
|
||||
@@ -632,9 +656,8 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
return;
|
||||
|
||||
#if 0
|
||||
DEBUGP(DRTP,
|
||||
"endpoint:0x%x payload hdr payload %u -> endp payload %u\n",
|
||||
ENDPOINT_NUMBER(endp), rtp_hdr->payload_type, payload);
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "payload hdr payload %u -> endp payload %u\n",
|
||||
rtp_hdr->payload_type, payload);
|
||||
rtp_hdr->payload_type = payload;
|
||||
#endif
|
||||
}
|
||||
@@ -644,32 +667,26 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
* function is used to convert between RFC 5993 and TS 101318, which we normally
|
||||
* use.
|
||||
* Return 0 on sucess, negative on errors like invalid data length. */
|
||||
static int rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
|
||||
static int rfc5993_hr_convert(struct mgcp_endpoint *endp, struct msgb *msg)
|
||||
{
|
||||
/* NOTE: *data has an overall length of RTP_BUF_SIZE, so there is
|
||||
* plenty of space available to store the slightly larger, converted
|
||||
* data */
|
||||
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
|
||||
if (*len < sizeof(struct rtp_hdr)) {
|
||||
if (msgb_length(msg) < sizeof(struct rtp_hdr)) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n",
|
||||
*len, sizeof(struct rtp_hdr));
|
||||
msgb_length(msg), sizeof(struct rtp_hdr));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rtp_hdr = (struct rtp_hdr *)data;
|
||||
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
|
||||
|
||||
if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr)) {
|
||||
if (msgb_length(msg) == GSM_HR_BYTES + sizeof(struct rtp_hdr)) {
|
||||
/* TS 101318 encoding => RFC 5993 encoding */
|
||||
msgb_put(msg, 1);
|
||||
memmove(rtp_hdr->data + 1, rtp_hdr->data, GSM_HR_BYTES);
|
||||
rtp_hdr->data[0] = 0x00;
|
||||
(*len) += 1;
|
||||
|
||||
} else if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr) + 1) {
|
||||
} else if (msgb_length(msg) == GSM_HR_BYTES + sizeof(struct rtp_hdr) + 1) {
|
||||
/* RFC 5993 encoding => TS 101318 encoding */
|
||||
memmove(rtp_hdr->data, rtp_hdr->data + 1, GSM_HR_BYTES);
|
||||
(*len) -= 1;
|
||||
msgb_trim(msg, msgb_length(msg) - 1);
|
||||
} else {
|
||||
/* It is possible that multiple payloads occur in one RTP
|
||||
* packet. This is not supported yet. */
|
||||
@@ -684,25 +701,24 @@ static int rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
|
||||
* efficient encoding scheme where all fields are packed together one after
|
||||
* another and an octet aligned mode where all fields are aligned to octet
|
||||
* boundaries. This function is used to convert between the two modes */
|
||||
static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, char *data, int *len,
|
||||
static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
|
||||
bool target_is_oa)
|
||||
{
|
||||
/* NOTE: *data has an overall length of RTP_BUF_SIZE, so there is
|
||||
/* NOTE: the msgb has an allocated length of RTP_BUF_SIZE, so there is
|
||||
* plenty of space available to store the slightly larger, converted
|
||||
* data */
|
||||
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
unsigned int payload_len;
|
||||
int rc;
|
||||
|
||||
if (*len < sizeof(struct rtp_hdr)) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n", *len, sizeof(struct rtp_hdr));
|
||||
if (msgb_length(msg) < sizeof(struct rtp_hdr)) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n", msgb_length(msg), sizeof(struct rtp_hdr));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rtp_hdr = (struct rtp_hdr *)data;
|
||||
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
|
||||
|
||||
payload_len = *len - sizeof(struct rtp_hdr);
|
||||
payload_len = msgb_length(msg) - sizeof(struct rtp_hdr);
|
||||
|
||||
if (osmo_amr_is_oa(rtp_hdr->data, payload_len)) {
|
||||
if (!target_is_oa)
|
||||
@@ -730,9 +746,7 @@ static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, char *data, int *len,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*len = rc + sizeof(struct rtp_hdr);
|
||||
|
||||
return 0;
|
||||
return msgb_trim(msg, rc + sizeof(struct rtp_hdr));
|
||||
}
|
||||
|
||||
/* Check if a conversion between octet-aligned and bandwith-efficient mode is
|
||||
@@ -770,15 +784,14 @@ 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 */
|
||||
static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
|
||||
int len)
|
||||
static void forward_data(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!tap->enabled)
|
||||
return;
|
||||
|
||||
rc = sendto(fd, buf, len, 0, (struct sockaddr *)&tap->forward,
|
||||
rc = sendto(fd, msgb_data(msg), msgb_length(msg), 0, (struct sockaddr *)&tap->forward,
|
||||
sizeof(tap->forward));
|
||||
|
||||
if (rc < 0)
|
||||
@@ -786,28 +799,46 @@ static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
|
||||
"Forwarding tapped (debug) voice data failed.\n");
|
||||
}
|
||||
|
||||
/* Generate an RTP header if it is missing */
|
||||
static void gen_rtp_header(struct msgb *msg, struct mgcp_rtp_end *rtp_end,
|
||||
struct mgcp_rtp_state *state)
|
||||
{
|
||||
struct rtp_hdr *hdr = (struct rtp_hdr *)msgb_data(msg);
|
||||
|
||||
if (hdr->version > 0)
|
||||
return;
|
||||
|
||||
hdr->version = 2;
|
||||
hdr->payload_type = rtp_end->codec->payload_type;
|
||||
hdr->timestamp = osmo_htonl(get_current_ts(rtp_end->codec->rate));
|
||||
hdr->sequence = osmo_htons(state->alt_rtp_tx_sequence);
|
||||
hdr->ssrc = state->alt_rtp_tx_ssrc;
|
||||
}
|
||||
|
||||
|
||||
/*! Send RTP/RTCP data to a specified destination connection.
|
||||
* \param[in] endp associated endpoint (for configuration, logging)
|
||||
* \param[in] is_rtp flag to specify if the packet is of type RTP or RTCP
|
||||
* \param[in] spoofed source address (set to NULL to disable)
|
||||
* \param[in] buf buffer that contains the RTP/RTCP data
|
||||
* \param[in] len length of the buffer that contains the RTP/RTCP data
|
||||
* \param[in] conn_src associated source connection
|
||||
* \param[in] conn_dst associated destination connection
|
||||
* \returns 0 on success, -1 on ERROR */
|
||||
* \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] spoofed source address (set to NULL to disable).
|
||||
* \param[in] buf buffer that contains the RTP/RTCP data.
|
||||
* \param[in] len length of the buffer that contains the RTP/RTCP data.
|
||||
* \param[in] conn_src associated source connection.
|
||||
* \param[in] conn_dst associated destination connection.
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
char *buf, int len, struct mgcp_conn_rtp *conn_src,
|
||||
struct msgb *msg, struct mgcp_conn_rtp *conn_src,
|
||||
struct mgcp_conn_rtp *conn_dst)
|
||||
{
|
||||
/*! When no destination connection is available (e.g. when only one
|
||||
* connection in loopback mode exists), then the source connection
|
||||
* shall be specified as destination connection */
|
||||
|
||||
struct mgcp_trunk_config *tcfg = endp->tcfg;
|
||||
struct mgcp_trunk *trunk = endp->trunk;
|
||||
struct mgcp_rtp_end *rtp_end;
|
||||
struct mgcp_rtp_state *rtp_state;
|
||||
char *dest_name;
|
||||
int rc;
|
||||
int len;
|
||||
|
||||
OSMO_ASSERT(conn_src);
|
||||
OSMO_ASSERT(conn_dst);
|
||||
@@ -819,7 +850,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
}
|
||||
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "loop:%d, mode:%d%s\n",
|
||||
tcfg->audio_loop, conn_src->conn->mode,
|
||||
trunk->audio_loop, conn_src->conn->mode,
|
||||
conn_src->conn->mode == MGCP_CONN_LOOPBACK ? " (loopback)" : "");
|
||||
|
||||
/* FIXME: It is legal that the payload type on the egress connection is
|
||||
@@ -831,7 +862,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
* should not occur if transcoding is consequently avoided. Until
|
||||
* we have transcoding support in osmo-mgw we can not resolve this. */
|
||||
if (is_rtp) {
|
||||
rc = mgcp_patch_pt(conn_src, conn_dst, buf, len);
|
||||
rc = mgcp_patch_pt(conn_src, conn_dst, msg);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"can not patch PT because no suitable egress codec was found.\n");
|
||||
@@ -844,8 +875,13 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
rtp_state = &conn_src->state;
|
||||
dest_name = conn_dst->conn->name;
|
||||
|
||||
/* Ensure we have an alternative SSRC in case we need it, see also
|
||||
* gen_rtp_header() */
|
||||
if (rtp_state->alt_rtp_tx_ssrc == 0)
|
||||
rtp_state->alt_rtp_tx_ssrc = rand();
|
||||
|
||||
if (!rtp_end->output_enabled) {
|
||||
rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_DROPPED_PACKETS_CTR]);
|
||||
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_DROPPED_PACKETS_CTR);
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"output disabled, drop to %s %s "
|
||||
"rtp_port:%u rtcp_port:%u\n",
|
||||
@@ -856,21 +892,26 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
} else if (is_rtp) {
|
||||
int cont;
|
||||
int nbytes = 0;
|
||||
int buflen = len;
|
||||
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->cfg->rtp_processing_cb(endp, rtp_end,
|
||||
buf, &buflen,
|
||||
(char*)msgb_data(msg), &buflen,
|
||||
RTP_BUF_SIZE);
|
||||
if (cont < 0)
|
||||
break;
|
||||
|
||||
if (addr)
|
||||
mgcp_patch_and_count(endp, rtp_state, rtp_end,
|
||||
addr, buf, buflen);
|
||||
addr, msg);
|
||||
|
||||
if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
|
||||
rc = amr_oa_bwe_convert(endp, buf, &buflen,
|
||||
rc = amr_oa_bwe_convert(endp, msg,
|
||||
conn_dst->end.codec->param.amr_octet_aligned);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
@@ -881,7 +922,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
else if (rtp_end->rfc5993_hr_convert
|
||||
&& strcmp(conn_src->end.codec->subtype_name,
|
||||
"GSM-HR-08") == 0) {
|
||||
rc = rfc5993_hr_convert(endp, buf, &buflen);
|
||||
rc = rfc5993_hr_convert(endp, msg);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
|
||||
break;
|
||||
@@ -897,7 +938,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
|
||||
buf, buflen);
|
||||
msg);
|
||||
|
||||
/* FIXME: HACK HACK HACK. See OS#2459.
|
||||
* The ip.access nano3G needs the first RTP payload's first two bytes to read hex
|
||||
@@ -906,7 +947,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
*/
|
||||
if (!rtp_state->patched_first_rtp_payload
|
||||
&& conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
|
||||
uint8_t *data = (uint8_t *) & buf[12];
|
||||
uint8_t *data = msgb_data(msg) + 12;
|
||||
if (data[0] == 0xe0) {
|
||||
data[0] = 0xe4;
|
||||
data[1] = 0x00;
|
||||
@@ -917,21 +958,21 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
}
|
||||
}
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtp.fd,
|
||||
&rtp_end->addr,
|
||||
rtp_end->rtp_port, buf, buflen);
|
||||
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
|
||||
(char*)msgb_data(msg), msgb_length(msg));
|
||||
|
||||
if (len <= 0)
|
||||
return len;
|
||||
|
||||
rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
|
||||
rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
|
||||
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
|
||||
rtp_state->alt_rtp_tx_sequence++;
|
||||
|
||||
nbytes += len;
|
||||
buflen = cont;
|
||||
} while (buflen > 0);
|
||||
return nbytes;
|
||||
} else if (!tcfg->omit_rtcp) {
|
||||
} else if (!trunk->omit_rtcp) {
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"send to %s %s rtp_port:%u rtcp_port:%u\n",
|
||||
dest_name, inet_ntoa(rtp_end->addr),
|
||||
@@ -940,10 +981,11 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtcp.fd,
|
||||
&rtp_end->addr,
|
||||
rtp_end->rtcp_port, buf, len);
|
||||
rtp_end->rtcp_port, (char*)msgb_data(msg), msgb_length(msg));
|
||||
|
||||
rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
|
||||
rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
|
||||
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
|
||||
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
|
||||
rtp_state->alt_rtp_tx_sequence++;
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -951,45 +993,6 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper function for mgcp_recv(),
|
||||
Receive one RTP Packet + Originating address from file descriptor */
|
||||
static int receive_from(struct mgcp_endpoint *endp, int fd,
|
||||
struct sockaddr_in *addr, char *buf, int bufsize)
|
||||
{
|
||||
int rc;
|
||||
socklen_t slen = sizeof(*addr);
|
||||
struct sockaddr_in addr_sink;
|
||||
char buf_sink[RTP_BUF_SIZE];
|
||||
bool tossed = false;
|
||||
|
||||
if (!addr)
|
||||
addr = &addr_sink;
|
||||
if (!buf) {
|
||||
tossed = true;
|
||||
buf = buf_sink;
|
||||
bufsize = sizeof(buf_sink);
|
||||
}
|
||||
|
||||
rc = recvfrom(fd, buf, bufsize, 0, (struct sockaddr *)addr, &slen);
|
||||
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"receiving %u bytes length packet from %s:%u ...\n",
|
||||
rc, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to receive packet, errno: %d/%s\n",
|
||||
errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tossed) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "packet tossed\n");
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Check if the origin (addr) matches the address/port data of the RTP
|
||||
* connections. */
|
||||
static int check_rtp_origin(struct mgcp_conn_rtp *conn,
|
||||
@@ -1082,7 +1085,7 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn)
|
||||
|
||||
/* Do some basic checks to make sure that the RTCP packets we are going to
|
||||
* process are not complete garbage */
|
||||
static int check_rtcp(char *buf, unsigned int buf_size)
|
||||
static int check_rtcp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
|
||||
{
|
||||
struct rtcp_hdr *hdr;
|
||||
unsigned int len;
|
||||
@@ -1090,33 +1093,43 @@ static int check_rtcp(char *buf, unsigned int buf_size)
|
||||
|
||||
/* RTPC packets that are just a header without data do not make
|
||||
* any sense. */
|
||||
if (buf_size < sizeof(struct rtcp_hdr))
|
||||
if (msgb_length(msg) < sizeof(struct rtcp_hdr)) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP packet too short (%u < %zu)\n",
|
||||
msgb_length(msg), sizeof(struct rtcp_hdr));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Make sure that the length of the received packet does not exceed
|
||||
* the available buffer size */
|
||||
hdr = (struct rtcp_hdr *)buf;
|
||||
hdr = (struct rtcp_hdr *)msgb_data(msg);
|
||||
len = (osmo_ntohs(hdr->length) + 1) * 4;
|
||||
if (len > buf_size)
|
||||
if (len > msgb_length(msg)) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP header length exceeds packet size (%u > %u)\n",
|
||||
len, msgb_length(msg));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Make sure we accept only packets that have a proper packet type set
|
||||
* See also: http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
|
||||
type = hdr->type;
|
||||
if ((type < 192 || type > 195) && (type < 200 || type > 213))
|
||||
if ((type < 192 || type > 195) && (type < 200 || type > 213)) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP header: invalid type: %u\n", type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Do some basic checks to make sure that the RTP packets we are going to
|
||||
* process are not complete garbage */
|
||||
static int check_rtp(char *buf, unsigned int buf_size)
|
||||
static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
|
||||
{
|
||||
/* RTP packets that are just a header without data do not make
|
||||
* any sense. */
|
||||
if (buf_size < sizeof(struct rtp_hdr))
|
||||
return -EINVAL;
|
||||
size_t min_size = sizeof(struct rtp_hdr);
|
||||
if (msgb_length(msg) < min_size) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
|
||||
msgb_length(msg), min_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* FIXME: Add more checks, the reason why we do not check more than
|
||||
* the length is because we currently handle IUUP packets as RTP
|
||||
@@ -1127,86 +1140,15 @@ static int check_rtp(char *buf, unsigned int buf_size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Receive RTP data from a specified source connection and dispatch it to a
|
||||
* destination connection. */
|
||||
static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
|
||||
unsigned int buf_size, struct osmo_fd *fd)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
struct mgcp_conn_rtp *conn;
|
||||
struct mgcp_trunk_config *tcfg;
|
||||
int rc;
|
||||
|
||||
conn = (struct mgcp_conn_rtp*) fd->data;
|
||||
endp = conn->conn->endp;
|
||||
tcfg = endp->tcfg;
|
||||
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "receiving RTP/RTCP packet...\n");
|
||||
|
||||
rc = receive_from(endp, fd->fd, addr, buf, buf_size);
|
||||
if (rc <= 0)
|
||||
return -1;
|
||||
|
||||
/* FIXME: The way how we detect the protocol looks odd. We should look
|
||||
* into the packet header. Also we should introduce a packet type
|
||||
* MGCP_PROTO_IUUP because currently we handle IUUP packets like RTP
|
||||
* packets which is problematic. */
|
||||
*proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
|
||||
|
||||
if (*proto == MGCP_PROTO_RTP) {
|
||||
if (check_rtp(buf, rc) < 0) {
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"invalid RTP packet received -- packet tossed\n");
|
||||
return -1;
|
||||
}
|
||||
} else if (*proto == MGCP_PROTO_RTCP) {
|
||||
if (check_rtcp(buf, rc) < 0) {
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"invalid RTCP packet received -- packet tossed\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "");
|
||||
LOGPC(DRTP, LOGL_DEBUG, "receiving from %s %s %d\n",
|
||||
conn->conn->name, inet_ntoa(addr->sin_addr),
|
||||
ntohs(addr->sin_port));
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn->conn));
|
||||
|
||||
/* Check if the origin of the RTP packet seems plausible */
|
||||
if (tcfg->rtp_accept_all == 0) {
|
||||
if (check_rtp_origin(conn, addr) != 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Filter out dummy message */
|
||||
if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_NOTICE,
|
||||
"dummy message received\n");
|
||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
||||
"packet tossed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Increment RX statistics */
|
||||
rate_ctr_inc(&conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
|
||||
rate_ctr_add(&conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], rc);
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data(fd->fd, &conn->tap_in, buf, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Send RTP data. Possible options are standard RTP packet
|
||||
* transmission or trsmission via an osmux connection */
|
||||
static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
|
||||
unsigned int buf_size,
|
||||
struct mgcp_conn_rtp *conn_src,
|
||||
struct mgcp_conn_rtp *conn_dst)
|
||||
static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
endp = conn_src->conn->endp;
|
||||
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
|
||||
enum rtp_proto proto = mc->proto;
|
||||
struct mgcp_conn_rtp *conn_src = mc->conn_src;
|
||||
struct sockaddr_in *from_addr = mc->from_addr;
|
||||
struct mgcp_endpoint *endp = conn_src->conn->endp;
|
||||
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "destin conn:%s\n",
|
||||
mgcp_conn_dump(conn_dst->conn));
|
||||
@@ -1225,13 +1167,13 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
|
||||
"endpoint type is MGCP_RTP_DEFAULT, "
|
||||
"using mgcp_send() to forward data directly\n");
|
||||
return mgcp_send(endp, proto == MGCP_PROTO_RTP,
|
||||
addr, buf, buf_size, conn_src, conn_dst);
|
||||
from_addr, msg, conn_src, conn_dst);
|
||||
case MGCP_OSMUX_BSC_NAT:
|
||||
case MGCP_OSMUX_BSC:
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"endpoint type is MGCP_OSMUX_BSC_NAT, "
|
||||
"using osmux_xfrm_to_osmux() to forward data through OSMUX\n");
|
||||
return osmux_xfrm_to_osmux(buf, buf_size, conn_dst);
|
||||
return osmux_xfrm_to_osmux((char*)msgb_data(msg), msgb_length(msg), conn_dst);
|
||||
}
|
||||
|
||||
/* If the data has not been handled/forwarded until here, it will
|
||||
@@ -1243,19 +1185,22 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
|
||||
}
|
||||
|
||||
/*! dispatch incoming RTP packet to opposite RTP connection.
|
||||
* \param[in] proto protocol (MGCP_CONN_TYPE_RTP or MGCP_CONN_TYPE_RTCP)
|
||||
* \param[in] addr socket address where the RTP packet has been received from
|
||||
* \param[in] buf buffer that hold the RTP payload
|
||||
* \param[in] buf_size size data length of buf
|
||||
* \param[in] conn originating connection
|
||||
* \returns 0 on success, -1 on ERROR */
|
||||
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
unsigned int buf_size, struct mgcp_conn *conn)
|
||||
* \param[in] proto protocol (MGCP_CONN_TYPE_RTP or MGCP_CONN_TYPE_RTCP).
|
||||
* \param[in] addr socket address where the RTP packet has been received from.
|
||||
* \param[in] buf buffer that hold the RTP payload.
|
||||
* \param[in] buf_size size data length of buf.
|
||||
* \param[in] conn originating connection.
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||
{
|
||||
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
|
||||
struct mgcp_conn_rtp *conn_src = mc->conn_src;
|
||||
struct mgcp_conn *conn = conn_src->conn;
|
||||
struct mgcp_conn *conn_dst;
|
||||
struct sockaddr_in *from_addr = mc->from_addr;
|
||||
|
||||
/*! NOTE: This callback function implements the endpoint specific
|
||||
* dispatch bahviour of an rtp bridge/proxy endpoint. It is assumed
|
||||
* dispatch behaviour of an rtp bridge/proxy endpoint. It is assumed
|
||||
* that the endpoint will hold only two connections. This premise
|
||||
* is used to determine the opposite connection (it is always the
|
||||
* connection that is not the originating connection). Once the
|
||||
@@ -1271,11 +1216,10 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
* address data from the UDP packet header to patch the
|
||||
* outgoing address in connection on the fly */
|
||||
if (conn->u.rtp.end.rtp_port == 0) {
|
||||
conn->u.rtp.end.addr = addr->sin_addr;
|
||||
conn->u.rtp.end.rtp_port = addr->sin_port;
|
||||
conn->u.rtp.end.addr = from_addr->sin_addr;
|
||||
conn->u.rtp.end.rtp_port = from_addr->sin_port;
|
||||
}
|
||||
return mgcp_send_rtp(proto, addr, buf,
|
||||
buf_size, &conn->u.rtp, &conn->u.rtp);
|
||||
return mgcp_send_rtp(conn_src, msg);
|
||||
}
|
||||
|
||||
/* Find a destination connection. */
|
||||
@@ -1294,8 +1238,8 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
|
||||
/* There is no destination conn, stop here */
|
||||
if (!conn_dst) {
|
||||
LOGPCONN(conn, DRTP, LOGL_ERROR,
|
||||
"unable to find destination conn\n");
|
||||
LOGPCONN(conn, DRTP, LOGL_DEBUG,
|
||||
"no connection to forward an incoming RTP packet to\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1307,15 +1251,44 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
}
|
||||
|
||||
/* Dispatch RTP packet to destination RTP connection */
|
||||
return mgcp_send_rtp(proto, addr, buf,
|
||||
buf_size, &conn->u.rtp, &conn_dst->u.rtp);
|
||||
return mgcp_send_rtp(&conn_dst->u.rtp, msg);
|
||||
}
|
||||
|
||||
/*! dispatch incoming RTP packet to E1 subslot, handle RTCP packets locally.
|
||||
* \param[in] proto protocol (MGCP_CONN_TYPE_RTP or MGCP_CONN_TYPE_RTCP).
|
||||
* \param[in] addr socket address where the RTP packet has been received from.
|
||||
* \param[in] buf buffer that hold the RTP payload.
|
||||
* \param[in] buf_size size data length of buf.
|
||||
* \param[in] conn originating connection.
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
int mgcp_dispatch_e1_bridge_cb(struct msgb *msg)
|
||||
{
|
||||
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
|
||||
struct mgcp_conn_rtp *conn_src = mc->conn_src;
|
||||
struct mgcp_conn *conn = conn_src->conn;
|
||||
struct sockaddr_in *from_addr = mc->from_addr;
|
||||
|
||||
/* Check if the connection is in loopback mode, if yes, just send the
|
||||
* incoming data back to the origin */
|
||||
if (conn->mode == MGCP_CONN_LOOPBACK) {
|
||||
/* When we are in loopback mode, we loop back all incoming
|
||||
* packets back to their origin. We will use the originating
|
||||
* address data from the UDP packet header to patch the
|
||||
* outgoing address in connection on the fly */
|
||||
if (conn->u.rtp.end.rtp_port == 0) {
|
||||
conn->u.rtp.end.addr = from_addr->sin_addr;
|
||||
conn->u.rtp.end.rtp_port = from_addr->sin_port;
|
||||
}
|
||||
return mgcp_send_rtp(conn_src, msg);
|
||||
}
|
||||
|
||||
/* Forward to E1 */
|
||||
return mgcp_e1_send_rtp(conn->endp, conn->u.rtp.end.codec, msg);
|
||||
}
|
||||
|
||||
/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
|
||||
* \param[in] endp Endpoint on which the connection resides.
|
||||
* \param[in] conn Connection that is about to be removed (ignored).
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
* \param[in] conn Connection that is about to be removed (ignored). */
|
||||
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
|
||||
{
|
||||
struct mgcp_conn *conn_cleanup;
|
||||
@@ -1331,6 +1304,21 @@ void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *co
|
||||
}
|
||||
}
|
||||
|
||||
/*! cleanup an endpoint when a connection on an E1 endpoint is removed.
|
||||
* \param[in] endp Endpoint on which the connection resides.
|
||||
* \param[in] conn Connection that is about to be removed (ignored). */
|
||||
void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
|
||||
{
|
||||
/* Cleanup tasks for E1 are the same as for regular endpoint. The
|
||||
* shut down of the E1 part is handled separately. */
|
||||
mgcp_cleanup_rtp_bridge_cb(endp, conn);
|
||||
}
|
||||
|
||||
static bool is_dummy_msg(enum rtp_proto proto, struct msgb *msg)
|
||||
{
|
||||
return msgb_length(msg) == 1 && msgb_data(msg)[0] == MGCP_DUMMY_LOAD;
|
||||
}
|
||||
|
||||
/* Handle incoming RTP data from NET */
|
||||
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
@@ -1344,23 +1332,83 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
struct mgcp_conn_rtp *conn_src;
|
||||
struct mgcp_endpoint *endp;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
char buf[RTP_BUF_SIZE];
|
||||
int proto;
|
||||
int len;
|
||||
socklen_t slen = sizeof(addr);
|
||||
int ret;
|
||||
enum rtp_proto proto;
|
||||
struct osmo_rtp_msg_ctx *mc;
|
||||
struct msgb *msg = msgb_alloc(RTP_BUF_SIZE, "RTP-rx");
|
||||
int rc;
|
||||
|
||||
conn_src = (struct mgcp_conn_rtp *)fd->data;
|
||||
OSMO_ASSERT(conn_src);
|
||||
endp = conn_src->conn->endp;
|
||||
OSMO_ASSERT(endp);
|
||||
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "source conn:%s\n",
|
||||
mgcp_conn_dump(conn_src->conn));
|
||||
proto = (fd == &conn_src->end.rtp)? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
|
||||
|
||||
/* Receive packet */
|
||||
len = mgcp_recv(&proto, &addr, buf, sizeof(buf), fd);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
ret = recvfrom(fd->fd, msgb_data(msg), msg->data_len, 0, (struct sockaddr *)&addr, &slen);
|
||||
|
||||
if (ret <= 0) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(errno));
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
msgb_put(msg, ret);
|
||||
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
|
||||
proto == MGCP_PROTO_RTP ? "RTP" : "RTPC",
|
||||
msgb_length(msg), inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (is_dummy_msg(proto, msg)) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx dummy packet (dropped)\n");
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Since the msgb remains owned and freed by this function, the msg ctx data struct can just be on the stack and
|
||||
* needs not be allocated with the msgb. */
|
||||
mc = OSMO_RTP_MSG_CTX(msg);
|
||||
*mc = (struct osmo_rtp_msg_ctx){
|
||||
.proto = proto,
|
||||
.conn_src = conn_src,
|
||||
.from_addr = &addr,
|
||||
};
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "msg ctx: %d %p %s\n",
|
||||
mc->proto, mc->conn_src,
|
||||
osmo_hexdump((void*)mc->from_addr, sizeof(struct sockaddr_in)));
|
||||
|
||||
/* Increment RX statistics */
|
||||
rate_ctr_inc(&conn_src->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
|
||||
rate_ctr_add(&conn_src->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], msgb_length(msg));
|
||||
/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data(fd->fd, &conn_src->tap_in, msg);
|
||||
|
||||
rc = rx_rtp(msg);
|
||||
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int rx_rtp(struct msgb *msg)
|
||||
{
|
||||
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
|
||||
struct mgcp_conn_rtp *conn_src = mc->conn_src;
|
||||
struct sockaddr_in *from_addr = mc->from_addr;
|
||||
struct mgcp_conn *conn = conn_src->conn;
|
||||
struct mgcp_trunk *trunk = conn->endp->trunk;
|
||||
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx_rtp(%u bytes)\n", msgb_length(msg));
|
||||
|
||||
mgcp_conn_watchdog_kick(conn_src->conn);
|
||||
|
||||
@@ -1369,23 +1417,26 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
* define, then we check if the incoming payload matches that
|
||||
* expectation. */
|
||||
if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
|
||||
int oa = amr_oa_check(buf, len);
|
||||
int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
|
||||
if (oa < 0)
|
||||
return -1;
|
||||
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if the origin of the RTP packet seems plausible */
|
||||
if (!trunk->rtp_accept_all && check_rtp_origin(conn_src, from_addr))
|
||||
return -1;
|
||||
|
||||
/* Execute endpoint specific implementation that handles the
|
||||
* dispatching of the RTP data */
|
||||
return endp->type->dispatch_rtp_cb(proto, &addr, buf, len,
|
||||
conn_src->conn);
|
||||
return conn->endp->type->dispatch_rtp_cb(msg);
|
||||
}
|
||||
|
||||
/*! set IP Type of Service parameter.
|
||||
* \param[in] fd associated file descriptor
|
||||
* \param[in] tos dscp value
|
||||
* \returns 0 on success, -1 on ERROR */
|
||||
* \param[in] fd associated file descriptor.
|
||||
* \param[in] tos dscp value.
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
int mgcp_set_ip_tos(int fd, int tos)
|
||||
{
|
||||
int ret;
|
||||
@@ -1397,10 +1448,10 @@ int mgcp_set_ip_tos(int fd, int tos)
|
||||
}
|
||||
|
||||
/*! 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
|
||||
* \returns 0 on success, -1 on ERROR */
|
||||
* \param[in] source_addr source (local) address to bind on.
|
||||
* \param[in] fd associated file descriptor.
|
||||
* \param[in] port to bind on.
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port)
|
||||
{
|
||||
int rc;
|
||||
@@ -1420,11 +1471,10 @@ int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port)
|
||||
|
||||
/* 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, int endpno)
|
||||
struct mgcp_rtp_end *rtp_end, struct mgcp_endpoint *endp)
|
||||
{
|
||||
/* NOTE: The port that is used for RTCP is the RTP port incremented by one
|
||||
* (e.g. RTP-Port = 16000 ==> RTCP-Port = 16001) */
|
||||
struct mgcp_endpoint *endp = &cfg->trunk.endpoints[endpno];
|
||||
|
||||
if (mgcp_create_bind(source_addr, &rtp_end->rtp,
|
||||
rtp_end->local_port) != 0) {
|
||||
@@ -1446,7 +1496,7 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
|
||||
mgcp_set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp);
|
||||
mgcp_set_ip_tos(rtp_end->rtcp.fd, cfg->endp_dscp);
|
||||
|
||||
rtp_end->rtp.when = BSC_FD_READ;
|
||||
rtp_end->rtp.when = OSMO_FD_READ;
|
||||
if (osmo_fd_register(&rtp_end->rtp) != 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to register RTP port %d\n",
|
||||
@@ -1454,7 +1504,7 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
|
||||
goto cleanup2;
|
||||
}
|
||||
|
||||
rtp_end->rtcp.when = BSC_FD_READ;
|
||||
rtp_end->rtcp.when = OSMO_FD_READ;
|
||||
if (osmo_fd_register(&rtp_end->rtcp) != 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to register RTCP port %d\n",
|
||||
@@ -1477,10 +1527,10 @@ cleanup0:
|
||||
}
|
||||
|
||||
/*! bind RTP port to endpoint/connection.
|
||||
* \param[in] endp endpoint that holds the RTP connection
|
||||
* \param[in] rtp_port port number to bind on
|
||||
* \param[in] conn associated RTP connection
|
||||
* \returns 0 on success, -1 on ERROR */
|
||||
* \param[in] endp endpoint that holds the RTP connection.
|
||||
* \param[in] rtp_port port number to bind on.
|
||||
* \param[in] conn associated RTP connection.
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
@@ -1511,8 +1561,7 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
|
||||
mgcp_get_local_addr(local_ip_addr, conn);
|
||||
|
||||
return bind_rtp(endp->cfg, local_ip_addr, end,
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return bind_rtp(endp->cfg, local_ip_addr, end, endp);
|
||||
}
|
||||
|
||||
/*! free allocated RTP and RTCP ports.
|
||||
|
@@ -23,10 +23,11 @@
|
||||
#include <osmocom/netif/amr.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
|
||||
static struct osmo_fd osmux_fd;
|
||||
|
||||
@@ -197,14 +198,15 @@ static struct mgcp_conn_rtp*
|
||||
osmux_conn_lookup(struct mgcp_config *cfg, uint8_t cid,
|
||||
struct in_addr *from_addr)
|
||||
{
|
||||
struct mgcp_trunk *trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
struct mgcp_endpoint *endp;
|
||||
struct mgcp_conn *conn = NULL;
|
||||
struct mgcp_conn_rtp * conn_rtp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<cfg->trunk.number_endpoints; i++) {
|
||||
for (i = 0; i < trunk->number_endpoints; i++) {
|
||||
|
||||
endp = &cfg->trunk.endpoints[i];
|
||||
endp = trunk->endpoints[i];
|
||||
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
if (conn->type != MGCP_CONN_TYPE_RTP)
|
||||
@@ -234,13 +236,15 @@ static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data)
|
||||
{
|
||||
struct mgcp_conn_rtp *conn = data;
|
||||
struct mgcp_endpoint *endp = conn->conn->endp;
|
||||
struct sockaddr_in addr = {
|
||||
.sin_addr = conn->end.addr,
|
||||
.sin_port = conn->end.rtp_port,
|
||||
}; /* FIXME: not set/used in cb */
|
||||
struct sockaddr_in addr = { /* FIXME: do we know the source address?? */ };
|
||||
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
|
||||
*mc = (struct osmo_rtp_msg_ctx){
|
||||
.proto = MGCP_PROTO_RTP,
|
||||
.conn_src = conn,
|
||||
.from_addr = &addr,
|
||||
};
|
||||
|
||||
|
||||
endp->type->dispatch_rtp_cb(MGCP_PROTO_RTP, &addr, (char *)msg->data, msg->len, conn->conn);
|
||||
endp->type->dispatch_rtp_cb(msg);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
@@ -406,7 +410,7 @@ int osmux_init(int role, struct mgcp_config *cfg)
|
||||
return ret;
|
||||
}
|
||||
mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp);
|
||||
osmux_fd.when |= BSC_FD_READ;
|
||||
osmux_fd.when |= OSMO_FD_READ;
|
||||
|
||||
ret = osmo_fd_register(&osmux_fd);
|
||||
if (ret < 0) {
|
||||
|
@@ -32,14 +32,16 @@
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/mgcp/mgcp_network.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_stat.h>
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
@@ -53,83 +55,6 @@ struct mgcp_request {
|
||||
#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
|
||||
{ .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
|
||||
|
||||
static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
|
||||
[MGCP_CRCX_SUCCESS] = {"crcx:success", "CRCX command processed successfully."},
|
||||
[MGCP_CRCX_FAIL_BAD_ACTION] = {"crcx:bad_action", "bad action in CRCX command."},
|
||||
[MGCP_CRCX_FAIL_UNHANDLED_PARAM] = {"crcx:unhandled_param", "unhandled parameter in CRCX command."},
|
||||
[MGCP_CRCX_FAIL_MISSING_CALLID] = {"crcx:missing_callid", "missing CallId in CRCX command."},
|
||||
[MGCP_CRCX_FAIL_INVALID_MODE] = {"crcx:invalid_mode", "invalid connection mode in CRCX command."},
|
||||
[MGCP_CRCX_FAIL_LIMIT_EXCEEDED] = {"crcx:limit_exceeded", "limit of concurrent connections was reached."},
|
||||
[MGCP_CRCX_FAIL_UNKNOWN_CALLID] = {"crcx:unkown_callid", "unknown CallId in CRCX command."},
|
||||
[MGCP_CRCX_FAIL_ALLOC_CONN] = {"crcx:alloc_conn_fail", "connection allocation failure."},
|
||||
[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC] = {"crcx:no_remote_conn_desc", "no opposite end specified for connection."},
|
||||
[MGCP_CRCX_FAIL_START_RTP] = {"crcx:start_rtp_failure", "failure to start RTP processing."},
|
||||
[MGCP_CRCX_FAIL_REJECTED_BY_POLICY] = {"crcx:conn_rejected", "connection rejected by policy."},
|
||||
[MGCP_CRCX_FAIL_NO_OSMUX] = {"crcx:no_osmux", "no osmux offered by peer."},
|
||||
[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS] = {"crcx:conn_opt", "connection options invalid."},
|
||||
[MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = {"crcx:codec_nego", "codec negotiation failure."},
|
||||
[MGCP_CRCX_FAIL_BIND_PORT] = {"crcx:bind_port", "port bind failure."},
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
|
||||
.group_name_prefix = "crcx",
|
||||
.group_description = "crxc statistics",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_ctr = ARRAY_SIZE(mgcp_crcx_ctr_desc),
|
||||
.ctr_desc = mgcp_crcx_ctr_desc
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
|
||||
[MGCP_MDCX_SUCCESS] = {"mdcx:success", "MDCX command processed successfully."},
|
||||
[MGCP_MDCX_FAIL_WILDCARD] = {"mdcx:wildcard", "wildcard endpoint names in MDCX commands are unsupported."},
|
||||
[MGCP_MDCX_FAIL_NO_CONN] = {"mdcx:no_conn", "endpoint specified in MDCX command has no active connections."},
|
||||
[MGCP_MDCX_FAIL_INVALID_CALLID] = {"mdcx:callid", "invalid CallId specified in MDCX command."},
|
||||
[MGCP_MDCX_FAIL_INVALID_CONNID] = {"mdcx:connid", "invalid connection ID specified in MDCX command."},
|
||||
[MGCP_MDCX_FAIL_UNHANDLED_PARAM] = {"crcx:unhandled_param", "unhandled parameter in MDCX command."},
|
||||
[MGCP_MDCX_FAIL_NO_CONNID] = {"mdcx:no_connid", "no connection ID specified in MDCX command."},
|
||||
[MGCP_MDCX_FAIL_CONN_NOT_FOUND] = {"mdcx:conn_not_found", "connection specified in MDCX command does not exist."},
|
||||
[MGCP_MDCX_FAIL_INVALID_MODE] = {"mdcx:invalid_mode", "invalid connection mode in MDCX command."},
|
||||
[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = {"mdcx:conn_opt", "connection options invalid."},
|
||||
[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] = {"mdcx:no_remote_conn_desc", "no opposite end specified for connection."},
|
||||
[MGCP_MDCX_FAIL_START_RTP] = {"mdcx:start_rtp_failure", "failure to start RTP processing."},
|
||||
[MGCP_MDCX_FAIL_REJECTED_BY_POLICY] = {"mdcx:conn_rejected", "connection rejected by policy."},
|
||||
[MGCP_MDCX_DEFERRED_BY_POLICY] = {"mdcx:conn_deferred", "connection deferred by policy."},
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
|
||||
.group_name_prefix = "mdcx",
|
||||
.group_description = "mdcx statistics",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_ctr = ARRAY_SIZE(mgcp_mdcx_ctr_desc),
|
||||
.ctr_desc = mgcp_mdcx_ctr_desc
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
|
||||
[MGCP_DLCX_SUCCESS] = {"dlcx:success", "DLCX command processed successfully."},
|
||||
[MGCP_DLCX_FAIL_WILDCARD] = {"dlcx:wildcard", "wildcard names in DLCX commands are unsupported."},
|
||||
[MGCP_DLCX_FAIL_NO_CONN] = {"dlcx:no_conn", "endpoint specified in DLCX command has no active connections."},
|
||||
[MGCP_DLCX_FAIL_INVALID_CALLID] = {"dlcx:callid", "CallId specified in DLCX command mismatches endpoint's CallId ."},
|
||||
[MGCP_DLCX_FAIL_INVALID_CONNID] = {"dlcx:connid", "connection ID specified in DLCX command does not exist on endpoint."},
|
||||
[MGCP_DLCX_FAIL_UNHANDLED_PARAM] = {"dlcx:unhandled_param", "unhandled parameter in DLCX command."},
|
||||
[MGCP_DLCX_FAIL_REJECTED_BY_POLICY] = {"dlcx:rejected", "connection deletion rejected by policy."},
|
||||
[MGCP_DLCX_DEFERRED_BY_POLICY] = {"dlcx:deferred", "connection deletion deferred by policy."},
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
|
||||
.group_name_prefix = "dlcx",
|
||||
.group_description = "dlcx statistics",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_ctr = ARRAY_SIZE(mgcp_dlcx_ctr_desc),
|
||||
.ctr_desc = mgcp_dlcx_ctr_desc
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
|
||||
.group_name_prefix = "all_rtp_conn",
|
||||
.group_description = "aggregated statistics for all rtp connections",
|
||||
.class_id = 1,
|
||||
.num_ctr = ARRAY_SIZE(all_rtp_conn_rate_ctr_desc),
|
||||
.ctr_desc = all_rtp_conn_rate_ctr_desc
|
||||
};
|
||||
|
||||
static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data);
|
||||
static struct msgb *handle_create_con(struct mgcp_parse_data *data);
|
||||
@@ -236,11 +161,11 @@ static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
|
||||
* Remember the last transmission per endpoint.
|
||||
*/
|
||||
if (endp) {
|
||||
struct mgcp_trunk_config *tcfg = endp->tcfg;
|
||||
struct mgcp_trunk *trunk = endp->trunk;
|
||||
talloc_free(endp->last_response);
|
||||
talloc_free(endp->last_trans);
|
||||
endp->last_trans = talloc_strdup(tcfg->endpoints, trans);
|
||||
endp->last_response = talloc_strndup(tcfg->endpoints,
|
||||
endp->last_trans = talloc_strdup(trunk->endpoints, trans);
|
||||
endp->last_response = talloc_strndup(trunk->endpoints,
|
||||
(const char *)res->l2h,
|
||||
msgb_l2len(res));
|
||||
}
|
||||
@@ -278,10 +203,8 @@ static int add_params(struct msgb *msg, const struct mgcp_endpoint *endp,
|
||||
|
||||
/* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
|
||||
if (endp->wildcarded_req
|
||||
&& endp->tcfg->trunk_type == MGCP_TRUNK_VIRTUAL) {
|
||||
rc = msgb_printf(msg, "Z: %s%x@%s\r\n",
|
||||
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
|
||||
ENDPOINT_NUMBER(endp), endp->cfg->domain);
|
||||
&& endp->trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
|
||||
rc = msgb_printf(msg, "Z: %s\r\n", endp->name);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -361,24 +284,32 @@ static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
* - or a response (three numbers, space, transaction id) */
|
||||
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
{
|
||||
struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
|
||||
struct mgcp_parse_data pdata;
|
||||
int rc, i, code, handled = 0;
|
||||
struct msgb *resp = NULL;
|
||||
char *data;
|
||||
|
||||
/* Count all messages, even incorect ones */
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_MSGS_TOTAL]);
|
||||
|
||||
if (msgb_l2len(msg) < 4) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_FAIL_MSG_PARSE]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mgcp_msg_terminate_nul(msg))
|
||||
if (mgcp_msg_terminate_nul(msg)) {
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_FAIL_MSG_PARSE]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
|
||||
|
||||
/* attempt to treat it as a response */
|
||||
if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_FAIL_MSG_PARSE]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -394,12 +325,14 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
if (pdata.endp && pdata.trans
|
||||
&& pdata.endp->last_trans
|
||||
&& strcmp(pdata.endp->last_trans, pdata.trans) == 0) {
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_MSGS_RETRANSMITTED]);
|
||||
return do_retransmission(pdata.endp);
|
||||
}
|
||||
|
||||
/* check for general parser failure */
|
||||
if (rc < 0) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "%s: failed to find the endpoint\n", msg->l2h);
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_FAIL_NO_ENDPOINT]);
|
||||
return create_err_response(NULL, -rc, (const char *) msg->l2h, pdata.trans);
|
||||
}
|
||||
|
||||
@@ -413,9 +346,13 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
if (handled) {
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_MSGS_HANDLED]);
|
||||
} else {
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_MSGS_UNHANDLED]);
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n",
|
||||
&msg->l2h[0]);
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
@@ -435,13 +372,10 @@ static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
|
||||
static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
int i;
|
||||
struct mgcp_rtp_end *end;
|
||||
struct mgcp_port_range *range;
|
||||
unsigned int tries;
|
||||
|
||||
OSMO_ASSERT(conn);
|
||||
end = &conn->end;
|
||||
OSMO_ASSERT(end);
|
||||
|
||||
range = &endp->cfg->net_ports;
|
||||
|
||||
@@ -659,13 +593,13 @@ static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
|
||||
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
|
||||
struct mgcp_rtp_end *rtp)
|
||||
{
|
||||
struct mgcp_trunk_config *tcfg = endp->tcfg;
|
||||
struct mgcp_trunk *trunk = endp->trunk;
|
||||
|
||||
int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc;
|
||||
int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
|
||||
|
||||
rtp->force_aligned_timing = tcfg->force_aligned_timing;
|
||||
rtp->force_aligned_timing = trunk->force_aligned_timing;
|
||||
rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
|
||||
rtp->rfc5993_hr_convert = tcfg->rfc5993_hr_convert;
|
||||
rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
|
||||
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
|
||||
"Configuring RTP endpoint: local port %d%s%s\n",
|
||||
@@ -797,9 +731,9 @@ static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
|
||||
/* CRCX command handler, processes the received command */
|
||||
static struct msgb *handle_create_con(struct mgcp_parse_data *p)
|
||||
{
|
||||
struct mgcp_trunk_config *tcfg = p->endp->tcfg;
|
||||
struct mgcp_trunk *trunk = p->endp->trunk;
|
||||
struct mgcp_endpoint *endp = p->endp;
|
||||
struct rate_ctr_group *rate_ctrs = tcfg->mgcp_crcx_ctr_group;
|
||||
struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
|
||||
int error_code = 400;
|
||||
const char *local_options = NULL;
|
||||
const char *callid = NULL;
|
||||
@@ -812,6 +746,10 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
|
||||
int rc;
|
||||
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
|
||||
if (!mgcp_endp_avail(endp)) {
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_AVAIL]);
|
||||
return create_err_response(NULL, 501, "CRCX", p->trans);
|
||||
}
|
||||
|
||||
/* parse CallID C: and LocalParameters L: */
|
||||
for_each_line(line, p->save) {
|
||||
@@ -882,7 +820,7 @@ mgcp_header_done:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint full, max. %i connections allowed!\n",
|
||||
endp->type->max_conns);
|
||||
if (tcfg->force_realloc) {
|
||||
if (trunk->force_realloc) {
|
||||
/* There is no more room for a connection, make some
|
||||
* room by blindly tossing the oldest of the two two
|
||||
* connections */
|
||||
@@ -901,7 +839,7 @@ mgcp_header_done:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: already seized by other call (%s)\n",
|
||||
endp->callid);
|
||||
if (tcfg->force_realloc)
|
||||
if (trunk->force_realloc)
|
||||
/* This is not our call, toss everything by releasing
|
||||
* the entire endpoint. (rude!) */
|
||||
mgcp_endp_release(endp);
|
||||
@@ -913,13 +851,19 @@ mgcp_header_done:
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the callid, creation of another connection will only be possible
|
||||
* when the callid matches up. (Connections are distinguished by their
|
||||
* connection ids) */
|
||||
endp->callid = talloc_strdup(tcfg->endpoints, callid);
|
||||
if (!endp->callid) {
|
||||
/* Claim endpoint resources. This will also set the callid,
|
||||
* creating additional connections will only be possible if
|
||||
* the callid matches up (see above). */
|
||||
rc = mgcp_endp_claim(endp, callid);
|
||||
if (rc != 0) {
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_CLAIM]);
|
||||
return create_err_response(endp, 502, "CRCX", p->trans);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(conn_name, sizeof(conn_name), "%s", callid);
|
||||
_conn = mgcp_conn_alloc(tcfg->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
|
||||
_conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
|
||||
if (!_conn) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: unable to allocate RTP connection\n");
|
||||
@@ -927,6 +871,7 @@ mgcp_header_done:
|
||||
goto error2;
|
||||
|
||||
}
|
||||
|
||||
conn = mgcp_conn_get_rtp(endp, _conn->id);
|
||||
OSMO_ASSERT(conn);
|
||||
|
||||
@@ -954,7 +899,7 @@ mgcp_header_done:
|
||||
|
||||
/* Set local connection options, if present */
|
||||
if (local_options) {
|
||||
rc = set_local_cx_options(endp->tcfg->endpoints,
|
||||
rc = set_local_cx_options(endp->trunk->endpoints,
|
||||
&endp->local_options, local_options);
|
||||
if (rc != 0) {
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
|
||||
@@ -974,8 +919,8 @@ mgcp_header_done:
|
||||
goto error2;
|
||||
}
|
||||
|
||||
conn->end.fmtp_extra = talloc_strdup(tcfg->endpoints,
|
||||
tcfg->audio_fmtp_extra);
|
||||
conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
|
||||
trunk->audio_fmtp_extra);
|
||||
|
||||
if (p->cfg->force_ptime) {
|
||||
conn->end.packet_duration_ms = p->cfg->force_ptime;
|
||||
@@ -1010,8 +955,7 @@ mgcp_header_done:
|
||||
/* policy CB */
|
||||
if (p->cfg->policy_cb) {
|
||||
int rc;
|
||||
rc = p->cfg->policy_cb(tcfg, ENDPOINT_NUMBER(endp),
|
||||
MGCP_ENDP_CRCX, p->trans);
|
||||
rc = p->cfg->policy_cb(endp, MGCP_ENDP_CRCX, p->trans);
|
||||
switch (rc) {
|
||||
case MGCP_POLICY_REJECT:
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
|
||||
@@ -1033,17 +977,18 @@ mgcp_header_done:
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
|
||||
"CRCX: Creating connection: port: %u\n", conn->end.local_port);
|
||||
if (p->cfg->change_cb)
|
||||
p->cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX);
|
||||
p->cfg->change_cb(endp, MGCP_ENDP_CRCX);
|
||||
|
||||
/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
|
||||
OSMO_ASSERT(tcfg->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
|
||||
OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
|
||||
if (conn->conn->mode & MGCP_CONN_RECV_ONLY
|
||||
&& tcfg->keepalive_interval != MGCP_KEEPALIVE_NEVER)
|
||||
&& trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
|
||||
send_dummy(endp, conn);
|
||||
|
||||
LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: connection successfully created\n");
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_SUCCESS]);
|
||||
mgcp_endp_update(endp);
|
||||
return create_response_with_sdp(endp, conn, "CRCX", p->trans, true);
|
||||
error2:
|
||||
mgcp_endp_release(endp);
|
||||
@@ -1052,16 +997,11 @@ error2:
|
||||
return create_err_response(endp, error_code, "CRCX", p->trans);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* MDCX command handler, processes the received command */
|
||||
static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
||||
{
|
||||
struct mgcp_trunk_config *tcfg = p->endp->tcfg;
|
||||
struct mgcp_endpoint *endp = p->endp;
|
||||
struct rate_ctr_group *rate_ctrs = tcfg->mgcp_mdcx_ctr_group;
|
||||
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.mgcp_mdcx_ctr_group;
|
||||
int error_code = 500;
|
||||
int silent = 0;
|
||||
int have_sdp = 0;
|
||||
@@ -1075,6 +1015,11 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
||||
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
|
||||
|
||||
if (!mgcp_endp_avail(endp)) {
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_AVAIL]);
|
||||
return create_err_response(NULL, 501, "MDCX", p->trans);
|
||||
}
|
||||
|
||||
/* Prohibit wildcarded requests */
|
||||
if (endp->wildcarded_req) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
@@ -1169,7 +1114,7 @@ mgcp_header_done:
|
||||
|
||||
/* Set local connection options, if present */
|
||||
if (local_options) {
|
||||
rc = set_local_cx_options(endp->tcfg->endpoints,
|
||||
rc = set_local_cx_options(endp->trunk->endpoints,
|
||||
&endp->local_options, local_options);
|
||||
if (rc != 0) {
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
|
||||
@@ -1228,8 +1173,7 @@ mgcp_header_done:
|
||||
/* policy CB */
|
||||
if (p->cfg->policy_cb) {
|
||||
int rc;
|
||||
rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
|
||||
MGCP_ENDP_MDCX, p->trans);
|
||||
rc = p->cfg->policy_cb(endp, MGCP_ENDP_MDCX, p->trans);
|
||||
switch (rc) {
|
||||
case MGCP_POLICY_REJECT:
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
|
||||
@@ -1258,13 +1202,12 @@ mgcp_header_done:
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
|
||||
"MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
|
||||
if (p->cfg->change_cb)
|
||||
p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
|
||||
MGCP_ENDP_MDCX);
|
||||
p->cfg->change_cb(endp, MGCP_ENDP_MDCX);
|
||||
|
||||
/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
|
||||
OSMO_ASSERT(endp->tcfg->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
|
||||
OSMO_ASSERT(endp->trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
|
||||
if (conn->conn->mode & MGCP_CONN_RECV_ONLY
|
||||
&& endp->tcfg->keepalive_interval != MGCP_KEEPALIVE_NEVER)
|
||||
&& endp->trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
|
||||
send_dummy(endp, conn);
|
||||
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_SUCCESS]);
|
||||
@@ -1273,6 +1216,7 @@ mgcp_header_done:
|
||||
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
|
||||
"MDCX: connection successfully modified\n");
|
||||
mgcp_endp_update(endp);
|
||||
return create_response_with_sdp(endp, conn, "MDCX", p->trans, false);
|
||||
error3:
|
||||
return create_err_response(endp, error_code, "MDCX", p->trans);
|
||||
@@ -1285,9 +1229,8 @@ out_silent:
|
||||
/* DLCX command handler, processes the received command */
|
||||
static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
{
|
||||
struct mgcp_trunk_config *tcfg = p->endp->tcfg;
|
||||
struct mgcp_endpoint *endp = p->endp;
|
||||
struct rate_ctr_group *rate_ctrs = tcfg->mgcp_dlcx_ctr_group;
|
||||
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.mgcp_dlcx_ctr_group;
|
||||
int error_code = 400;
|
||||
int silent = 0;
|
||||
char *line;
|
||||
@@ -1298,6 +1241,11 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: deleting connection ...\n");
|
||||
|
||||
if (!mgcp_endp_avail(endp)) {
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_AVAIL]);
|
||||
return create_err_response(NULL, 501, "DLCX", p->trans);
|
||||
}
|
||||
|
||||
/* Prohibit wildcarded requests */
|
||||
if (endp->wildcarded_req) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
@@ -1348,8 +1296,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
/* policy CB */
|
||||
if (p->cfg->policy_cb) {
|
||||
int rc;
|
||||
rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
|
||||
MGCP_ENDP_DLCX, p->trans);
|
||||
rc = p->cfg->policy_cb(endp, MGCP_ENDP_DLCX, p->trans);
|
||||
switch (rc) {
|
||||
case MGCP_POLICY_REJECT:
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "DLCX: rejected by policy\n");
|
||||
@@ -1413,8 +1360,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
}
|
||||
|
||||
if (p->cfg->change_cb)
|
||||
p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
|
||||
MGCP_ENDP_DLCX);
|
||||
p->cfg->change_cb(endp, MGCP_ENDP_DLCX);
|
||||
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_SUCCESS]);
|
||||
if (silent)
|
||||
@@ -1443,7 +1389,7 @@ static struct msgb *handle_rsip(struct mgcp_parse_data *p)
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
|
||||
|
||||
if (p->cfg->reset_cb)
|
||||
p->cfg->reset_cb(p->endp->tcfg);
|
||||
p->cfg->reset_cb(p->endp->trunk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1489,37 +1435,37 @@ static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
|
||||
|
||||
/* Connection keepalive timer, will take care that dummy packets are send
|
||||
* regularly, so that NAT connections stay open */
|
||||
static void mgcp_keepalive_timer_cb(void *_tcfg)
|
||||
static void mgcp_keepalive_timer_cb(void *_trunk)
|
||||
{
|
||||
struct mgcp_trunk_config *tcfg = _tcfg;
|
||||
struct mgcp_trunk *trunk = _trunk;
|
||||
struct mgcp_conn *conn;
|
||||
int i;
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
|
||||
tcfg->trunk_nr);
|
||||
trunk->trunk_nr);
|
||||
|
||||
/* Do not accept invalid configuration values
|
||||
* valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
|
||||
* values greater 0 */
|
||||
OSMO_ASSERT(tcfg->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
|
||||
OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
|
||||
|
||||
/* The dummy packet functionality has been disabled, we will exit
|
||||
* immediately, no further timer is scheduled, which means we will no
|
||||
* longer send dummy packets even when we did before */
|
||||
if (tcfg->keepalive_interval == MGCP_KEEPALIVE_NEVER)
|
||||
if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
|
||||
return;
|
||||
|
||||
/* In cases where only one dummy packet is sent, we do not need
|
||||
* the timer since the functions that handle the CRCX and MDCX are
|
||||
* triggering the sending of the dummy packet. So we behave like in
|
||||
* the MGCP_KEEPALIVE_NEVER case */
|
||||
if (tcfg->keepalive_interval == MGCP_KEEPALIVE_ONCE)
|
||||
if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
|
||||
return;
|
||||
|
||||
/* Send walk over all endpoints and send out dummy packets through
|
||||
* every connection present on each endpoint */
|
||||
for (i = 1; i < tcfg->number_endpoints; ++i) {
|
||||
struct mgcp_endpoint *endp = &tcfg->endpoints[i];
|
||||
for (i = 1; i < trunk->number_endpoints; ++i) {
|
||||
struct mgcp_endpoint *endp = trunk->endpoints[i];
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
if (conn->mode == MGCP_CONN_RECV_ONLY)
|
||||
send_dummy(endp, &conn->u.rtp);
|
||||
@@ -1528,75 +1474,29 @@ static void mgcp_keepalive_timer_cb(void *_tcfg)
|
||||
|
||||
/* Schedule the keepalive timer for the next round */
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
|
||||
tcfg->trunk_nr);
|
||||
osmo_timer_schedule(&tcfg->keepalive_timer, tcfg->keepalive_interval,
|
||||
trunk->trunk_nr);
|
||||
osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
|
||||
0);
|
||||
}
|
||||
|
||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval)
|
||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
|
||||
{
|
||||
tcfg->keepalive_interval = interval;
|
||||
osmo_timer_setup(&tcfg->keepalive_timer, mgcp_keepalive_timer_cb, tcfg);
|
||||
trunk->keepalive_interval = interval;
|
||||
osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
|
||||
|
||||
if (interval <= 0)
|
||||
osmo_timer_del(&tcfg->keepalive_timer);
|
||||
osmo_timer_del(&trunk->keepalive_timer);
|
||||
else
|
||||
osmo_timer_schedule(&tcfg->keepalive_timer,
|
||||
tcfg->keepalive_interval, 0);
|
||||
}
|
||||
|
||||
static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group)
|
||||
{
|
||||
rate_ctr_group_free(rate_ctr_group);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alloc_mgcp_rate_counters(struct mgcp_trunk_config *trunk, void *ctx)
|
||||
{
|
||||
/* FIXME: Each new rate counter group requires a unique index. At the
|
||||
* moment we generate an index using a counter, but perhaps there is
|
||||
* a better way of assigning indices? */
|
||||
static unsigned int crcx_rate_ctr_index = 0;
|
||||
static unsigned int mdcx_rate_ctr_index = 0;
|
||||
static unsigned int dlcx_rate_ctr_index = 0;
|
||||
static unsigned int all_rtp_conn_rate_ctr_index = 0;
|
||||
|
||||
if (trunk->mgcp_crcx_ctr_group == NULL) {
|
||||
trunk->mgcp_crcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index);
|
||||
if (!trunk->mgcp_crcx_ctr_group)
|
||||
return -1;
|
||||
talloc_set_destructor(trunk->mgcp_crcx_ctr_group, free_rate_counter_group);
|
||||
crcx_rate_ctr_index++;
|
||||
}
|
||||
if (trunk->mgcp_mdcx_ctr_group == NULL) {
|
||||
trunk->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index);
|
||||
if (!trunk->mgcp_mdcx_ctr_group)
|
||||
return -1;
|
||||
talloc_set_destructor(trunk->mgcp_mdcx_ctr_group, free_rate_counter_group);
|
||||
mdcx_rate_ctr_index++;
|
||||
}
|
||||
if (trunk->mgcp_dlcx_ctr_group == NULL) {
|
||||
trunk->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index);
|
||||
if (!trunk->mgcp_dlcx_ctr_group)
|
||||
return -1;
|
||||
talloc_set_destructor(trunk->mgcp_dlcx_ctr_group, free_rate_counter_group);
|
||||
dlcx_rate_ctr_index++;
|
||||
}
|
||||
if (trunk->all_rtp_conn_stats == NULL) {
|
||||
trunk->all_rtp_conn_stats = rate_ctr_group_alloc(ctx, &all_rtp_conn_rate_ctr_group_desc,
|
||||
all_rtp_conn_rate_ctr_index);
|
||||
if (!trunk->all_rtp_conn_stats)
|
||||
return -1;
|
||||
talloc_set_destructor(trunk->all_rtp_conn_stats, free_rate_counter_group);
|
||||
all_rtp_conn_rate_ctr_index++;
|
||||
}
|
||||
return 0;
|
||||
osmo_timer_schedule(&trunk->keepalive_timer,
|
||||
trunk->keepalive_interval, 0);
|
||||
}
|
||||
|
||||
/*! allocate configuration with default values.
|
||||
* (called once at startup by main function) */
|
||||
struct mgcp_config *mgcp_config_alloc(void)
|
||||
{
|
||||
/* FIXME: This is unrelated to the protocol, put this in some
|
||||
* appropiate place! */
|
||||
struct mgcp_config *cfg;
|
||||
|
||||
cfg = talloc_zero(NULL, struct mgcp_config);
|
||||
@@ -1620,112 +1520,19 @@ struct mgcp_config *mgcp_config_alloc(void)
|
||||
|
||||
cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
|
||||
|
||||
/* default trunk handling */
|
||||
cfg->trunk.cfg = cfg;
|
||||
cfg->trunk.trunk_nr = 0;
|
||||
cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL;
|
||||
cfg->trunk.audio_name = talloc_strdup(cfg, "AMR/8000");
|
||||
cfg->trunk.audio_payload = 126;
|
||||
cfg->trunk.audio_send_ptime = 1;
|
||||
cfg->trunk.audio_send_name = 1;
|
||||
cfg->trunk.omit_rtcp = 0;
|
||||
mgcp_trunk_set_keepalive(&cfg->trunk, MGCP_KEEPALIVE_ONCE);
|
||||
if (alloc_mgcp_rate_counters(&cfg->trunk, cfg) < 0) {
|
||||
INIT_LLIST_HEAD(&cfg->trunks);
|
||||
|
||||
/* Allocate virtual trunk */
|
||||
if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
|
||||
talloc_free(cfg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_LLIST_HEAD(&cfg->trunks);
|
||||
mgcp_ratectr_global_alloc(cfg, &cfg->ratectr);
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
/*! allocate configuration with default values.
|
||||
* (called once at startup by VTY)
|
||||
* \param[in] cfg mgcp configuration
|
||||
* \param[in] nr trunk number
|
||||
* \returns pointer to allocated trunk configuration */
|
||||
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr)
|
||||
{
|
||||
struct mgcp_trunk_config *trunk;
|
||||
|
||||
trunk = talloc_zero(cfg, struct mgcp_trunk_config);
|
||||
if (!trunk) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
trunk->cfg = cfg;
|
||||
trunk->trunk_type = MGCP_TRUNK_E1;
|
||||
trunk->trunk_nr = nr;
|
||||
trunk->audio_name = talloc_strdup(cfg, "AMR/8000");
|
||||
trunk->audio_payload = 126;
|
||||
trunk->audio_send_ptime = 1;
|
||||
trunk->audio_send_name = 1;
|
||||
trunk->vty_number_endpoints = 33;
|
||||
trunk->omit_rtcp = 0;
|
||||
mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
|
||||
alloc_mgcp_rate_counters(trunk, trunk);
|
||||
llist_add_tail(&trunk->entry, &cfg->trunks);
|
||||
|
||||
return trunk;
|
||||
}
|
||||
|
||||
/*! get trunk configuration by trunk number (index).
|
||||
* \param[in] cfg mgcp configuration
|
||||
* \param[in] index trunk number
|
||||
* \returns pointer to trunk configuration, NULL on error */
|
||||
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index)
|
||||
{
|
||||
struct mgcp_trunk_config *trunk;
|
||||
|
||||
llist_for_each_entry(trunk, &cfg->trunks, entry)
|
||||
if (trunk->trunk_nr == index)
|
||||
return trunk;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! allocate endpoints and set default values.
|
||||
* (called once at startup by VTY)
|
||||
* \param[in] tcfg trunk configuration
|
||||
* \returns 0 on success, -1 on failure */
|
||||
int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
|
||||
{
|
||||
int i;
|
||||
|
||||
tcfg->endpoints = _talloc_zero_array(tcfg->cfg,
|
||||
sizeof(struct mgcp_endpoint),
|
||||
tcfg->vty_number_endpoints,
|
||||
"endpoints");
|
||||
if (!tcfg->endpoints)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < tcfg->vty_number_endpoints; ++i) {
|
||||
INIT_LLIST_HEAD(&tcfg->endpoints[i].conns);
|
||||
tcfg->endpoints[i].cfg = tcfg->cfg;
|
||||
tcfg->endpoints[i].tcfg = tcfg;
|
||||
|
||||
switch (tcfg->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
tcfg->endpoints[i].type = &ep_typeset.rtp;
|
||||
break;
|
||||
case MGCP_TRUNK_E1:
|
||||
/* FIXME: Implement E1 allocation */
|
||||
LOGP(DLMGCP, LOGL_FATAL, "E1 trunks not implemented!\n");
|
||||
break;
|
||||
default:
|
||||
osmo_panic("Cannot allocate unimplemented trunk type %d! %s:%d\n",
|
||||
tcfg->trunk_type, __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
tcfg->number_endpoints = tcfg->vty_number_endpoints;
|
||||
alloc_mgcp_rate_counters(tcfg, tcfg->cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
|
||||
{
|
||||
return write(cfg->gw_fd.bfd.fd, buf, len);
|
||||
@@ -1756,17 +1563,16 @@ int mgcp_send_reset_all(struct mgcp_config *cfg)
|
||||
|
||||
/*! Reset a single endpoint by sending RSIP message to self.
|
||||
* (called by VTY)
|
||||
* \param[in] endp trunk endpoint
|
||||
* \param[in] endpoint number
|
||||
* \param[in] endp to reset
|
||||
* \returns 0 on success, -1 on error */
|
||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint)
|
||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
|
||||
{
|
||||
char buf[MGCP_ENDPOINT_MAXLEN + 128];
|
||||
int len;
|
||||
int rc;
|
||||
|
||||
len = snprintf(buf, sizeof(buf),
|
||||
"RSIP 39 %x@%s MGCP 1.0\r\n", endpoint, endp->cfg->domain);
|
||||
"RSIP 39 %s MGCP 1.0\r\n", endp->name);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
|
229
src/libosmo-mgcp/mgcp_ratectr.c
Normal file
229
src/libosmo-mgcp/mgcp_ratectr.c
Normal file
@@ -0,0 +1,229 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* rate-counter implementation */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_ratectr.h>
|
||||
|
||||
static const struct rate_ctr_desc mgcp_general_ctr_desc[] = {
|
||||
/* rx_msgs = rx_msgs_retransmitted + rx_msgs_handled + rx_msgs_unhandled + err_rx_msg_parse + err_rx_no_endpoint */
|
||||
[MGCP_GENERAL_RX_MSGS_TOTAL] = { "mgcp:rx_msgs", "total number of MGCP messages received." },
|
||||
[MGCP_GENERAL_RX_MSGS_RETRANSMITTED] = { "mgcp:rx_msgs_retransmitted", "number of received retransmissions." },
|
||||
[MGCP_GENERAL_RX_MSGS_HANDLED] = { "mgcp:rx_msgs_handled", "number of handled MGCP messages." },
|
||||
[MGCP_GENERAL_RX_MSGS_UNHANDLED] = { "mgcp:rx_msgs_unhandled", "number of unhandled MGCP messages." },
|
||||
[MGCP_GENERAL_RX_FAIL_MSG_PARSE] = { "mgcp:err_rx_msg_parse", "error parsing MGCP message." },
|
||||
[MGCP_GENERAL_RX_FAIL_NO_ENDPOINT] =
|
||||
{ "mgcp:err_rx_no_endpoint", "can't find MGCP endpoint, probably we've used all allocated endpoints." },
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
|
||||
.group_name_prefix = "mgcp",
|
||||
.group_description = "mgcp general statistics",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_ctr = ARRAY_SIZE(mgcp_general_ctr_desc),
|
||||
.ctr_desc = mgcp_general_ctr_desc
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
|
||||
[MGCP_CRCX_SUCCESS] = { "crcx:success", "CRCX command processed successfully." },
|
||||
[MGCP_CRCX_FAIL_BAD_ACTION] = { "crcx:bad_action", "bad action in CRCX command." },
|
||||
[MGCP_CRCX_FAIL_UNHANDLED_PARAM] = { "crcx:unhandled_param", "unhandled parameter in CRCX command." },
|
||||
[MGCP_CRCX_FAIL_MISSING_CALLID] = { "crcx:missing_callid", "missing CallId in CRCX command." },
|
||||
[MGCP_CRCX_FAIL_INVALID_MODE] = { "crcx:invalid_mode", "invalid connection mode in CRCX command." },
|
||||
[MGCP_CRCX_FAIL_LIMIT_EXCEEDED] = { "crcx:limit_exceeded", "limit of concurrent connections was reached." },
|
||||
[MGCP_CRCX_FAIL_UNKNOWN_CALLID] = { "crcx:unkown_callid", "unknown CallId in CRCX command." },
|
||||
[MGCP_CRCX_FAIL_ALLOC_CONN] = { "crcx:alloc_conn_fail", "connection allocation failure." },
|
||||
[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC] =
|
||||
{ "crcx:no_remote_conn_desc", "no opposite end specified for connection." },
|
||||
[MGCP_CRCX_FAIL_START_RTP] = { "crcx:start_rtp_failure", "failure to start RTP processing." },
|
||||
[MGCP_CRCX_FAIL_REJECTED_BY_POLICY] = { "crcx:conn_rejected", "connection rejected by policy." },
|
||||
[MGCP_CRCX_FAIL_NO_OSMUX] = { "crcx:no_osmux", "no osmux offered by peer." },
|
||||
[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS] = { "crcx:conn_opt", "connection options invalid." },
|
||||
[MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = { "crcx:codec_nego", "codec negotiation failure." },
|
||||
[MGCP_CRCX_FAIL_BIND_PORT] = { "crcx:bind_port", "port bind failure." },
|
||||
[MGCP_CRCX_FAIL_AVAIL] = { "crcx:unavailable", "endpoint unavailable." },
|
||||
[MGCP_CRCX_FAIL_CLAIM] = { "crcx:claim", "endpoint can not be claimed." },
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
|
||||
.group_name_prefix = "crcx",
|
||||
.group_description = "crxc statistics",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_ctr = ARRAY_SIZE(mgcp_crcx_ctr_desc),
|
||||
.ctr_desc = mgcp_crcx_ctr_desc
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
|
||||
[MGCP_MDCX_SUCCESS] = { "mdcx:success", "MDCX command processed successfully." },
|
||||
[MGCP_MDCX_FAIL_WILDCARD] = { "mdcx:wildcard", "wildcard endpoint names in MDCX commands are unsupported." },
|
||||
[MGCP_MDCX_FAIL_NO_CONN] = { "mdcx:no_conn", "endpoint specified in MDCX command has no active connections." },
|
||||
[MGCP_MDCX_FAIL_INVALID_CALLID] = { "mdcx:callid", "invalid CallId specified in MDCX command." },
|
||||
[MGCP_MDCX_FAIL_INVALID_CONNID] = { "mdcx:connid", "invalid connection ID specified in MDCX command." },
|
||||
[MGCP_MDCX_FAIL_UNHANDLED_PARAM] = { "crcx:unhandled_param", "unhandled parameter in MDCX command." },
|
||||
[MGCP_MDCX_FAIL_NO_CONNID] = { "mdcx:no_connid", "no connection ID specified in MDCX command." },
|
||||
[MGCP_MDCX_FAIL_CONN_NOT_FOUND] =
|
||||
{ "mdcx:conn_not_found", "connection specified in MDCX command does not exist." },
|
||||
[MGCP_MDCX_FAIL_INVALID_MODE] = { "mdcx:invalid_mode", "invalid connection mode in MDCX command." },
|
||||
[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = { "mdcx:conn_opt", "connection options invalid." },
|
||||
[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] =
|
||||
{ "mdcx:no_remote_conn_desc", "no opposite end specified for connection." },
|
||||
[MGCP_MDCX_FAIL_START_RTP] = { "mdcx:start_rtp_failure", "failure to start RTP processing." },
|
||||
[MGCP_MDCX_FAIL_REJECTED_BY_POLICY] = { "mdcx:conn_rejected", "connection rejected by policy." },
|
||||
[MGCP_MDCX_DEFERRED_BY_POLICY] = { "mdcx:conn_deferred", "connection deferred by policy." },
|
||||
[MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." },
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
|
||||
.group_name_prefix = "mdcx",
|
||||
.group_description = "mdcx statistics",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_ctr = ARRAY_SIZE(mgcp_mdcx_ctr_desc),
|
||||
.ctr_desc = mgcp_mdcx_ctr_desc
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
|
||||
[MGCP_DLCX_SUCCESS] = { "dlcx:success", "DLCX command processed successfully." },
|
||||
[MGCP_DLCX_FAIL_WILDCARD] = { "dlcx:wildcard", "wildcard names in DLCX commands are unsupported." },
|
||||
[MGCP_DLCX_FAIL_NO_CONN] = { "dlcx:no_conn", "endpoint specified in DLCX command has no active connections." },
|
||||
[MGCP_DLCX_FAIL_INVALID_CALLID] =
|
||||
{ "dlcx:callid", "CallId specified in DLCX command mismatches endpoint's CallId ." },
|
||||
[MGCP_DLCX_FAIL_INVALID_CONNID] =
|
||||
{ "dlcx:connid", "connection ID specified in DLCX command does not exist on endpoint." },
|
||||
[MGCP_DLCX_FAIL_UNHANDLED_PARAM] = { "dlcx:unhandled_param", "unhandled parameter in DLCX command." },
|
||||
[MGCP_DLCX_FAIL_REJECTED_BY_POLICY] = { "dlcx:rejected", "connection deletion rejected by policy." },
|
||||
[MGCP_DLCX_DEFERRED_BY_POLICY] = { "dlcx:deferred", "connection deletion deferred by policy." },
|
||||
[MGCP_DLCX_FAIL_AVAIL] = { "dlcx:unavailable", "endpoint unavailable." },
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
|
||||
.group_name_prefix = "dlcx",
|
||||
.group_description = "dlcx statistics",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_ctr = ARRAY_SIZE(mgcp_dlcx_ctr_desc),
|
||||
.ctr_desc = mgcp_dlcx_ctr_desc
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc e1_rate_ctr_desc[] = {
|
||||
[E1_I460_TRAU_RX_FAIL_CTR] = {"e1:rx_fail", "Inbound I.460 TRAU failures."},
|
||||
[E1_I460_TRAU_TX_FAIL_CTR] = {"e1:tx_fail", "Outbound I.460 TRAU failures."},
|
||||
[E1_I460_TRAU_MUX_EMPTY_CTR] = {"e1:i460", "Outbound I.460 MUX queue empty."}
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
|
||||
.group_name_prefix = "e1",
|
||||
.group_description = "e1 statistics",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_ctr = ARRAY_SIZE(e1_rate_ctr_desc),
|
||||
.ctr_desc = e1_rate_ctr_desc
|
||||
};
|
||||
|
||||
const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
|
||||
.group_name_prefix = "all_rtp_conn",
|
||||
.group_description = "aggregated statistics for all rtp connections",
|
||||
.class_id = 1,
|
||||
.num_ctr = ARRAY_SIZE(all_rtp_conn_rate_ctr_desc),
|
||||
.ctr_desc = all_rtp_conn_rate_ctr_desc
|
||||
};
|
||||
|
||||
static int free_rate_counter_group(struct rate_ctr_group *rate_ctr_group)
|
||||
{
|
||||
rate_ctr_group_free(rate_ctr_group);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! allocate global rate counters into a given rate counter struct
|
||||
* (called once at startup)
|
||||
* \param[in] ctx talloc context.
|
||||
* \param[out] ratectr struct that holds the counters
|
||||
* \returns 0 on success, -EINVAL on failure */
|
||||
int mgcp_ratectr_global_alloc(void *ctx, struct mgcp_ratectr_global *ratectr)
|
||||
{
|
||||
/* FIXME: Each new rate counter group requires a unique index. At the
|
||||
* moment we generate an index using a counter, but perhaps there is
|
||||
* a better way of assigning indices? */
|
||||
static unsigned int general_rate_ctr_index = 0;
|
||||
|
||||
if (ratectr->mgcp_general_ctr_group == NULL) {
|
||||
ratectr->mgcp_general_ctr_group =
|
||||
rate_ctr_group_alloc(ctx, &mgcp_general_ctr_group_desc, general_rate_ctr_index);
|
||||
if (!ratectr->mgcp_general_ctr_group)
|
||||
return -EINVAL;
|
||||
talloc_set_destructor(ratectr->mgcp_general_ctr_group, free_rate_counter_group);
|
||||
general_rate_ctr_index++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! allocate trunk specific rate counters into a given rate counter struct
|
||||
* (called once on trunk initialization)
|
||||
* \param[in] ctx talloc context.
|
||||
* \param[out] ratectr struct that holds the counters
|
||||
* \returns 0 on success, -EINVAL on failure */
|
||||
int mgcp_ratectr_trunk_alloc(void *ctx, struct mgcp_ratectr_trunk *ratectr)
|
||||
{
|
||||
/* FIXME: see comment in mgcp_ratectr_global_alloc() */
|
||||
static unsigned int crcx_rate_ctr_index = 0;
|
||||
static unsigned int mdcx_rate_ctr_index = 0;
|
||||
static unsigned int dlcx_rate_ctr_index = 0;
|
||||
static unsigned int all_rtp_conn_rate_ctr_index = 0;
|
||||
|
||||
if (ratectr->mgcp_crcx_ctr_group == NULL) {
|
||||
ratectr->mgcp_crcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index);
|
||||
if (!ratectr->mgcp_crcx_ctr_group)
|
||||
return -EINVAL;
|
||||
talloc_set_destructor(ratectr->mgcp_crcx_ctr_group, free_rate_counter_group);
|
||||
crcx_rate_ctr_index++;
|
||||
}
|
||||
if (ratectr->mgcp_mdcx_ctr_group == NULL) {
|
||||
ratectr->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index);
|
||||
if (!ratectr->mgcp_mdcx_ctr_group)
|
||||
return -EINVAL;
|
||||
talloc_set_destructor(ratectr->mgcp_mdcx_ctr_group, free_rate_counter_group);
|
||||
mdcx_rate_ctr_index++;
|
||||
}
|
||||
if (ratectr->mgcp_dlcx_ctr_group == NULL) {
|
||||
ratectr->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(ctx, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index);
|
||||
if (!ratectr->mgcp_dlcx_ctr_group)
|
||||
return -EINVAL;
|
||||
talloc_set_destructor(ratectr->mgcp_dlcx_ctr_group, free_rate_counter_group);
|
||||
dlcx_rate_ctr_index++;
|
||||
}
|
||||
if (ratectr->all_rtp_conn_stats == NULL) {
|
||||
ratectr->all_rtp_conn_stats = rate_ctr_group_alloc(ctx, &all_rtp_conn_rate_ctr_group_desc,
|
||||
all_rtp_conn_rate_ctr_index);
|
||||
if (!ratectr->all_rtp_conn_stats)
|
||||
return -EINVAL;
|
||||
talloc_set_destructor(ratectr->all_rtp_conn_stats, free_rate_counter_group);
|
||||
all_rtp_conn_rate_ctr_index++;
|
||||
}
|
||||
if (ratectr->e1_stats == NULL) {
|
||||
ratectr->e1_stats = rate_ctr_group_alloc(ctx, &e1_rate_ctr_group_desc, mdcx_rate_ctr_index);
|
||||
if (!ratectr->e1_stats)
|
||||
return -EINVAL;
|
||||
talloc_set_destructor(ratectr->e1_stats, free_rate_counter_group);
|
||||
mdcx_rate_ctr_index++;
|
||||
}
|
||||
return 0;
|
||||
}
|
@@ -22,11 +22,15 @@
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
@@ -358,10 +362,11 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
break;
|
||||
default:
|
||||
if (p->endp)
|
||||
/* TODO: Check spec: We used the bare endpoint number before,
|
||||
* now we use the endpoint name as a whole? Is this allowed? */
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Unhandled SDP option: '%c'/%d on 0x%x\n",
|
||||
line[0], line[0],
|
||||
ENDPOINT_NUMBER(p->endp));
|
||||
"Unhandled SDP option: '%c'/%d on %s\n",
|
||||
line[0], line[0], endp->name);
|
||||
else
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Unhandled SDP option: '%c'/%d\n",
|
||||
@@ -381,7 +386,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
|
||||
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
|
||||
if (rc < 0)
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
|
||||
}
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
@@ -476,6 +481,8 @@ static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsign
|
||||
|
||||
for (i = 0; i < fmtp_params_len; i++) {
|
||||
rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Add amr octet align parameter */
|
||||
if (fmtp_params[i].param.amr_octet_aligned_present) {
|
||||
@@ -494,7 +501,7 @@ static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsign
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = msgb_printf(sdp, "\r\n", fmtp_params[i].payload_type);
|
||||
rc = msgb_printf(sdp, "\r\n");
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -557,7 +564,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
|
||||
if (endp->tcfg->audio_send_name) {
|
||||
if (endp->trunk->audio_send_name) {
|
||||
rc = add_rtpmap(sdp, payload_type, audio_name);
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
@@ -573,7 +580,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
}
|
||||
if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
|
||||
if (conn->end.packet_duration_ms > 0 && endp->trunk->audio_send_ptime) {
|
||||
rc = msgb_printf(sdp, "a=ptime:%u\r\n",
|
||||
conn->end.packet_duration_ms);
|
||||
if (rc < 0)
|
||||
|
@@ -22,10 +22,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp/mgcp_stat.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_stat.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
|
||||
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
|
||||
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)
|
||||
|
255
src/libosmo-mgcp/mgcp_trunk.c
Normal file
255
src/libosmo-mgcp/mgcp_trunk.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/* Trunk handling */
|
||||
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* (C) 2017-2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_e1.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
|
||||
/*! allocate trunk and add it (if required) to the trunk list.
|
||||
* (called once at startup by VTY).
|
||||
* \param[in] cfg mgcp configuration.
|
||||
* \param[in] ttype trunk type.
|
||||
* \param[in] nr trunk number.
|
||||
* \returns pointer to allocated trunk, NULL on failure. */
|
||||
struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
|
||||
{
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
trunk = talloc_zero(cfg, struct mgcp_trunk);
|
||||
if (!trunk) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
trunk->cfg = cfg;
|
||||
trunk->trunk_type = ttype;
|
||||
trunk->trunk_nr = nr;
|
||||
|
||||
trunk->audio_send_ptime = 1;
|
||||
trunk->audio_send_name = 1;
|
||||
trunk->v.vty_number_endpoints = 32;
|
||||
trunk->omit_rtcp = 0;
|
||||
|
||||
mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
|
||||
|
||||
llist_add_tail(&trunk->entry, &cfg->trunks);
|
||||
|
||||
mgcp_ratectr_trunk_alloc(cfg, &trunk->ratectr);
|
||||
|
||||
return trunk;
|
||||
}
|
||||
|
||||
/*! allocate endpoints and set default values
|
||||
* (called once at startup by VTY).
|
||||
* \param[in] trunk trunk configuration.
|
||||
* \returns 0 on success, -1 on failure. */
|
||||
int mgcp_trunk_alloc_endpts(struct mgcp_trunk *trunk)
|
||||
{
|
||||
int i;
|
||||
struct mgcp_endpoint *endp;
|
||||
unsigned int number_endpoints;
|
||||
unsigned int first_endpoint_nr;
|
||||
|
||||
/* This function is called once on startup by the VTY to allocate the
|
||||
* endpoints. The number of endpoints must not change througout the
|
||||
* runtime of the MGW */
|
||||
OSMO_ASSERT(trunk->number_endpoints == 0);
|
||||
OSMO_ASSERT(trunk->endpoints == NULL);
|
||||
|
||||
switch (trunk->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
/* Due to historical reasons the endpoints on the virtual
|
||||
* trunk start counting at 1. */
|
||||
first_endpoint_nr = 1;
|
||||
number_endpoints = trunk->v.vty_number_endpoints;
|
||||
break;
|
||||
case MGCP_TRUNK_E1:
|
||||
/* The first timeslot on an E1 line is reserved for framing
|
||||
* and alignment and can not be used for audio transport */
|
||||
first_endpoint_nr = 1 * MGCP_ENDP_E1_SUBSLOTS;
|
||||
number_endpoints = (NUM_E1_TS-1) * MGCP_ENDP_E1_SUBSLOTS;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
/* Make sure the amount of requested endpoints does not execeed
|
||||
* sane limits. The VTY already limits the possible amount,
|
||||
* however miss-initialization of the struct or memory corruption
|
||||
* could still lead to an excessive allocation of endpoints, so
|
||||
* better stop early if that is the case. */
|
||||
OSMO_ASSERT(number_endpoints < 65534);
|
||||
|
||||
/* allocate pointer array for the endpoints */
|
||||
trunk->endpoints = talloc_zero_array(trunk->cfg, struct mgcp_endpoint*,
|
||||
number_endpoints);
|
||||
if (!trunk->endpoints)
|
||||
return -1;
|
||||
|
||||
/* create endpoints */
|
||||
for (i = 0; i < number_endpoints; i++) {
|
||||
endp = mgcp_endp_alloc(trunk, i + first_endpoint_nr);
|
||||
if (!endp) {
|
||||
talloc_free(trunk->endpoints);
|
||||
return -1;
|
||||
}
|
||||
trunk->endpoints[i] = endp;
|
||||
}
|
||||
|
||||
/* make the endpoints we just created available to the MGW code */
|
||||
trunk->number_endpoints = number_endpoints;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Equip trunk with endpoints and resources
|
||||
* (called once at startup by VTY).
|
||||
* \param[in] trunk trunk configuration.
|
||||
* \returns 0 on success, -1 on failure. */
|
||||
int mgcp_trunk_equip(struct mgcp_trunk *trunk)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* Allocate endpoints */
|
||||
if(mgcp_trunk_alloc_endpts(trunk) != 0)
|
||||
return -1;
|
||||
|
||||
/* Allocate resources */
|
||||
switch (trunk->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
/* No additional initaliziation required here, virtual
|
||||
* endpoints will open/close network sockets themselves
|
||||
* on demand. */
|
||||
break;
|
||||
case MGCP_TRUNK_E1:
|
||||
/* The TS initalization happens once on startup for all
|
||||
* timeslots. This only affects the i460 multiplexer. Until
|
||||
* now no E1 resources are claimed yet. This happens on demand
|
||||
* when the related endpoint is actually used */
|
||||
memset(trunk->e1.i460_ts, 0, sizeof(trunk->e1.i460_ts));
|
||||
for (i = 0; i < (NUM_E1_TS-1); i++)
|
||||
osmo_i460_ts_init(&trunk->e1.i460_ts[i]);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! get trunk configuration by trunk number (index).
|
||||
* \param[in] cfg mgcp configuration.
|
||||
* \param[in] ttype trunk type.
|
||||
* \param[in] nr trunk number.
|
||||
* \returns pointer to trunk configuration, NULL on error. */
|
||||
struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, int nr)
|
||||
{
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
llist_for_each_entry(trunk, &cfg->trunks, entry) {
|
||||
if (trunk->trunk_nr == nr && trunk->trunk_type == ttype)
|
||||
return trunk;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Made public for unit-testing, do not use from outside this file */
|
||||
int e1_trunk_nr_from_epname(const char *epname)
|
||||
{
|
||||
unsigned long int trunk_nr;
|
||||
size_t prefix_len;
|
||||
char *str_trunk_nr_end;
|
||||
|
||||
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_E1_TRUNK) - 1;
|
||||
if (strncmp(epname, MGCP_ENDPOINT_PREFIX_E1_TRUNK, prefix_len) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
errno = 0;
|
||||
trunk_nr = strtoul(epname + prefix_len, &str_trunk_nr_end, 10);
|
||||
if (errno == ERANGE || trunk_nr > 64
|
||||
|| epname + prefix_len == str_trunk_nr_end
|
||||
|| str_trunk_nr_end[0] != '/')
|
||||
return -EINVAL;
|
||||
else
|
||||
return trunk_nr;
|
||||
}
|
||||
|
||||
/*! Find a trunk by the trunk prefix in the endpoint name.
|
||||
* \param[in] epname endpoint name with trunk prefix to look up.
|
||||
* \param[in] cfg that contains the trunks where the endpoint is located.
|
||||
* \returns trunk or NULL if trunk was not found. */
|
||||
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname)
|
||||
{
|
||||
size_t prefix_len;
|
||||
char epname_lc[MGCP_ENDPOINT_MAXLEN];
|
||||
int trunk_nr;
|
||||
|
||||
osmo_str_tolower_buf(epname_lc, sizeof(epname_lc), epname);
|
||||
epname = epname_lc;
|
||||
|
||||
prefix_len = sizeof(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK) - 1;
|
||||
if (strncmp(epname, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, prefix_len) == 0) {
|
||||
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
}
|
||||
|
||||
trunk_nr = e1_trunk_nr_from_epname(epname);
|
||||
if (trunk_nr >= 0)
|
||||
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_E1, trunk_nr);
|
||||
|
||||
/* Earlier versions of osmo-mgw were accepting endpoint names
|
||||
* without trunk prefix. This is normally not allowed, each MGCP
|
||||
* request should supply an endpoint name with trunk prefix.
|
||||
* However in order to stay compatible with old versions of
|
||||
* osmo-bsc and osmo-msc we still accept endpoint names without
|
||||
* trunk prefix and just assume that the virtual trunk should
|
||||
* be selected. There is even a TTCN3 test for this, see also:
|
||||
* MGCP_Test.TC_crcx_noprefix */
|
||||
if ((epname[0] >= '0' && epname[0] <= '9') || (epname[0] >= 'a' && epname[0] <= 'f')) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "missing trunk prefix in endpoint name \"%s\", assuming trunk \"%s\"!\n", epname,
|
||||
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
|
||||
return mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "unable to find trunk for endpoint name \"%s\"!\n", epname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Find a trunk (E1) by its associated E1 line number.
|
||||
* \param[in] num e1 line number.
|
||||
* \returns trunk or NULL if trunk was not found. */
|
||||
struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num)
|
||||
{
|
||||
/*! When used on trunks other than E1, the result will always be NULL. */
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
llist_for_each_entry(trunk, &cfg->trunks, entry) {
|
||||
if (trunk->trunk_type == MGCP_TRUNK_E1 && trunk->e1.vty_line_nr == num)
|
||||
return trunk;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,10 @@ AM_CFLAGS = \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMOCTRL_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(LIBOSMOABIS_CFLAGS) \
|
||||
$(LIBOSMOTRAU_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -26,5 +29,8 @@ osmo_mgw_LDADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMOTRAU_LIBS) \
|
||||
$(NULL)
|
||||
|
@@ -30,14 +30,18 @@
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/vty.h>
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_ctrl.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
@@ -48,22 +52,30 @@
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/ctrl/control_vty.h>
|
||||
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/stats.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/abis/abis.h>
|
||||
|
||||
#include "../../bscconfig.h"
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
|
||||
/* can be changed once libosmocore 1.4.0 is released */
|
||||
#ifndef OSMO_CTRL_PORT_MGW
|
||||
#define OSMO_CTRL_PORT_MGW 4267
|
||||
#endif
|
||||
|
||||
/* FIXME: Make use of the rtp proxy code */
|
||||
|
||||
static struct mgcp_config *cfg;
|
||||
static struct mgcp_trunk_config *reset_trunk;
|
||||
static struct mgcp_trunk *reset_trunk;
|
||||
static int reset_endpoints = 0;
|
||||
static int daemonize = 0;
|
||||
|
||||
@@ -89,18 +101,21 @@ static void print_help()
|
||||
printf(" -s --disable-color\n");
|
||||
printf(" -D --daemonize Fork the process into a background daemon\n");
|
||||
printf(" -V --version Print the version number\n");
|
||||
printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
static int long_option = 0;
|
||||
static struct option long_options[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
{"config-file", 1, 0, 'c'},
|
||||
{"daemonize", 0, 0, 'D'},
|
||||
{"version", 0, 0, 'V'},
|
||||
{"disable-color", 0, 0, 's'},
|
||||
{"vty-ref-xml", 0, &long_option, 1},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
@@ -114,6 +129,15 @@ static void handle_options(int argc, char **argv)
|
||||
print_help();
|
||||
exit(0);
|
||||
break;
|
||||
case 0:
|
||||
switch (long_option) {
|
||||
case 1:
|
||||
vty_dump_xml_ref(stdout);
|
||||
exit(0);
|
||||
default:
|
||||
fprintf(stderr, "error parsing cmdline options\n");
|
||||
exit(2);
|
||||
}
|
||||
case 'c':
|
||||
config_file = talloc_strdup(tall_bsc_ctx, optarg);
|
||||
break;
|
||||
@@ -140,13 +164,13 @@ static void handle_options(int argc, char **argv)
|
||||
|
||||
/* Callback function to be called when the RSIP ("Reset in Progress") mgcp
|
||||
* command is received */
|
||||
static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
|
||||
static int mgcp_rsip_cb(struct mgcp_trunk *trunk)
|
||||
{
|
||||
/* Set flag so that, when read_call_agent() is called next time
|
||||
* the reset can progress */
|
||||
reset_endpoints = 1;
|
||||
|
||||
reset_trunk = tcfg;
|
||||
reset_trunk = trunk;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -195,7 +219,7 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
|
||||
/* Walk over all endpoints and trigger a release, this will release all
|
||||
* endpoints, possible open connections are forcefully dropped */
|
||||
for (i = 1; i < reset_trunk->number_endpoints; ++i)
|
||||
mgcp_endp_release(&reset_trunk->endpoints[i]);
|
||||
mgcp_endp_release(reset_trunk->endpoints[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -247,7 +271,13 @@ static const struct log_info_cat log_categories[] = {
|
||||
.description = "RTP stream handling",
|
||||
.color = "\033[1;30m",
|
||||
.enabled = 1,.loglevel = LOGL_NOTICE,
|
||||
},
|
||||
},
|
||||
[DE1] = {
|
||||
.name = "DE1",
|
||||
.description = "E1 line handling",
|
||||
.color = "\033[1;31m",
|
||||
.enabled = 1,.loglevel = LOGL_NOTICE,
|
||||
},
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
@@ -267,6 +297,7 @@ int main(int argc, char **argv)
|
||||
|
||||
osmo_init_ignore_signals();
|
||||
osmo_init_logging2(tall_bsc_ctx, &log_info);
|
||||
libosmo_abis_init(tall_bsc_ctx);
|
||||
|
||||
cfg = mgcp_config_alloc();
|
||||
if (!cfg)
|
||||
@@ -278,6 +309,8 @@ int main(int argc, char **argv)
|
||||
osmo_talloc_vty_add_cmds();
|
||||
osmo_stats_vty_add_cmds();
|
||||
mgcp_vty_init();
|
||||
ctrl_vty_init(cfg);
|
||||
e1inp_vty_init();
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
@@ -294,6 +327,12 @@ int main(int argc, char **argv)
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
cfg->ctrl = mgw_ctrl_interface_setup(cfg, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
|
||||
if (!cfg->ctrl) {
|
||||
fprintf(stderr, "Failed to init the control interface on %s:%u. Exiting\n",
|
||||
ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
|
||||
}
|
||||
|
||||
/* Set the reset callback function. This functions is called when the
|
||||
* mgcp-command "RSIP" (Reset in Progress) is received */
|
||||
cfg->reset_cb = mgcp_rsip_cb;
|
||||
|
@@ -11,6 +11,8 @@ AM_CFLAGS = \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(LIBOSMOABIS_CFLAGS) \
|
||||
$(LIBOSMOTRAU_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -35,7 +37,10 @@ mgcp_test_LDADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMOTRAU_LIBS) \
|
||||
$(LIBRARY_DL) \
|
||||
$(LIBRARY_DLSYM) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
-lm \
|
||||
$(NULL)
|
||||
|
@@ -22,10 +22,12 @@
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/vty.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_stat.h>
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
|
||||
@@ -70,7 +72,7 @@ static void test_strline(void)
|
||||
}
|
||||
|
||||
#define AUEP1 "AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0\r\n"
|
||||
#define AUEP1_RET "200 158663169 OK\r\n"
|
||||
#define AUEP1_RET "500 158663169 FAIL\r\n"
|
||||
#define AUEP2 "AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0\r\n"
|
||||
#define AUEP2_RET "500 18983213 FAIL\r\n"
|
||||
#define EMPTY "\r\n"
|
||||
@@ -81,7 +83,7 @@ static void test_strline(void)
|
||||
#define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0\r\n"
|
||||
#define MDCX_ERR_RET "500 18983213 FAIL\r\n"
|
||||
#define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0\r\n"
|
||||
#define MDCX_RET "400 18983214 FAIL\r\n"
|
||||
#define MDCX_RET "500 18983214 FAIL\r\n"
|
||||
|
||||
#define MDCX3 \
|
||||
"MDCX 18983215 1@mgw MGCP 1.0\r\n" \
|
||||
@@ -591,14 +593,25 @@ static struct msgb *create_msg(const char *str, const char *conn_id)
|
||||
return msg;
|
||||
}
|
||||
|
||||
static int last_endpoint = -1;
|
||||
static char last_endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
|
||||
static int mgcp_test_policy_cb(struct mgcp_trunk_config *cfg, int endpoint,
|
||||
int state, const char *transactio_id)
|
||||
static int mgcp_test_policy_cb(struct mgcp_endpoint *endp,
|
||||
int state, const char *transaction_id)
|
||||
{
|
||||
fprintf(stderr, "Policy CB got state %d on endpoint 0x%x\n",
|
||||
state, endpoint);
|
||||
last_endpoint = endpoint;
|
||||
unsigned int i;
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
fprintf(stderr, "Policy CB got state %d on endpoint %s\n",
|
||||
state, endp->name);
|
||||
|
||||
trunk = endp->trunk;
|
||||
last_endpoint[0] = '\0';
|
||||
for (i = 0; i < trunk->number_endpoints; i++) {
|
||||
if (strcmp(endp->name, trunk->endpoints[i]->name) == 0)
|
||||
osmo_strlcpy(last_endpoint, trunk->endpoints[i]->name,
|
||||
sizeof(last_endpoint));
|
||||
}
|
||||
|
||||
return MGCP_POLICY_CONT;
|
||||
}
|
||||
|
||||
@@ -641,11 +654,11 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp)
|
||||
return real_clock_gettime(clk_id, tp);
|
||||
}
|
||||
|
||||
static void mgcp_endpoints_release(struct mgcp_trunk_config *trunk)
|
||||
static void mgcp_endpoints_release(struct mgcp_trunk *trunk)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < trunk->number_endpoints; i++)
|
||||
mgcp_endp_release(&trunk->endpoints[i]);
|
||||
for (i = 0; i < trunk->number_endpoints; i++)
|
||||
mgcp_endp_release(trunk->endpoints[i]);
|
||||
}
|
||||
|
||||
#define CONN_UNMODIFIED (0x1000)
|
||||
@@ -749,23 +762,21 @@ static void test_messages(void)
|
||||
{
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_endpoint *endp;
|
||||
struct mgcp_trunk_config *trunk2;
|
||||
struct mgcp_trunk *trunk;
|
||||
int i;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
char last_conn_id[256];
|
||||
int rc;
|
||||
|
||||
cfg = mgcp_config_alloc();
|
||||
trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
|
||||
cfg->trunk.vty_number_endpoints = 64;
|
||||
mgcp_endpoints_allocate(&cfg->trunk);
|
||||
trunk->v.vty_number_endpoints = 64;
|
||||
mgcp_trunk_equip(trunk);
|
||||
cfg->policy_cb = mgcp_test_policy_cb;
|
||||
|
||||
memset(last_conn_id, 0, sizeof(last_conn_id));
|
||||
|
||||
trunk2 = mgcp_trunk_alloc(cfg, 1);
|
||||
mgcp_endpoints_allocate(trunk2);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
const struct mgcp_test *t = &tests[i];
|
||||
struct msgb *inp;
|
||||
@@ -774,10 +785,10 @@ static void test_messages(void)
|
||||
printf("\n================================================\n");
|
||||
printf("Testing %s\n", t->name);
|
||||
|
||||
last_endpoint = -1;
|
||||
last_endpoint[0] = '\0';
|
||||
dummy_packets = 0;
|
||||
|
||||
osmo_talloc_replace_string(cfg, &cfg->trunk.audio_fmtp_extra,
|
||||
osmo_talloc_replace_string(cfg, &trunk->audio_fmtp_extra,
|
||||
t->extra_fmtp);
|
||||
|
||||
inp = create_msg(t->req, last_conn_id);
|
||||
@@ -809,8 +820,9 @@ static void test_messages(void)
|
||||
if (dummy_packets)
|
||||
printf("Dummy packets: %d\n", dummy_packets);
|
||||
|
||||
if (last_endpoint != -1) {
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
if (last_endpoint[0] != '\0') {
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
|
||||
conn = mgcp_conn_get_rtp(endp, "1");
|
||||
if (conn) {
|
||||
@@ -865,10 +877,9 @@ static void test_messages(void)
|
||||
|
||||
/* Check detected payload type */
|
||||
if (conn && t->ptype != PTYPE_IGNORE) {
|
||||
OSMO_ASSERT(last_endpoint != -1);
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
OSMO_ASSERT(last_endpoint[0] != '\0');
|
||||
|
||||
fprintf(stderr, "endpoint 0x%x: "
|
||||
fprintf(stderr, "endpoint:%s: "
|
||||
"payload type %d (expected %d)\n",
|
||||
last_endpoint,
|
||||
conn->end.codec->payload_type, t->ptype);
|
||||
@@ -882,29 +893,26 @@ static void test_messages(void)
|
||||
}
|
||||
}
|
||||
|
||||
mgcp_endpoints_release(trunk2);
|
||||
mgcp_endpoints_release(&cfg->trunk);
|
||||
mgcp_endpoints_release(trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
static void test_retransmission(void)
|
||||
{
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_trunk_config *trunk2;
|
||||
struct mgcp_trunk *trunk;
|
||||
int i;
|
||||
char last_conn_id[256];
|
||||
int rc;
|
||||
|
||||
cfg = mgcp_config_alloc();
|
||||
trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
|
||||
cfg->trunk.vty_number_endpoints = 64;
|
||||
mgcp_endpoints_allocate(&cfg->trunk);
|
||||
trunk->v.vty_number_endpoints = 64;
|
||||
mgcp_trunk_equip(trunk);
|
||||
|
||||
memset(last_conn_id, 0, sizeof(last_conn_id));
|
||||
|
||||
trunk2 = mgcp_trunk_alloc(cfg, 1);
|
||||
mgcp_endpoints_allocate(trunk2);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(retransmit); i++) {
|
||||
const struct mgcp_test *t = &retransmit[i];
|
||||
struct msgb *inp;
|
||||
@@ -943,8 +951,7 @@ static void test_retransmission(void)
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
mgcp_endpoints_release(trunk2);
|
||||
mgcp_endpoints_release(&cfg->trunk);
|
||||
mgcp_endpoints_release(trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
@@ -958,18 +965,16 @@ static int rqnt_cb(struct mgcp_endpoint *endp, char _tone)
|
||||
static void test_rqnt_cb(void)
|
||||
{
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_trunk_config *trunk2;
|
||||
struct mgcp_trunk *trunk;
|
||||
struct msgb *inp, *msg;
|
||||
char conn_id[256];
|
||||
|
||||
cfg = mgcp_config_alloc();
|
||||
trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
cfg->rqnt_cb = rqnt_cb;
|
||||
|
||||
cfg->trunk.vty_number_endpoints = 64;
|
||||
mgcp_endpoints_allocate(&cfg->trunk);
|
||||
|
||||
trunk2 = mgcp_trunk_alloc(cfg, 1);
|
||||
mgcp_endpoints_allocate(trunk2);
|
||||
trunk->v.vty_number_endpoints = 64;
|
||||
mgcp_trunk_equip(trunk);
|
||||
|
||||
inp = create_msg(CRCX, NULL);
|
||||
msg = mgcp_handle_message(cfg, inp);
|
||||
@@ -998,8 +1003,7 @@ static void test_rqnt_cb(void)
|
||||
inp = create_msg(DLCX, conn_id);
|
||||
msgb_free(mgcp_handle_message(cfg, inp));
|
||||
msgb_free(inp);
|
||||
mgcp_endpoints_release(trunk2);
|
||||
mgcp_endpoints_release(&cfg->trunk);
|
||||
mgcp_endpoints_release(trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
@@ -1035,8 +1039,9 @@ static void test_packet_loss_calc(void)
|
||||
{
|
||||
int i;
|
||||
struct mgcp_endpoint endp;
|
||||
struct mgcp_endpoint *endpoints[1];
|
||||
struct mgcp_config cfg = {0};
|
||||
struct mgcp_trunk_config trunk;
|
||||
struct mgcp_trunk trunk;
|
||||
|
||||
printf("Testing packet loss calculation.\n");
|
||||
|
||||
@@ -1045,9 +1050,10 @@ static void test_packet_loss_calc(void)
|
||||
|
||||
endp.cfg = &cfg;
|
||||
endp.type = &ep_typeset.rtp;
|
||||
trunk.vty_number_endpoints = 1;
|
||||
trunk.endpoints = &endp;
|
||||
endp.tcfg = &trunk;
|
||||
trunk.v.vty_number_endpoints = 1;
|
||||
trunk.endpoints = endpoints;
|
||||
trunk.endpoints[0] = &endp;
|
||||
endp.trunk = &trunk;
|
||||
INIT_LLIST_HEAD(&endp.conns);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pl_test_dat); ++i) {
|
||||
@@ -1256,19 +1262,19 @@ struct rtp_packet_info test_rtp_packets1[] = {
|
||||
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *rtp_end,
|
||||
struct sockaddr_in *addr, char *data, int len);
|
||||
struct sockaddr_in *addr, struct msgb *msg);
|
||||
|
||||
static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
{
|
||||
int i;
|
||||
|
||||
struct mgcp_trunk_config trunk;
|
||||
struct mgcp_trunk trunk;
|
||||
struct mgcp_endpoint endp;
|
||||
struct mgcp_endpoint *endpoints[1];
|
||||
struct mgcp_config cfg = {0};
|
||||
struct mgcp_rtp_state state;
|
||||
struct mgcp_rtp_end *rtp;
|
||||
struct sockaddr_in addr = { 0 };
|
||||
char buffer[4096];
|
||||
uint32_t last_ssrc = 0;
|
||||
uint32_t last_timestamp = 0;
|
||||
uint32_t last_seqno = 0;
|
||||
@@ -1295,12 +1301,13 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
endp.cfg = &cfg;
|
||||
endp.type = &ep_typeset.rtp;
|
||||
|
||||
trunk.vty_number_endpoints = 1;
|
||||
trunk.endpoints = &endp;
|
||||
trunk.v.vty_number_endpoints = 1;
|
||||
trunk.endpoints = endpoints;
|
||||
trunk.endpoints[0] = &endp;
|
||||
trunk.force_constant_ssrc = patch_ssrc;
|
||||
trunk.force_aligned_timing = patch_ts;
|
||||
|
||||
endp.tcfg = &trunk;
|
||||
endp.trunk = &trunk;
|
||||
|
||||
INIT_LLIST_HEAD(&endp.conns);
|
||||
_conn = mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
|
||||
@@ -1316,16 +1323,17 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
|
||||
struct rtp_packet_info *info = test_rtp_packets1 + i;
|
||||
struct msgb *msg = msgb_alloc(4096, __func__);
|
||||
|
||||
force_monotonic_time_us = round(1000000.0 * info->txtime);
|
||||
|
||||
OSMO_ASSERT(info->len <= sizeof(buffer));
|
||||
OSMO_ASSERT(info->len <= msgb_tailroom(msg));
|
||||
OSMO_ASSERT(info->len >= 0);
|
||||
memmove(buffer, info->data, info->len);
|
||||
msg->l3h = msgb_put(msg, info->len);
|
||||
memcpy((char*)msgb_l3(msg), info->data, info->len);
|
||||
mgcp_rtp_end_config(&endp, 1, rtp);
|
||||
|
||||
mgcp_patch_and_count(&endp, &state, rtp, &addr,
|
||||
buffer, info->len);
|
||||
mgcp_patch_and_count(&endp, &state, rtp, &addr, msg);
|
||||
|
||||
if (state.out_stream.ssrc != last_ssrc) {
|
||||
printf("Output SSRC changed to %08x\n",
|
||||
@@ -1352,6 +1360,8 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
|
||||
last_timestamp = state.out_stream.last_timestamp;
|
||||
last_seqno = state.out_stream.last_seq;
|
||||
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
force_monotonic_time_us = -1;
|
||||
@@ -1361,7 +1371,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
static void test_multilple_codec(void)
|
||||
{
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_trunk_config *trunk2;
|
||||
struct mgcp_trunk *trunk;
|
||||
struct mgcp_endpoint *endp;
|
||||
struct msgb *inp, *resp;
|
||||
struct in_addr addr;
|
||||
@@ -1371,15 +1381,13 @@ static void test_multilple_codec(void)
|
||||
printf("Testing multiple payload types\n");
|
||||
|
||||
cfg = mgcp_config_alloc();
|
||||
cfg->trunk.vty_number_endpoints = 64;
|
||||
mgcp_endpoints_allocate(&cfg->trunk);
|
||||
trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
trunk->v.vty_number_endpoints = 64;
|
||||
mgcp_trunk_equip(trunk);
|
||||
cfg->policy_cb = mgcp_test_policy_cb;
|
||||
|
||||
trunk2 = mgcp_trunk_alloc(cfg, 1);
|
||||
mgcp_endpoints_allocate(trunk2);
|
||||
|
||||
/* Allocate endpoint 1@mgw with two codecs */
|
||||
last_endpoint = -1;
|
||||
last_endpoint[0] = '\0';
|
||||
inp = create_msg(CRCX_MULT_1, NULL);
|
||||
resp = mgcp_handle_message(cfg, inp);
|
||||
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
|
||||
@@ -1387,14 +1395,15 @@ static void test_multilple_codec(void)
|
||||
msgb_free(inp);
|
||||
msgb_free(resp);
|
||||
|
||||
OSMO_ASSERT(last_endpoint == 1);
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/1@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
|
||||
/* Allocate 2@mgw with three codecs, last one ignored */
|
||||
last_endpoint = -1;
|
||||
last_endpoint[0] = '\0';
|
||||
inp = create_msg(CRCX_MULT_2, NULL);
|
||||
resp = mgcp_handle_message(cfg, inp);
|
||||
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
|
||||
@@ -1402,8 +1411,9 @@ static void test_multilple_codec(void)
|
||||
msgb_free(inp);
|
||||
msgb_free(resp);
|
||||
|
||||
OSMO_ASSERT(last_endpoint == 2);
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/2@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
@@ -1414,7 +1424,7 @@ static void test_multilple_codec(void)
|
||||
* it makes and since we already decided in OS#2658 that a missing
|
||||
* LCO should pick a sane default codec, it makes sense to expect
|
||||
* the same behaviour if SDP lacks proper payload type information */
|
||||
last_endpoint = -1;
|
||||
last_endpoint[0] = '\0';
|
||||
inp = create_msg(CRCX_MULT_3, NULL);
|
||||
resp = mgcp_handle_message(cfg, inp);
|
||||
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
|
||||
@@ -1422,14 +1432,15 @@ static void test_multilple_codec(void)
|
||||
msgb_free(inp);
|
||||
msgb_free(resp);
|
||||
|
||||
OSMO_ASSERT(last_endpoint == 3);
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/3@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||
|
||||
/* Allocate 4@mgw with a single codec */
|
||||
last_endpoint = -1;
|
||||
last_endpoint[0] = '\0';
|
||||
inp = create_msg(CRCX_MULT_4, NULL);
|
||||
resp = mgcp_handle_message(cfg, inp);
|
||||
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
|
||||
@@ -1437,37 +1448,38 @@ static void test_multilple_codec(void)
|
||||
msgb_free(inp);
|
||||
msgb_free(resp);
|
||||
|
||||
OSMO_ASSERT(last_endpoint == 4);
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/4@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
|
||||
/* Allocate 5@mgw at select GSM.. */
|
||||
last_endpoint = -1;
|
||||
/* Allocate 5@mgw and let osmo-mgw pick a codec from the list */
|
||||
last_endpoint[0] = '\0';
|
||||
inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
|
||||
talloc_free(cfg->trunk.audio_name);
|
||||
cfg->trunk.audio_name = "GSM/8000";
|
||||
cfg->trunk.no_audio_transcoding = 1;
|
||||
trunk->no_audio_transcoding = 1;
|
||||
resp = mgcp_handle_message(cfg, inp);
|
||||
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
|
||||
sizeof(conn_id)) == 0);
|
||||
msgb_free(inp);
|
||||
msgb_free(resp);
|
||||
|
||||
OSMO_ASSERT(last_endpoint == 5);
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 3);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||
|
||||
inp = create_msg(MDCX_NAT_DUMMY, conn_id);
|
||||
last_endpoint = -1;
|
||||
last_endpoint[0] = '\0';
|
||||
resp = mgcp_handle_message(cfg, inp);
|
||||
msgb_free(inp);
|
||||
msgb_free(resp);
|
||||
OSMO_ASSERT(last_endpoint == 5);
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 3);
|
||||
@@ -1487,23 +1499,23 @@ static void test_multilple_codec(void)
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(!conn);
|
||||
|
||||
last_endpoint = -1;
|
||||
last_endpoint[0] = '\0';
|
||||
inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
|
||||
cfg->trunk.no_audio_transcoding = 0;
|
||||
trunk->no_audio_transcoding = 0;
|
||||
resp = mgcp_handle_message(cfg, inp);
|
||||
OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
|
||||
sizeof(conn_id)) == 0);
|
||||
msgb_free(inp);
|
||||
msgb_free(resp);
|
||||
|
||||
OSMO_ASSERT(last_endpoint == 5);
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||
|
||||
mgcp_endpoints_release(trunk2);
|
||||
mgcp_endpoints_release(&cfg->trunk);
|
||||
mgcp_endpoints_release(trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
@@ -1513,14 +1525,17 @@ static void test_no_cycle(void)
|
||||
struct mgcp_endpoint *endp;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
struct mgcp_conn *_conn = NULL;
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
printf("Testing no sequence flow on initial packet\n");
|
||||
|
||||
cfg = mgcp_config_alloc();
|
||||
cfg->trunk.vty_number_endpoints = 64;
|
||||
mgcp_endpoints_allocate(&cfg->trunk);
|
||||
trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
trunk->v.vty_number_endpoints = 64;
|
||||
mgcp_trunk_equip(trunk);
|
||||
|
||||
endp = &cfg->trunk.endpoints[1];
|
||||
endp = mgcp_endp_by_name(NULL, "rtpbridge/1@mgw", cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
|
||||
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP,
|
||||
"test-connection");
|
||||
@@ -1552,28 +1567,26 @@ static void test_no_cycle(void)
|
||||
OSMO_ASSERT(conn->state.stats.cycles == UINT16_MAX + 1);
|
||||
OSMO_ASSERT(conn->state.stats.max_seq == 0);
|
||||
|
||||
mgcp_endpoints_release(&cfg->trunk);
|
||||
mgcp_endpoints_release(trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
static void test_no_name(void)
|
||||
{
|
||||
struct mgcp_trunk_config *trunk2;
|
||||
struct mgcp_trunk *trunk;
|
||||
struct mgcp_config *cfg;
|
||||
struct msgb *inp, *msg;
|
||||
|
||||
printf("Testing no rtpmap name\n");
|
||||
cfg = mgcp_config_alloc();
|
||||
trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
|
||||
cfg->trunk.vty_number_endpoints = 64;
|
||||
cfg->trunk.audio_send_name = 0;
|
||||
mgcp_endpoints_allocate(&cfg->trunk);
|
||||
trunk->v.vty_number_endpoints = 64;
|
||||
trunk->audio_send_name = 0;
|
||||
mgcp_trunk_equip(trunk);
|
||||
|
||||
cfg->policy_cb = mgcp_test_policy_cb;
|
||||
|
||||
trunk2 = mgcp_trunk_alloc(cfg, 1);
|
||||
mgcp_endpoints_allocate(trunk2);
|
||||
|
||||
inp = create_msg(CRCX, NULL);
|
||||
msg = mgcp_handle_message(cfg, inp);
|
||||
|
||||
@@ -1585,8 +1598,7 @@ static void test_no_name(void)
|
||||
msgb_free(inp);
|
||||
msgb_free(msg);
|
||||
|
||||
mgcp_endpoints_release(trunk2);
|
||||
mgcp_endpoints_release(&cfg->trunk);
|
||||
mgcp_endpoints_release(trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
@@ -2105,6 +2117,48 @@ void test_conn_id_matching()
|
||||
talloc_free(conn);
|
||||
}
|
||||
|
||||
void test_e1_trunk_nr_from_epname()
|
||||
{
|
||||
int trunk_nr;
|
||||
|
||||
/* Note: e1_trunk_nr_from_epname does not check the text
|
||||
* after the E1 trunk number, after the delimiter
|
||||
* character "/" arbitrary text may follow. */
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-1/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == 1);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-2/s-2/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == 2);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-3/s-23/su32-0");
|
||||
OSMO_ASSERT(trunk_nr == 3);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-3/xxxxxxx");
|
||||
OSMO_ASSERT(trunk_nr == 3);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-24/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == 24);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-64/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == 64);
|
||||
|
||||
/* The following endpoint strings should fail, either the
|
||||
* trunk number exceeds the valid range or the trunk prefix
|
||||
* is wrong. Also when the delimiter character "/" at the
|
||||
* end of the trunk is wrong the parsing should fail. */
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-0/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == -EINVAL);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-65/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == -EINVAL);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1--1/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == -EINVAL);
|
||||
trunk_nr = e1_trunk_nr_from_epname("xxxxxx4zyz");
|
||||
OSMO_ASSERT(trunk_nr == -EINVAL);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1+2/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == -EINVAL);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e2-24/s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == -EINVAL);
|
||||
trunk_nr = e1_trunk_nr_from_epname("ds/e1-24s-1/su16-0");
|
||||
OSMO_ASSERT(trunk_nr == -EINVAL);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
|
||||
@@ -2130,6 +2184,7 @@ int main(int argc, char **argv)
|
||||
test_check_local_cx_options(ctx);
|
||||
test_mgcp_codec_pt_translate();
|
||||
test_conn_id_matching();
|
||||
test_e1_trunk_nr_from_epname();
|
||||
|
||||
OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
|
||||
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
|
||||
|
@@ -35,6 +35,7 @@ mgcp_client_test_LDADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBRARY_DL) \
|
||||
$(LIBRARY_DLSYM) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
|
@@ -547,6 +547,57 @@ static void test_map_codec_to_pt_and_map_pt_to_codec(void)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void test_mgcp_client_e1_epname(void)
|
||||
{
|
||||
char *epname;
|
||||
|
||||
if (mgcp)
|
||||
talloc_free(mgcp);
|
||||
mgcp = mgcp_client_init(ctx, &conf);
|
||||
|
||||
/* Valid endpoint names */
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 1, 15, 64, 0);
|
||||
printf("%s\n", epname);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 2, 14, 32, 0);
|
||||
printf("%s\n", epname);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 3, 13, 32, 4);
|
||||
printf("%s\n", epname);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 4, 12, 16, 0);
|
||||
printf("%s\n", epname);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 5, 11, 16, 2);
|
||||
printf("%s\n", epname);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 6, 10, 16, 4);
|
||||
printf("%s\n", epname);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 7, 9, 16, 6);
|
||||
printf("%s\n", epname);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 8, 8, 8, 0);
|
||||
printf("%s\n", epname);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 9, 7, 8, 1);
|
||||
printf("%s\n", epname);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 10, 6, 8, 2);
|
||||
printf("%s\n", epname);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 11, 5, 8, 3);
|
||||
printf("%s\n", epname);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 12, 4, 8, 4);
|
||||
printf("%s\n", epname);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 13, 3, 8, 5);
|
||||
printf("%s\n", epname);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 14, 2, 8, 6);
|
||||
printf("%s\n", epname);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 1, 8, 7);
|
||||
printf("%s\n", epname);
|
||||
|
||||
/* A few invalid enpoint names */
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 1, 128, 0);
|
||||
OSMO_ASSERT(epname == NULL);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 1, 8, 16);
|
||||
OSMO_ASSERT(epname == NULL);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 0, 8, 2);
|
||||
OSMO_ASSERT(epname == NULL);
|
||||
epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 15, 64, 8, 2);
|
||||
OSMO_ASSERT(epname == NULL);
|
||||
}
|
||||
|
||||
static const struct log_info_cat log_categories[] = {
|
||||
};
|
||||
|
||||
@@ -575,6 +626,7 @@ int main(int argc, char **argv)
|
||||
test_sdp_section_start();
|
||||
test_map_codec_to_pt_and_map_pt_to_codec();
|
||||
test_map_pt_to_codec();
|
||||
test_mgcp_client_e1_epname();
|
||||
|
||||
printf("Done\n");
|
||||
fprintf(stderr, "Done\n");
|
||||
|
@@ -72,4 +72,9 @@ DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
|
||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
|
||||
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
|
||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
|
||||
DLMGCP MGCP client: using endpoint domain '@mgw'
|
||||
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw), rate(128)/offset(0) combination is invalid!
|
||||
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!
|
||||
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-0/su8-2@mgw), E1-timeslot number (0) is invalid!
|
||||
DLMGCP Cannot compose MGCP e1-endpoint name (ds/e1-15/s-64/su8-2@mgw), E1-timeslot number (64) is invalid!
|
||||
Done
|
||||
|
@@ -178,4 +178,19 @@ test_sdp_section_start() test [9]:
|
||||
2 <= 2
|
||||
100 <= 100
|
||||
|
||||
ds/e1-1/s-15/su64-0@mgw
|
||||
ds/e1-2/s-14/su32-0@mgw
|
||||
ds/e1-3/s-13/su32-4@mgw
|
||||
ds/e1-4/s-12/su16-0@mgw
|
||||
ds/e1-5/s-11/su16-2@mgw
|
||||
ds/e1-6/s-10/su16-4@mgw
|
||||
ds/e1-7/s-9/su16-6@mgw
|
||||
ds/e1-8/s-8/su8-0@mgw
|
||||
ds/e1-9/s-7/su8-1@mgw
|
||||
ds/e1-10/s-6/su8-2@mgw
|
||||
ds/e1-11/s-5/su8-3@mgw
|
||||
ds/e1-12/s-4/su8-4@mgw
|
||||
ds/e1-13/s-3/su8-5@mgw
|
||||
ds/e1-14/s-2/su8-6@mgw
|
||||
ds/e1-15/s-1/su8-7@mgw
|
||||
Done
|
||||
|
Reference in New Issue
Block a user