Compare commits

...

29 Commits

Author SHA1 Message Date
Neels Hofmeyr
3ed3eb8e0f systemd service: add save_log_tail
Change-Id: I36d149d1dbfe30a4e51122e671ab801b6fd18c74
2018-12-27 17:40:10 +01:00
Neels Hofmeyr
fdc5f111aa fix double free: fix wrong fix of earlier msgb leaks
Change-Id: I432559b2be703ac8db6e8d219936dd997b5b38e9
2018-12-27 16:31:40 +01:00
Neels Hofmeyr
02d612a926 osmo-mgw: fix RTP-rx memleaks introduced by IuUP patch
Change-Id: Iafe52e5f29a53f31a1e374aebe3a92c929b2ac2d
2018-12-27 15:46:14 +01:00
efistokl
944af2f641 iuup_cn_node.c: make it work for AMR 12.2k codec
rx_data: prepend hardcoded AMR 12.2 header "0xf03c" to the front of RTP
payload
osmo_iuup_cn_tx_payload: strip AMR header (2 bytes) from the front of RTP
payload
2018-12-21 23:38:44 +01:00
Neels Hofmeyr
f2a65b8bbb add simplistic IuUP FSM and strip/add IuUP headers
This should really be using the FSM in libosmocore/laforge/iu_up: take the best
of both sides and integate in the libosmocore FSM implementation, then use it
here.
- in libosmocore, the FSM definition is nicer.
- here, we have correct header checksums.

This patch here also adds RTP header stripping/adding functionality, after
introducing using msgb to pass data around.

Change-Id: Ibc70e0aa00476926dd1f4ea8139c34f31f9cdfa3
2018-12-21 23:36:29 +01:00
Neels Hofmeyr
332308d994 mgcp_client: tweak some log levels INFO -> {DEBUG,ERROR}
Change-Id: Ie4ecb4b82a7a1e476c58d0a6056525733254adbb
2018-12-21 03:07:53 +01:00
Neels Hofmeyr
099332824e mgcp_client: make domain part of endpoint configurable
So far, both osmo-msc and osmo-bsc always pass endpoint names of the form
'...@mgw' to osmo-mgw. Allow configuring the 'mgw' part.

Note that the actual way to pass a differing name is to pass a composed
'rtpbridge/*@foo' to mgcp_msg_gen() in the struct mgcp_msg. So this merely adds
a common VTY config for the domain name part, changes to clients are necessary.

- add mgcp_client_rtpbridge_wildcard() (useful for AoIP endpoints)
- add mgcp_client_endpoint_domain() (useful for SCCPlite endpoints)
- add mgcp client vty cfg 'mgw endpoint-domain NAME'

Rationale: reading pcaps becomes so much easier when each of osmo-bsc and
osmo-msc address their MGW with differing domain names. Otherwise, both will
have a '0@mgw' endpoint and it gets really confusing.

Also: our MGCP clients osmo-bsc and osmo-msc use code dup to compose the
initial 'rtpbridge/*@mgw' rtpbridge wildcard. It should be defined by this API
instead.

This will be used by:
* osmo-msc I87ac11847d1a6d165ee9a2b5d8a4978e7ac73433
* osmo-bsc I492023e9dca0233ec0a077032455d9f2e3880f78

After these, with according configuration, there can be a '0@bsc' and a '0@msc'
endpoint.

osmo-mgw-for-bsc.cfg:
 mgcp
  domain bsc

osmo-bsc.cfg:
 msc 0
  mgw endpoint-domain bsc

osmo-mgw-for-msc.cfg:
 mgcp
  domain msc

osmo-msc.cfg:
 msc
  mgw endpoint-domain msc

(By default, everything will still use '@mgw')

Change-Id: Ia662016f29dd8727d9c4626d726729641e21e1f8
2018-12-20 00:34:17 +01:00
Neels Hofmeyr
0a40379214 mgcp_client: logging tweaks
Fix typos, use osmo_sock_get_name2() to show the tx source and target IP:port,
shorten some wording.

Depends: I8ad89ac447c9c582742e70d082072bdd40a5a398 (libosmocore)
Change-Id: Iae728192f499330d16836d9435648f6b8ed213b6
2018-12-19 22:47:53 +00:00
Neels Hofmeyr
72bc8da537 mgcp_client: drop a bunch of dead code
Remove public API that makes no sense anymore and is dead code.

I see the dropped API as a dead-end initial misconception of the early mgcp
client, and it doesn't really make sense to drag this stuff along. It has not
been used by osmo-msc,-bsc for a long time now, and just confuses the reader.

It is public API, yes, and older versions of osmo-msc / osmo-bsc will not be
able to compile against this, but even if it did, the resulting MGCP client
would not work with the current osmo-mgw: this API is still based on the
premise that the MGCP client dictates the MGW endpoint numbers, a concept that
cannot be used with the current osmo-mgw. Instead, osmo-mgw expects a
wildcarded endpoint upon CRCX and assigns its own endpoint names.

Also, the bts-base configuration is unused and a legacy of when osmo-bsc_mgcp
had explicit BTS and CN sides.

Change-Id: I98a9f1f17a1c4ab20cea3b08c7d21663592134d6
2018-12-19 22:22:34 +00:00
Neels Hofmeyr
c3132fd508 mgcp_client_vty: fix missing talloc_free
If the vty client enters multiple local / remote addresses, that leaks talloc
memory of the previously set addresses. Free those first, if any, using
osmo_talloc_replace_string().

Change-Id: I331b3d53b5eb330b87d798f952077a043674d409
2018-12-19 22:19:17 +00:00
Neels Hofmeyr
96c3107daf osmo-mgw: err-log: include expected domain name
Add the expected domain name, and move the error log to where the expected
domain name is compared.

Change-Id: I59f40dc9263f686852f103ca904fc0a6702d7c8e
2018-12-19 17:58:42 +01:00
Neels Hofmeyr
ad21a0e166 check_rtp: on IP:port errors, log the IP and port
Half of those are obviously zero, but I'd rather print the raw data instead of
adding string constants, even if the condition must always lead to 0.0.0.0:0.

Rationale: I had osmo-mgw listen on 0.0.0.0 and got the error message
  DRTP ERROR endpoint:0x1 destination IP-address is invalid
which didn't convey that 0.0.0.0 is regarded as invalid.

Change-Id: I9e98d464a27038904797c5c10735a98ef5b7b9c3
2018-12-17 13:38:12 +00:00
Neels Hofmeyr
32d15cc8ba drop/replace very weird logging in mgcp_client.c
mgcp_do_write() is the final stage of writing data towards the MGCP server
(MGW). In that function, drop an unconditional iteration and copy of the MGCP
message to a static string buffer for no apparent reason besides debug logging.

Instead, use osmo_escape_str() with a limited length, which can just be an
inline format argument in the LOGP() statement. This way, the string mangling
is simpler and only gets run when DMGCP is actually on debug log level.

Change-Id: Id6877ed7fd7dbe009b2ece8792d5160d040c1aaa
2018-12-17 13:37:18 +00:00
Oliver Smith
1fd50e5306 contrib: fix makedistcheck with disabled systemd
EXTRA_DIST files need to be distributed, no matter if the systemd option
is configured or not.

Change-Id: I5014d98e06e033d04be45585b34458c90b1ced00
2018-12-06 13:54:53 +01:00
Oliver Smith
6802374574 contrib/jenkins.sh: build and publish manuals
Add new environment variables WITH_MANUALS and PUBLISH to control if
the manuals should be built and uploaded. Describe all environment vars
on top of the file.

When WITH_MANUALS is set, install osmo-gsm-manuals like any other
dependency and add --enable-manuals to the configure flags (for "make"
and "make distcheck"). Add the bin subdir of the installed files to
PATH, so osmo-gsm-manuals-check-depends can be used by ./configure.

Related: OS#3385
Change-Id: I24179b21ce4ff182649243e286c87c824b889454
2018-12-05 13:09:33 +01:00
Oliver Smith
82c1d1eb20 Fix DISTCHECK_CONFIGURE_FLAGS override
Set AM_DISTCHECK_CONFIGURE_FLAGS in Makefile.am instead of
DISTCHECK_CONFIGURE_FLAGS. This is the recommended way from the
automake manual, as otherwise the flag can't be changed by the user
anymore.

Related: OS#3718
Change-Id: Ie9db1519e89d2a8ca1e403e480f57d72fc25ab75
2018-12-04 15:36:37 +01:00
Oliver Smith
5a5169e15c jenkins.sh: remove leftover MGCP env variable
Jenkins currently runs the build twice, once with
MGCP="--enable-mgcp-transcoding" and once with
MGCP="--disable-mgcp-transcoding". The configure.ac does not have this
parameter, so let's remove the confusing variable.

This is in preparation for describing all environment variables used in
jenkins.sh (follow up commit).

Change-Id: I6223afef0f34d90170a691d7d5b1d9542c34c6b0
2018-12-03 16:52:11 +01:00
Oliver Smith
7c0b70a94e build manuals moved here from osmo-gsm-manuals.git
Moved to doc/manuals/, with full commit history, in preceding merge commit.
Now incorporate in the build system.

Build with:

$ autoreconf -fi
$ ./configure --enable-manuals
$ make

Shared files from osmo-gsm-manuals.git are found automatically if
- the repository is checked out in ../osmo-gsm-manuals; or
- if it osmo-gsm-manuals was installed with "make install"; or
- OSMO_GSM_MANUALS_DIR is set.

Related: OS#3385
Change-Id: I504f05a49721f2dfe105b6c5fd1995c4e7a0be9c
2018-11-27 18:07:37 +01:00
Neels Hofmeyr
ca6a8495e1 Merge history from osmo-gsm-manuals.git
Change-Id: Ibbc4f41f7672159d8596ceb84b60e5c849b98074
2018-11-27 18:07:16 +01:00
Neels Hofmeyr
c0a2196f91 mgw: update vty reference
Change-Id: Ib30ea8b02f8a950648b85e7ebc96a40ba5a36b34
2018-11-27 18:07:00 +01:00
Neels Hofmeyr
da026abc27 OsmoMGW: document the 'X-Osmo-IGN' MGCP extension
See also Ia6fe5ead4b601931c1bf41b29fc1b237aac37d2c in osmo-mgw.git, which
changes the initial single-character implementation to match this specification
of string tokens separated by spaces.

Change-Id: If15a88c3b5b40fd1d24ad0f94f3231f678669ab1
2018-11-27 18:07:00 +01:00
Daniel Willmann
27780313ac Add initial OsmoMGW manual
Change-Id: I1b4ff96309d280c6a63105a6e06a82ec2e7b6908
2018-11-27 18:07:00 +01:00
Daniel Willmann
e5ed946bdb mgw: Add new VTY reference
Change-Id: Ic8ca983f3358aae9362fb21ff3a111f9c4f3935b
2018-11-27 18:07:00 +01:00
Harald Welte
c319b79baa vty-ref: Update URI of docbook 5.0 schema
... to match the /etc/xml/catalog file on debian (no "www" in hostname)

Change-Id: Id9f3579c7f2bc3af13fe30b5268f249b6f59ed0d
2018-11-27 18:07:00 +01:00
Neels Hofmeyr
2818db8b33 OsmoMGW: update VTY reference
This is the first update since the libosmocore changes to the 'show
online-help' generated output. Hence the produced document now benefits from
the structural improvements:
- not repeating common commands for every node;
- using section names that match the VTY prompt.

Change-Id: I79804ec0e61cc19a679f079a083a5ea2ea3ee2de
2018-11-27 18:07:00 +01:00
Philipp Maier
0415de74b8 osmo-mgw: Add vty reference manual
add missing vty reference manual for osmo-mgw

Change-Id: If9d8cdcbbbf14b23e48af5b9ae1c56c2a38219a2
2018-11-27 18:07:00 +01:00
Neels Hofmeyr
4075b8805d Importing history from osmo-gsm-manuals.git
Change-Id: Ie9bf61a61d00a74cb327853f26dd1a7e1481fdfa
2018-11-27 18:05:58 +01:00
Stefan Sperling
8ab3fbbaf2 add DLCX command statistics to osmo-mgw
Add a counter group for DLCX commands. The group contains counters for
successful connection processing as well as various error conditions.
This provides a quick overview of DLCX failures on each trunk throughout
the lifetime of the osmo-mgw process.

The counters are displayed by 'show mgcp stats' and 'show rate-counters'

While here, rename MGCP_MDCX_FAIL_DEFERRED_BY_POLICY to
MGCP_MDCX_DEFERRED_BY_POLICY; we have decided that deferred connections
aren't failures, and this keeps names used by DLCX and MDCX in sync.

Also remove some allocation failure checks with OSMO_ASSERT(); such
checks aren't en vogue anymore.

Change-Id: Ie0dde2faf02fd68a69f986973d39b1bea367039b
Depends: I80d36181600901ae2e0f321dc02b5d54ddc94139I
Related: OS#2660
2018-11-27 14:58:21 +00:00
Neels Hofmeyr
cc39218dce fix osmo-mgw -s; fixes osmo-mgw.service using -s
Even though osmo-mgw advertises the -s option, the getopt configuration lacks
-s and that option does not work. Thus the osmo-mgw.service file that uses -s
was unable to start.

Add 's' to the getopt configuration, fix -s and hence also fix the .service
file.

Change-Id: I6f298aef73eb3486d04706910e9fdbaaebaf2481
2018-11-25 22:43:16 +01:00
48 changed files with 3488 additions and 662 deletions

11
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
if HAVE_SYSTEMD
EXTRA_DIST = osmo-mgw.service
if HAVE_SYSTEMD
systemdsystemunit_DATA = \
osmo-mgw.service
endif

View File

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

View File

@@ -1,3 +1,4 @@
SUBDIRS = \
examples \
manuals \
$(NULL)

16
doc/manuals/Makefile.am Normal file
View 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

View 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

View File

@@ -0,0 +1,4 @@
[[counters]]
== Counters
include::./counters_generated.adoc[]

View 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

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

View 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 well
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

View 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.

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

View 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[]

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

View File

@@ -0,0 +1,2 @@
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
</vtydoc>

File diff suppressed because it is too large Load Diff

View File

@@ -29,6 +29,7 @@
/* Debug Areas of the code */
enum {
DRTP,
DIUUP,
Debug_LastEntry,
};

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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);
}

View 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" },
{}
};

View File

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

View File

@@ -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.

View File

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

View File

@@ -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++;
}

View File

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

View File

@@ -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 = {

View File

@@ -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
View 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
View 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
View File

58
tests/iuup/iuup_test.ok Normal file
View 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.

View File

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

View File

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

View File

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

View File

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

View File

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