mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-11-02 04:53:24 +00:00
Compare commits
29 Commits
efistokl/i
...
35c3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ed3eb8e0f | ||
|
|
fdc5f111aa | ||
|
|
02d612a926 | ||
|
|
944af2f641 | ||
|
|
f2a65b8bbb | ||
|
|
332308d994 | ||
|
|
099332824e | ||
|
|
0a40379214 | ||
|
|
72bc8da537 | ||
|
|
c3132fd508 | ||
|
|
96c3107daf | ||
|
|
ad21a0e166 | ||
|
|
32d15cc8ba | ||
|
|
1fd50e5306 | ||
|
|
6802374574 | ||
|
|
82c1d1eb20 | ||
|
|
5a5169e15c | ||
|
|
7c0b70a94e | ||
|
|
ca6a8495e1 | ||
|
|
c0a2196f91 | ||
|
|
da026abc27 | ||
|
|
27780313ac | ||
|
|
e5ed946bdb | ||
|
|
c319b79baa | ||
|
|
2818db8b33 | ||
|
|
0415de74b8 | ||
|
|
4075b8805d | ||
|
|
8ab3fbbaf2 | ||
|
|
cc39218dce |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -83,3 +83,14 @@ gsn_restart
|
||||
src/openbsc.cfg*
|
||||
writtenconfig/
|
||||
gtphub_restart_count
|
||||
|
||||
# manuals
|
||||
doc/manuals/*.html
|
||||
doc/manuals/*.svg
|
||||
doc/manuals/*.pdf
|
||||
doc/manuals/*__*.png
|
||||
doc/manuals/*.check
|
||||
doc/manuals/generated/
|
||||
doc/manuals/osmomsc-usermanual.xml
|
||||
doc/manuals/common
|
||||
doc/manuals/build
|
||||
|
||||
@@ -24,7 +24,7 @@ pkgconfig_DATA = \
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = git-version-gen osmoappdesc.py .version
|
||||
|
||||
DISTCHECK_CONFIGURE_FLAGS = \
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||
|
||||
@RELMAKE@
|
||||
|
||||
@@ -24,3 +24,4 @@
|
||||
# If any interfaces have been removed or changed since the last public release, a=0.
|
||||
#
|
||||
#library what description / commit summary line
|
||||
libosmo-mgcp-client various Drop legacy API functions and mgcp_client_conf members
|
||||
|
||||
44
configure.ac
44
configure.ac
@@ -128,6 +128,48 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
|
||||
AC_MSG_RESULT([$enable_ext_tests])
|
||||
AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
|
||||
|
||||
# Generate manuals
|
||||
AC_ARG_ENABLE(manuals,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-manuals],
|
||||
[Generate manual PDFs [default=no]],
|
||||
)],
|
||||
[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"])
|
||||
AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"])
|
||||
AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals"
|
||||
fallback])
|
||||
if test x"$osmo_ac_build_manuals" = x"yes"
|
||||
then
|
||||
# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback)
|
||||
if test -n "$OSMO_GSM_MANUALS_DIR"; then
|
||||
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)"
|
||||
else
|
||||
OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)"
|
||||
if test -n "$OSMO_GSM_MANUALS_DIR"; then
|
||||
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)"
|
||||
else
|
||||
OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals"
|
||||
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)"
|
||||
fi
|
||||
fi
|
||||
if ! test -d "$OSMO_GSM_MANUALS_DIR"; then
|
||||
AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.")
|
||||
fi
|
||||
|
||||
# Find and run check-depends
|
||||
CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh"
|
||||
if ! test -x "$CHECK_DEPENDS"; then
|
||||
CHECK_DEPENDS="osmo-gsm-manuals-check-depends"
|
||||
fi
|
||||
if ! $CHECK_DEPENDS; then
|
||||
AC_MSG_ERROR("missing dependencies for --enable-manuals")
|
||||
fi
|
||||
|
||||
# Put in Makefile with absolute path
|
||||
OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")"
|
||||
AC_SUBST([OSMO_GSM_MANUALS_DIR])
|
||||
fi
|
||||
|
||||
# https://www.freedesktop.org/software/systemd/man/daemon.html
|
||||
AC_ARG_WITH([systemdsystemunitdir],
|
||||
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
|
||||
@@ -164,8 +206,10 @@ AC_OUTPUT(
|
||||
tests/atlocal
|
||||
tests/mgcp_client/Makefile
|
||||
tests/mgcp/Makefile
|
||||
tests/iuup/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
doc/manuals/Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
Makefile)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
# jenkins build helper script for openbsc. This is how we build on jenkins.osmocom.org
|
||||
#
|
||||
# environment variables:
|
||||
# * WITH_MANUALS: build manual PDFs if set to "1"
|
||||
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
|
||||
#
|
||||
|
||||
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
|
||||
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
|
||||
@@ -23,10 +28,18 @@ verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
|
||||
|
||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export LD_LIBRARY_PATH="$inst/lib"
|
||||
export PATH="$inst/bin:$PATH"
|
||||
|
||||
osmo-build-dep.sh libosmo-abis
|
||||
osmo-build-dep.sh libosmo-netif
|
||||
|
||||
# Additional configure options and depends
|
||||
CONFIG=""
|
||||
if [ "$WITH_MANUALS" = "1" ]; then
|
||||
osmo-build-dep.sh osmo-gsm-manuals
|
||||
CONFIG="--enable-manuals"
|
||||
fi
|
||||
|
||||
set +x
|
||||
echo
|
||||
echo
|
||||
@@ -37,13 +50,17 @@ set -x
|
||||
|
||||
cd "$base"
|
||||
autoreconf --install --force
|
||||
./configure $MGCP --enable-vty-tests --enable-external-tests --enable-werror
|
||||
./configure --enable-vty-tests --enable-external-tests --enable-werror $CONFIG
|
||||
$MAKE $PARALLEL_MAKE
|
||||
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
|
||||
|| cat-testlogs.sh
|
||||
LD_LIBRARY_PATH="$inst/lib" \
|
||||
DISTCHECK_CONFIGURE_FLAGS="$MGCP --enable-vty-tests --enable-external-tests" \
|
||||
DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests $CONFIG" \
|
||||
$MAKE distcheck \
|
||||
|| cat-testlogs.sh
|
||||
|
||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
|
||||
make -C "$base/doc/manuals" publish
|
||||
fi
|
||||
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
if HAVE_SYSTEMD
|
||||
EXTRA_DIST = osmo-mgw.service
|
||||
|
||||
if HAVE_SYSTEMD
|
||||
systemdsystemunit_DATA = \
|
||||
osmo-mgw.service
|
||||
endif
|
||||
|
||||
@@ -6,6 +6,7 @@ Type=simple
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
|
||||
RestartSec=2
|
||||
ExecStopPost=/usr/local/bin/save_log_tail osmo-mgw
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
SUBDIRS = \
|
||||
examples \
|
||||
manuals \
|
||||
$(NULL)
|
||||
|
||||
16
doc/manuals/Makefile.am
Normal file
16
doc/manuals/Makefile.am
Normal file
@@ -0,0 +1,16 @@
|
||||
EXTRA_DIST = osmomgw-usermanual.adoc \
|
||||
osmomgw-usermanual-docinfo.xml \
|
||||
osmomgw-vty-reference.xml \
|
||||
chapters \
|
||||
vty
|
||||
|
||||
if BUILD_MANUALS
|
||||
ASCIIDOC = osmomgw-usermanual.adoc
|
||||
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
|
||||
|
||||
VTY_REFERENCE = osmomgw-vty-reference.xml
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
|
||||
|
||||
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
|
||||
endif
|
||||
57
doc/manuals/chapters/configuration.adoc
Normal file
57
doc/manuals/chapters/configuration.adoc
Normal file
@@ -0,0 +1,57 @@
|
||||
== Configuring OsmoMGW
|
||||
|
||||
A basic configation of OsmoMGW mainly consists of specifying the IP address
|
||||
and port on which to listen to MGCP commands, but changing the port range at
|
||||
which the RTP streams terminate as well as limiting operation to a single call
|
||||
agent can be done as well as changing the number of endpoints.
|
||||
|
||||
=== Configuring MGCP
|
||||
|
||||
By default OsmoMGW listens for MGCP on port 2427 on any IP address. If a call
|
||||
agent address is configured then OsmoMGW will only answer MGCP commands from
|
||||
that IP port 2727, otherwise all sources are handled. A domain can be
|
||||
specified
|
||||
|
||||
.Example: MGCP configuration
|
||||
----
|
||||
OsmoMGW(config-mgcp)# bind ip 127.0.0.1
|
||||
OsmoMGW(config-mgcp)# bind port 2427
|
||||
OsmoMGW(config-mgcp)# call-agent ip 127.0.0.1
|
||||
OsmoMGW(config-mgcp)# domain mgw-bsc
|
||||
OsmoMGW(config-mgcp)# local ip 127.0.0.1
|
||||
----
|
||||
|
||||
=== Configuring the trunk
|
||||
|
||||
The first trunk (trunk 0) is considered a virtual trunk in OsmoMGW. All
|
||||
endpoints of type "rtpbridge" are routed here. The virtual trunk is configured
|
||||
in the config-mgcp context.
|
||||
|
||||
All other trunks are configured in the config-mgcp-trunk context, but the
|
||||
commands used are identical. Right now trunks are considered only for ds/e1
|
||||
type endpoints which are not yet implemented. Don't use trunks other than the
|
||||
"virtual" trunk 0.
|
||||
|
||||
.Example: MGCP trunk configuration
|
||||
----
|
||||
OsmoMGW(config-mgcp)# number endpoints 63 <1>
|
||||
OsmoMGW(config-mgcp)# rtp bind-ip 10.0.0.1 <2>
|
||||
OsmoMGW(config-mgcp)# rtp port-range 12000-14000 <3>
|
||||
----
|
||||
<1> Maximum number of endpoints that can be allocated at once
|
||||
<2> Use this IP when binding RTP endpoints
|
||||
<3> Use ports in this range when binding RTP endpoints
|
||||
|
||||
There are some options to tweak how RTP forwarding behaves in OsmoMGW:
|
||||
|
||||
.Example: MGCP trunk rtp options
|
||||
----
|
||||
OsmoMGW(config-mgcp)# rtp keep-alive 30 <1>
|
||||
OsmoMGW(config-mgcp)# rtp-patch ssrc <2>
|
||||
OsmoMGW(config-mgcp)# rtp-patch timestamp <3>
|
||||
----
|
||||
<1> Send dummy UDP packets periodically to RTP destination
|
||||
<2> Hide SSRC changes
|
||||
<3> Ensure RTP timestamp is aligned with frame duration
|
||||
|
||||
|
||||
4
doc/manuals/chapters/counters.adoc
Normal file
4
doc/manuals/chapters/counters.adoc
Normal file
@@ -0,0 +1,4 @@
|
||||
[[counters]]
|
||||
== Counters
|
||||
|
||||
include::./counters_generated.adoc[]
|
||||
14
doc/manuals/chapters/counters_generated.adoc
Normal file
14
doc/manuals/chapters/counters_generated.adoc
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
// autogenerated by show asciidoc counters
|
||||
These counters and their description based on OsmoMGW 1.3.0.34-9cd52 (OsmoMGW).
|
||||
|
||||
=== Rate Counters
|
||||
|
||||
// generating tables for rate_ctr_group
|
||||
== Osmo Stat Items
|
||||
|
||||
// generating tables for osmo_stat_items
|
||||
== Osmo Counters
|
||||
|
||||
// generating tables for osmo_counters
|
||||
// there are no ungrouped osmo_counters
|
||||
68
doc/manuals/chapters/mgcp_extensions.adoc
Normal file
68
doc/manuals/chapters/mgcp_extensions.adoc
Normal file
@@ -0,0 +1,68 @@
|
||||
== MGCP Extensions
|
||||
|
||||
The MGCP protocol is extendable. The following non-standard extensions are
|
||||
understood by OsmoMGW.
|
||||
|
||||
=== `X-Osmo-IGN`
|
||||
|
||||
`X-Osmo-IGN` indicates to OsmoMGW that specific items of an endpoint should be
|
||||
ignored, so that it is lenient on mismatching values that would normally
|
||||
indicate collisions or configuration errors.
|
||||
|
||||
==== `X-Osmo-IGN` Format
|
||||
|
||||
The value part of X-Osmo-IGN must be one or more items separated by one or more
|
||||
spaces. Each item consists of one or more non-whitespace characters.
|
||||
|
||||
.Example: `X-Osmo-IGN` format with three ficticious items "X", "abc" and "123".
|
||||
----
|
||||
X-Osmo-IGN: X abc 123
|
||||
----
|
||||
|
||||
`X-Osmo-IGN` must be issued in the MGCP section (typically as its last item),
|
||||
before the SDP section starts.
|
||||
|
||||
==== Supported `X-Osmo-IGN` Items
|
||||
|
||||
Currently, the following `X-Osmo-IGN` items are supported:
|
||||
|
||||
* `C`: ignore CallID mismatches, i.e. differing "C" values between connections
|
||||
on the same endpoint.
|
||||
|
||||
.Note:
|
||||
`X-Osmo-IGN` does not support ignoring mismatches on the domain part of
|
||||
an endpoint name, e.g. ignoring a mismatch on "example.com" in
|
||||
`rtpbridge/123abc@example.com`. Instead, you may globally configure OsmoMGW
|
||||
with `mgcp` / `domain *` to permit all domain parts.
|
||||
|
||||
===== `X-Osmo-IGN: C`
|
||||
|
||||
By default, OsmoMGW verifies that all CallIDs ("C" values) match for all
|
||||
connections on any one given endpoint. To ignore CallID mismatches, pass a `C`
|
||||
in the `X-Osmo-IGN` header, for the first or the second `CRCX` on an endpoint.
|
||||
When the `X-Osmo-IGN: C` is sent for any one `CRCX` on an endpoint, CallID
|
||||
mismatches will be ignored for that and all subsequent messages for that
|
||||
endpoint. Hence this header only needs to be included once per endpoint, in any
|
||||
`CRCX` message that precedes or coincides with a CallID mismatch.
|
||||
|
||||
This is particularly useful for a BSC that is connected to an A/SCCPlite MSC,
|
||||
where the BSC and MSC each are expected to configure their respective own
|
||||
connection on a shared endpoint. For A/SCCPlite, it is impossible for the BSC
|
||||
to know the CallID that the MSC will use, so CallID mismatches are inevitable.
|
||||
See also OsmoBSC, which will by default pass the `X-Osmo-IGN: C` header for
|
||||
endpoints associated with an A/SCCPlite MSC.
|
||||
|
||||
.Example: `CRCX` message that instructs OsmoMGW to ignore CallID mismatches
|
||||
----
|
||||
CRCX 2 rtpbridge/123abc@mgw MGCP 1.0
|
||||
M: recvonly
|
||||
C: 2
|
||||
L: p:20
|
||||
X-Osmo-IGN: C
|
||||
|
||||
v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
a=ptime:40
|
||||
----
|
||||
100
doc/manuals/chapters/overview.adoc
Normal file
100
doc/manuals/chapters/overview.adoc
Normal file
@@ -0,0 +1,100 @@
|
||||
[[overview]]
|
||||
== Overview
|
||||
|
||||
This manual should help you getting started with OsmoMGW. It will cover
|
||||
aspects of configuring and running the media gateway.
|
||||
|
||||
[[intro_overview]]
|
||||
=== About OsmoMGW
|
||||
|
||||
OsmoMGW is the Osmocom implementation of a media gateway to handle user
|
||||
plane (voice) traffic in cellular networks. It can connect and optionally
|
||||
transcode RTP voice streams between different network elements such as BTSs
|
||||
and external entities like SIP. It is typically co-located with both OsmoBSC
|
||||
and OsmoMSC and controlled by them via MGCP, the Media Gateway Control
|
||||
Protocol.
|
||||
|
||||
[[fig-bsc]]
|
||||
.OsmoMGW used with OsmoBSC
|
||||
[graphviz]
|
||||
----
|
||||
digraph G {
|
||||
rankdir = LR;
|
||||
OsmoBTS -> OsmoBSC [label="Abis/IP"];
|
||||
OsmoBSC -> OsmoMSC [label="3GPP AoIP"];
|
||||
OsmoBSC -> OsmoMGW [label="MGCP"];
|
||||
OsmoBTS -> OsmoMGW [label="RTP",dir=both];
|
||||
OsmoMGW -> OsmoMSC [label="RTP",dir=both];
|
||||
{rank=same OsmoBSC OsmoMGW}
|
||||
OsmoMGW [color=red];
|
||||
}
|
||||
----
|
||||
|
||||
[[fig-msc]]
|
||||
.OsmoMGW used with OsmoMSC
|
||||
[graphviz]
|
||||
----
|
||||
digraph G {
|
||||
rankdir = LR;
|
||||
BTS -> BSC [label="Abis"];
|
||||
BSC -> OsmoMSC [label="3GPP AoIP"];
|
||||
OsmoMSC -> OsmoMGW [label="MGCP"];
|
||||
BSC -> OsmoMGW [label="RTP",dir=both];
|
||||
OsmoMSC -> OsmoSIP [label="MNCC"];
|
||||
OsmoSIP -> PBX [label="SIP Trunk"];
|
||||
OsmoMGW -> PBX [label="RTP",dir=both];
|
||||
{rank=same OsmoMSC OsmoMGW}
|
||||
OsmoSIP [label="osmo-sip-connector"];
|
||||
OsmoMGW [color=red];
|
||||
|
||||
hNodeB -> OsmoHNBGW [label="Iuh"];
|
||||
OsmoHNBGW -> OsmoMSC [label="IuCS"];
|
||||
hNodeB -> OsmoMGW [label="RTP",dir=both];
|
||||
}
|
||||
----
|
||||
|
||||
=== Software Components
|
||||
|
||||
OsmoMGW contains a variety of different software components, which we’ll
|
||||
quickly describe in this section.
|
||||
|
||||
==== MGCP Implementation
|
||||
|
||||
OsmoMGW brings its own MGCP implementation through which OsmoMGW is
|
||||
controlled.
|
||||
|
||||
The commands implemented are CRCX, MDCX, DLCX and RSIP. The command AUEP is
|
||||
implemented as a stub and will simply respond with OK.
|
||||
|
||||
==== RTP implementation
|
||||
|
||||
Support for IuUP which is used in 3G cells is quite lacking at the moment.
|
||||
3G<->3G and 2G<->2G calls should work, but 3G<->2G does not.
|
||||
|
||||
==== Audio transcoder
|
||||
|
||||
Transcoding is currently not supported in OsmoMGW.
|
||||
|
||||
=== Limitations
|
||||
|
||||
Osmux is not yet supported in OsmoMGW.
|
||||
|
||||
At the moment (July 2018), OsmoMGW only implements RTP proxy / RTP bridge
|
||||
type endpoints, to each of which two RTP connections can be established.
|
||||
We are planning to add endpoint types for:
|
||||
|
||||
- classic E1/T1 timeslots (64kBps alaw/ulaw)
|
||||
- classic E1/T1 16k sub-slots with TRAU frames for classic BTS support
|
||||
- announcement/playout end-points
|
||||
- conference endpoints
|
||||
|
||||
=== Additional resources
|
||||
|
||||
You can find the OsmoMGW issue tracker and wiki online at
|
||||
|
||||
- https://osmocom.org/projects/osmomgw
|
||||
- https://osmocom.org/projects/osmomgw/wiki
|
||||
|
||||
RFC 3435 for MGCP is located at
|
||||
|
||||
- https://tools.ietf.org/html/rfc3435
|
||||
25
doc/manuals/chapters/running.adoc
Normal file
25
doc/manuals/chapters/running.adoc
Normal file
@@ -0,0 +1,25 @@
|
||||
== Running OsmoMGW
|
||||
|
||||
The OsmoMGW executable (`osmo-mgw`) offers the following command-line
|
||||
arguments:
|
||||
|
||||
=== SYNOPSIS
|
||||
|
||||
*osmo-mgw* [-h|-V] [-D] [-c 'CONFIGFILE'] [-s]
|
||||
|
||||
=== OPTIONS
|
||||
|
||||
*-h, --help*::
|
||||
Print a short help message about the supported options
|
||||
*-V, --version*::
|
||||
Print the compile-time version number of the OsmoBTS program
|
||||
*-D, --daemonize*::
|
||||
Fork the process as a daemon into background.
|
||||
*-c, --config-file 'CONFIGFILE'*::
|
||||
Specify the file and path name of the configuration file to be
|
||||
used. If none is specified, use `osmo-mgw.cfg` in the current
|
||||
working directory.
|
||||
*-s, --disable-color*::
|
||||
Disable colors for logging to stderr. This has mostly been
|
||||
deprecated by VTY based logging configuration, see <<logging>>
|
||||
for more information.
|
||||
47
doc/manuals/osmomgw-usermanual-docinfo.xml
Normal file
47
doc/manuals/osmomgw-usermanual-docinfo.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<revhistory>
|
||||
<revision>
|
||||
<revnumber>1</revnumber>
|
||||
<date>July 24th, 2018</date>
|
||||
<authorinitials>DW</authorinitials>
|
||||
<revremark>
|
||||
Initial version
|
||||
</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Daniel</firstname>
|
||||
<surname>Willmann</surname>
|
||||
<email>dwillmann@sysmocom.de</email>
|
||||
<authorinitials>DW</authorinitials>
|
||||
<affiliation>
|
||||
<shortaffil>sysmocom</shortaffil>
|
||||
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
|
||||
</affiliation>
|
||||
</author>
|
||||
</authorgroup>
|
||||
|
||||
<copyright>
|
||||
<year>2018</year>
|
||||
<holder>sysmocom - s.f.m.c. GmbH</holder>
|
||||
</copyright>
|
||||
|
||||
<legalnotice>
|
||||
<para>
|
||||
Permission is granted to copy, distribute and/or modify this
|
||||
document under the terms of the GNU Free Documentation License,
|
||||
Version 1.3 or any later version published by the Free Software
|
||||
Foundation; with the Invariant Sections being just 'Foreword',
|
||||
'Acknowledgements' and 'Preface', with no Front-Cover Texts,
|
||||
and no Back-Cover Texts. A copy of the license is included in
|
||||
the section entitled "GNU Free Documentation License".
|
||||
</para>
|
||||
<para>
|
||||
The Asciidoc source code of this manual can be found at
|
||||
<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
|
||||
http://git.osmocom.org/osmo-gsm-manuals/
|
||||
</ulink>
|
||||
</para>
|
||||
</legalnotice>
|
||||
|
||||
33
doc/manuals/osmomgw-usermanual.adoc
Normal file
33
doc/manuals/osmomgw-usermanual.adoc
Normal file
@@ -0,0 +1,33 @@
|
||||
:gfdl-enabled:
|
||||
:program-name: OsmoMGW
|
||||
|
||||
OsmoMGW User Manual
|
||||
====================
|
||||
Daniel Willmann <dwillmann@sysmocom.de>
|
||||
|
||||
|
||||
include::./common/chapters/preface.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/overview.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/running.adoc[]
|
||||
|
||||
include::./common/chapters/vty.adoc[]
|
||||
|
||||
include::./common/chapters/logging.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/configuration.adoc[]
|
||||
|
||||
include::{srcdir}/chapters/mgcp_extensions.adoc[]
|
||||
|
||||
//include::{srcdir}/chapters/counters.adoc[]
|
||||
|
||||
include::./common/chapters/port_numbers.adoc[]
|
||||
|
||||
include::./common/chapters/bibliography.adoc[]
|
||||
|
||||
include::./common/chapters/glossary.adoc[]
|
||||
|
||||
include::./common/chapters/gfdl.adoc[]
|
||||
|
||||
|
||||
38
doc/manuals/osmomgw-vty-reference.xml
Normal file
38
doc/manuals/osmomgw-vty-reference.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
ex:ts=2:sw=42sts=2:et
|
||||
-*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
-->
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
|
||||
"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
|
||||
<!ENTITY chapter-vty SYSTEM "./common/chapters/vty.xml" >
|
||||
<!ENTITY sections-vty SYSTEM "generated/docbook_vty.xml" >
|
||||
]>
|
||||
|
||||
<book>
|
||||
<info>
|
||||
<revhistory>
|
||||
<revision>
|
||||
<revnumber>v1</revnumber>
|
||||
<date>12th December 2017</date>
|
||||
<authorinitials>pm</authorinitials>
|
||||
<revremark>Initial</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
|
||||
<title>OsmoMGW VTY Reference</title>
|
||||
|
||||
<copyright>
|
||||
<year>2017</year>
|
||||
</copyright>
|
||||
|
||||
<legalnotice>
|
||||
<para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved.
|
||||
</para>
|
||||
</legalnotice>
|
||||
</info>
|
||||
|
||||
<!-- Main chapters-->
|
||||
&chapter-vty;
|
||||
</book>
|
||||
|
||||
2
doc/manuals/vty/mgw_vty_additions.xml
Normal file
2
doc/manuals/vty/mgw_vty_additions.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
|
||||
</vtydoc>
|
||||
1645
doc/manuals/vty/mgw_vty_reference.xml
Normal file
1645
doc/manuals/vty/mgw_vty_reference.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,7 @@
|
||||
/* Debug Areas of the code */
|
||||
enum {
|
||||
DRTP,
|
||||
DIUUP,
|
||||
Debug_LastEntry,
|
||||
};
|
||||
|
||||
|
||||
41
include/osmocom/mgcp/iuup_cn_node.h
Normal file
41
include/osmocom/mgcp/iuup_cn_node.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* IuUP CN node, minimal implementation */
|
||||
|
||||
/* _____IuUP_CN_____
|
||||
* | |
|
||||
* UE <--> RNC --PDU-> osmo_iuup_cn_rx_pdu() -+-> ---+-> rx_payload()
|
||||
* | | |
|
||||
* | <-PDU-- tx_msg() <-------------+-- <-+--- osmo_iuup_cn_tx_payload()
|
||||
* | |
|
||||
* -----------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct osmo_iuup_cn;
|
||||
struct msgb;
|
||||
|
||||
typedef int (*osmo_iuup_data_cb_t)(struct msgb *msg, void *node_priv);
|
||||
|
||||
struct osmo_iuup_cn_cfg {
|
||||
void *node_priv;
|
||||
|
||||
/* When the IuUP peer sent a voice packet, the clean RTP without the IuUP header is fed to this
|
||||
* callback. */
|
||||
osmo_iuup_data_cb_t rx_payload;
|
||||
|
||||
/* IuUP handler requests that a PDU shall be sent to the IuUP peer (e.g. the RNC).
|
||||
* It is guaranteed that the msgb->dst pointer is preserved or copied from the msgb that
|
||||
* originated the request. */
|
||||
osmo_iuup_data_cb_t tx_msg;
|
||||
};
|
||||
|
||||
bool osmo_iuup_cn_is_iuup_init(struct msgb *msg);
|
||||
|
||||
struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
|
||||
const char *name_fmt, ...);
|
||||
void osmo_iuup_cn_free(struct osmo_iuup_cn *cn);
|
||||
|
||||
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *payload);
|
||||
|
||||
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu);
|
||||
117
include/osmocom/mgcp/iuup_protocol.h
Normal file
117
include/osmocom/mgcp/iuup_protocol.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* IuUP protocol handling, minimal implementation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/endian.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#define OSMO_IUUP_HEADROOM 32
|
||||
|
||||
enum osmo_iuup_pdu_type {
|
||||
OSMO_IUUP_PDU_DATA_WITH_CRC = 0,
|
||||
OSMO_IUUP_PDU_CONTROL_PROCEDURE = 14,
|
||||
};
|
||||
|
||||
enum osmo_iuup_acknack {
|
||||
OSMO_IUUP_ACKNACK_PROCEDURE = 0,
|
||||
OSMO_IUUP_ACKNACK_ACK = 1,
|
||||
OSMO_IUUP_ACKNACK_NACK = 2,
|
||||
};
|
||||
|
||||
enum osmo_iuup_procedure {
|
||||
OSMO_IUUP_PROC_INITIALIZATION = 0,
|
||||
OSMO_IUUP_PROC_RATE_CONTROL = 1,
|
||||
OSMO_IUUP_PROC_TIME_ALIGNMENT = 2,
|
||||
OSMO_IUUP_PROC_ERROR_EVENT = 3,
|
||||
};
|
||||
|
||||
enum osmo_iuup_frame_good {
|
||||
OSMO_IUUP_FRAME_GOOD = 0,
|
||||
OSMO_IUUP_FRAME_BAD = 1,
|
||||
OSMO_IUUP_FRAME_BAD_DUE_TO_RADIO = 2,
|
||||
};
|
||||
|
||||
struct osmo_iuup_hdr_ctrl {
|
||||
#if OSMO_IS_BIG_ENDIAN
|
||||
uint8_t pdu_type:4,
|
||||
ack_nack:2,
|
||||
frame_nr:2;
|
||||
uint8_t mode_version:4,
|
||||
procedure:4;
|
||||
uint8_t header_crc:6,
|
||||
payload_crc_hi:2;
|
||||
uint8_t payload_crc_lo;
|
||||
uint8_t payload[0];
|
||||
#elif OSMO_IS_LITTLE_ENDIAN
|
||||
uint8_t frame_nr:2,
|
||||
ack_nack:2,
|
||||
pdu_type:4;
|
||||
uint8_t procedure:4,
|
||||
mode_version:4;
|
||||
uint8_t payload_crc_hi:2,
|
||||
header_crc:6;
|
||||
uint8_t payload_crc_lo;
|
||||
uint8_t payload[0];
|
||||
#endif
|
||||
} __attribute__((packed));
|
||||
|
||||
union osmo_iuup_hdr_ctrl_payload {
|
||||
struct {
|
||||
#if OSMO_IS_BIG_ENDIAN
|
||||
uint8_t spare:3,
|
||||
iptis_present:1,
|
||||
subflows:3,
|
||||
chain:1;
|
||||
#elif OSMO_IS_LITTLE_ENDIAN
|
||||
uint8_t spare:3,
|
||||
iptis_present:1,
|
||||
subflows:3,
|
||||
chain:1;
|
||||
#endif
|
||||
} initialization;
|
||||
|
||||
struct {
|
||||
#if OSMO_IS_BIG_ENDIAN
|
||||
uint8_t error_distance:2,
|
||||
error_cause:6;
|
||||
#elif OSMO_IS_LITTLE_ENDIAN
|
||||
uint8_t error_cause:6,
|
||||
error_distance:2;
|
||||
#endif
|
||||
} error_event;
|
||||
};
|
||||
|
||||
extern const struct value_string osmo_iuup_error_cause_names[];
|
||||
static inline const char *osmo_iuup_error_cause_name(uint8_t val)
|
||||
{ return get_value_string(osmo_iuup_error_cause_names, val); }
|
||||
|
||||
struct osmo_iuup_hdr_data {
|
||||
#if OSMO_IS_BIG_ENDIAN
|
||||
uint8_t pdu_type:4,
|
||||
frame_nr:4;
|
||||
uint8_t frame_good:2,
|
||||
rfci:6;
|
||||
uint8_t header_crc:6,
|
||||
payload_crc_hi:2;
|
||||
uint8_t payload_crc_lo;
|
||||
#elif OSMO_IS_LITTLE_ENDIAN
|
||||
uint8_t frame_nr:4,
|
||||
pdu_type:4;
|
||||
uint8_t rfci:6,
|
||||
frame_good:2;
|
||||
uint8_t payload_crc_hi:2,
|
||||
header_crc:6;
|
||||
uint8_t payload_crc_lo;
|
||||
#endif
|
||||
uint8_t payload[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
int osmo_iuup_classify(bool log_errors,
|
||||
const char *log_label,
|
||||
struct msgb *pdu,
|
||||
struct osmo_iuup_hdr_ctrl **is_ctrl,
|
||||
struct osmo_iuup_hdr_data **is_data);
|
||||
bool osmo_iuup_is_init(struct msgb *pdu);
|
||||
void osmo_iuup_make_init_ack(struct msgb *ack);
|
||||
void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len);
|
||||
@@ -152,9 +152,20 @@ enum {
|
||||
MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
|
||||
MGCP_MDCX_FAIL_START_RTP,
|
||||
MGCP_MDCX_FAIL_REJECTED_BY_POLICY,
|
||||
MGCP_MDCX_FAIL_DEFERRED_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;
|
||||
@@ -198,6 +209,8 @@ struct mgcp_trunk_config {
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -23,15 +23,28 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/msgb.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);
|
||||
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)->dst))
|
||||
|
||||
#define LOG_ENDP(endp, level, fmt, args...) \
|
||||
LOGP(DRTP, level, "%x@ " fmt, ENDPOINT_NUMBER(endp), ## args)
|
||||
|
||||
/* Callback type for RTP dispatcher functions (e.g mgcp_dispatch_rtp_bridge_cb, see below).
|
||||
* The fields OSMO_RTP_MSG_PROTO, OSMO_RTP_MSG_CONN_SRC, OSMO_RTP_MSG_FROM_ADDR 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()
|
||||
|
||||
@@ -37,6 +37,13 @@
|
||||
#define CONN_ID_BTS "0"
|
||||
#define CONN_ID_NET "1"
|
||||
|
||||
#define LOG_CONN(conn, level, fmt, args...) \
|
||||
LOGP(DRTP, level, "(%d@ I:%s) " fmt, \
|
||||
ENDPOINT_NUMBER((conn)->endp), (conn)->id, ## args)
|
||||
|
||||
#define LOG_CONN_RTP(conn_rtp, level, fmt, args...) \
|
||||
LOG_CONN(conn_rtp->conn, level, fmt, ## args)
|
||||
|
||||
enum mgcp_trunk_type {
|
||||
MGCP_TRUNK_VIRTUAL,
|
||||
MGCP_TRUNK_E1,
|
||||
@@ -200,6 +207,8 @@ struct mgcp_conn_rtp {
|
||||
} osmux;
|
||||
|
||||
struct rate_ctr_group *rate_ctr_group;
|
||||
|
||||
struct osmo_iuup_cn *iuup;
|
||||
};
|
||||
|
||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
||||
@@ -259,11 +268,10 @@ struct mgcp_parse_data {
|
||||
};
|
||||
|
||||
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 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(int proto, struct sockaddr_in *addr, char *buf,
|
||||
unsigned int buf_size, struct mgcp_conn *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_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
@@ -328,3 +336,8 @@ enum {
|
||||
#define PTYPE_UNDEFINED (-1)
|
||||
|
||||
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
|
||||
|
||||
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);
|
||||
|
||||
@@ -22,9 +22,10 @@ struct mgcp_client_conf {
|
||||
int local_port;
|
||||
const char *remote_addr;
|
||||
int remote_port;
|
||||
uint16_t first_endpoint;
|
||||
uint16_t last_endpoint;
|
||||
uint16_t bts_base;
|
||||
|
||||
/* By default, we are always addressing the MGW with e.g. 'rtpbridge/123@mgw'.
|
||||
* If this is nonempty, the contained name will be used instead of 'mgw'. */
|
||||
char endpoint_domain_name[64];
|
||||
};
|
||||
|
||||
typedef unsigned int mgcp_trans_id_t;
|
||||
@@ -123,8 +124,8 @@ const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp);
|
||||
uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp);
|
||||
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp);
|
||||
|
||||
int mgcp_client_next_endpoint(struct mgcp_client *client);
|
||||
void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client);
|
||||
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp);
|
||||
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp);
|
||||
|
||||
/* Invoked when an MGCP response is received or sending failed. When the
|
||||
* response is passed as NULL, this indicates failure during transmission. */
|
||||
@@ -137,20 +138,6 @@ int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
|
||||
|
||||
enum mgcp_connection_mode;
|
||||
|
||||
struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
|
||||
uint16_t rtp_endpoint, unsigned int call_id,
|
||||
enum mgcp_connection_mode mode)
|
||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
|
||||
|
||||
struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
|
||||
uint16_t rtp_endpoint, const char *rtp_conn_addr,
|
||||
uint16_t rtp_port, enum mgcp_connection_mode mode)
|
||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
|
||||
|
||||
struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
|
||||
unsigned int call_id)
|
||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
|
||||
|
||||
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
|
||||
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
|
||||
|
||||
|
||||
@@ -190,75 +190,9 @@ void mgcp_client_conf_init(struct mgcp_client_conf *conf)
|
||||
.local_port = -1,
|
||||
.remote_addr = NULL,
|
||||
.remote_port = -1,
|
||||
.first_endpoint = 0,
|
||||
.last_endpoint = 0,
|
||||
.bts_base = 0,
|
||||
};
|
||||
}
|
||||
|
||||
/* Test if a given endpoint id is currently in use */
|
||||
static bool endpoint_in_use(uint16_t id, struct mgcp_client *client)
|
||||
{
|
||||
struct mgcp_inuse_endpoint *endpoint;
|
||||
llist_for_each_entry(endpoint, &client->inuse_endpoints, entry) {
|
||||
if (endpoint->id == id)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! Pick next free endpoint ID.
|
||||
* \param[in,out] client MGCP client descriptor.
|
||||
* \returns 0 on success, -EINVAL on error. */
|
||||
int mgcp_client_next_endpoint(struct mgcp_client *client)
|
||||
{
|
||||
int i;
|
||||
uint16_t first_endpoint = client->actual.first_endpoint;
|
||||
uint16_t last_endpoint = client->actual.last_endpoint;
|
||||
struct mgcp_inuse_endpoint *endpoint;
|
||||
|
||||
/* Use the maximum permitted range if the VTY
|
||||
* configuration does not specify a range */
|
||||
if (client->actual.last_endpoint == 0) {
|
||||
first_endpoint = 1;
|
||||
last_endpoint = 65534;
|
||||
}
|
||||
|
||||
/* Test the permitted endpoint range for an endpoint
|
||||
* number that is not in use. When a suitable endpoint
|
||||
* number can be found, seize it by adding it to the
|
||||
* inuse list. */
|
||||
for (i=first_endpoint;i<last_endpoint;i++)
|
||||
{
|
||||
if (endpoint_in_use(i,client) == false) {
|
||||
endpoint = talloc_zero(client, struct mgcp_inuse_endpoint);
|
||||
endpoint->id = i;
|
||||
llist_add_tail(&endpoint->entry, &client->inuse_endpoints);
|
||||
return endpoint->id;
|
||||
}
|
||||
}
|
||||
|
||||
/* All endpoints are busy! */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! Release a seized endpoint ID to make it available again for other calls.
|
||||
* \param[in] id Endpoint ID
|
||||
* \param[in,out] client MGCP client descriptor. */
|
||||
/* Release a seized endpoint id to make it available again for other calls */
|
||||
void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client)
|
||||
{
|
||||
struct mgcp_inuse_endpoint *endpoint;
|
||||
struct mgcp_inuse_endpoint *endpoint_tmp;
|
||||
llist_for_each_entry_safe(endpoint, endpoint_tmp, &client->inuse_endpoints, entry) {
|
||||
if (endpoint->id == id) {
|
||||
llist_del(&endpoint->entry);
|
||||
talloc_free(endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mgcp_client_handle_response(struct mgcp_client *mgcp,
|
||||
struct mgcp_response_pending *pending,
|
||||
struct mgcp_response *response)
|
||||
@@ -271,7 +205,7 @@ static void mgcp_client_handle_response(struct mgcp_client *mgcp,
|
||||
if (pending->response_cb)
|
||||
pending->response_cb(response, pending->priv);
|
||||
else
|
||||
LOGP(DLMGCP, LOGL_INFO, "MGCP response ignored (NULL cb)\n");
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "MGCP response ignored (NULL cb)\n");
|
||||
talloc_free(pending);
|
||||
}
|
||||
|
||||
@@ -717,11 +651,13 @@ static int mgcp_do_read(struct osmo_fd *fd)
|
||||
|
||||
ret = read(fd->fd, msg->data, 4096 - 128);
|
||||
if (ret <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno));
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to read: %s: %d='%s'\n", osmo_sock_get_name2(fd->fd),
|
||||
errno, strerror(errno));
|
||||
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
} else if (ret > 4096 - 128) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Too much data: %d\n", ret);
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Too much data: %s: %d\n", osmo_sock_get_name2(fd->fd), ret);
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
@@ -735,26 +671,15 @@ static int mgcp_do_read(struct osmo_fd *fd)
|
||||
static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
|
||||
{
|
||||
int ret;
|
||||
static char strbuf[4096];
|
||||
unsigned int l = msg->len < sizeof(strbuf) ? msg->len : sizeof(strbuf);
|
||||
unsigned int i;
|
||||
|
||||
osmo_strlcpy(strbuf, (const char*)msg->data, l);
|
||||
for (i = 0; i < sizeof(strbuf); i++) {
|
||||
if (strbuf[i] == '\n' || strbuf[i] == '\r') {
|
||||
strbuf[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
DEBUGP(DLMGCP, "Tx MGCP msg to MGCP GW: '%s'\n", strbuf);
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len);
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Tx MGCP: %s: len=%u '%s'...\n",
|
||||
osmo_sock_get_name2(fd->fd), msg->len, osmo_escape_str((const char*)msg->data, OSMO_MIN(42, msg->len)));
|
||||
|
||||
ret = write(fd->fd, msg->data, msg->len);
|
||||
if (ret != msg->len)
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to forward message to MGCP"
|
||||
" GW: %s\n", strerror(errno));
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
|
||||
osmo_sock_get_name2(fd->fd), errno, strerror(errno),
|
||||
msg->len, osmo_escape_str((const char*)msg->data, OSMO_MIN(42, msg->len)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -780,9 +705,9 @@ struct mgcp_client *mgcp_client_init(void *ctx,
|
||||
mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
|
||||
MGCP_CLIENT_REMOTE_PORT_DEFAULT;
|
||||
|
||||
mgcp->actual.first_endpoint = conf->first_endpoint > 0 ? (uint16_t)conf->first_endpoint : 0;
|
||||
mgcp->actual.last_endpoint = conf->last_endpoint > 0 ? (uint16_t)conf->last_endpoint : 0;
|
||||
mgcp->actual.bts_base = conf->bts_base > 0 ? (uint16_t)conf->bts_base : 4000;
|
||||
osmo_strlcpy(mgcp->actual.endpoint_domain_name, conf->endpoint_domain_name,
|
||||
sizeof(mgcp->actual.endpoint_domain_name));
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "MGCP client: using endpoint domain '@%s'\n", mgcp_client_endpoint_domain(mgcp));
|
||||
|
||||
return mgcp;
|
||||
}
|
||||
@@ -814,12 +739,12 @@ static int init_socket(struct mgcp_client *mgcp)
|
||||
|
||||
/* Choose a new port number to try next */
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"MGCPGW faild to bind to port %u, retrying with port %u -- check configuration!\n",
|
||||
mgcp->actual.local_port, mgcp->actual.local_port + 1);
|
||||
"MGCPGW failed to bind to %s:%u, retrying with port %u\n",
|
||||
mgcp->actual.local_addr, mgcp->actual.local_port, mgcp->actual.local_port + 1);
|
||||
mgcp->actual.local_port++;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_FATAL, "MGCPGW faild to find a port to bind on %i times.\n", i);
|
||||
LOGP(DLMGCP, LOGL_FATAL, "MGCPGW failed to find a port to bind on %i times.\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -857,9 +782,7 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
|
||||
wq->read_cb = mgcp_do_read;
|
||||
wq->write_cb = mgcp_do_write;
|
||||
|
||||
LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s:%u -> %s:%u\n",
|
||||
mgcp->actual.local_addr, mgcp->actual.local_port,
|
||||
mgcp->actual.remote_addr, mgcp->actual.remote_port);
|
||||
LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s\n", osmo_sock_get_name2(wq->bfd.fd));
|
||||
|
||||
return 0;
|
||||
error_close_fd:
|
||||
@@ -892,6 +815,32 @@ uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
|
||||
return mgcp->remote_addr;
|
||||
}
|
||||
|
||||
/* To compose endpoint names, usually for CRCX, use this as domain name.
|
||||
* For example, snprintf("rtpbridge\*@%s", mgcp_client_endpoint_domain(mgcp)). */
|
||||
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp)
|
||||
{
|
||||
return mgcp->actual.endpoint_domain_name[0]? mgcp->actual.endpoint_domain_name : "mgw";
|
||||
}
|
||||
|
||||
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
|
||||
{
|
||||
static char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
int rc;
|
||||
|
||||
#define RTPBRIDGE_WILDCARD_FMT "rtpbridge/*@%s"
|
||||
rc = snprintf(endpoint, sizeof(endpoint), RTPBRIDGE_WILDCARD_FMT, mgcp_client_endpoint_domain(mgcp));
|
||||
if (rc > sizeof(endpoint) - 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP endpoint exceeds maximum length ('" RTPBRIDGE_WILDCARD_FMT "')\n",
|
||||
mgcp_client_endpoint_domain(mgcp));
|
||||
return NULL;
|
||||
}
|
||||
if (rc < 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot compose MGCP endpoint name\n");
|
||||
return NULL;
|
||||
}
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
struct mgcp_response_pending * mgcp_client_pending_add(
|
||||
struct mgcp_client *mgcp,
|
||||
mgcp_trans_id_t trans_id,
|
||||
@@ -949,7 +898,7 @@ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
|
||||
msgb_free(msg);
|
||||
goto mgcp_tx_error;
|
||||
} else
|
||||
LOGP(DLMGCP, LOGL_INFO, "Queued %u bytes for MGCP GW\n",
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Queued %u bytes for MGCP GW\n",
|
||||
msgb_l2len(msg));
|
||||
return 0;
|
||||
|
||||
@@ -973,10 +922,10 @@ int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id)
|
||||
struct mgcp_response_pending *pending = mgcp_client_response_pending_get(mgcp, trans_id);
|
||||
if (!pending) {
|
||||
/*! Note: it is not harmful to cancel a transaction twice. */
|
||||
LOGP(DLMGCP, LOGL_INFO, "Cannot cancel, no such transaction: %u\n", trans_id);
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot cancel, no such transaction: %u\n", trans_id);
|
||||
return -ENOENT;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_INFO, "Canceled transaction %u\n", trans_id);
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Canceled transaction %u\n", trans_id);
|
||||
talloc_free(pending);
|
||||
return 0;
|
||||
/*! We don't really need to clean up the wqueue: In all sane cases, the msgb has already been sent
|
||||
@@ -988,54 +937,6 @@ int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id)
|
||||
*/
|
||||
}
|
||||
|
||||
static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id,
|
||||
const char *buf, int len)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
if (len > (4096 - 128)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot send to MGCP GW:"
|
||||
" message too large: %d\n", len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
|
||||
OSMO_ASSERT(msg);
|
||||
|
||||
char *dst = (char*)msgb_put(msg, len);
|
||||
memcpy(dst, buf, len);
|
||||
msg->l2h = msg->data;
|
||||
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static struct msgb *mgcp_msg_from_str(mgcp_trans_id_t trans_id,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
static char compose[4096 - 128];
|
||||
va_list ap;
|
||||
int len;
|
||||
OSMO_ASSERT(fmt);
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(compose, sizeof(compose), fmt, ap);
|
||||
va_end(ap);
|
||||
if (len >= sizeof(compose)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Message too large: trans_id=%u len=%d\n",
|
||||
trans_id, len);
|
||||
return NULL;
|
||||
}
|
||||
if (len < 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to compose message: trans_id=%u len=%d\n",
|
||||
trans_id, len);
|
||||
return NULL;
|
||||
}
|
||||
return mgcp_msg_from_buf(trans_id, compose, len);
|
||||
}
|
||||
|
||||
static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
|
||||
{
|
||||
/* avoid zero trans_id to distinguish from unset trans_id */
|
||||
@@ -1044,52 +945,6 @@ static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
|
||||
return mgcp->next_trans_id ++;
|
||||
}
|
||||
|
||||
struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
|
||||
uint16_t rtp_endpoint, unsigned int call_id,
|
||||
enum mgcp_connection_mode mode)
|
||||
{
|
||||
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
|
||||
return mgcp_msg_from_str(trans_id,
|
||||
"CRCX %u %x@mgw MGCP 1.0\r\n"
|
||||
"C: %x\r\n"
|
||||
"L: p:20, a:AMR, nt:IN\r\n"
|
||||
"M: %s\r\n"
|
||||
,
|
||||
trans_id,
|
||||
rtp_endpoint,
|
||||
call_id,
|
||||
mgcp_client_cmode_name(mode));
|
||||
}
|
||||
|
||||
struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
|
||||
uint16_t rtp_endpoint, const char *rtp_conn_addr,
|
||||
uint16_t rtp_port, enum mgcp_connection_mode mode)
|
||||
|
||||
{
|
||||
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
|
||||
return mgcp_msg_from_str(trans_id,
|
||||
"MDCX %u %x@mgw MGCP 1.0\r\n"
|
||||
"M: %s\r\n"
|
||||
"\r\n"
|
||||
"c=IN IP4 %s\r\n"
|
||||
"m=audio %u RTP/AVP 255\r\n"
|
||||
,
|
||||
trans_id,
|
||||
rtp_endpoint,
|
||||
mgcp_client_cmode_name(mode),
|
||||
rtp_conn_addr,
|
||||
rtp_port);
|
||||
}
|
||||
|
||||
struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
|
||||
unsigned int call_id)
|
||||
{
|
||||
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
|
||||
return mgcp_msg_from_str(trans_id,
|
||||
"DLCX %u %x@mgw MGCP 1.0\r\n"
|
||||
"C: %x\r\n", trans_id, rtp_endpoint, call_id);
|
||||
}
|
||||
|
||||
#define MGCP_CRCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
|
||||
MGCP_MSG_PRESENCE_CALL_ID | \
|
||||
MGCP_MSG_PRESENCE_CONN_MODE)
|
||||
|
||||
@@ -43,8 +43,9 @@ DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
|
||||
if (!global_mgcp_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
OSMO_ASSERT(global_mgcp_client_ctx);
|
||||
global_mgcp_client_conf->local_addr =
|
||||
talloc_strdup(global_mgcp_client_ctx, argv[0]);
|
||||
osmo_talloc_replace_string(global_mgcp_client_ctx,
|
||||
(char**)&global_mgcp_client_conf->local_addr,
|
||||
argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
|
||||
@@ -75,8 +76,9 @@ DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
|
||||
if (!global_mgcp_client_conf)
|
||||
return CMD_ERR_NOTHING_TODO;
|
||||
OSMO_ASSERT(global_mgcp_client_ctx);
|
||||
global_mgcp_client_conf->remote_addr =
|
||||
talloc_strdup(global_mgcp_client_ctx, argv[0]);
|
||||
osmo_talloc_replace_string(global_mgcp_client_ctx,
|
||||
(char**)&global_mgcp_client_conf->remote_addr,
|
||||
argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
|
||||
@@ -99,23 +101,14 @@ ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
|
||||
MGW_STR "remote bind to connect to MGCP gateway with\n"
|
||||
"remote bind port\n")
|
||||
|
||||
DEFUN(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
|
||||
DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
|
||||
"mgw endpoint-range <1-65534> <1-65534>",
|
||||
MGW_STR "usable range of endpoint identifiers\n"
|
||||
"set first usable endpoint identifier\n"
|
||||
"set last usable endpoint identifier\n")
|
||||
MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
|
||||
"-\n" "-\n")
|
||||
{
|
||||
uint16_t first_endpoint = atoi(argv[0]);
|
||||
uint16_t last_endpoint = atoi(argv[1]);
|
||||
|
||||
if (last_endpoint < first_endpoint) {
|
||||
vty_out(vty, "last endpoint must be greater than first endpoint!%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
global_mgcp_client_conf->first_endpoint = first_endpoint;
|
||||
global_mgcp_client_conf->last_endpoint = last_endpoint;
|
||||
vty_out(vty, "Please do not use legacy config 'mgw endpoint-range'"
|
||||
" (the range can no longer be defined by the MGCP client)%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
||||
@@ -126,14 +119,15 @@ ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
||||
|
||||
#define BTS_START_STR "First UDP port allocated for the BTS side\n"
|
||||
#define UDP_PORT_STR "UDP Port number\n"
|
||||
DEFUN(cfg_mgw_rtp_bts_base_port,
|
||||
DEFUN_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
||||
cfg_mgw_rtp_bts_base_port_cmd,
|
||||
"mgw bts-base <0-65534>",
|
||||
MGW_STR
|
||||
BTS_START_STR
|
||||
UDP_PORT_STR)
|
||||
"DEPRECATED: there is no explicit BTS side in current osmo-mgw\n" "-\n")
|
||||
{
|
||||
global_mgcp_client_conf->bts_base = atoi(argv[0]);
|
||||
vty_out(vty, "Please do not use legacy config 'mgw bts-base'"
|
||||
" (there is no explicit BTS side in an MGW anymore)%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
||||
@@ -143,13 +137,21 @@ ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
|
||||
BTS_START_STR
|
||||
UDP_PORT_STR)
|
||||
|
||||
DEFUN(cfg_mgw_endpoint_domain_name,
|
||||
cfg_mgw_endpoint_domain_name_cmd,
|
||||
"mgw endpoint-domain NAME",
|
||||
MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
|
||||
"Domain name, should be alphanumeric.\n")
|
||||
{
|
||||
osmo_strlcpy(global_mgcp_client_conf->endpoint_domain_name, argv[0],
|
||||
sizeof(global_mgcp_client_conf->endpoint_domain_name));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int mgcp_client_config_write(struct vty *vty, const char *indent)
|
||||
{
|
||||
const char *addr;
|
||||
int port;
|
||||
uint16_t first_endpoint;
|
||||
uint16_t last_endpoint;
|
||||
uint16_t bts_base;
|
||||
|
||||
addr = global_mgcp_client_conf->local_addr;
|
||||
if (addr)
|
||||
@@ -169,18 +171,9 @@ int mgcp_client_config_write(struct vty *vty, const char *indent)
|
||||
vty_out(vty, "%smgw remote-port %u%s", indent,
|
||||
(uint16_t)port, VTY_NEWLINE);
|
||||
|
||||
first_endpoint = global_mgcp_client_conf->first_endpoint;
|
||||
last_endpoint = global_mgcp_client_conf->last_endpoint;
|
||||
if (last_endpoint != 0) {
|
||||
vty_out(vty, "%smgw endpoint-range %u %u%s", indent,
|
||||
first_endpoint, last_endpoint, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
bts_base = global_mgcp_client_conf->bts_base;
|
||||
if (bts_base) {
|
||||
vty_out(vty, "%smgw bts-base %u%s", indent,
|
||||
bts_base, VTY_NEWLINE);
|
||||
}
|
||||
if (global_mgcp_client_conf->endpoint_domain_name[0])
|
||||
vty_out(vty, "%smgw endpoint-domain %s%s", indent,
|
||||
global_mgcp_client_conf->endpoint_domain_name, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -196,6 +189,7 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
|
||||
install_element(node, &cfg_mgw_remote_port_cmd);
|
||||
install_element(node, &cfg_mgw_endpoint_range_cmd);
|
||||
install_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
|
||||
install_element(node, &cfg_mgw_endpoint_domain_name_cmd);
|
||||
|
||||
/* deprecated 'mgcpgw' commands */
|
||||
install_element(node, &cfg_mgcpgw_local_ip_cmd);
|
||||
|
||||
@@ -40,4 +40,6 @@ libosmo_mgcp_a_SOURCES = \
|
||||
mgcp_conn.c \
|
||||
mgcp_stat.c \
|
||||
mgcp_endp.c \
|
||||
iuup_protocol.c \
|
||||
iuup_cn_node.c \
|
||||
$(NULL)
|
||||
|
||||
216
src/libosmo-mgcp/iuup_cn_node.c
Normal file
216
src/libosmo-mgcp/iuup_cn_node.c
Normal file
@@ -0,0 +1,216 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* IuUP Core Network side protocol handling, minimal implementation */
|
||||
|
||||
/*
|
||||
* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Neels Hofmeyr <neels@hofmeyr.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <talloc.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include <osmocom/netif/rtp.h>
|
||||
|
||||
#include <osmocom/mgcp/iuup_cn_node.h>
|
||||
#include <osmocom/mgcp/iuup_protocol.h>
|
||||
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
|
||||
#define LOG_IUUP_CN(cn, level, fmt, args...) \
|
||||
LOGP(DIUUP, level, "(%s) " fmt, (cn)->name, ## args)
|
||||
|
||||
struct osmo_iuup_cn {
|
||||
struct osmo_iuup_cn_cfg cfg;
|
||||
char *name;
|
||||
uint8_t next_frame_nr;
|
||||
int rtp_payload_type;
|
||||
};
|
||||
|
||||
struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
|
||||
const char *name_fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
struct osmo_iuup_cn *cn = talloc_zero(ctx, struct osmo_iuup_cn);
|
||||
OSMO_ASSERT(cn);
|
||||
|
||||
cn->cfg = *cfg;
|
||||
|
||||
if (!name_fmt)
|
||||
name_fmt = "-";
|
||||
|
||||
va_start(ap, name_fmt);
|
||||
cn->name = talloc_vasprintf(cn, name_fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
LOGP(DIUUP, LOGL_INFO, "(%s) Initializing IuUP node\n", cn->name);
|
||||
|
||||
if (!osmo_identifier_valid(cn->name)) {
|
||||
LOGP(DIUUP, LOGL_ERROR, "Attempting to set illegal id for IuUP CN instance: %s\n",
|
||||
osmo_quote_str(cn->name, -1));
|
||||
talloc_free(cn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cn;
|
||||
}
|
||||
|
||||
void osmo_iuup_cn_free(struct osmo_iuup_cn *cn)
|
||||
{
|
||||
talloc_free(cn);
|
||||
}
|
||||
|
||||
static int rx_data(struct osmo_iuup_cn *cn, struct msgb *pdu,
|
||||
struct osmo_iuup_hdr_data *hdr)
|
||||
{
|
||||
/* Remove the IuUP bit from the middle of the buffer by writing the RTP header forward. */
|
||||
/* And append AMR 12.2k header "0xf03c". - AD HOC fix */
|
||||
unsigned int pre_hdr_len = ((uint8_t*)hdr) - pdu->data;
|
||||
memmove(pdu->data + sizeof(*hdr) - 2, pdu->data, pre_hdr_len);
|
||||
((uint8_t*)hdr)[2] = 0xf0;
|
||||
((uint8_t*)hdr)[3] = 0x3c;
|
||||
msgb_pull(pdu, sizeof(*hdr) - 2);
|
||||
|
||||
LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP stripping IuUP header from RTP data\n", cn->name);
|
||||
cn->cfg.rx_payload(pdu, cn->cfg.node_priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tx_init_ack(struct osmo_iuup_cn *cn, struct msgb *src_pdu)
|
||||
{
|
||||
/* Send Initialization Ack PDU back to the sender */
|
||||
int rc;
|
||||
struct msgb *ack = msgb_alloc(4096, "IuUP Initialization Ack");
|
||||
OSMO_ASSERT(ack);
|
||||
ack->dst = src_pdu->dst;
|
||||
|
||||
/* Just copy the RTP header that was sent... TODO: tweak some RTP values?? */
|
||||
memcpy(msgb_put(ack, sizeof(struct rtp_hdr)), src_pdu->data, sizeof(struct rtp_hdr));
|
||||
|
||||
osmo_iuup_make_init_ack(ack);
|
||||
|
||||
LOGP(DIUUP, LOGL_DEBUG, "(%s) Sending Initialization ACK %p\n", cn->name, cn->cfg.node_priv);
|
||||
rc = cn->cfg.tx_msg(ack, cn->cfg.node_priv);
|
||||
msgb_free(ack);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int rx_control(struct osmo_iuup_cn *cn, struct msgb *pdu,
|
||||
struct osmo_iuup_hdr_ctrl *hdr)
|
||||
{
|
||||
switch (hdr->procedure) {
|
||||
case OSMO_IUUP_PROC_INITIALIZATION:
|
||||
switch (hdr->ack_nack) {
|
||||
case OSMO_IUUP_ACKNACK_PROCEDURE:
|
||||
LOGP(DIUUP, LOGL_INFO, "(%s) Rx IuUP Initialization, sending ACK\n", cn->name);
|
||||
cn->rtp_payload_type = ((struct rtp_hdr*)pdu->data)->payload_type;
|
||||
return tx_init_ack(cn, pdu);
|
||||
|
||||
default:
|
||||
LOGP(DIUUP, LOGL_DEBUG, "(%s) Rx IuUP Initialization, unhandled ack_nack = %d\n",
|
||||
cn->name, hdr->ack_nack);
|
||||
break;
|
||||
}
|
||||
/* Continue to log "unexpected procedure" below. */
|
||||
break;
|
||||
|
||||
case OSMO_IUUP_PROC_ERROR_EVENT:
|
||||
{
|
||||
union osmo_iuup_hdr_ctrl_payload *p = (void*)hdr->payload;
|
||||
LOGP(DIUUP, LOGL_ERROR,
|
||||
"(%s) Rx IuUP Error Event: distance=%u, cause=%u=\"%s\"\n",
|
||||
cn->name, p->error_event.error_distance, p->error_event.error_cause,
|
||||
osmo_iuup_error_cause_name(p->error_event.error_cause));
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
LOG_IUUP_CN(cn, LOGL_ERROR,
|
||||
"Rx control PDU with unexpected procedure: 0x%x acknack=0x%x\n",
|
||||
hdr->procedure, hdr->ack_nack);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Feed a received PDU to the IuUP CN node. This function takes ownership of the msgb, it must not be
|
||||
* freed by the caller. */
|
||||
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu)
|
||||
{
|
||||
struct osmo_iuup_hdr_ctrl *is_ctrl;
|
||||
struct osmo_iuup_hdr_data *is_data;
|
||||
int rc;
|
||||
|
||||
rc = osmo_iuup_classify(true, cn->name, pdu, &is_ctrl, &is_data);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (is_ctrl)
|
||||
return rx_control(cn, pdu, is_ctrl);
|
||||
if (is_data)
|
||||
return rx_data(cn, pdu, is_data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static uint8_t next_frame_nr(struct osmo_iuup_cn *cn)
|
||||
{
|
||||
uint8_t frame_nr = cn->next_frame_nr;
|
||||
cn->next_frame_nr = (frame_nr + 1) & 0x0f;
|
||||
return frame_nr;
|
||||
}
|
||||
|
||||
/* Send this RTP packet to the IuUP peer: add IuUP header and call the tx_msg() to transmit the resulting
|
||||
* message to the IuUP peer.
|
||||
* Returns 0 on success, negative on error. */
|
||||
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu)
|
||||
{
|
||||
struct rtp_hdr *rtp_was, *rtp;
|
||||
struct osmo_iuup_hdr_data *iuup_hdr;
|
||||
|
||||
/* Splice an IuUP header in between RTP header and payload data */
|
||||
rtp_was = (void*)pdu->data;
|
||||
|
||||
/* copy the RTP header part backwards by the size needed for the IuUP header */
|
||||
/* also strips 2 bytes from the front of RTP payload - AMR header - AD HOC fix */
|
||||
rtp = (void*)msgb_push(pdu, sizeof(*iuup_hdr) - 2);
|
||||
memmove(rtp, rtp_was, sizeof(*rtp));
|
||||
|
||||
/* Send the same payload type to the peer (erm...) */
|
||||
rtp->payload_type = cn->rtp_payload_type;
|
||||
|
||||
iuup_hdr = (void*)rtp->data;
|
||||
|
||||
*iuup_hdr = (struct osmo_iuup_hdr_data){
|
||||
.pdu_type = OSMO_IUUP_PDU_DATA_WITH_CRC,
|
||||
.frame_nr = next_frame_nr(cn),
|
||||
.frame_good = OSMO_IUUP_FRAME_GOOD,
|
||||
};
|
||||
|
||||
osmo_iuup_set_checksums((uint8_t*)iuup_hdr, pdu->tail - (uint8_t*)iuup_hdr);
|
||||
LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP inserting IuUP header in RTP data (frame nr %u)\n",
|
||||
cn->name, iuup_hdr->frame_nr);
|
||||
|
||||
return cn->cfg.tx_msg(pdu, cn->cfg.node_priv);
|
||||
}
|
||||
286
src/libosmo-mgcp/iuup_protocol.c
Normal file
286
src/libosmo-mgcp/iuup_protocol.c
Normal file
@@ -0,0 +1,286 @@
|
||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* IuUP Core Network side protocol, minimal implementation */
|
||||
|
||||
/*
|
||||
* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Neels Hofmeyr <neels@hofmeyr.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <osmocom/mgcp/iuup_protocol.h>
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
#include <osmocom/netif/rtp.h>
|
||||
|
||||
/* Calculating two bytes of CRC is ok to do by a loop */
|
||||
static uint8_t header_crc6(const uint8_t *hdr)
|
||||
{
|
||||
int bit;
|
||||
/* Polynomial: D^6 + D^5 + D^3 + D^2 + D^1 + 1
|
||||
* that's 1101111 or 0x6f;
|
||||
* align its lowest bit with a uint16_t's highest bit: */
|
||||
uint32_t polynomial = 0x6f << 15; // 00110111 10000000 00000000
|
||||
uint32_t remainder = ( ((uint32_t)hdr[0]) << 8 | hdr[1] ) << 6;
|
||||
|
||||
for (bit = 15; bit >= 0; bit--)
|
||||
{
|
||||
if (remainder & (0x40 << bit))
|
||||
remainder ^= polynomial;
|
||||
polynomial >>= 1;
|
||||
}
|
||||
|
||||
return remainder;
|
||||
}
|
||||
|
||||
/*
|
||||
* Charles Michael Heard's CRC-10 code, from
|
||||
*
|
||||
* http://web.archive.org/web/20061005231950/http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/crc10.html
|
||||
*
|
||||
* with the CRC table initialized with values computed by
|
||||
* his "gen_byte_crc10_table()" routine, rather than by calling that
|
||||
* routine at run time, and with various data type cleanups.
|
||||
*/
|
||||
static const uint16_t byte_crc10_table[256] = {
|
||||
0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
|
||||
0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
|
||||
0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
|
||||
0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
|
||||
0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
|
||||
0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
|
||||
0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
|
||||
0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
|
||||
0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
|
||||
0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
|
||||
0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
|
||||
0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
|
||||
0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
|
||||
0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
|
||||
0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
|
||||
0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
|
||||
0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
|
||||
0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
|
||||
0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
|
||||
0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
|
||||
0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
|
||||
0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
|
||||
0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
|
||||
0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
|
||||
0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
|
||||
0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
|
||||
0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
|
||||
0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
|
||||
0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
|
||||
0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
|
||||
0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
|
||||
0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1
|
||||
};
|
||||
|
||||
static uint16_t crc10(uint16_t crc10_accum, const uint8_t *payload, unsigned int payload_len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < payload_len; i++) {
|
||||
crc10_accum = ((crc10_accum << 8) & 0x300)
|
||||
^ byte_crc10_table[(crc10_accum >> 2) & 0xff]
|
||||
^ payload[i];
|
||||
}
|
||||
return crc10_accum;
|
||||
}
|
||||
|
||||
/* When a payload of a multiple of bytes has run through, we need to still feed 10 bits of zeros into the
|
||||
* CRC10 to get the payload's checksum result that we can send to a peer. That can't be done with above
|
||||
* table, because it acts as if full 16 bits are fed. This stops after 10 bits. */
|
||||
static uint16_t crc10_remainder(uint16_t crc10_accum)
|
||||
{
|
||||
int bit;
|
||||
/* Polynomial: D^10 + D^9 + D^5 + D^4 + D^1 + 1
|
||||
* that's 11000110011 or 0x633;
|
||||
* align its lowest bit with a 10bit value's highest bit: */
|
||||
uint32_t polynomial = 0x633 << 9; // 1100 01100110 00000000
|
||||
uint32_t remainder = ((uint32_t)crc10_accum) << 10;
|
||||
|
||||
/* Run on 10 bits */
|
||||
for (bit = 9; bit >= 0; bit--)
|
||||
{
|
||||
if (remainder & ((1 << 10) << bit))
|
||||
remainder ^= polynomial;
|
||||
polynomial >>= 1;
|
||||
}
|
||||
|
||||
return remainder & 0x3ff;
|
||||
}
|
||||
|
||||
static uint16_t payload_crc10(const uint8_t *payload, unsigned int payload_len)
|
||||
{
|
||||
uint16_t crc10_accum = crc10(0, payload, payload_len);
|
||||
return crc10_remainder(crc10_accum);
|
||||
}
|
||||
|
||||
/* Given an IuUP PDU data block, write the correct header and payload CRC checksums at the right places.
|
||||
*/
|
||||
void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len)
|
||||
{
|
||||
/* For both data and ctrl, the checksums and payload are at the same offset */
|
||||
struct osmo_iuup_hdr_data *hdr = (void*)iuup_header_and_payload;
|
||||
uint16_t crc;
|
||||
unsigned int payload_len;
|
||||
|
||||
hdr->header_crc = header_crc6(iuup_header_and_payload);
|
||||
|
||||
payload_len = iuup_header_and_payload + header_and_payload_len - hdr->payload;
|
||||
crc = payload_crc10(hdr->payload, payload_len);
|
||||
hdr->payload_crc_hi = (crc >> 8) & 0x3;
|
||||
hdr->payload_crc_lo = crc & 0xff;
|
||||
|
||||
}
|
||||
|
||||
/* Validate minimum message sizes, IuUP PDU type, header- and payload checksums. If it is a Control
|
||||
* Procedure PDU, return the header position in is_ctrl, if it is a Data PDU, return the header position
|
||||
* in is_data. If log_errors is true, log on DIUUP with the given log label for context. Return NULL in
|
||||
* both is_ctrl and is_data, and return a negative error code if the PDU could not be identified as a
|
||||
* valid RTP PDU containing an IuUP part. */
|
||||
int osmo_iuup_classify(bool log_errors,
|
||||
const char *log_label,
|
||||
struct msgb *pdu,
|
||||
struct osmo_iuup_hdr_ctrl **is_ctrl,
|
||||
struct osmo_iuup_hdr_data **is_data)
|
||||
{
|
||||
struct rtp_hdr *rtp = (void*)pdu->data;
|
||||
struct osmo_iuup_hdr_ctrl *hdr = (void*)rtp->data;
|
||||
unsigned int payload_len;
|
||||
uint16_t crc_calculated;
|
||||
uint16_t crc_from_peer;
|
||||
|
||||
#define ERR(fmt, args...) do { \
|
||||
if (log_errors) \
|
||||
LOGP(DIUUP, LOGL_ERROR, "(%s) " fmt, log_label? : "-", ## args); \
|
||||
return -EINVAL; \
|
||||
} while (0)
|
||||
|
||||
if (is_ctrl)
|
||||
*is_ctrl = NULL;
|
||||
if (is_data)
|
||||
*is_data = NULL;
|
||||
|
||||
/* We need at least a header of 4 bytes. The osmo_iuup_hdr_ctrl already includes a byte of
|
||||
* payload, so use osmo_iuup_hdr_data to check the minimum here. */
|
||||
if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_data)))
|
||||
ERR("IuUP PDU too short: %u\n", pdu->len);
|
||||
|
||||
/* Let's not validate checksums if the header type isn't sane */
|
||||
switch (hdr->pdu_type) {
|
||||
case OSMO_IUUP_PDU_DATA_WITH_CRC:
|
||||
/* If the caller isn't interested in data PDUs, cut short here. */
|
||||
if (!is_data)
|
||||
return 0;
|
||||
break;
|
||||
case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
|
||||
/* If the caller isn't interested in control PDUs, cut short here. */
|
||||
if (!is_ctrl)
|
||||
return 0;
|
||||
if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_ctrl)))
|
||||
ERR("IuUP control PDU too short: %u\n", pdu->len);
|
||||
break;
|
||||
default:
|
||||
ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
|
||||
}
|
||||
|
||||
/* For both data and ctrl, the checksums and payload are at the same offset */
|
||||
|
||||
crc_calculated = header_crc6((uint8_t*)hdr);
|
||||
if (crc_calculated != hdr->header_crc)
|
||||
ERR("IuUP PDU with invalid header CRC (peer sent 0x%x, calculated 0x%x)\n",
|
||||
hdr->header_crc, crc_calculated);
|
||||
|
||||
payload_len = pdu->tail - hdr->payload;
|
||||
crc_calculated = payload_crc10(hdr->payload, payload_len);
|
||||
crc_from_peer = (((uint16_t)hdr->payload_crc_hi) << 8) | hdr->payload_crc_lo;
|
||||
if (crc_from_peer != crc_calculated)
|
||||
ERR("IuUP PDU with invalid payload CRC (peer sent 0x%x, calculated 0x%x)\n",
|
||||
crc_from_peer, crc_calculated);
|
||||
|
||||
switch (hdr->pdu_type) {
|
||||
case OSMO_IUUP_PDU_DATA_WITH_CRC:
|
||||
if (is_data)
|
||||
*is_data = (void*)hdr;
|
||||
return 0;
|
||||
case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
|
||||
if (is_ctrl)
|
||||
*is_ctrl = hdr;
|
||||
return 0;
|
||||
default:
|
||||
ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
|
||||
}
|
||||
#undef ERR
|
||||
}
|
||||
|
||||
/* Return true if this RTP packet contains an IuUP Initialization header (detect IuUP peer). */
|
||||
bool osmo_iuup_is_init(struct msgb *pdu)
|
||||
{
|
||||
struct osmo_iuup_hdr_ctrl *is_ctrl;
|
||||
osmo_iuup_classify(false, NULL, pdu, &is_ctrl, NULL);
|
||||
return is_ctrl
|
||||
&& is_ctrl->procedure == OSMO_IUUP_PROC_INITIALIZATION
|
||||
&& is_ctrl->ack_nack == OSMO_IUUP_ACKNACK_PROCEDURE;
|
||||
}
|
||||
|
||||
/* Append an IuUP Initialization ACK message */
|
||||
void osmo_iuup_make_init_ack(struct msgb *ack)
|
||||
{
|
||||
/* Send Initialization Ack PDU back to the sender */
|
||||
struct osmo_iuup_hdr_ctrl *hdr;
|
||||
OSMO_ASSERT(ack);
|
||||
|
||||
hdr = (void*)msgb_put(ack, sizeof(*hdr));
|
||||
|
||||
*hdr = (struct osmo_iuup_hdr_ctrl){
|
||||
.pdu_type = OSMO_IUUP_PDU_CONTROL_PROCEDURE,
|
||||
.ack_nack = OSMO_IUUP_ACKNACK_ACK,
|
||||
.procedure = OSMO_IUUP_PROC_INITIALIZATION,
|
||||
};
|
||||
|
||||
osmo_iuup_set_checksums((uint8_t*)hdr, sizeof(*hdr));
|
||||
}
|
||||
|
||||
const struct value_string osmo_iuup_error_cause_names[] = {
|
||||
{ 0, "CRC error of frame header" },
|
||||
{ 1, "CRC error of frame payload" },
|
||||
{ 2, "Unexpected frame number" },
|
||||
{ 3, "Frame loss" },
|
||||
{ 4, "PDU type unknown" },
|
||||
{ 5, "Unknown procedure" },
|
||||
{ 6, "Unknown reserved value" },
|
||||
{ 7, "Unknown field" },
|
||||
{ 8, "Frame too short" },
|
||||
{ 9, "Missing fields" },
|
||||
{ 16, "Unexpected PDU type" },
|
||||
{ 17, "spare" },
|
||||
{ 18, "Unexpected procedure" },
|
||||
{ 19, "Unexpected RFCI" },
|
||||
{ 20, "Unexpected value" },
|
||||
{ 42, "Initialisation failure" },
|
||||
{ 43, "Initialisation failure (network error, timer expiry)" },
|
||||
{ 44, "Initialisation failure (Iu UP function error, repeated NACK)" },
|
||||
{ 45, "Rate control failure" },
|
||||
{ 46, "Error event failure" },
|
||||
{ 47, "Time Alignment not supported" },
|
||||
{ 48, "Requested Time Alignment not possible" },
|
||||
{ 49, "Iu UP Mode version not supported" },
|
||||
{}
|
||||
};
|
||||
@@ -223,8 +223,10 @@ static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
|
||||
if (!strcmp(cfg->domain, "*"))
|
||||
return 0;
|
||||
|
||||
if (strcmp(domain_to_check+1, cfg->domain) != 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;
|
||||
}
|
||||
@@ -244,7 +246,6 @@ static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
|
||||
|
||||
/* Check if the domainname in the request is correct */
|
||||
if (check_domain_name(cfg, mgcp)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s'\n", mgcp);
|
||||
*cause = -500;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
#include <osmocom/mgcp/iuup_cn_node.h>
|
||||
#include <osmocom/mgcp/iuup_protocol.h>
|
||||
|
||||
|
||||
#define RTP_SEQ_MOD (1 << 16)
|
||||
@@ -50,11 +52,13 @@
|
||||
#define RTP_MAX_MISORDER 100
|
||||
#define RTP_BUF_SIZE 4096
|
||||
|
||||
enum {
|
||||
enum rtp_proto {
|
||||
MGCP_PROTO_RTP,
|
||||
MGCP_PROTO_RTCP,
|
||||
};
|
||||
|
||||
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
|
||||
@@ -481,14 +485,14 @@ 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;
|
||||
|
||||
OSMO_ASSERT(len >= sizeof(struct rtp_hdr));
|
||||
rtp_hdr = (struct rtp_hdr *)data;
|
||||
OSMO_ASSERT(msg->len >= sizeof(struct rtp_hdr));
|
||||
rtp_hdr = (struct rtp_hdr *)msg->data;
|
||||
|
||||
pt_in = rtp_hdr->payload_type;
|
||||
pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
|
||||
@@ -508,7 +512,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;
|
||||
@@ -516,11 +520,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 = msg->len;
|
||||
|
||||
if (len < sizeof(*rtp_hdr))
|
||||
return;
|
||||
|
||||
rtp_hdr = (struct rtp_hdr *)data;
|
||||
rtp_hdr = (struct rtp_hdr *)msg->data;
|
||||
seq = ntohs(rtp_hdr->sequence);
|
||||
timestamp = ntohl(rtp_hdr->timestamp);
|
||||
arrival_time = get_current_ts(rtp_end->codec->rate);
|
||||
@@ -653,15 +658,14 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
|
||||
/* 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, msg->data, msg->len, 0, (struct sockaddr *)&tap->forward,
|
||||
sizeof(tap->forward));
|
||||
|
||||
if (rc < 0)
|
||||
@@ -679,7 +683,7 @@ static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
|
||||
* \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
|
||||
@@ -691,6 +695,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
struct mgcp_rtp_state *rtp_state;
|
||||
char *dest_name;
|
||||
int rc;
|
||||
int len;
|
||||
|
||||
OSMO_ASSERT(conn_src);
|
||||
OSMO_ASSERT(conn_dst);
|
||||
@@ -719,7 +724,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) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x can not patch PT because no suitable egress codec was found.\n",
|
||||
@@ -746,18 +751,18 @@ 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 = msg->len;
|
||||
do {
|
||||
/* Run transcoder */
|
||||
cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
|
||||
buf, &buflen,
|
||||
(char*)msg->data, &buflen,
|
||||
RTP_BUF_SIZE);
|
||||
if (cont < 0)
|
||||
break;
|
||||
|
||||
if (addr)
|
||||
mgcp_patch_and_count(endp, rtp_state, rtp_end,
|
||||
addr, buf, buflen);
|
||||
addr, msg);
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x process/send to %s %s "
|
||||
"rtp_port:%u rtcp_port:%u\n",
|
||||
@@ -768,8 +773,9 @@ 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);
|
||||
|
||||
#if 0
|
||||
/* FIXME: HACK HACK HACK. See OS#2459.
|
||||
* The ip.access nano3G needs the first RTP payload's first two bytes to read hex
|
||||
* 'e400', or it will reject the RAB assignment. It seems to not harm other femto
|
||||
@@ -777,7 +783,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 = msg->data + 12;
|
||||
if (data[0] == 0xe0) {
|
||||
data[0] = 0xe4;
|
||||
data[1] = 0x00;
|
||||
@@ -788,10 +794,13 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
ENDPOINT_NUMBER(endp));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtp.fd,
|
||||
&rtp_end->addr,
|
||||
rtp_end->rtp_port, buf, buflen);
|
||||
if (conn_dst->iuup)
|
||||
len = osmo_iuup_cn_tx_payload(conn_dst->iuup, msg);
|
||||
else
|
||||
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
|
||||
(char*)msg->data, msg->len);
|
||||
|
||||
if (len <= 0)
|
||||
return len;
|
||||
@@ -814,7 +823,7 @@ 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*)msg->data, msg->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);
|
||||
@@ -825,46 +834,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);
|
||||
|
||||
LOGP(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) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x failed to receive packet, errno: %d/%s\n",
|
||||
ENDPOINT_NUMBER(endp), errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tossed) {
|
||||
LOGP(DRTP, LOGL_ERROR, "endpoint:0x%x packet tossed\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -945,22 +914,22 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn)
|
||||
* and IP-address for outgoing data. */
|
||||
if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0 && conn->end.rtp_port == 0) {
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x destination IP-address and rtp port is (not yet) known\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
"endpoint:0x%x destination IP-address and rtp port is (not yet) known (%s:%u)\n",
|
||||
ENDPOINT_NUMBER(endp), inet_ntoa(conn->end.addr), conn->end.rtp_port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x destination IP-address is invalid\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
"endpoint:0x%x destination IP-address is invalid (%s:%u)\n",
|
||||
ENDPOINT_NUMBER(endp), inet_ntoa(conn->end.addr), conn->end.rtp_port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (conn->end.rtp_port == 0) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x destination rtp port is invalid\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
"endpoint:0x%x destination rtp port is invalid (%s:%u)\n",
|
||||
ENDPOINT_NUMBER(endp), inet_ntoa(conn->end.addr), conn->end.rtp_port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -969,7 +938,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;
|
||||
@@ -977,33 +946,45 @@ 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 (msg->len < sizeof(struct rtcp_hdr)) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP packet too short (%u < %zu)\n",
|
||||
msg->len, 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 *)msg->data;
|
||||
len = (osmo_ntohs(hdr->length) + 1) * 4;
|
||||
if (len > buf_size)
|
||||
if (len > msg->len) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP header length exceeds packet size (%u > %u)\n",
|
||||
len, msg->len);
|
||||
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 (conn_src->iuup)
|
||||
min_size += sizeof(struct osmo_iuup_hdr_data);
|
||||
if (msg->len < min_size) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
|
||||
msg->len, 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
|
||||
@@ -1014,91 +995,14 @@ 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;
|
||||
|
||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x receiving RTP/RTCP packet...\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
|
||||
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) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid RTP packet received -- packet tossed\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
}
|
||||
} else if (*proto == MGCP_PROTO_RTCP) {
|
||||
if (check_rtcp(buf, rc) < 0) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x invalid RTCP packet received -- packet tossed\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x ", ENDPOINT_NUMBER(endp));
|
||||
LOGPC(DRTP, LOGL_DEBUG, "receiving from %s %s %d\n",
|
||||
conn->conn->name, inet_ntoa(addr->sin_addr),
|
||||
ntohs(addr->sin_port));
|
||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x conn:%s\n", ENDPOINT_NUMBER(endp),
|
||||
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) {
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"endpoint:0x%x dummy message received\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x packet tossed\n", ENDPOINT_NUMBER(endp));
|
||||
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;
|
||||
enum rtp_proto proto = OSMO_RTP_MSG_CTX(msg)->proto;
|
||||
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
|
||||
struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
|
||||
struct mgcp_endpoint *endp = conn_src->conn->endp;
|
||||
|
||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x destin conn:%s\n",
|
||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn_dst->conn));
|
||||
@@ -1118,14 +1022,14 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
|
||||
"using mgcp_send() to forward data directly\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
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:
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x endpoint type is MGCP_OSMUX_BSC_NAT, "
|
||||
"using osmux_xfrm_to_osmux() to forward data through OSMUX\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return osmux_xfrm_to_osmux(buf, buf_size, conn_dst);
|
||||
return osmux_xfrm_to_osmux((char*)msg->data, msg->len, conn_dst);
|
||||
}
|
||||
|
||||
/* If the data has not been handled/forwarded until here, it will
|
||||
@@ -1145,9 +1049,10 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
|
||||
* \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)
|
||||
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||
{
|
||||
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
|
||||
struct mgcp_conn *conn = conn_src->conn;
|
||||
struct mgcp_conn *conn_dst;
|
||||
struct mgcp_endpoint *endp;
|
||||
endp = conn->endp;
|
||||
@@ -1191,9 +1096,7 @@ 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);
|
||||
}
|
||||
|
||||
/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
|
||||
@@ -1215,6 +1118,76 @@ void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *co
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_dummy_msg(enum rtp_proto proto, struct msgb *msg)
|
||||
{
|
||||
return msg->len == 1 && msg->data[0] == MGCP_DUMMY_LOAD;
|
||||
}
|
||||
|
||||
struct pdu_ctx {
|
||||
struct sockaddr_in *from_addr;
|
||||
struct mgcp_conn_rtp *conn_src;
|
||||
};
|
||||
|
||||
/* IuUP CN node has stripped an IuUP header and forwards RTP data to distribute to the peers. */
|
||||
int iuup_rx_payload(struct msgb *msg, void *node_priv)
|
||||
{
|
||||
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "iuup_rx_payload(%u bytes)\n", msg->len);
|
||||
return rx_rtp(msg);
|
||||
}
|
||||
|
||||
/* IuUP CN node has composed a message that contains an IuUP header and asks us to send to the IuUP peer.
|
||||
*/
|
||||
int iuup_tx_msg(struct msgb *msg, void *node_priv)
|
||||
{
|
||||
const struct in_addr zero_addr = {};
|
||||
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
|
||||
struct mgcp_conn_rtp *conn_dst = node_priv;
|
||||
struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
|
||||
struct mgcp_rtp_end *rtp_end = &conn_dst->end;
|
||||
struct in_addr to_addr = rtp_end->addr;
|
||||
uint16_t to_port = rtp_end->rtp_port;
|
||||
|
||||
if (conn_src == conn_dst
|
||||
&& !memcmp(&zero_addr, &to_addr, sizeof(zero_addr)) && !to_port) {
|
||||
LOG_CONN_RTP(conn_dst, LOGL_DEBUG, "iuup_tx_msg(): direct IuUP reply\n");
|
||||
/* IuUP wants to send a message back to the same peer that sent an RTP package, but there
|
||||
* is no address configured for that peer yet. It is probably an IuUP Initialization ACK
|
||||
* reply. Use the sender address to send the reply.
|
||||
*
|
||||
* During 3G RAB Assignment, a 3G cell might first probe the MGW and expect an IuUP
|
||||
* Initialization ACK before it replies to the MSC with a successful RAB Assignment; only
|
||||
* after that reply does MSC officially know which RTP address+port the 3G cell wants to
|
||||
* use and can tell this MGW about it, so this "loopback" is, for some 3G cells, the only
|
||||
* chance we have to get a successful RAB Assignment done (particularly the nano3G does
|
||||
* this). */
|
||||
to_addr = from_addr->sin_addr;
|
||||
to_port = from_addr->sin_port;
|
||||
}
|
||||
|
||||
LOG_CONN_RTP(conn_dst, LOGL_DEBUG, "iuup_tx_msg(%u bytes) to %s:%u\n", msg->len,
|
||||
inet_ntoa(to_addr), ntohs(to_port));
|
||||
|
||||
return mgcp_udp_send(rtp_end->rtp.fd, &to_addr, to_port, (char*)msg->data, msg->len);
|
||||
}
|
||||
|
||||
static void iuup_init(struct mgcp_conn_rtp *conn_src)
|
||||
{
|
||||
struct osmo_iuup_cn_cfg cfg = {
|
||||
.node_priv = conn_src,
|
||||
.rx_payload = iuup_rx_payload,
|
||||
.tx_msg = iuup_tx_msg,
|
||||
};
|
||||
|
||||
if (conn_src->iuup) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_NOTICE, "Rx IuUP init, but already initialized. Ignoring.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
conn_src->iuup = osmo_iuup_cn_init(conn_src->conn, &cfg, "endp_%d_conn_%s",
|
||||
ENDPOINT_NUMBER(conn_src->conn->endp), conn_src->conn->id);
|
||||
}
|
||||
|
||||
/* Handle incoming RTP data from NET */
|
||||
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
@@ -1228,43 +1201,110 @@ 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_headroom(RTP_BUF_SIZE + OSMO_IUUP_HEADROOM,
|
||||
OSMO_IUUP_HEADROOM, "RTP-rx");
|
||||
int rc;
|
||||
|
||||
conn_src = (struct mgcp_conn_rtp *)fd->data;
|
||||
OSMO_ASSERT(conn_src);
|
||||
endp = conn_src->conn->endp;
|
||||
OSMO_ASSERT(endp);
|
||||
|
||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x source conn:%s\n",
|
||||
ENDPOINT_NUMBER(endp), 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, msg->data, 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",
|
||||
msg->len, 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;
|
||||
}
|
||||
|
||||
mc = (struct osmo_rtp_msg_ctx){
|
||||
.proto = proto,
|
||||
.conn_src = conn_src,
|
||||
.from_addr = &addr,
|
||||
};
|
||||
OSMO_RTP_MSG_CTX(msg) = &mc;
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "msg ctx: %d %p %s\n",
|
||||
OSMO_RTP_MSG_CTX(msg)->proto,
|
||||
OSMO_RTP_MSG_CTX(msg)->conn_src,
|
||||
osmo_hexdump((void*)OSMO_RTP_MSG_CTX(msg)->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], msg->len);
|
||||
/* 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);
|
||||
|
||||
if (proto == MGCP_PROTO_RTP && osmo_iuup_is_init(msg))
|
||||
iuup_init(conn_src);
|
||||
|
||||
if (conn_src->iuup && proto == MGCP_PROTO_RTP)
|
||||
rc = osmo_iuup_cn_rx_pdu(conn_src->iuup, msg);
|
||||
else
|
||||
rc = rx_rtp(msg);
|
||||
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int rx_rtp(struct msgb *msg)
|
||||
{
|
||||
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
|
||||
struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
|
||||
struct mgcp_conn *conn = conn_src->conn;
|
||||
struct mgcp_trunk_config *tcfg = conn->endp->tcfg;
|
||||
|
||||
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx_rtp(%u bytes)\n", msg->len);
|
||||
|
||||
/* Check if the connection is in loopback mode, if yes, just send the
|
||||
* incoming data back to the origin */
|
||||
if (conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
|
||||
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_src->end.rtp_port == 0) {
|
||||
conn_src->end.addr = addr.sin_addr;
|
||||
conn_src->end.rtp_port = addr.sin_port;
|
||||
conn_src->end.addr = from_addr->sin_addr;
|
||||
conn_src->end.rtp_port = from_addr->sin_port;
|
||||
}
|
||||
return mgcp_send_rtp(proto, &addr, buf,
|
||||
len, conn_src, conn_src);
|
||||
return mgcp_send_rtp(conn_src, msg);
|
||||
}
|
||||
|
||||
/* Check if the origin of the RTP packet seems plausible */
|
||||
if (!tcfg->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.
|
||||
|
||||
@@ -261,8 +261,7 @@ static void scheduled_tx_net_cb(struct msgb *msg, void *data)
|
||||
|
||||
/* Send RTP data to NET */
|
||||
/* FIXME: Get rid of conn_bts and conn_net! */
|
||||
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
|
||||
conn_bts, conn_net);
|
||||
mgcp_send(endp, 1, &addr, msg, conn_bts, conn_net);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
@@ -288,8 +287,7 @@ static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
|
||||
|
||||
/* Send RTP data to BTS */
|
||||
/* FIXME: Get rid of conn_bts and conn_net! */
|
||||
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
|
||||
conn_net, conn_bts);
|
||||
mgcp_send(endp, 1, &addr, msg, conn_net, conn_bts);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
|
||||
[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_FAIL_DEFERRED_BY_POLICY] = {"mdcx:conn_deferred", "connection deferred 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 = {
|
||||
@@ -104,6 +104,25 @@ const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_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",
|
||||
@@ -1192,7 +1211,7 @@ mgcp_header_done:
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"MDCX: endpoint:0x%x deferred by policy\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_DEFERRED_BY_POLICY]);
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_DEFERRED_BY_POLICY]);
|
||||
return NULL;
|
||||
break;
|
||||
case MGCP_POLICY_CONT:
|
||||
@@ -1237,7 +1256,9 @@ 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;
|
||||
int error_code = 400;
|
||||
int silent = 0;
|
||||
char *line;
|
||||
@@ -1254,6 +1275,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"DLCX: endpoint:0x%x wildcarded endpoint names not supported.\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_WILDCARD]);
|
||||
return create_err_response(endp, 507, "DLCX", p->trans);
|
||||
}
|
||||
|
||||
@@ -1261,6 +1283,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"DLCX: endpoint:0x%x endpoint is not holding a connection.\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_NO_CONN]);
|
||||
return create_err_response(endp, 515, "DLCX", p->trans);
|
||||
}
|
||||
|
||||
@@ -1272,13 +1295,16 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
case 'C':
|
||||
if (mgcp_verify_call_id(endp, line + 3) != 0) {
|
||||
error_code = 516;
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_INVALID_CALLID]);
|
||||
goto error3;
|
||||
}
|
||||
break;
|
||||
case 'I':
|
||||
conn_id = (const char *)line + 3;
|
||||
if ((error_code = mgcp_verify_ci(endp, conn_id)))
|
||||
if ((error_code = mgcp_verify_ci(endp, conn_id))) {
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_INVALID_CONNID]);
|
||||
goto error3;
|
||||
}
|
||||
break;
|
||||
case 'Z':
|
||||
silent = strcmp("noanswer", line + 3) == 0;
|
||||
@@ -1287,6 +1313,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: endpoint:0x%x Unhandled MGCP option: '%c'/%d\n",
|
||||
ENDPOINT_NUMBER(endp), line[0], line[0]);
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_UNHANDLED_PARAM]);
|
||||
return create_err_response(NULL, 539, "DLCX", p->trans);
|
||||
break;
|
||||
}
|
||||
@@ -1302,12 +1329,14 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: endpoint:0x%x rejected by policy\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_REJECTED_BY_POLICY]);
|
||||
if (silent)
|
||||
goto out_silent;
|
||||
return create_err_response(endp, 400, "DLCX", p->trans);
|
||||
break;
|
||||
case MGCP_POLICY_DEFER:
|
||||
/* stop processing */
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_DEFERRED_BY_POLICY]);
|
||||
return NULL;
|
||||
break;
|
||||
case MGCP_POLICY_CONT:
|
||||
@@ -1320,9 +1349,13 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
* wildcarded DLCX and drop all connections at once. (See also
|
||||
* RFC3435 Section F.7) */
|
||||
if (!conn_id) {
|
||||
int num_conns = llist_count(&endp->conns);
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: endpoint:0x%x missing ci (connectionIdentifier), will remove all connections at once\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
"DLCX: endpoint:0x%x missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
|
||||
num_conns, ENDPOINT_NUMBER(endp));
|
||||
|
||||
if (num_conns > 0)
|
||||
rate_ctr_add(&rate_ctrs->ctr[MGCP_DLCX_SUCCESS], num_conns);
|
||||
|
||||
mgcp_endp_release(endp);
|
||||
|
||||
@@ -1334,9 +1367,10 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
|
||||
/* Find the connection */
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
if (!conn)
|
||||
if (!conn) {
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_INVALID_CONNID]);
|
||||
goto error3;
|
||||
|
||||
}
|
||||
/* save the statistics of the current connection */
|
||||
mgcp_format_stats(stats, sizeof(stats), conn->conn);
|
||||
|
||||
@@ -1361,6 +1395,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
|
||||
MGCP_ENDP_DLCX);
|
||||
|
||||
rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_SUCCESS]);
|
||||
if (silent)
|
||||
goto out_silent;
|
||||
return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats);
|
||||
@@ -1503,24 +1538,27 @@ static void alloc_mgcp_rate_counters(struct mgcp_trunk_config *trunk, void *ctx)
|
||||
* 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);
|
||||
OSMO_ASSERT(trunk->mgcp_crcx_ctr_group);
|
||||
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);
|
||||
OSMO_ASSERT(trunk->mgcp_mdcx_ctr_group);
|
||||
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);
|
||||
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);
|
||||
OSMO_ASSERT(trunk->all_rtp_conn_stats);
|
||||
talloc_set_destructor(trunk->all_rtp_conn_stats, free_rate_counter_group);
|
||||
all_rtp_conn_rate_ctr_index++;
|
||||
}
|
||||
|
||||
@@ -249,6 +249,10 @@ static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg, int show_
|
||||
vty_out(vty, " %s:%s", cfg->mgcp_crcx_ctr_group->desc->group_description, VTY_NEWLINE);
|
||||
vty_out_rate_ctr_group_fmt(vty, " %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_crcx_ctr_group);
|
||||
}
|
||||
if (show_stats && cfg->mgcp_dlcx_ctr_group) {
|
||||
vty_out(vty, " %s:%s", cfg->mgcp_dlcx_ctr_group->desc->group_description, VTY_NEWLINE);
|
||||
vty_out_rate_ctr_group_fmt(vty, " %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_dlcx_ctr_group);
|
||||
}
|
||||
if (show_stats && cfg->mgcp_mdcx_ctr_group) {
|
||||
vty_out(vty, " %s:%s", cfg->mgcp_mdcx_ctr_group->desc->group_description, VTY_NEWLINE);
|
||||
vty_out_rate_ctr_group_fmt(vty, " %25n: %10c (%S/s %M/m %H/h %D/d) %d", cfg->mgcp_mdcx_ctr_group);
|
||||
|
||||
@@ -104,7 +104,7 @@ static void handle_options(int argc, char **argv)
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hc:VD", long_options, &option_index);
|
||||
c = getopt_long(argc, argv, "hc:sVD", long_options, &option_index);
|
||||
|
||||
if (c == -1)
|
||||
break;
|
||||
@@ -244,6 +244,12 @@ static const struct log_info_cat log_categories[] = {
|
||||
.color = "\033[1;30m",
|
||||
.enabled = 1,.loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DIUUP] = {
|
||||
.name = "DIUUP",
|
||||
.description = "IuUP within RTP stream handling",
|
||||
.color = "\033[1;31m",
|
||||
.enabled = 1,.loglevel = LOGL_NOTICE,
|
||||
},
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
SUBDIRS = \
|
||||
mgcp_client \
|
||||
mgcp \
|
||||
iuup \
|
||||
$(NULL)
|
||||
|
||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||
|
||||
45
tests/iuup/Makefile.am
Normal file
45
tests/iuup/Makefile.am
Normal file
@@ -0,0 +1,45 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
-ggdb3 \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
iuup_test.ok \
|
||||
iuup_test.err \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
iuup_test \
|
||||
$(NULL)
|
||||
|
||||
iuup_test_SOURCES = \
|
||||
iuup_test.c \
|
||||
$(NULL)
|
||||
|
||||
iuup_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBRARY_DL) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
-lm \
|
||||
$(NULL)
|
||||
|
||||
update_exp:
|
||||
$(builddir)/iuup_test >$(srcdir)/iuup_test.ok 2>$(srcdir)/iuup_test.err
|
||||
156
tests/iuup/iuup_test.c
Normal file
156
tests/iuup/iuup_test.c
Normal file
@@ -0,0 +1,156 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/mgcp/iuup_cn_node.h>
|
||||
#include <osmocom/mgcp/iuup_protocol.h>
|
||||
|
||||
void *ctx = NULL;
|
||||
|
||||
static const char *dump(struct msgb *msg)
|
||||
{
|
||||
return osmo_hexdump_nospc(msg->data, msg->len);
|
||||
}
|
||||
|
||||
struct msgb *msgb_from_hex(const char *label, const char *hex)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_headroom(4096 + OSMO_IUUP_HEADROOM,
|
||||
OSMO_IUUP_HEADROOM, label);
|
||||
unsigned char *rc;
|
||||
msg->l2h = msg->data;
|
||||
rc = msgb_put(msg, osmo_hexparse(hex, msg->data, msgb_tailroom(msg)));
|
||||
OSMO_ASSERT(rc == msg->l2h);
|
||||
return msg;
|
||||
}
|
||||
|
||||
const char *expect_rx_payload = NULL;
|
||||
int rx_payload(struct msgb *msg, void *node_priv)
|
||||
{
|
||||
printf("rx_payload() invoked by iuup_cn!\n");
|
||||
printf(" [IuUP] -RTP->\n");
|
||||
printf("%s\n", dump(msg));
|
||||
printf("node_priv=%p\n", node_priv);
|
||||
if (!expect_rx_payload) {
|
||||
printf("ERROR: did not expect rx_payload()\n");
|
||||
exit(-1);
|
||||
} else if (strcmp(expect_rx_payload, dump(msg))) {
|
||||
printf("ERROR: mismatches expected msg %s\n", expect_rx_payload);
|
||||
exit(-1);
|
||||
} else
|
||||
printf("ok: matches expected msg\n");
|
||||
expect_rx_payload = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *expect_tx_msg = NULL;
|
||||
int tx_msg(struct msgb *msg, void *node_priv)
|
||||
{
|
||||
printf("tx_msg() invoked by iuup_cn!\n");
|
||||
printf(" <-PDU- [IuUP]\n");
|
||||
printf("%s\n", dump(msg));
|
||||
printf("node_priv=%p\n", node_priv);
|
||||
if (!expect_tx_msg) {
|
||||
printf("ERROR: did not expect tx_msg()\n");
|
||||
exit(-1);
|
||||
} else if (strcmp(expect_tx_msg, dump(msg))) {
|
||||
printf("ERROR: mismatches expected msg %s\n", expect_tx_msg);
|
||||
exit(-1);
|
||||
} else
|
||||
printf("ok: matches expected msg\n");
|
||||
expect_tx_msg = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rx_pdu(struct osmo_iuup_cn *cn, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
printf(" -PDU-> [IuUP]\n");
|
||||
printf("%s\n", dump(msg));
|
||||
rc = osmo_iuup_cn_rx_pdu(cn, msg);
|
||||
printf("rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tx_payload(struct osmo_iuup_cn *cn, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
printf(" [IuUP] <-RTP-\n");
|
||||
printf("%s\n", dump(msg));
|
||||
rc = osmo_iuup_cn_tx_payload(cn, msg);
|
||||
printf("rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void test_cn_session()
|
||||
{
|
||||
void *node_priv = (void*)0x2342;
|
||||
|
||||
struct osmo_iuup_cn_cfg cfg = {
|
||||
.node_priv = node_priv,
|
||||
.rx_payload = rx_payload,
|
||||
.tx_msg = tx_msg,
|
||||
};
|
||||
|
||||
struct osmo_iuup_cn *cn = osmo_iuup_cn_init(ctx, &cfg, __func__);
|
||||
OSMO_ASSERT(cn);
|
||||
|
||||
printf("\nSend IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack\n");
|
||||
expect_tx_msg = "8060dc5219495e3f00010111" /* RTP header */
|
||||
"e4002400"; /* IuUP Init Ack */
|
||||
rx_pdu(cn,
|
||||
msgb_from_hex("IuUP-Init",
|
||||
"8060dc5219495e3f00010111" /* <- RTP header */
|
||||
"e000df99" /* <- IuUP header */
|
||||
"160051673c01270000820000001710000100" /* IuUP params */));
|
||||
|
||||
#define RTP_HEADER "8060944c6256042c00010102"
|
||||
#define IUUP_HEADER "0100e2b3"
|
||||
#define RTP_PAYLOAD "6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0"
|
||||
printf("\nReceive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet\n");
|
||||
printf("i.e. should strip away " IUUP_HEADER "\n");
|
||||
expect_rx_payload = RTP_HEADER "f03c" RTP_PAYLOAD;
|
||||
rx_pdu(cn,
|
||||
msgb_from_hex("IuUP-Data",
|
||||
RTP_HEADER IUUP_HEADER RTP_PAYLOAD));
|
||||
|
||||
printf("\nTransmit RTP. Expecting tx_msg() with inserted IuUP header\n");
|
||||
expect_tx_msg = RTP_HEADER "000002b3" RTP_PAYLOAD;
|
||||
tx_payload(cn,
|
||||
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
|
||||
|
||||
printf("\nMore RTP, each time the Frame Nr advances, causing a new header CRC.\n");
|
||||
expect_tx_msg = RTP_HEADER "0100e2b3" RTP_PAYLOAD;
|
||||
tx_payload(cn,
|
||||
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
|
||||
expect_tx_msg = RTP_HEADER "02007eb3" RTP_PAYLOAD;
|
||||
tx_payload(cn,
|
||||
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
|
||||
expect_tx_msg = RTP_HEADER "03009eb3" RTP_PAYLOAD;
|
||||
tx_payload(cn,
|
||||
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
|
||||
|
||||
printf("All done.\n");
|
||||
}
|
||||
|
||||
static const struct log_info_cat log_categories[] = {
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
.cat = log_categories,
|
||||
.num_cat = ARRAY_SIZE(log_categories),
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ctx = talloc_named_const(NULL, 0, __FILE__);
|
||||
void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
|
||||
osmo_init_logging2(ctx, &log_info);
|
||||
|
||||
test_cn_session();
|
||||
|
||||
talloc_free(msgb_ctx);
|
||||
return 0;
|
||||
}
|
||||
0
tests/iuup/iuup_test.err
Normal file
0
tests/iuup/iuup_test.err
Normal file
58
tests/iuup/iuup_test.ok
Normal file
58
tests/iuup/iuup_test.ok
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
Send IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack
|
||||
-PDU-> [IuUP]
|
||||
8060dc5219495e3f00010111e000df99160051673c01270000820000001710000100
|
||||
tx_msg() invoked by iuup_cn!
|
||||
<-PDU- [IuUP]
|
||||
8060dc5219495e3f00010111e4002400
|
||||
node_priv=0x2342
|
||||
ok: matches expected msg
|
||||
rc=0
|
||||
|
||||
Receive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet
|
||||
i.e. should strip away 0100e2b3
|
||||
-PDU-> [IuUP]
|
||||
8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
rx_payload() invoked by iuup_cn!
|
||||
[IuUP] -RTP->
|
||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
node_priv=0x2342
|
||||
ok: matches expected msg
|
||||
rc=0
|
||||
|
||||
Transmit RTP. Expecting tx_msg() with inserted IuUP header
|
||||
[IuUP] <-RTP-
|
||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
tx_msg() invoked by iuup_cn!
|
||||
<-PDU- [IuUP]
|
||||
8060944c6256042c00010102000002b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
node_priv=0x2342
|
||||
ok: matches expected msg
|
||||
rc=0
|
||||
|
||||
More RTP, each time the Frame Nr advances, causing a new header CRC.
|
||||
[IuUP] <-RTP-
|
||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
tx_msg() invoked by iuup_cn!
|
||||
<-PDU- [IuUP]
|
||||
8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
node_priv=0x2342
|
||||
ok: matches expected msg
|
||||
rc=0
|
||||
[IuUP] <-RTP-
|
||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
tx_msg() invoked by iuup_cn!
|
||||
<-PDU- [IuUP]
|
||||
8060944c6256042c0001010202007eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
node_priv=0x2342
|
||||
ok: matches expected msg
|
||||
rc=0
|
||||
[IuUP] <-RTP-
|
||||
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
tx_msg() invoked by iuup_cn!
|
||||
<-PDU- [IuUP]
|
||||
8060944c6256042c0001010203009eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||
node_priv=0x2342
|
||||
ok: matches expected msg
|
||||
rc=0
|
||||
All done.
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
@@ -1189,7 +1190,7 @@ 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)
|
||||
{
|
||||
@@ -1200,7 +1201,6 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
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;
|
||||
@@ -1247,16 +1247,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",
|
||||
@@ -1283,6 +1284,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;
|
||||
|
||||
@@ -135,87 +135,6 @@ mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
|
||||
return trans_id;
|
||||
}
|
||||
|
||||
void test_crcx(void)
|
||||
{
|
||||
struct msgb *msg;
|
||||
mgcp_trans_id_t trans_id;
|
||||
|
||||
printf("\n===== %s =====\n", __func__);
|
||||
|
||||
if (mgcp)
|
||||
talloc_free(mgcp);
|
||||
mgcp = mgcp_client_init(ctx, &conf);
|
||||
|
||||
msg = mgcp_msg_crcx(mgcp, 23, 42, MGCP_CONN_LOOPBACK);
|
||||
trans_id = dummy_mgcp_send(msg);
|
||||
|
||||
reply_to(trans_id, 200, "OK",
|
||||
"I: 1\r\n\r\n"
|
||||
"v=0\r\n"
|
||||
"o=- 1 23 IN IP4 10.9.1.120\r\n"
|
||||
"s=-\r\n"
|
||||
"c=IN IP4 10.9.1.120\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 16002 RTP/AVP 110 96\r\n"
|
||||
"a=rtpmap:110 AMR/8000\r\n"
|
||||
"a=rtpmap:96 GSM-EFR/8000\r\n"
|
||||
"a=ptime:20\r\n");
|
||||
}
|
||||
|
||||
void test_crcx_long_conn_id(void)
|
||||
{
|
||||
struct msgb *msg;
|
||||
mgcp_trans_id_t trans_id;
|
||||
|
||||
printf("\n===== %s =====\n", __func__);
|
||||
|
||||
if (mgcp)
|
||||
talloc_free(mgcp);
|
||||
mgcp = mgcp_client_init(ctx, &conf);
|
||||
|
||||
msg = mgcp_msg_crcx(mgcp, 23, 42, MGCP_CONN_LOOPBACK);
|
||||
trans_id = dummy_mgcp_send(msg);
|
||||
|
||||
reply_to(trans_id, 200, "OK",
|
||||
"I: 123456789abcdef0123456789ABCDEF0\r\n\r\n"
|
||||
"v=0\r\n"
|
||||
"o=- 1 23 IN IP4 10.9.1.120\r\n"
|
||||
"s=-\r\n"
|
||||
"c=IN IP4 10.9.1.120\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 16002 RTP/AVP 110 96\r\n"
|
||||
"a=rtpmap:110 AMR/8000\r\n"
|
||||
"a=rtpmap:96 GSM-EFR/8000\r\n"
|
||||
"a=ptime:20\r\n");
|
||||
}
|
||||
|
||||
void test_crcx_too_long_conn_id(void)
|
||||
{
|
||||
struct msgb *msg;
|
||||
mgcp_trans_id_t trans_id;
|
||||
|
||||
printf("\n===== %s =====\n", __func__);
|
||||
|
||||
if (mgcp)
|
||||
talloc_free(mgcp);
|
||||
mgcp = mgcp_client_init(ctx, &conf);
|
||||
|
||||
msg = mgcp_msg_crcx(mgcp, 23, 42, MGCP_CONN_LOOPBACK);
|
||||
trans_id = dummy_mgcp_send(msg);
|
||||
|
||||
reply_to(trans_id, 200, "OK",
|
||||
"I: 123456789abcdef0123456789ABCDEF01001029\r\n\r\n"
|
||||
"v=0\r\n"
|
||||
"o=- 1 23 IN IP4 10.9.1.120\r\n"
|
||||
"s=-\r\n"
|
||||
"c=IN IP4 10.9.1.120\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 16002 RTP/AVP 110 96\r\n"
|
||||
"a=rtpmap:110 AMR/8000\r\n"
|
||||
"a=rtpmap:96 GSM-EFR/8000\r\n"
|
||||
"a=ptime:20\r\n");
|
||||
}
|
||||
|
||||
void test_mgcp_msg(void)
|
||||
{
|
||||
struct msgb *msg;
|
||||
@@ -618,14 +537,11 @@ int main(int argc, char **argv)
|
||||
|
||||
mgcp_client_conf_init(&conf);
|
||||
|
||||
test_crcx();
|
||||
test_mgcp_msg();
|
||||
test_mgcp_client_cancel();
|
||||
test_sdp_section_start();
|
||||
test_map_codec_to_pt_and_map_pt_to_codec();
|
||||
test_map_pt_to_codec();
|
||||
test_crcx_long_conn_id();
|
||||
test_crcx_too_long_conn_id();
|
||||
|
||||
printf("Done\n");
|
||||
fprintf(stderr, "Done\n");
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
DLMGCP MGCP client: using endpoint domain '@mgw'
|
||||
DLMGCP message buffer to small, can not generate MGCP message
|
||||
|
||||
test_mgcp_client_cancel():
|
||||
DLMGCP MGCP client: using endpoint domain '@mgw'
|
||||
- composed msg with trans_id=1
|
||||
- not in queue yet, cannot cancel yet
|
||||
DLMGCP Cannot cancel, no such transaction: 1
|
||||
@@ -66,6 +68,4 @@ 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 Failed to parse MGCP response (parameter label: I): the received conn ID is too long: 39, maximum is 32 characters
|
||||
DLMGCP Cannot parse MGCP response (head parameters)
|
||||
Done
|
||||
|
||||
@@ -1,46 +1,4 @@
|
||||
|
||||
===== test_crcx =====
|
||||
composed:
|
||||
-----
|
||||
CRCX 1 17@mgw MGCP 1.0
|
||||
C: 2a
|
||||
L: p:20, a:AMR, nt:IN
|
||||
M: loopback
|
||||
|
||||
-----
|
||||
composed response:
|
||||
-----
|
||||
200 1 OK
|
||||
I: 1
|
||||
|
||||
v=0
|
||||
o=- 1 23 IN IP4 10.9.1.120
|
||||
s=-
|
||||
c=IN IP4 10.9.1.120
|
||||
t=0 0
|
||||
m=audio 16002 RTP/AVP 110 96
|
||||
a=rtpmap:110 AMR/8000
|
||||
a=rtpmap:96 GSM-EFR/8000
|
||||
a=ptime:20
|
||||
|
||||
-----
|
||||
response cb received:
|
||||
head.response_code = 200
|
||||
head.trans_id = 1
|
||||
head.conn_id = 1
|
||||
head.comment = OK
|
||||
audio_port = 16002
|
||||
audio_ip = 10.9.1.120
|
||||
ptime = 20
|
||||
codecs_len = 2
|
||||
codecs[0] = 112
|
||||
codecs[1] = 110
|
||||
ptmap_len = 2
|
||||
ptmap[0].codec = 112
|
||||
ptmap[0].pt = 110
|
||||
ptmap[1].codec = 110
|
||||
ptmap[1].pt = 96
|
||||
|
||||
Generated CRCX message:
|
||||
CRCX 1 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
@@ -196,72 +154,4 @@ test_sdp_section_start() test [9]:
|
||||
2 <= 2
|
||||
100 <= 100
|
||||
|
||||
|
||||
===== test_crcx_long_conn_id =====
|
||||
composed:
|
||||
-----
|
||||
CRCX 1 17@mgw MGCP 1.0
|
||||
C: 2a
|
||||
L: p:20, a:AMR, nt:IN
|
||||
M: loopback
|
||||
|
||||
-----
|
||||
composed response:
|
||||
-----
|
||||
200 1 OK
|
||||
I: 123456789abcdef0123456789ABCDEF0
|
||||
|
||||
v=0
|
||||
o=- 1 23 IN IP4 10.9.1.120
|
||||
s=-
|
||||
c=IN IP4 10.9.1.120
|
||||
t=0 0
|
||||
m=audio 16002 RTP/AVP 110 96
|
||||
a=rtpmap:110 AMR/8000
|
||||
a=rtpmap:96 GSM-EFR/8000
|
||||
a=ptime:20
|
||||
|
||||
-----
|
||||
response cb received:
|
||||
head.response_code = 200
|
||||
head.trans_id = 1
|
||||
head.conn_id = 123456789abcdef0123456789ABCDEF0
|
||||
head.comment = OK
|
||||
audio_port = 16002
|
||||
audio_ip = 10.9.1.120
|
||||
ptime = 20
|
||||
codecs_len = 2
|
||||
codecs[0] = 112
|
||||
codecs[1] = 110
|
||||
ptmap_len = 2
|
||||
ptmap[0].codec = 112
|
||||
ptmap[0].pt = 110
|
||||
ptmap[1].codec = 110
|
||||
ptmap[1].pt = 96
|
||||
|
||||
===== test_crcx_too_long_conn_id =====
|
||||
composed:
|
||||
-----
|
||||
CRCX 1 17@mgw MGCP 1.0
|
||||
C: 2a
|
||||
L: p:20, a:AMR, nt:IN
|
||||
M: loopback
|
||||
|
||||
-----
|
||||
composed response:
|
||||
-----
|
||||
200 1 OK
|
||||
I: 123456789abcdef0123456789ABCDEF01001029
|
||||
|
||||
v=0
|
||||
o=- 1 23 IN IP4 10.9.1.120
|
||||
s=-
|
||||
c=IN IP4 10.9.1.120
|
||||
t=0 0
|
||||
m=audio 16002 RTP/AVP 110 96
|
||||
a=rtpmap:110 AMR/8000
|
||||
a=rtpmap:96 GSM-EFR/8000
|
||||
a=ptime:20
|
||||
|
||||
-----
|
||||
Done
|
||||
|
||||
@@ -13,3 +13,10 @@ AT_KEYWORDS([mgcp])
|
||||
cat $abs_srcdir/mgcp/mgcp_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([iuup])
|
||||
AT_KEYWORDS([iuup])
|
||||
cat $abs_srcdir/iuup/iuup_test.ok > expout
|
||||
cat $abs_srcdir/iuup/iuup_test.err > experr
|
||||
AT_CHECK([$abs_top_builddir/tests/iuup/iuup_test], [], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
Reference in New Issue
Block a user