mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 00:02:01 +00:00
Compare commits
96 Commits
laforge/li
...
stsp/show_
Author | SHA1 | Date | |
---|---|---|---|
|
e3c2eaeec0 | ||
|
704c4f0adf | ||
|
bc0346e080 | ||
|
5928dc9345 | ||
|
04da5e5e98 | ||
|
54b4f82f91 | ||
|
3d7b58d77a | ||
|
604410cd13 | ||
|
b340f90c9e | ||
|
3c8ccb6724 | ||
|
d4e6aa42ca | ||
|
e6df0e47e7 | ||
|
54eb0e1204 | ||
|
b38fb8911f | ||
|
1b3a385b9d | ||
|
dbd70c7b68 | ||
|
a19547b7a1 | ||
|
06823731d8 | ||
|
ed1cff5ab9 | ||
|
f2321b7a72 | ||
|
4219904cb2 | ||
|
b2753f2044 | ||
|
ba61f68137 | ||
|
9e1d164469 | ||
|
0ec1d4e17c | ||
|
d761d355f9 | ||
|
8d064dfd24 | ||
|
7181cc1f02 | ||
|
60f8e31a2f | ||
|
086c3f3c67 | ||
|
d0b470d1a9 | ||
|
77f76d0be5 | ||
|
892dec0be9 | ||
|
c430d19112 | ||
|
7b8e419d92 | ||
|
4f7613eeb1 | ||
|
01f039538e | ||
|
d2e3a52230 | ||
|
2924825bc6 | ||
|
bd86f94a09 | ||
|
1083533db4 | ||
|
0793d2f5d5 | ||
|
a8c6a9c37a | ||
|
2da99a2946 | ||
|
c3eed40f00 | ||
|
515e341ff6 | ||
|
8bda7a7766 | ||
|
df5d219f39 | ||
|
5656fbf49d | ||
|
19d640e806 | ||
|
207ab51270 | ||
|
c3cc654add | ||
|
b911b879d8 | ||
|
af07f66ca3 | ||
|
dd0c522cd4 | ||
|
37d11c80da | ||
|
1355d7e4f7 | ||
|
fdd603c4c8 | ||
|
1fc6999a77 | ||
|
220f487238 | ||
|
edc00f4ea7 | ||
|
a49e32a1ab | ||
|
dd4ede34ad | ||
|
3aa815755d | ||
|
3261dc7540 | ||
|
771b26a043 | ||
|
36a81129ec | ||
|
a390d0ba52 | ||
|
b759473d10 | ||
|
c3cfae2cf8 | ||
|
3cbfb8a53c | ||
|
275ac97036 | ||
|
abe8c897fd | ||
|
e9d645b3b3 | ||
|
7f0966c13d | ||
|
55295f7b07 | ||
|
9d25d7a2e6 | ||
|
490cbaa89e | ||
|
eb0bde09bb | ||
|
3b12e1b011 | ||
|
2138779559 | ||
|
12943ea0c1 | ||
|
6efb43a210 | ||
|
03cc48474c | ||
|
5672563f7e | ||
|
106743c66b | ||
|
8890dfa634 | ||
|
10f32dbfbf | ||
|
e35eeae8e0 | ||
|
abbb6b9088 | ||
|
3f35a3708a | ||
|
33eafe050b | ||
|
1d1b98f4a4 | ||
|
a0ac30faa5 | ||
|
49e3d5a9c9 | ||
|
3338135ead |
@@ -20,7 +20,6 @@ pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = \
|
||||
libosmo-legacy-mgcp.pc \
|
||||
libosmo-mgcp-client.pc \
|
||||
libosmo-mgcp.pc \
|
||||
$(NULL)
|
||||
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
|
@@ -24,6 +24,3 @@
|
||||
# If any interfaces have been removed or changed since the last public release, a=0.
|
||||
#
|
||||
#library what description / commit summary line
|
||||
libosmo-mgcp API/ABI change parse and represent connection identifiers as hex strings
|
||||
libosmo-mgcp API/ABI change connection identifiers are assigned by the server, not CA
|
||||
libosmo-mgcp-client API/ABI change parse and store connection identifier in response
|
30
configure.ac
30
configure.ac
@@ -39,10 +39,10 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
|
||||
AC_SUBST(LIBRARY_DL)
|
||||
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.2.0)
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
@@ -56,6 +56,24 @@ then
|
||||
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(werror,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-werror],
|
||||
[Turn all compiler warnings into errors, with exceptions:
|
||||
a) deprecation (allow upstream to mark deprecation without breaking builds);
|
||||
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
|
||||
]
|
||||
)],
|
||||
[werror=$enableval], [werror="no"])
|
||||
if test x"$werror" = x"yes"
|
||||
then
|
||||
WERROR_FLAGS="-Werror"
|
||||
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
|
||||
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
|
||||
CFLAGS="$CFLAGS $WERROR_FLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
|
||||
fi
|
||||
|
||||
# Enable/disable transcoding within osmo-bsc_mgcp?
|
||||
AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [Build the MGCP gateway with internal transcoding enabled.])],
|
||||
[osmo_ac_mgcp_transcoding="$enableval"],[osmo_ac_mgcp_transcoding="no"])
|
||||
@@ -126,13 +144,15 @@ 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")
|
||||
|
||||
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
|
||||
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
|
||||
|
||||
dnl Generate the output
|
||||
AM_CONFIG_HEADER(bscconfig.h)
|
||||
|
||||
AC_OUTPUT(
|
||||
libosmo-legacy-mgcp.pc
|
||||
libosmo-mgcp-client.pc
|
||||
libosmo-mgcp.pc
|
||||
include/Makefile
|
||||
include/osmocom/Makefile
|
||||
include/osmocom/legacy_mgcp/Makefile
|
||||
|
@@ -37,7 +37,7 @@ set -x
|
||||
|
||||
cd "$base"
|
||||
autoreconf --install --force
|
||||
./configure $MGCP --enable-vty-tests --enable-external-tests
|
||||
./configure $MGCP --enable-vty-tests --enable-external-tests --enable-werror
|
||||
$MAKE $PARALLEL_MAKE
|
||||
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
|
||||
|| cat-testlogs.sh
|
||||
|
125
debian/changelog
vendored
125
debian/changelog
vendored
@@ -1,3 +1,128 @@
|
||||
osmo-mgw (1.3.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* contrib: Add osmo-mgw systemd service
|
||||
* legacy: mgcp_protocol: Don't print osmux stats if it is off
|
||||
* mgcp_stat: Don't print osmux stats if it is off
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* fix segfault: DLCX for unknown endpoint: dont try to log NULL endpoint
|
||||
* MGCP endpoints: parse as decimal, not hex
|
||||
* add --enable-sanitize config option
|
||||
* legacy_mgcp: mgcp_test: sanitize: free msgb_ctx
|
||||
* mgcp_test: test_packet_error_detection: sanitize: free all conns
|
||||
* mgcp_test: test_no_cycle: sanitize: free endp
|
||||
* mgcp_test: sanitize: free msgb_ctx
|
||||
* mgcp_client: don't configure "bts base"
|
||||
* Revert "mgcp_client: don't configure "bts base"" until osmo-msc is ready
|
||||
* mgcp_client: add transaction cleanup
|
||||
* mgcp_client_test makefile: add update_exp target
|
||||
* cosmetic: mgcp_network: typo in log
|
||||
* osmo-mgw: Add talloc context introspection via VTY
|
||||
* mgcp_client: show failure by MGCP SDP section parsing test
|
||||
* mgcp_client: cosmetic: clean up SDP params parsing
|
||||
* mgcp_client: detect SDP section-start parsing errors
|
||||
* compiler warning: ignore deprecated in mgcp_client_test.c
|
||||
* configure: add --enable-werror
|
||||
* jenkins.sh: add --enable-werror to configure flags
|
||||
* cosmetic: mgcp, legacy_mgcp: drop unused vty.h definitions
|
||||
* use osmo_init_logging2() with proper talloc ctx
|
||||
|
||||
[ Philipp Maier ]
|
||||
* osmux: fix nullpointer dereference
|
||||
* cosmetic: guard dead osmux vty code with ifdef
|
||||
* cosmetic: remove prefix "net" from rtp related vty commands
|
||||
* doc: update sample config file
|
||||
* cosmetic: use correct VTY port number constant
|
||||
* vty: simplify endpoint allocation
|
||||
* vty: do not change number_endpoints at runtime
|
||||
* MGCP: Connection Identifiers are hex strings
|
||||
* libosmo-mgcp: Connection Identifiers are allocated by MGW, not CA
|
||||
* client: use osmo_strlcpy instead of strncpy
|
||||
* cosmetic: fix sourcecode formatting
|
||||
* cosmetic: clearly mark endpoint numbers as hex
|
||||
* client: use string as connection identifier
|
||||
* conn: remove assertions
|
||||
* mgcp_test: fix wrong strcmp() parameters
|
||||
* mgcp_test: fix nullpointer dereference
|
||||
* mgcp_test: add returncode check
|
||||
* mgcp_test: fix possible double free
|
||||
* mcgp_client: mgcp_msg_gen(): add checks to verify params
|
||||
* network: use originating RTP packet address for loopback
|
||||
* client: mgcp_response_parse_params: check rtp port
|
||||
* mgcp: allow endpoints beginning from zero
|
||||
* client/common: move constant MGCP_ENDPOINT_MAXLEN
|
||||
* mgcp: make domain name configurable
|
||||
* cosmetic: protocol: remove unnecessary nul termination
|
||||
* client: do not insist on \n\n when parsing MGCP messages
|
||||
* main: display mgcp ip/port
|
||||
* client: make callid in MDCX mandatory
|
||||
* client: add missing mandatory SDP fields
|
||||
* mgcp: permit wildcarded endpoint assignment (CRCX)
|
||||
* mgcp: add prefix to virtual trunk
|
||||
* client: eliminate destructive parameter parsing
|
||||
* client: eliminate destructive head parsing
|
||||
* cosmetic: client: add doxygen comments
|
||||
* protocol: fix problem with line break and OSMUX
|
||||
* protocol: fix missing carriage return
|
||||
* client: fix sdp parameter ordering
|
||||
* protocol: check the packetization in local cx options
|
||||
* cosmetic: remove spaces from pointer symbol
|
||||
* client: Do not accept endpoint ids with wildcards in responses
|
||||
* client: do not accept endpoint ids without @ character in responses
|
||||
* client: prohibit endpoint ids without @ character
|
||||
* protocol: on wildcarded CRCX return endpoint number as hex
|
||||
* msg: fix response code on exhausted endp resources
|
||||
* cosmetic: move mgcp_release_endp() to mgcp_ep.c
|
||||
* client: use heap to store mgcp_response
|
||||
* ep: move endpoint struct and define to mgcp_ep.h
|
||||
* cosmetic: rename mgcp_release_endp to mgcp_endp_release
|
||||
* cosmetic: rename mgcp_ep.c/h to mgcp_endp.c/h
|
||||
* protocol: reject DLCX/CRCX/MDCX on unsupported parameters
|
||||
* protocol: exit cleanly when local cx options check fails
|
||||
* cosmetic: Add missing \n on log line
|
||||
* protocol: check requested connection mode
|
||||
* protocol: fix tagging of wildcarded requests
|
||||
* protocol: prohibit wildcarded requests for MDCX and DLCX
|
||||
* mgcp: fix use-after-free and add callback for endpoint cleanup
|
||||
* client: add an optional FSM interface
|
||||
* mgcp_client_fsm: Add FSM event names
|
||||
* cosmetic: mgcp_client_fsm: rename enums
|
||||
* cosmetic: rename function .._conn_reset() to .._conn_init()
|
||||
* mgcp_conn: do not touch u.rtp in mgcp_conn_alloc()
|
||||
* cosmetic: rename .._codec_reset() to .._codec_init()
|
||||
* mgcp_conn: add function mgcp_rtp_conn_cleanup()
|
||||
* stats: use libosmocore rate counter for in/out_stream.err_ts_counter
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* debian/control: correct library dependency of osmo-mgw against libosmo-mgcp1
|
||||
* debian: include systemd service osmo-mgw.service
|
||||
* Revert "stats: use libosmocore rate counter for in/out_stream.err_ts_counter"
|
||||
|
||||
[ Harald Welte ]
|
||||
* cosmetic: fix whitespaces; we use tabs for indentation
|
||||
* Fix possible buffer overflow in mgcp_conn_dump()
|
||||
* osmo-mgw: Update copyright statement
|
||||
* osmo-mgw: Config file is osmo-mgw.cfg, and not mgcp.cfg
|
||||
* osmo-mgw: Fix copyright notice
|
||||
* strct mgcp_rtp_state: Group + document struct members related to patching
|
||||
* mgcp_rtp_state: grup 'stats' members into sub-structure
|
||||
* mgcp_rtp_end: Group statistics members into 'stats' sub-struct
|
||||
* libosmo-mgcp: Cosmetic spelling fixes in comments
|
||||
* mgcp_msg: We must parse endpoint numbers as hex, not decimal!
|
||||
* mgcp_internal.h: document more struct members with comments
|
||||
* centralize handling of common errors like "endpoint not found"
|
||||
* Return proper MGCP Error codes, as per spec
|
||||
* osmo-mgw: Use libosmocore socket abstraction
|
||||
* osmo-bsc_mgcp: Add LIBOSMONETIF_{CFLAGS,LIBS}
|
||||
* libosmo-mgcp-client is GPLv2+, not AGPLv3+
|
||||
* Turn libosmo-mgcp into local, non-installed library
|
||||
|
||||
[ Stefan Sperling ]
|
||||
* enable osmo_fsm vty commands in libosmo-mgcp-client vty
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 03 May 2018 17:40:35 +0200
|
||||
|
||||
osmo-mgw (1.2.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
|
21
debian/control
vendored
21
debian/control
vendored
@@ -16,25 +16,10 @@ Homepage: https://osmocom.org/projects/osmo-mgw
|
||||
Package: osmo-mgw
|
||||
Architecture: any
|
||||
Multi-Arch: foreign
|
||||
Depends: libosmo-mgcp1, ${misc:Depends}, ${shlibs:Depends}
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
|
||||
|
||||
Package: libosmo-mgcp1
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Pre-Depends: ${misc:Pre-Depends}
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: libosmo-mgcp: Osmocom's Media Gateway server library
|
||||
|
||||
Package: libosmo-mgcp-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-mgcp1 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: libosmo-mgcp: Osmocom's Media Gateway server library
|
||||
|
||||
Package: libosmo-mgcp-client2
|
||||
Package: libosmo-mgcp-client3
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
@@ -46,7 +31,7 @@ Package: libosmo-mgcp-client-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: libosmo-mgcp-client2 (= ${binary:Version}), ${misc:Depends}
|
||||
Depends: libosmo-mgcp-client3 (= ${binary:Version}), ${misc:Depends}
|
||||
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
|
||||
|
||||
Package: osmo-bsc-mgcp
|
||||
|
19
debian/copyright
vendored
19
debian/copyright
vendored
@@ -21,6 +21,25 @@ License: AGPL-3.0+
|
||||
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/>.
|
||||
|
||||
Files: src/libosmo-mgcp-client/* include/osmocom/mgcp_client/*
|
||||
Copyright: 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
|
||||
Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
|
||||
2009 by Harald Welte <laforge@gnumonks.org>
|
||||
2009-2011 by Holger Hans Peter Freyther
|
||||
License: GPL-2.0+
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Files: src/libosmo-legacy-mgcp/g711common.h
|
||||
Copyright: 2000 Abramo Bagnara <abramo@alsa-project.org>
|
||||
License: GPL-2.0+
|
||||
|
4
debian/libosmo-mgcp-dev.install
vendored
4
debian/libosmo-mgcp-dev.install
vendored
@@ -1,4 +0,0 @@
|
||||
usr/include/osmocom/mgcp
|
||||
usr/lib/*/libosmo-mgcp.so
|
||||
usr/lib/*/libosmo-mgcp.a
|
||||
usr/lib/*/pkgconfig/libosmo-mgcp.pc
|
1
debian/libosmo-mgcp1.install
vendored
1
debian/libosmo-mgcp1.install
vendored
@@ -1 +0,0 @@
|
||||
usr/lib/*/libosmo-mgcp.so.*
|
@@ -7,6 +7,7 @@ nobase_include_HEADERS = \
|
||||
osmocom/legacy_mgcp/mgcp_internal.h \
|
||||
osmocom/legacy_mgcp/osmux.h \
|
||||
osmocom/mgcp_client/mgcp_client.h \
|
||||
osmocom/mgcp_client/mgcp_client_fsm.h \
|
||||
osmocom/mgcp_client/mgcp_common.h \
|
||||
osmocom/mgcp/mgcp.h \
|
||||
osmocom/mgcp/mgcp_common.h \
|
||||
|
@@ -243,6 +243,12 @@ struct mgcp_config {
|
||||
* message.
|
||||
*/
|
||||
uint16_t osmux_dummy;
|
||||
|
||||
/* Use a jitterbuffer on the bts-side receiver */
|
||||
bool bts_use_jibuf;
|
||||
/* Minimum and maximum buffer size for the jitter buffer, in ms */
|
||||
uint32_t bts_jitter_delay_min;
|
||||
uint32_t bts_jitter_delay_max;
|
||||
};
|
||||
|
||||
/* config management */
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/netif/jibuf.h>
|
||||
|
||||
#define CI_UNUSED 0
|
||||
|
||||
@@ -198,6 +199,14 @@ struct mgcp_endpoint {
|
||||
uint32_t octets;
|
||||
} stats;
|
||||
} osmux;
|
||||
|
||||
/* Jitter buffer */
|
||||
struct osmo_jibuf* bts_jb;
|
||||
/* Use a jitterbuffer on the bts-side receiver */
|
||||
bool bts_use_jibuf;
|
||||
/* Minimum and maximum buffer size for the jitter buffer, in ms */
|
||||
uint32_t bts_jitter_delay_min;
|
||||
uint32_t bts_jitter_delay_max;
|
||||
};
|
||||
|
||||
#define for_each_line(line, save) \
|
||||
@@ -335,3 +344,8 @@ static inline const char *mgcp_bts_src_addr(struct mgcp_endpoint *endp)
|
||||
}
|
||||
|
||||
int mgcp_msg_terminate_nul(struct msgb *msg);
|
||||
|
||||
/**
|
||||
* Internal jitter buffer related
|
||||
*/
|
||||
void mgcp_dejitter_udp_send(struct msgb *msg, void *data);
|
||||
|
@@ -1,31 +1,8 @@
|
||||
#ifndef OPENBSC_VTY_H
|
||||
#define OPENBSC_VTY_H
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/buffer.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
struct gsm_network;
|
||||
struct vty;
|
||||
|
||||
void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *);
|
||||
|
||||
struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base);
|
||||
|
||||
extern struct cmd_element cfg_description_cmd;
|
||||
extern struct cmd_element cfg_no_description_cmd;
|
||||
|
||||
enum mgcp_vty_node {
|
||||
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
TRUNK_NODE,
|
||||
};
|
||||
|
||||
struct log_info;
|
||||
int bsc_vty_init(struct gsm_network *network);
|
||||
int bsc_vty_init_extra(void);
|
||||
|
||||
void msc_vty_init(struct gsm_network *msc_network);
|
||||
|
||||
struct gsm_network *gsmnet_from_vty(struct vty *vty);
|
||||
|
||||
#endif
|
||||
|
@@ -3,7 +3,8 @@ noinst_HEADERS = \
|
||||
mgcp_msg.h \
|
||||
mgcp_conn.h \
|
||||
mgcp_stat.h \
|
||||
mgcp_ep.h \
|
||||
mgcp_endp.h \
|
||||
mgcp_sdp.h \
|
||||
mgcp_codec.h \
|
||||
debug.h \
|
||||
$(NULL)
|
||||
|
@@ -105,12 +105,12 @@ struct mgcp_port_range {
|
||||
};
|
||||
|
||||
/* There are up to three modes in which the keep-alive dummy packet can be
|
||||
* sent. The beviour is controlled viw the keepalive_interval member of the
|
||||
* sent. The behaviour is controlled via the keepalive_interval member of the
|
||||
* trunk config. If that member is set to 0 (MGCP_KEEPALIVE_NEVER) no dummy-
|
||||
* packet is sent at all and the timer that sends regular dummy packets
|
||||
* is no longer scheduled. If the keepalive_interval is set to -1, only
|
||||
* one dummy packet is sent when an CRCX or an MDCX is performed. No timer
|
||||
* is scheduled. For all vales greater 0, the a timer is scheduled and the
|
||||
* is scheduled. For all vales greater 0, the timer is scheduled and the
|
||||
* value is used as interval. See also mgcp_keepalive_timer_cb(),
|
||||
* handle_modify_con(), and handle_create_con() */
|
||||
#define MGCP_KEEPALIVE_ONCE (-1)
|
||||
@@ -212,6 +212,8 @@ struct mgcp_config {
|
||||
* message.
|
||||
*/
|
||||
uint16_t osmux_dummy;
|
||||
/* domain name of the media gateway */
|
||||
char domain[255+1];
|
||||
};
|
||||
|
||||
/* config management */
|
||||
@@ -220,7 +222,6 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
|
||||
enum mgcp_role role);
|
||||
int mgcp_vty_init(void);
|
||||
int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg);
|
||||
void mgcp_release_endp(struct mgcp_endpoint *endp);
|
||||
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
|
||||
|
||||
/*
|
||||
|
6
include/osmocom/mgcp/mgcp_codec.h
Normal file
6
include/osmocom/mgcp/mgcp_codec.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
|
||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name);
|
||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
|
@@ -68,8 +68,22 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Maximum length of the comment field */
|
||||
#define MGCP_COMMENT_MAXLEN 256
|
||||
|
||||
/* String length of Connection Identifiers
|
||||
* (see also RFC3435 2.1.3.2 Names of Connections) */
|
||||
#define MGCP_CONN_ID_LENGTH 32+1
|
||||
|
||||
/* String length of Endpoint Identifiers.
|
||||
/ (see also RFC3435 section 3.2.1.3) */
|
||||
#define MGCP_ENDPOINT_MAXLEN (255*2+1+1)
|
||||
|
||||
/* A prefix to denote the virtual trunk (RTP on both ends) */
|
||||
#define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/"
|
||||
|
||||
/* Maximal number of payload types / codecs that can be negotiated via SDP at
|
||||
* at once. */
|
||||
#define MGCP_MAX_CODECS 10
|
||||
|
||||
#endif
|
||||
|
99
include/osmocom/mgcp/mgcp_endp.h
Normal file
99
include/osmocom/mgcp/mgcp_endp.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/* Endpoint types */
|
||||
|
||||
/*
|
||||
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct sockaddr_in;
|
||||
struct mgcp_conn;
|
||||
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);
|
||||
|
||||
/* Callback type for endpoint specific cleanup actions. This function
|
||||
* is automatically executed when a connection is freed (see mgcp_conn_free()
|
||||
* in mgcp_conn.c). Depending on the type of the endpoint there may be endpoint
|
||||
* specific things to take care of once a connection has been removed. */
|
||||
typedef void (*mgcp_cleanup_cp) (struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn *conn);
|
||||
|
||||
/*! MGCP endpoint properties */
|
||||
struct mgcp_endpoint_type {
|
||||
/*!< maximum number of connections */
|
||||
int max_conns;
|
||||
|
||||
/*!< callback that defines how to dispatch incoming RTP data */
|
||||
mgcp_dispatch_rtp_cb dispatch_rtp_cb;
|
||||
|
||||
/*!< callback that implements endpoint specific cleanup actions */
|
||||
mgcp_cleanup_cp cleanup_cb;
|
||||
};
|
||||
|
||||
/*! MGCP endpoint typeset */
|
||||
struct mgcp_endpoint_typeset {
|
||||
struct mgcp_endpoint_type rtp;
|
||||
};
|
||||
|
||||
/*! static MGCP endpoint typeset (pre-initalized, read-only) */
|
||||
extern const struct mgcp_endpoint_typeset ep_typeset;
|
||||
|
||||
/*! MGCP endpoint model */
|
||||
struct mgcp_endpoint {
|
||||
|
||||
/*!< Call identifier string (as supplied by the call agant) */
|
||||
char *callid;
|
||||
|
||||
/*!< Local connection options (see mgcp_internal.h) */
|
||||
struct mgcp_lco local_options;
|
||||
|
||||
/*!< List with connections active on this endpoint */
|
||||
struct llist_head conns;
|
||||
|
||||
/*!< Backpointer to the MGW configuration */
|
||||
struct mgcp_config *cfg;
|
||||
|
||||
/*!< Backpointer to the Trunk specific configuration */
|
||||
struct mgcp_trunk_config *tcfg;
|
||||
|
||||
/*!< Endpoint properties (see above) */
|
||||
const struct mgcp_endpoint_type *type;
|
||||
|
||||
/*!< Last MGCP transmission (in case re-transmission is required) */
|
||||
char *last_trans;
|
||||
|
||||
/*!< Last MGCP response (in case re-transmission is required) */
|
||||
char *last_response;
|
||||
|
||||
/*!< Memorize if this endpoint was choosen by the MGW (wildcarded, true)
|
||||
* or if the user has choosen the particular endpoint explicitly. */
|
||||
bool wildcarded_req;
|
||||
};
|
||||
|
||||
/*! Extract endpoint number for a given endpoint */
|
||||
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
|
||||
|
||||
void mgcp_endp_release(struct mgcp_endpoint *endp);
|
||||
|
@@ -1,50 +0,0 @@
|
||||
/* Endpoint types */
|
||||
|
||||
/*
|
||||
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct sockaddr_in;
|
||||
struct mgcp_conn;
|
||||
|
||||
/* 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);
|
||||
|
||||
/*! MGCP endpoint properties */
|
||||
struct mgcp_endpoint_type {
|
||||
/*!< maximum number of connections */
|
||||
int max_conns;
|
||||
|
||||
/*!< callback that defines how to dispatch incoming RTP data */
|
||||
mgcp_dispatch_rtp_cb dispatch_rtp_cb;
|
||||
};
|
||||
|
||||
/*! MGCP endpoint typeset */
|
||||
struct mgcp_endpoint_typeset {
|
||||
struct mgcp_endpoint_type rtp;
|
||||
};
|
||||
|
||||
/*! static MGCP endpoint typeset (pre-initalized, read-only) */
|
||||
extern const struct mgcp_endpoint_typeset ep_typeset;
|
@@ -27,6 +27,8 @@
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/counter.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
|
||||
#define CI_UNUSED 0
|
||||
|
||||
@@ -44,33 +46,43 @@ struct mgcp_rtp_stream_state {
|
||||
uint32_t ssrc;
|
||||
uint16_t last_seq;
|
||||
uint32_t last_timestamp;
|
||||
uint32_t err_ts_counter;
|
||||
struct rate_ctr *err_ts_ctr;
|
||||
int32_t last_tsdelta;
|
||||
uint32_t last_arrival_time;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_state {
|
||||
/* has this state structure been initialized? */
|
||||
int initialized;
|
||||
int patch_ssrc;
|
||||
|
||||
uint32_t orig_ssrc;
|
||||
struct {
|
||||
/* are we patching the SSRC value? */
|
||||
int patch_ssrc;
|
||||
/* original SSRC (to which we shall patch any different SSRC) */
|
||||
uint32_t orig_ssrc;
|
||||
/* offset to apply on the sequence number */
|
||||
int seq_offset;
|
||||
/* offset to apply on the timestamp number */
|
||||
int32_t timestamp_offset;
|
||||
} patch;
|
||||
|
||||
int seq_offset;
|
||||
|
||||
int32_t timestamp_offset;
|
||||
/* duration of a packet (FIXME: in which unit?) */
|
||||
uint32_t packet_duration;
|
||||
|
||||
struct mgcp_rtp_stream_state in_stream;
|
||||
struct mgcp_rtp_stream_state out_stream;
|
||||
|
||||
/* jitter and packet loss calculation */
|
||||
int stats_initialized;
|
||||
uint16_t stats_base_seq;
|
||||
uint16_t stats_max_seq;
|
||||
uint32_t stats_ssrc;
|
||||
uint32_t stats_jitter;
|
||||
int32_t stats_transit;
|
||||
int stats_cycles;
|
||||
struct {
|
||||
int initialized;
|
||||
uint16_t base_seq;
|
||||
uint16_t max_seq;
|
||||
uint32_t ssrc;
|
||||
uint32_t jitter;
|
||||
int32_t transit;
|
||||
int cycles;
|
||||
} stats;
|
||||
|
||||
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
|
||||
};
|
||||
|
||||
@@ -85,43 +97,61 @@ struct mgcp_rtp_codec {
|
||||
char *subtype_name;
|
||||
};
|
||||
|
||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
|
||||
struct mgcp_rtp_end {
|
||||
/* statistics */
|
||||
unsigned int packets_rx;
|
||||
unsigned int octets_rx;
|
||||
unsigned int packets_tx;
|
||||
unsigned int octets_tx;
|
||||
unsigned int dropped_packets;
|
||||
struct {
|
||||
unsigned int packets_rx;
|
||||
unsigned int octets_rx;
|
||||
unsigned int packets_tx;
|
||||
unsigned int octets_tx;
|
||||
unsigned int dropped_packets;
|
||||
} stats;
|
||||
|
||||
/* local IP address of the RTP socket */
|
||||
struct in_addr addr;
|
||||
|
||||
/* in network byte order */
|
||||
int rtp_port, rtcp_port;
|
||||
|
||||
/* audio codec information */
|
||||
struct mgcp_rtp_codec codec;
|
||||
struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */
|
||||
/* currently selected audio codec */
|
||||
struct mgcp_rtp_codec *codec;
|
||||
|
||||
/* array with assigned audio codecs to choose from (SDP) */
|
||||
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
|
||||
|
||||
/* number of assigned audio codecs (SDP) */
|
||||
unsigned int codecs_assigned;
|
||||
|
||||
/* per endpoint data */
|
||||
int frames_per_packet;
|
||||
uint32_t packet_duration_ms;
|
||||
int maximum_packet_time; /* -1: not set */
|
||||
char *fmtp_extra;
|
||||
/* are we transmitting packets (1) or dropping (0) outbound packets */
|
||||
int output_enabled;
|
||||
/* FIXME: This parameter can be set + printed, but is nowhere used! */
|
||||
int force_output_ptime;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
|
||||
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
|
||||
int force_aligned_timing;
|
||||
/* FIXME: not used anymore, used to be [external] transcoding related */
|
||||
void *rtp_process_data;
|
||||
|
||||
/* Each end has a separate socket for RTP and RTCP */
|
||||
struct osmo_fd rtp;
|
||||
struct osmo_fd rtcp;
|
||||
|
||||
/* local UDP port number of the RTP socket; RTCP is +1 */
|
||||
int local_port;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_tap {
|
||||
/* is this tap active (1) or not (0) */
|
||||
int enabled;
|
||||
/* IP/port to which we're forwarding the tapped data */
|
||||
struct sockaddr_in forward;
|
||||
};
|
||||
|
||||
@@ -157,7 +187,7 @@ struct mgcp_conn_rtp {
|
||||
/* Sequence bits */
|
||||
struct mgcp_rtp_state state;
|
||||
|
||||
/* taps for the rtp connection */
|
||||
/* taps for the rtp connection; one per direction */
|
||||
struct mgcp_rtp_tap tap_in;
|
||||
struct mgcp_rtp_tap tap_out;
|
||||
|
||||
@@ -179,6 +209,8 @@ struct mgcp_conn_rtp {
|
||||
uint32_t octets;
|
||||
} stats;
|
||||
} osmux;
|
||||
|
||||
struct rate_ctr_group *rate_ctr_group;
|
||||
};
|
||||
|
||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
||||
@@ -204,7 +236,7 @@ struct mgcp_conn {
|
||||
/*!< copy of the mode to restore the original setting (VTY) */
|
||||
enum mgcp_connection_mode mode_orig;
|
||||
|
||||
/*!< connection id to identify the conntion */
|
||||
/*!< connection id to identify the connection */
|
||||
char id[MGCP_CONN_ID_LENGTH];
|
||||
|
||||
/*!< human readable name (vty, logging) */
|
||||
@@ -223,25 +255,9 @@ struct mgcp_conn {
|
||||
|
||||
struct mgcp_endpoint_type;
|
||||
|
||||
struct mgcp_endpoint {
|
||||
char *callid;
|
||||
struct mgcp_lco local_options;
|
||||
|
||||
struct llist_head conns;
|
||||
|
||||
/* backpointer */
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_trunk_config *tcfg;
|
||||
|
||||
const struct mgcp_endpoint_type *type;
|
||||
|
||||
/* fields for re-transmission */
|
||||
char *last_trans;
|
||||
char *last_response;
|
||||
};
|
||||
|
||||
|
||||
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
|
||||
|
||||
|
||||
/**
|
||||
* Internal structure while parsing a request
|
||||
@@ -251,7 +267,6 @@ struct mgcp_parse_data {
|
||||
struct mgcp_endpoint *endp;
|
||||
char *trans;
|
||||
char *save;
|
||||
int found;
|
||||
};
|
||||
|
||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
@@ -260,6 +275,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
|
||||
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
unsigned int buf_size, struct mgcp_conn *conn);
|
||||
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
|
||||
@@ -273,6 +289,8 @@ static inline int endp_back_channel(int endpoint)
|
||||
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
|
||||
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
|
||||
|
||||
char *get_lco_identifier(const char *options);
|
||||
int check_local_cx_options(void *ctx, const char *options);
|
||||
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
|
||||
struct mgcp_rtp_end *rtp);
|
||||
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
|
||||
|
@@ -21,15 +21,11 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
|
||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn,
|
||||
struct mgcp_parse_data *p);
|
||||
|
||||
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
int payload_type, const char *audio_name);
|
||||
|
||||
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
|
||||
const char *addr);
|
||||
|
@@ -1,31 +1,8 @@
|
||||
#ifndef OPENBSC_VTY_H
|
||||
#define OPENBSC_VTY_H
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/buffer.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
struct gsm_network;
|
||||
struct vty;
|
||||
|
||||
void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *);
|
||||
|
||||
struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base);
|
||||
|
||||
extern struct cmd_element cfg_description_cmd;
|
||||
extern struct cmd_element cfg_no_description_cmd;
|
||||
|
||||
enum mgcp_vty_node {
|
||||
MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
TRUNK_NODE,
|
||||
};
|
||||
|
||||
struct log_info;
|
||||
int bsc_vty_init(struct gsm_network *network);
|
||||
int bsc_vty_init_extra(void);
|
||||
|
||||
void msc_vty_init(struct gsm_network *msc_network);
|
||||
|
||||
struct gsm_network *gsmnet_from_vty(struct vty *vty);
|
||||
|
||||
#endif
|
||||
|
@@ -26,11 +26,39 @@ struct mgcp_client_conf {
|
||||
|
||||
typedef unsigned int mgcp_trans_id_t;
|
||||
|
||||
/*! Enumeration of the codec types that mgcp_client is able to handle. */
|
||||
enum mgcp_codecs {
|
||||
CODEC_PCMU_8000_1 = 0,
|
||||
CODEC_GSM_8000_1 = 3,
|
||||
CODEC_PCMA_8000_1 = 8,
|
||||
CODEC_G729_8000_1 = 18,
|
||||
CODEC_GSMEFR_8000_1 = 110,
|
||||
CODEC_GSMHR_8000_1 = 111,
|
||||
CODEC_AMR_8000_1 = 112,
|
||||
CODEC_AMRWB_16000_1 = 113,
|
||||
};
|
||||
/* Note: when new codec types are added, the corresponding value strings
|
||||
* in mgcp_client.c (codec_table) must be updated as well. Enumerations
|
||||
* in enum mgcp_codecs must correspond to a valid payload type. However,
|
||||
* this is an internal assumption that is made to avoid lookup tables.
|
||||
* The API-User should not rely on this coincidence! */
|
||||
|
||||
/*! Structure to build a payload type map to allow the defiition custom payload
|
||||
* types. */
|
||||
struct ptmap {
|
||||
/*!< codec for which a payload type number should be defined */
|
||||
enum mgcp_codecs codec;
|
||||
|
||||
/*!< payload type number (96-127) */
|
||||
unsigned int pt;
|
||||
};
|
||||
|
||||
struct mgcp_response_head {
|
||||
int response_code;
|
||||
mgcp_trans_id_t trans_id;
|
||||
const char *comment;
|
||||
char comment[MGCP_COMMENT_MAXLEN];
|
||||
char conn_id[MGCP_CONN_ID_LENGTH];
|
||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
};
|
||||
|
||||
struct mgcp_response {
|
||||
@@ -38,6 +66,11 @@ struct mgcp_response {
|
||||
struct mgcp_response_head head;
|
||||
uint16_t audio_port;
|
||||
char audio_ip[INET_ADDRSTRLEN];
|
||||
unsigned int ptime;
|
||||
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_len;
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
};
|
||||
|
||||
enum mgcp_verb {
|
||||
@@ -55,9 +88,6 @@ enum mgcp_verb {
|
||||
#define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010
|
||||
#define MGCP_MSG_PRESENCE_CONN_MODE 0x0020
|
||||
|
||||
/* See also RFC3435 section 3.2.1.3 */
|
||||
#define MGCP_ENDPOINT_MAXLEN (255*2+1+1)
|
||||
|
||||
struct mgcp_msg {
|
||||
enum mgcp_verb verb;
|
||||
/* See MGCP_MSG_PRESENCE_* constants */
|
||||
@@ -68,6 +98,11 @@ struct mgcp_msg {
|
||||
uint16_t audio_port;
|
||||
char *audio_ip;
|
||||
enum mgcp_connection_mode conn_mode;
|
||||
unsigned int ptime;
|
||||
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_len;
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
};
|
||||
|
||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
|
||||
@@ -119,3 +154,9 @@ static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
|
||||
{
|
||||
return get_value_string(mgcp_client_connection_mode_strs, mode);
|
||||
}
|
||||
|
||||
enum mgcp_codecs map_str_to_codec(const char *str);
|
||||
unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
enum mgcp_codecs codec);
|
||||
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
unsigned int pt);
|
||||
|
44
include/osmocom/mgcp_client/mgcp_client_fsm.h
Normal file
44
include/osmocom/mgcp_client/mgcp_client_fsm.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_common.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
|
||||
/*! This struct organizes the connection infromation one connection side
|
||||
* (either remote or local). It is used to pass parameters (local) to the FSM
|
||||
* and get responses (remote) from the FSM as pointer attached to the FSM
|
||||
* event.
|
||||
*
|
||||
* When modifiying a connection, the endpoint and call_id members may be left
|
||||
* unpopulated. The call_id field is ignored in this case. If an endpoint
|
||||
* identifier is supplied it is checked against the internal state to make
|
||||
* sure it is correct. */
|
||||
struct mgcp_conn_peer {
|
||||
/*!< RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
|
||||
char addr[INET_ADDRSTRLEN];
|
||||
|
||||
/*!< RTP connection IP-Port (optional) */
|
||||
uint16_t port;
|
||||
|
||||
/*!< RTP endpoint */
|
||||
char endpoint[MGCP_ENDPOINT_MAXLEN];
|
||||
|
||||
/*!< CALL ID (unique per connection) */
|
||||
unsigned int call_id;
|
||||
|
||||
/*!< RTP packetization interval (optional) */
|
||||
unsigned int ptime;
|
||||
|
||||
/*!< RTP codec list (optional) */
|
||||
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
|
||||
|
||||
/*!< Number of codecs in RTP codec list (optional) */
|
||||
unsigned int codecs_len;
|
||||
};
|
||||
|
||||
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
|
||||
uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
|
||||
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
|
||||
void mgcp_conn_delete(struct osmo_fsm_inst *fi);
|
||||
|
||||
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
|
@@ -1,10 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: Osmocom Media Gateway Control Protocol library
|
||||
Description: C Utility Library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -losmo-mgcp
|
||||
Cflags: -I${includedir}/
|
@@ -24,7 +24,7 @@ AM_LDFLAGS = \
|
||||
|
||||
# This is not at all related to the release version, but a range of supported
|
||||
# API versions. Read TODO_RELEASE in the source tree's root!
|
||||
LEGACY_MGCP_LIBVERSION=0:0:0
|
||||
LEGACY_MGCP_LIBVERSION=0:1:0
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-legacy-mgcp.la \
|
||||
|
@@ -584,6 +584,36 @@ static int mgcp_send_transcoder(struct mgcp_rtp_end *end,
|
||||
return rc;
|
||||
}
|
||||
|
||||
void mgcp_dejitter_udp_send(struct msgb *msg, void *data)
|
||||
{
|
||||
struct mgcp_rtp_end *rtp_end = (struct mgcp_rtp_end *) data;
|
||||
|
||||
int rc = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr,
|
||||
rtp_end->rtp_port, (char*) msg->data, msg->len);
|
||||
if (rc != msg->len)
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to send data after jitter buffer: %d\n", rc);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static int enqueue_dejitter(struct osmo_jibuf *jb, struct mgcp_rtp_end *rtp_end, char *buf, int len)
|
||||
{
|
||||
struct msgb *msg;
|
||||
msg = msgb_alloc(len, "mgcp-jibuf");
|
||||
if (!msg)
|
||||
return -1;
|
||||
|
||||
memcpy(msg->data, buf, len);
|
||||
msgb_put(msg, len);
|
||||
|
||||
if (osmo_jibuf_enqueue(jb, msg) < 0) {
|
||||
rtp_end->dropped_packets += 1;
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
|
||||
struct sockaddr_in *addr, char *buf, int rc)
|
||||
{
|
||||
@@ -591,6 +621,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
|
||||
struct mgcp_rtp_end *rtp_end;
|
||||
struct mgcp_rtp_state *rtp_state;
|
||||
int tap_idx;
|
||||
struct osmo_jibuf *jb;
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint %x dest %s tcfg->audio_loop %d endp->conn_mode %d (== loopback: %d)\n",
|
||||
@@ -612,10 +643,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
|
||||
rtp_end = &endp->net_end;
|
||||
rtp_state = &endp->bts_state;
|
||||
tap_idx = MGCP_TAP_NET_OUT;
|
||||
jb = endp->bts_jb;
|
||||
} else {
|
||||
rtp_end = &endp->bts_end;
|
||||
rtp_state = &endp->net_state;
|
||||
tap_idx = MGCP_TAP_BTS_OUT;
|
||||
jb = NULL;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint %x dest %s net_end %s %d %d bts_end %s %d %d rtp_end %s %d %d\n",
|
||||
@@ -680,9 +713,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
|
||||
rtp_state->patched_first_rtp_payload = true;
|
||||
}
|
||||
|
||||
rc = mgcp_udp_send(rtp_end->rtp.fd,
|
||||
&rtp_end->addr,
|
||||
rtp_end->rtp_port, buf, len);
|
||||
if (jb)
|
||||
rc = enqueue_dejitter(jb, rtp_end, buf, len);
|
||||
else
|
||||
rc = mgcp_udp_send(rtp_end->rtp.fd,
|
||||
&rtp_end->addr,
|
||||
rtp_end->rtp_port, buf, len);
|
||||
|
||||
if (rc <= 0)
|
||||
return rc;
|
||||
|
@@ -267,7 +267,6 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmux_hdr *osmuxh;
|
||||
struct llist_head list;
|
||||
struct sockaddr_in addr;
|
||||
struct mgcp_config *cfg = ofd->data;
|
||||
uint32_t rem;
|
||||
@@ -297,8 +296,7 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
endp->osmux.stats.chunks++;
|
||||
rem = msg->len;
|
||||
|
||||
osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
|
||||
osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
|
||||
osmux_xfrm_output_sched(&endp->osmux.out, osmuxh);
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
@@ -359,7 +357,6 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmux_hdr *osmuxh;
|
||||
struct llist_head list;
|
||||
struct sockaddr_in addr;
|
||||
struct mgcp_config *cfg = ofd->data;
|
||||
uint32_t rem;
|
||||
@@ -389,8 +386,7 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
endp->osmux.stats.chunks++;
|
||||
rem = msg->len;
|
||||
|
||||
osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
|
||||
osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
|
||||
osmux_xfrm_output_sched(&endp->osmux.out, osmuxh);
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
@@ -470,9 +466,13 @@ int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint
|
||||
switch (endp->cfg->role) {
|
||||
case MGCP_BSC_NAT:
|
||||
endp->type = MGCP_OSMUX_BSC_NAT;
|
||||
osmux_xfrm_output_set_tx_cb(&endp->osmux.out,
|
||||
scheduled_tx_net_cb, endp);
|
||||
break;
|
||||
case MGCP_BSC:
|
||||
endp->type = MGCP_OSMUX_BSC;
|
||||
osmux_xfrm_output_set_tx_cb(&endp->osmux.out,
|
||||
scheduled_tx_bts_cb, endp);
|
||||
break;
|
||||
}
|
||||
endp->osmux.state = OSMUX_STATE_ENABLED;
|
||||
@@ -484,6 +484,11 @@ void osmux_disable_endpoint(struct mgcp_endpoint *endp)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n",
|
||||
ENDPOINT_NUMBER(endp), endp->osmux.cid);
|
||||
|
||||
/* We are closing, we don't need pending RTP packets to be transmitted */
|
||||
osmux_xfrm_output_set_tx_cb(&endp->osmux.out, NULL, NULL);
|
||||
osmux_xfrm_output_flush(&endp->osmux.out);
|
||||
|
||||
osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid);
|
||||
endp->osmux.state = OSMUX_STATE_DISABLED;
|
||||
endp->osmux.cid = -1;
|
||||
|
@@ -863,6 +863,11 @@ mgcp_header_done:
|
||||
goto error2;
|
||||
}
|
||||
|
||||
/* Apply Jiter buffer settings for this endpoint, they can be overriden by CRCX policy later */
|
||||
endp->bts_use_jibuf = endp->cfg->bts_use_jibuf;
|
||||
endp->bts_jitter_delay_min = endp->cfg->bts_jitter_delay_min;
|
||||
endp->bts_jitter_delay_max = endp->cfg->bts_jitter_delay_max;
|
||||
|
||||
endp->allocated = 1;
|
||||
|
||||
/* set up RTP media parameters */
|
||||
@@ -898,6 +903,13 @@ mgcp_header_done:
|
||||
case MGCP_POLICY_DEFER:
|
||||
/* stop processing */
|
||||
create_transcoder(endp);
|
||||
/* Set up jitter buffer if required after policy has updated jibuf endp values */
|
||||
if (endp->bts_use_jibuf) {
|
||||
endp->bts_jb = osmo_jibuf_alloc(tcfg->endpoints);
|
||||
osmo_jibuf_set_min_delay(endp->bts_jb, endp->bts_jitter_delay_min);
|
||||
osmo_jibuf_set_max_delay(endp->bts_jb, endp->bts_jitter_delay_max);
|
||||
osmo_jibuf_set_dequeue_cb(endp->bts_jb, mgcp_dejitter_udp_send, &endp->net_end);
|
||||
}
|
||||
return NULL;
|
||||
break;
|
||||
case MGCP_POLICY_CONT:
|
||||
@@ -906,6 +918,14 @@ mgcp_header_done:
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up jitter buffer if required after policy has updated jibuf endp values */
|
||||
if (endp->bts_use_jibuf) {
|
||||
endp->bts_jb = osmo_jibuf_alloc(tcfg->endpoints);
|
||||
osmo_jibuf_set_min_delay(endp->bts_jb, endp->bts_jitter_delay_min);
|
||||
osmo_jibuf_set_max_delay(endp->bts_jb, endp->bts_jitter_delay_max);
|
||||
osmo_jibuf_set_dequeue_cb(endp->bts_jb, mgcp_dejitter_udp_send, &endp->net_end);
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Creating endpoint on: 0x%x CI: %u port: %u/%u\n",
|
||||
ENDPOINT_NUMBER(endp), endp->ci,
|
||||
endp->net_end.local_port, endp->bts_end.local_port);
|
||||
@@ -1373,6 +1393,9 @@ int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
|
||||
void mgcp_release_endp(struct mgcp_endpoint *endp)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
|
||||
if (endp->bts_jb)
|
||||
osmo_jibuf_delete(endp->bts_jb);
|
||||
endp->bts_jb = NULL;
|
||||
endp->ci = CI_UNUSED;
|
||||
endp->allocated = 0;
|
||||
|
||||
@@ -1588,24 +1611,26 @@ void mgcp_format_stats(struct mgcp_endpoint *endp, char *msg, size_t size)
|
||||
msg += nchars;
|
||||
size -= nchars;
|
||||
|
||||
/* Error Counter */
|
||||
nchars = snprintf(msg, size,
|
||||
"\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u",
|
||||
endp->net_state.in_stream.err_ts_counter,
|
||||
endp->net_state.out_stream.err_ts_counter,
|
||||
endp->bts_state.in_stream.err_ts_counter,
|
||||
endp->bts_state.out_stream.err_ts_counter);
|
||||
if (nchars < 0 || nchars >= size)
|
||||
goto truncate;
|
||||
if (endp->cfg->osmux != OSMUX_USAGE_OFF) {
|
||||
/* Error Counter */
|
||||
nchars = snprintf(msg, size,
|
||||
"\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u",
|
||||
endp->net_state.in_stream.err_ts_counter,
|
||||
endp->net_state.out_stream.err_ts_counter,
|
||||
endp->bts_state.in_stream.err_ts_counter,
|
||||
endp->bts_state.out_stream.err_ts_counter);
|
||||
if (nchars < 0 || nchars >= size)
|
||||
goto truncate;
|
||||
|
||||
msg += nchars;
|
||||
size -= nchars;
|
||||
msg += nchars;
|
||||
size -= nchars;
|
||||
|
||||
if (endp->osmux.state == OSMUX_STATE_ENABLED) {
|
||||
snprintf(msg, size,
|
||||
"\r\nX-Osmux-ST: CR=%u, BR=%u",
|
||||
endp->osmux.stats.chunks,
|
||||
endp->osmux.stats.octets);
|
||||
if (endp->osmux.state == OSMUX_STATE_ENABLED) {
|
||||
snprintf(msg, size,
|
||||
"\r\nX-Osmux-ST: CR=%u, BR=%u",
|
||||
endp->osmux.stats.chunks,
|
||||
endp->osmux.stats.octets);
|
||||
}
|
||||
}
|
||||
truncate:
|
||||
msg[size - 1] = '\0';
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include <osmocom/legacy_mgcp/vty.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
|
||||
#define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
|
||||
@@ -164,6 +165,13 @@ static int config_write_mgcp(struct vty *vty)
|
||||
vty_out(vty, " osmux dummy %s%s",
|
||||
g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
|
||||
}
|
||||
if (g_cfg->bts_use_jibuf)
|
||||
vty_out(vty, " bts-jitter-buffer%s", VTY_NEWLINE);
|
||||
if (g_cfg->bts_jitter_delay_min)
|
||||
vty_out(vty, " bts-jitter-delay-min %"PRIu32"%s", g_cfg->bts_jitter_delay_min, VTY_NEWLINE);
|
||||
if (g_cfg->bts_jitter_delay_max)
|
||||
vty_out(vty, " bts-jitter-delay-max %"PRIu32"%s", g_cfg->bts_jitter_delay_max, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -241,6 +249,11 @@ DEFUN(show_mcgp, show_mgcp_cmd,
|
||||
|
||||
if (g_cfg->osmux)
|
||||
vty_out(vty, "Osmux used CID: %d%s", osmux_used_cid(), VTY_NEWLINE);
|
||||
vty_out(vty, "Jitter Buffer by default on Uplink : %s%s",
|
||||
g_cfg->bts_use_jibuf ? "on" : "off", VTY_NEWLINE);
|
||||
if (g_cfg->bts_use_jibuf)
|
||||
vty_out(vty, "Jitter Buffer delays: min=%"PRIu32" max=%"PRIu32"%s",
|
||||
g_cfg->bts_jitter_delay_min, g_cfg->bts_jitter_delay_max, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -1344,6 +1357,63 @@ DEFUN(cfg_mgcp_osmux_dummy,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define DEJITTER_STR "Uplink Jitter Buffer"
|
||||
DEFUN(cfg_mgcp_bts_use_jibuf,
|
||||
cfg_mgcp_bts_use_jibuf_cmd,
|
||||
"bts-jitter-buffer",
|
||||
DEJITTER_STR "\n")
|
||||
{
|
||||
g_cfg->bts_use_jibuf = true;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_no_bts_use_jibuf,
|
||||
cfg_mgcp_no_bts_use_jibuf_cmd,
|
||||
"no bts-jitter-buffer",
|
||||
NO_STR DEJITTER_STR "\n")
|
||||
{
|
||||
g_cfg->bts_use_jibuf = false;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_bts_jitter_delay_min,
|
||||
cfg_mgcp_bts_jitter_delay_min_cmd,
|
||||
"bts-jitter-buffer-delay-min <1-65535>",
|
||||
DEJITTER_STR " Minimum Delay in ms\n" "Minimum Delay in ms\n")
|
||||
{
|
||||
g_cfg->bts_jitter_delay_min = atoi(argv[0]);
|
||||
if (!g_cfg->bts_jitter_delay_min) {
|
||||
vty_out(vty, "bts-jitter-buffer-delay-min cannot be zero.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
if (g_cfg->bts_jitter_delay_min && g_cfg->bts_jitter_delay_max &&
|
||||
g_cfg->bts_jitter_delay_min > g_cfg->bts_jitter_delay_max) {
|
||||
vty_out(vty, "bts-jitter-buffer-delay-min cannot be bigger than " \
|
||||
"bts-jitter-buffer-delay-max.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_bts_jitter_delay_max,
|
||||
cfg_mgcp_bts_jitter_delay_max_cmd,
|
||||
"bts-jitter-buffer-delay-max <1-65535>",
|
||||
DEJITTER_STR " Maximum Delay in ms\n" "Maximum Delay in ms\n")
|
||||
{
|
||||
g_cfg->bts_jitter_delay_max = atoi(argv[0]);
|
||||
if (!g_cfg->bts_jitter_delay_max) {
|
||||
vty_out(vty, "bts-jitter-buffer-delay-max cannot be zero.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
if (g_cfg->bts_jitter_delay_min && g_cfg->bts_jitter_delay_max &&
|
||||
g_cfg->bts_jitter_delay_min > g_cfg->bts_jitter_delay_max) {
|
||||
vty_out(vty, "bts-jitter-buffer-delay-max cannot be smaller than " \
|
||||
"bts-jitter-buffer-delay-min.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int mgcp_vty_init(void)
|
||||
{
|
||||
install_element_ve(&show_mgcp_cmd);
|
||||
@@ -1411,6 +1481,10 @@ int mgcp_vty_init(void)
|
||||
install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_bts_use_jibuf_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_no_bts_use_jibuf_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_bts_jitter_delay_min_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_bts_jitter_delay_max_cmd);
|
||||
|
||||
|
||||
install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
|
||||
|
@@ -20,7 +20,7 @@ AM_LDFLAGS = \
|
||||
|
||||
# This is not at all related to the release version, but a range of supported
|
||||
# API versions. Read TODO_RELEASE in the source tree's root!
|
||||
MGCP_CLIENT_LIBVERSION=2:0:0
|
||||
MGCP_CLIENT_LIBVERSION=3:0:0
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp-client.la \
|
||||
@@ -29,6 +29,7 @@ lib_LTLIBRARIES = \
|
||||
libosmo_mgcp_client_la_SOURCES = \
|
||||
mgcp_client.c \
|
||||
mgcp_client_vty.c \
|
||||
mgcp_client_fsm.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)
|
||||
|
@@ -4,16 +4,16 @@
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/byteswap.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
|
||||
@@ -35,6 +36,152 @@
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Codec descripton for dynamic payload types (SDP) */
|
||||
static const struct value_string codec_table[] = {
|
||||
{ CODEC_PCMU_8000_1, "PCMU/8000/1" },
|
||||
{ CODEC_GSM_8000_1, "GSM/8000/1" },
|
||||
{ CODEC_PCMA_8000_1, "PCMA/8000/1" },
|
||||
{ CODEC_G729_8000_1, "G729/8000/1" },
|
||||
{ CODEC_GSMEFR_8000_1, "GSM-EFR/8000/1" },
|
||||
{ CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
|
||||
{ CODEC_AMR_8000_1, "AMR/8000/1" },
|
||||
{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
/* Get encoding name from a full codec string e,g.
|
||||
* ("CODEC/8000/2" => returns "CODEC") */
|
||||
static char *extract_codec_name(const char *str)
|
||||
{
|
||||
static char buf[64];
|
||||
unsigned int i;
|
||||
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
/* FIXME osmo_strlcpy */
|
||||
osmo_strlcpy(buf, str, sizeof(buf));
|
||||
|
||||
for (i = 0; i < strlen(buf); i++) {
|
||||
if (buf[i] == '/')
|
||||
buf[i] = '\0';
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*! Map a string to a codec.
|
||||
* \ptmap[in] str input string (e.g "GSM/8000/1", "GSM/8000" or "GSM")
|
||||
* \returns codec that corresponds to the given string representation. */
|
||||
enum mgcp_codecs map_str_to_codec(const char *str)
|
||||
{
|
||||
unsigned int i;
|
||||
char *codec_name;
|
||||
char str_buf[64];
|
||||
|
||||
osmo_strlcpy(str_buf, extract_codec_name(str), sizeof(str_buf));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(codec_table); i++) {
|
||||
codec_name = extract_codec_name(codec_table[i].str);
|
||||
if (!codec_name)
|
||||
continue;
|
||||
if (strcmp(codec_name, str_buf) == 0)
|
||||
return codec_table[i].value;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check the ptmap for illegal mappings */
|
||||
static int check_ptmap(struct ptmap *ptmap)
|
||||
{
|
||||
/* Check if there are mappings that leave the IANA assigned dynamic
|
||||
* payload type range. Under normal conditions such mappings should
|
||||
* not occur */
|
||||
|
||||
/* Its ok to have a 1:1 mapping in the statically defined
|
||||
* range, this won't hurt */
|
||||
if (ptmap->codec == ptmap->pt)
|
||||
return 0;
|
||||
|
||||
if (ptmap->codec < 96 || ptmap->codec > 127)
|
||||
goto error;
|
||||
if (ptmap->pt < 96 || ptmap->pt > 127)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"ptmap contains illegal mapping: codec=%u maps to pt=%u\n",
|
||||
ptmap->codec, ptmap->pt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! Map a codec to a payload type.
|
||||
* \ptmap[in] payload pointer to payload type map with specified payload types.
|
||||
* \ptmap[in] ptmap_len length of the payload type map.
|
||||
* \ptmap[in] codec the codec for which the payload type should be looked up.
|
||||
* \returns assigned payload type */
|
||||
unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
enum mgcp_codecs codec)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/*! Note: If the payload type map is empty or the codec is not found
|
||||
* in the map, then a 1:1 mapping is performed. If the codec falls
|
||||
* into the statically defined range or if the mapping table isself
|
||||
* tries to map to the statically defined range, then the mapping
|
||||
* is also ignored and a 1:1 mapping is performed instead. */
|
||||
|
||||
/* we may return the codec directly since enum mgcp_codecs directly
|
||||
* corresponds to the statićally assigned payload types */
|
||||
if (codec < 96 || codec > 127)
|
||||
return codec;
|
||||
|
||||
for (i = 0; i < ptmap_len; i++) {
|
||||
/* Skip illegal map entries */
|
||||
if (check_ptmap(ptmap) == 0 && ptmap->codec == codec)
|
||||
return ptmap->pt;
|
||||
ptmap++;
|
||||
}
|
||||
|
||||
/* If nothing is found, do not perform any mapping */
|
||||
return codec;
|
||||
}
|
||||
|
||||
/*! Map a payload type to a codec.
|
||||
* \ptmap[in] payload pointer to payload type map with specified payload types.
|
||||
* \ptmap[in] ptmap_len length of the payload type map.
|
||||
* \ptmap[in] payload type for which the codec should be looked up.
|
||||
* \returns codec that corresponds to the specified payload type */
|
||||
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
unsigned int pt)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/*! Note: If the payload type map is empty or the payload type is not
|
||||
* found in the map, then a 1:1 mapping is performed. If the payload
|
||||
* type falls into the statically defined range or if the mapping
|
||||
* table isself tries to map to the statically defined range, then
|
||||
* the mapping is also ignored and a 1:1 mapping is performed
|
||||
* instead. */
|
||||
|
||||
/* See also note in map_codec_to_pt() */
|
||||
if (pt < 96 || pt > 127)
|
||||
return pt;
|
||||
|
||||
for (i = 0; i < ptmap_len; i++) {
|
||||
if (check_ptmap(ptmap) == 0 && ptmap->pt == pt)
|
||||
return ptmap->codec;
|
||||
ptmap++;
|
||||
}
|
||||
|
||||
/* If nothing is found, do not perform any mapping */
|
||||
return pt;
|
||||
}
|
||||
|
||||
/*! Initalize MGCP client configuration struct with default values.
|
||||
* \param[out] conf Client configuration.*/
|
||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf)
|
||||
{
|
||||
/* NULL and -1 default to MGCP_CLIENT_*_DEFAULT values */
|
||||
@@ -61,7 +208,9 @@ static bool endpoint_in_use(uint16_t id, struct mgcp_client *client)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Find and seize an unsused endpoint id */
|
||||
/*! 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;
|
||||
@@ -94,6 +243,9 @@ int mgcp_client_next_endpoint(struct mgcp_client *client)
|
||||
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)
|
||||
{
|
||||
@@ -138,15 +290,12 @@ static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg)
|
||||
&comment_pos) != 2)
|
||||
goto response_parse_failure;
|
||||
|
||||
r->head.comment = r->body + comment_pos;
|
||||
osmo_strlcpy(r->head.comment, r->body + comment_pos, sizeof(r->head.comment));
|
||||
end = strchr(r->head.comment, '\r');
|
||||
if (!end)
|
||||
goto response_parse_failure;
|
||||
/* Mark the end of the comment */
|
||||
*end = '\0';
|
||||
r->body = end + 1;
|
||||
if (r->body[0] == '\n')
|
||||
r->body ++;
|
||||
return 0;
|
||||
|
||||
response_parse_failure:
|
||||
@@ -173,19 +322,114 @@ static bool mgcp_line_is_valid(const char *line)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Parse a line like "m=audio 16002 RTP/AVP 98" */
|
||||
static int mgcp_parse_audio_port(struct mgcp_response *r, const char *line)
|
||||
/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
|
||||
static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
|
||||
{
|
||||
if (sscanf(line, "m=audio %hu",
|
||||
&r->audio_port) != 1)
|
||||
goto response_parse_failure;
|
||||
char *pt_str;
|
||||
unsigned int pt;
|
||||
unsigned int count = 0;
|
||||
unsigned int i;
|
||||
|
||||
/* Extract port information */
|
||||
if (sscanf(line, "m=audio %hu", &r->audio_port) != 1)
|
||||
goto response_parse_failure_port;
|
||||
if (r->audio_port == 0)
|
||||
goto response_parse_failure_port;
|
||||
|
||||
/* Extract payload types */
|
||||
line = strstr(line, "RTP/AVP ");
|
||||
if (!line)
|
||||
goto exit;
|
||||
|
||||
pt_str = strtok(line, " ");
|
||||
while (1) {
|
||||
/* Do not allow excessive payload types */
|
||||
if (count > ARRAY_SIZE(r->codecs))
|
||||
goto response_parse_failure_pt;
|
||||
|
||||
pt_str = strtok(NULL, " ");
|
||||
if (!pt_str)
|
||||
break;
|
||||
pt = atoi(pt_str);
|
||||
|
||||
/* Do not allow duplicate payload types */
|
||||
for (i = 0; i < count; i++)
|
||||
if (r->codecs[i] == pt)
|
||||
goto response_parse_failure_pt;
|
||||
|
||||
/* Note: The payload type we store may not necessarly match
|
||||
* the codec types we have defined in enum mgcp_codecs. To
|
||||
* ensure that the end result only contains codec types which
|
||||
* match enum mgcp_codecs, we will go through afterwards and
|
||||
* remap the affected entries with the inrofmation we learn
|
||||
* from rtpmap */
|
||||
r->codecs[count] = pt;
|
||||
count++;
|
||||
}
|
||||
|
||||
r->codecs_len = count;
|
||||
|
||||
exit:
|
||||
return 0;
|
||||
|
||||
response_parse_failure:
|
||||
response_parse_failure_port:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse MGCP response header (audio port)\n");
|
||||
"Failed to parse SDP parameter port (%s)\n", line);
|
||||
return -EINVAL;
|
||||
|
||||
response_parse_failure_pt:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse SDP parameter payload types (%s)\n", line);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
|
||||
static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *line)
|
||||
{
|
||||
unsigned int pt;
|
||||
char codec_resp[64];
|
||||
unsigned int codec;
|
||||
|
||||
|
||||
if (strstr(line, "ptime")) {
|
||||
if (sscanf(line, "a=ptime:%u", &r->ptime) != 1)
|
||||
goto response_parse_failure_ptime;
|
||||
} else if (strstr(line, "rtpmap")) {
|
||||
if (sscanf(line, "a=rtpmap:%d %63s", &pt, codec_resp) == 2) {
|
||||
/* The MGW may assign an own payload type in the
|
||||
* response if the choosen codec falls into the IANA
|
||||
* assigned dynamic payload type range (96-127).
|
||||
* Normally the MGW should obey the 3gpp payload type
|
||||
* assignments, which are fixed, so we likely wont see
|
||||
* anything unexpected here. In order to be sure that
|
||||
* we will now check the codec string and if the result
|
||||
* does not match to what is IANA / 3gpp assigned, we
|
||||
* will create an entry in the ptmap table so we can
|
||||
* lookup later what has been assigned. */
|
||||
codec = map_str_to_codec(codec_resp);
|
||||
if (codec != pt) {
|
||||
if (r->ptmap_len < ARRAY_SIZE(r->ptmap)) {
|
||||
r->ptmap[r->ptmap_len].pt = pt;
|
||||
r->ptmap[r->ptmap_len].codec = codec;
|
||||
r->ptmap_len++;
|
||||
} else
|
||||
goto response_parse_failure_rtpmap;
|
||||
}
|
||||
|
||||
} else
|
||||
goto response_parse_failure_rtpmap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
response_parse_failure_ptime:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse SDP parameter, invalid ptime (%s)\n", line);
|
||||
return -EINVAL;
|
||||
response_parse_failure_rtpmap:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse SDP parameter, invalid rtpmap (%s)\n", line);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Parse a line like "c=IN IP4 10.11.12.13" */
|
||||
@@ -215,63 +459,115 @@ response_parse_failure:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* A new section is marked by a double line break, check a few more
|
||||
* patterns as there may be variants */
|
||||
static char *mgcp_find_section_end(char *string)
|
||||
{
|
||||
char *rc;
|
||||
|
||||
rc = strstr(string, "\n\n");
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = strstr(string, "\n\r\n\r");
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = strstr(string, "\r\n\r\n");
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Parse body (SDP) parameters of the MGCP response
|
||||
* \param[in,out] r Response data
|
||||
* \returns 0 on success, -EINVAL on error. */
|
||||
int mgcp_response_parse_params(struct mgcp_response *r)
|
||||
{
|
||||
char *line;
|
||||
int rc;
|
||||
OSMO_ASSERT(r->body);
|
||||
char *data = strstr(r->body, "\n\n");
|
||||
char *data;
|
||||
char *data_ptr;
|
||||
int i;
|
||||
|
||||
if (!data) {
|
||||
/* Since this functions performs a destructive parsing, we create a
|
||||
* local copy of the body data */
|
||||
OSMO_ASSERT(r->body);
|
||||
data = talloc_strdup(r, r->body);
|
||||
OSMO_ASSERT(data);
|
||||
|
||||
/* Find beginning of the parameter (SDP) section */
|
||||
data_ptr = mgcp_find_section_end(data);
|
||||
if (!data_ptr) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MGCP response: cannot find start of parameters\n");
|
||||
return -EINVAL;
|
||||
"MGCP response: cannot find start of SDP parameters\n");
|
||||
rc = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Advance to after the \n\n, replace the second \n with \0. That's
|
||||
* where the parameters start. */
|
||||
data ++;
|
||||
*data = '\0';
|
||||
data ++;
|
||||
/* data_ptr now points to the beginning of the section-end-marker; for_each_non_empty_line()
|
||||
* skips any \r and \n characters for free, so we don't need to skip the marker. */
|
||||
|
||||
for_each_non_empty_line(line, data) {
|
||||
for_each_non_empty_line(line, data_ptr) {
|
||||
if (!mgcp_line_is_valid(line))
|
||||
return -EINVAL;
|
||||
|
||||
switch (line[0]) {
|
||||
case 'm':
|
||||
rc = mgcp_parse_audio_port(r, line);
|
||||
case 'a':
|
||||
rc = mgcp_parse_audio_ptime_rtpmap(r, line);
|
||||
if (rc)
|
||||
return rc;
|
||||
goto exit;
|
||||
break;
|
||||
case 'm':
|
||||
rc = mgcp_parse_audio_port_pt(r, line);
|
||||
if (rc)
|
||||
goto exit;
|
||||
break;
|
||||
case 'c':
|
||||
rc = mgcp_parse_audio_ip(r, line);
|
||||
if (rc)
|
||||
return rc;
|
||||
goto exit;
|
||||
break;
|
||||
default:
|
||||
/* skip unhandled parameters */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
/* See also note in mgcp_parse_audio_port_pt() */
|
||||
for (i = 0; i < r->codecs_len; i++)
|
||||
r->codecs[i] = map_pt_to_codec(r->ptmap, r->ptmap_len, r->codecs[i]);
|
||||
|
||||
rc = 0;
|
||||
exit:
|
||||
talloc_free(data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Parse a line like "I: 0cedfd5a19542d197af9afe5231f1d61" */
|
||||
static int mgcp_parse_conn_id(struct mgcp_response *r, const char *line)
|
||||
/* Parse a line like "X: something" */
|
||||
static int mgcp_parse_head_param(char *result, unsigned int result_len,
|
||||
char label, const char *line)
|
||||
{
|
||||
char label_string[4];
|
||||
|
||||
/* Detect empty parameters */
|
||||
if (strlen(line) < 4)
|
||||
goto response_parse_failure;
|
||||
|
||||
if (memcmp("I: ", line, 3) != 0)
|
||||
/* Check if the label matches */
|
||||
snprintf(label_string, sizeof(label_string), "%c: ", label);
|
||||
if (memcmp(label_string, line, 3) != 0)
|
||||
goto response_parse_failure;
|
||||
|
||||
osmo_strlcpy(r->head.conn_id, line + 3, sizeof(r->head.conn_id));
|
||||
/* Copy payload part of the string to destinations (the label string
|
||||
* is always 3 chars long) */
|
||||
osmo_strlcpy(result, line + 3, result_len);
|
||||
return 0;
|
||||
|
||||
response_parse_failure:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse MGCP response (connectionIdentifier)\n");
|
||||
"Failed to parse MGCP response (parameter label: %c)\n", label);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -281,18 +577,51 @@ static int parse_head_params(struct mgcp_response *r)
|
||||
char *line;
|
||||
int rc = 0;
|
||||
OSMO_ASSERT(r->body);
|
||||
char *data = r->body;
|
||||
char *data_end = strstr(r->body, "\n\n");
|
||||
char *data;
|
||||
char *data_ptr;
|
||||
char *data_end;
|
||||
|
||||
/* Protect SDP body, for_each_non_empty_line() will
|
||||
* only parse until it hits \0 mark. */
|
||||
/* Since this functions performs a destructive parsing, we create a
|
||||
* local copy of the body data */
|
||||
data = talloc_zero_size(r, strlen(r->body)+1);
|
||||
OSMO_ASSERT(data);
|
||||
data_ptr = data;
|
||||
osmo_strlcpy(data, r->body, strlen(r->body));
|
||||
|
||||
/* If there is an SDP body attached, prevent for_each_non_empty_line()
|
||||
* into running in there, we are not yet interested in the parameters
|
||||
* stored there. */
|
||||
data_end = mgcp_find_section_end(data);
|
||||
if (data_end)
|
||||
*data_end = '\0';
|
||||
|
||||
for_each_non_empty_line(line, data) {
|
||||
for_each_non_empty_line(line, data_ptr) {
|
||||
switch (line[0]) {
|
||||
case 'Z':
|
||||
rc = mgcp_parse_head_param(r->head.endpoint,
|
||||
sizeof(r->head.endpoint),
|
||||
'Z', line);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
/* A specific endpoint identifier returned by the MGW
|
||||
* must not contain any wildcard characters */
|
||||
if (strstr(r->head.endpoint, "*") != NULL) {
|
||||
rc = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* A specific endpoint identifier returned by the MGW
|
||||
* must contain an @ character */
|
||||
if (strstr(r->head.endpoint, "@") == NULL) {
|
||||
rc = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
break;
|
||||
case 'I':
|
||||
rc = mgcp_parse_conn_id(r, line);
|
||||
rc = mgcp_parse_head_param(r->head.conn_id,
|
||||
sizeof(r->head.conn_id),
|
||||
'I', line);
|
||||
if (rc)
|
||||
goto exit;
|
||||
break;
|
||||
@@ -302,10 +631,7 @@ static int parse_head_params(struct mgcp_response *r)
|
||||
}
|
||||
}
|
||||
exit:
|
||||
/* Restore original state */
|
||||
if (data_end)
|
||||
*data_end = '\n';
|
||||
|
||||
talloc_free(data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -331,32 +657,42 @@ static struct mgcp_response_pending *mgcp_client_response_pending_get(
|
||||
*/
|
||||
int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg)
|
||||
{
|
||||
struct mgcp_response r = { 0 };
|
||||
struct mgcp_response *r;
|
||||
struct mgcp_response_pending *pending;
|
||||
int rc;
|
||||
|
||||
rc = mgcp_response_parse_head(&r, msg);
|
||||
r = talloc_zero(mgcp, struct mgcp_response);
|
||||
OSMO_ASSERT(r);
|
||||
|
||||
rc = mgcp_response_parse_head(r, msg);
|
||||
if (rc) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head)\n");
|
||||
return -1;
|
||||
rc = 1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = parse_head_params(&r);
|
||||
rc = parse_head_params(r);
|
||||
if (rc) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head parameters)\n");
|
||||
return -1;
|
||||
rc = 1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
pending = mgcp_client_response_pending_get(mgcp, r.head.trans_id);
|
||||
pending = mgcp_client_response_pending_get(mgcp, r->head.trans_id);
|
||||
if (!pending) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Cannot find matching MGCP transaction for trans_id %d\n",
|
||||
r.head.trans_id);
|
||||
return -ENOENT;
|
||||
r->head.trans_id);
|
||||
rc = -ENOENT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
mgcp_client_handle_response(mgcp, pending, &r);
|
||||
return 0;
|
||||
mgcp_client_handle_response(mgcp, pending, r);
|
||||
rc = 0;
|
||||
|
||||
error:
|
||||
talloc_free(r);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int mgcp_do_read(struct osmo_fd *fd)
|
||||
@@ -443,9 +779,11 @@ struct mgcp_client *mgcp_client_init(void *ctx,
|
||||
return mgcp;
|
||||
}
|
||||
|
||||
/*! Initalize client connection (opens socket only, no request is sent yet)
|
||||
* \param[in,out] mgcp MGCP client descriptor.
|
||||
* \returns 0 on success, -EINVAL on error. */
|
||||
int mgcp_client_connect(struct mgcp_client *mgcp)
|
||||
{
|
||||
int on;
|
||||
struct sockaddr_in addr;
|
||||
struct osmo_wqueue *wq;
|
||||
int rc;
|
||||
@@ -457,46 +795,19 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
|
||||
|
||||
wq = &mgcp->wq;
|
||||
|
||||
wq->bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (wq->bfd.fd < 0) {
|
||||
LOGP(DLMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
on = 1;
|
||||
if (setsockopt(wq->bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
|
||||
rc = osmo_sock_init2_ofd(&wq->bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||
mgcp->actual.local_addr, mgcp->actual.local_port,
|
||||
mgcp->actual.remote_addr, mgcp->actual.remote_port,
|
||||
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
|
||||
if (rc < 0) {
|
||||
LOGP(DLMGCP, LOGL_FATAL,
|
||||
"Failed to initialize socket for MGCP GW: %s\n",
|
||||
strerror(errno));
|
||||
rc = -errno;
|
||||
"Failed to initialize socket %s:%u -> %s:%u for MGCP GW: %s\n",
|
||||
mgcp->actual.local_addr, mgcp->actual.local_port,
|
||||
mgcp->actual.remote_addr, mgcp->actual.remote_port, strerror(errno));
|
||||
goto error_close_fd;
|
||||
}
|
||||
|
||||
/* bind socket */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
inet_aton(mgcp->actual.local_addr, &addr.sin_addr);
|
||||
addr.sin_port = htons(mgcp->actual.local_port);
|
||||
if (bind(wq->bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
LOGP(DLMGCP, LOGL_FATAL,
|
||||
"Failed to bind for MGCP GW to %s %u\n",
|
||||
mgcp->actual.local_addr, mgcp->actual.local_port);
|
||||
rc = -errno;
|
||||
goto error_close_fd;
|
||||
}
|
||||
|
||||
/* connect to the remote */
|
||||
inet_aton(mgcp->actual.remote_addr, &addr.sin_addr);
|
||||
addr.sin_port = htons(mgcp->actual.remote_port);
|
||||
if (connect(wq->bfd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
LOGP(DLMGCP, LOGL_FATAL,
|
||||
"Failed to connect to MGCP GW at %s %u: %s\n",
|
||||
mgcp->actual.remote_addr, mgcp->actual.remote_port,
|
||||
strerror(errno));
|
||||
rc = -errno;
|
||||
goto error_close_fd;
|
||||
}
|
||||
|
||||
mgcp->remote_addr = htonl(addr.sin_addr.s_addr);
|
||||
|
||||
osmo_wqueue_init(wq, 10);
|
||||
@@ -505,11 +816,6 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
|
||||
wq->read_cb = mgcp_do_read;
|
||||
wq->write_cb = mgcp_do_write;
|
||||
|
||||
if (osmo_fd_register(&wq->bfd) != 0) {
|
||||
LOGP(DLMGCP, LOGL_FATAL, "Failed to register BFD\n");
|
||||
rc = -EIO;
|
||||
goto error_close_fd;
|
||||
}
|
||||
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);
|
||||
@@ -521,17 +827,25 @@ error_close_fd:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! Get the IP-Aaddress of the associated MGW as string.
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \returns a pointer to the address string. */
|
||||
const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp)
|
||||
{
|
||||
return mgcp->actual.remote_addr;
|
||||
}
|
||||
|
||||
/*! Get the IP-Port of the associated MGW.
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \returns port number. */
|
||||
uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp)
|
||||
{
|
||||
return mgcp->actual.remote_port;
|
||||
}
|
||||
|
||||
/* Return the MGCP GW binary IPv4 address in network byte order. */
|
||||
/*! Get the IP-Aaddress of the associated MGW as its numeric representation.
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \returns IP-Address as 32 bit integer (network byte order) */
|
||||
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
|
||||
{
|
||||
return mgcp->remote_addr;
|
||||
@@ -604,29 +918,32 @@ mgcp_tx_error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Cancel a pending transaction.
|
||||
/*! Cancel a pending transaction.
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \param[in,out] trans_id Transaction id.
|
||||
* \returns 0 on success, -ENOENT on error.
|
||||
*
|
||||
* Should a priv pointer passed to mgcp_client_tx() become invalid, this function must be called. In
|
||||
* practical terms, if the caller of mgcp_client_tx() wishes to tear down a transaction without having
|
||||
* received a response this function must be called. The trans_id can be obtained by calling
|
||||
* mgcp_msg_trans_id() on the msgb produced by mgcp_msg_gen().
|
||||
*/
|
||||
* mgcp_msg_trans_id() on the msgb produced by mgcp_msg_gen(). */
|
||||
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) {
|
||||
/* INFO is sufficient, it is not harmful to cancel a transaction twice. */
|
||||
/*! Note: it is not harmful to cancel a transaction twice. */
|
||||
LOGP(DLMGCP, LOGL_INFO, "Cannot cancel, no such transaction: %u\n", trans_id);
|
||||
return -ENOENT;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_INFO, "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
|
||||
* out and is no longer in the wqueue. If it still is in the wqueue, then sending MGCP messages
|
||||
* per se is broken and the program should notice so by a full wqueue. Even if this was called
|
||||
* before we had a chance to send out the message and it is still going to be sent, we will just
|
||||
* ignore the reply to it later. Removing a msgb from the wqueue here would just introduce more
|
||||
* bug surface in terms of failing to update wqueue API's counters or some such.
|
||||
/*! We don't really need to clean up the wqueue: In all sane cases, the msgb has already been sent
|
||||
* out and is no longer in the wqueue. If it still is in the wqueue, then sending MGCP messages
|
||||
* per se is broken and the program should notice so by a full wqueue. Even if this was called
|
||||
* before we had a chance to send out the message and it is still going to be sent, we will just
|
||||
* ignore the reply to it later. Removing a msgb from the wqueue here would just introduce more
|
||||
* bug surface in terms of failing to update wqueue API's counters or some such.
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -736,17 +1053,137 @@ struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
|
||||
MGCP_MSG_PRESENCE_CALL_ID | \
|
||||
MGCP_MSG_PRESENCE_CONN_MODE)
|
||||
#define MGCP_MDCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
|
||||
MGCP_MSG_PRESENCE_CALL_ID | \
|
||||
MGCP_MSG_PRESENCE_CONN_ID)
|
||||
#define MGCP_DLCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
|
||||
#define MGCP_AUEP_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
|
||||
#define MGCP_RSIP_MANDATORY 0 /* none */
|
||||
|
||||
/* Helper function for mgcp_msg_gen(): Add LCO information to MGCP message */
|
||||
static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
|
||||
{
|
||||
unsigned int i;
|
||||
int rc = 0;
|
||||
const char *codec;
|
||||
unsigned int pt;
|
||||
|
||||
rc += msgb_printf(msg, "L:");
|
||||
|
||||
if (mgcp_msg->ptime)
|
||||
rc += msgb_printf(msg, " p:%u,", mgcp_msg->ptime);
|
||||
|
||||
if (mgcp_msg->codecs_len) {
|
||||
rc += msgb_printf(msg, " a:");
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
pt = mgcp_msg->codecs[i];
|
||||
codec = get_value_string_or_null(codec_table, pt);
|
||||
|
||||
/* Note: Use codec descriptors from enum mgcp_codecs
|
||||
* in mgcp_client only! */
|
||||
OSMO_ASSERT(codec);
|
||||
rc += msgb_printf(msg, "%s", extract_codec_name(codec));
|
||||
if (i < mgcp_msg->codecs_len - 1)
|
||||
rc += msgb_printf(msg, ";");
|
||||
}
|
||||
rc += msgb_printf(msg, ",");
|
||||
}
|
||||
|
||||
rc += msgb_printf(msg, " nt:IN\r\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Helper function for mgcp_msg_gen(): Add SDP information to MGCP message */
|
||||
static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_client *mgcp)
|
||||
{
|
||||
unsigned int i;
|
||||
int rc = 0;
|
||||
char local_ip[INET_ADDRSTRLEN];
|
||||
const char *codec;
|
||||
unsigned int pt;
|
||||
|
||||
/* Add separator to mark the beginning of the SDP block */
|
||||
rc += msgb_printf(msg, "\r\n");
|
||||
|
||||
/* Add SDP protocol version */
|
||||
rc += msgb_printf(msg, "v=0\r\n");
|
||||
|
||||
/* Determine local IP-Address */
|
||||
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Could not determine local IP-Address!\n");
|
||||
msgb_free(msg);
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Add owner/creator (SDP) */
|
||||
rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
|
||||
mgcp_msg->call_id, local_ip);
|
||||
|
||||
/* Add session name (none) */
|
||||
rc += msgb_printf(msg, "s=-\r\n");
|
||||
|
||||
/* Add RTP address and port */
|
||||
if (mgcp_msg->audio_port == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Invalid port number, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return -2;
|
||||
}
|
||||
if (strlen(mgcp_msg->audio_ip) <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Empty ip address, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return -2;
|
||||
}
|
||||
rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
|
||||
|
||||
/* Add time description, active time (SDP) */
|
||||
rc += msgb_printf(msg, "t=0 0\r\n");
|
||||
|
||||
rc += msgb_printf(msg, "m=audio %u RTP/AVP", mgcp_msg->audio_port);
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
||||
rc += msgb_printf(msg, " %u", pt);
|
||||
|
||||
}
|
||||
rc += msgb_printf(msg, "\r\n");
|
||||
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
||||
|
||||
/* Note: Only dynamic payload type from the range 96-127
|
||||
* require to be explained further via rtpmap. All others
|
||||
* are implcitly definedby the number in m=audio */
|
||||
if (pt >= 96 && pt <= 127) {
|
||||
codec = get_value_string_or_null(codec_table, mgcp_msg->codecs[i]);
|
||||
|
||||
/* Note: Use codec descriptors from enum mgcp_codecs
|
||||
* in mgcp_client only! */
|
||||
OSMO_ASSERT(codec);
|
||||
|
||||
rc += msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec);
|
||||
}
|
||||
}
|
||||
|
||||
if (mgcp_msg->ptime)
|
||||
rc += msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! Generate an MGCP message
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \param[in] mgcp_msg Message description
|
||||
* \returns message buffer on success, NULL on error. */
|
||||
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
{
|
||||
mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
|
||||
uint32_t mandatory_mask;
|
||||
struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
|
||||
int rc = 0;
|
||||
int rc_sdp;
|
||||
bool use_sdp = false;
|
||||
|
||||
msg->l2h = msg->data;
|
||||
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
|
||||
@@ -796,6 +1233,14 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strstr(mgcp_msg->endpoint, "@") == NULL) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Endpoint name (%s) lacks separator (@), can not generate MGCP message\n",
|
||||
mgcp_msg->endpoint);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
rc += msgb_printf(msg, " %s", mgcp_msg->endpoint);
|
||||
}
|
||||
|
||||
@@ -817,9 +1262,17 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id);
|
||||
}
|
||||
|
||||
/* Add local connection options */
|
||||
if (mgcp_msg->verb == MGCP_VERB_CRCX)
|
||||
rc += msgb_printf(msg, "L: p:20, a:AMR, nt:IN\r\n");
|
||||
/* Using SDP makes sense when a valid IP/Port combination is specifiec,
|
||||
* if we do not know this information yet, we fall back to LCO */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
|
||||
&& mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT)
|
||||
use_sdp = true;
|
||||
|
||||
/* Add local connection options (LCO) */
|
||||
if (!use_sdp
|
||||
&& (mgcp_msg->verb == MGCP_VERB_CRCX
|
||||
|| mgcp_msg->verb == MGCP_VERB_MDCX))
|
||||
rc += add_lco(msg, mgcp_msg);
|
||||
|
||||
/* Add mode */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE)
|
||||
@@ -827,26 +1280,15 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
msgb_printf(msg, "M: %s\r\n",
|
||||
mgcp_client_cmode_name(mgcp_msg->conn_mode));
|
||||
|
||||
/* Add RTP address and port (SDP) */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
|
||||
&& mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) {
|
||||
if (mgcp_msg->audio_port == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Invalid port number, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
/* Add session description protocol (SDP) */
|
||||
if (use_sdp
|
||||
&& (mgcp_msg->verb == MGCP_VERB_CRCX
|
||||
|| mgcp_msg->verb == MGCP_VERB_MDCX)) {
|
||||
rc_sdp = add_sdp(msg, mgcp_msg, mgcp);
|
||||
if (rc_sdp == -2)
|
||||
return NULL;
|
||||
}
|
||||
if (strlen(mgcp_msg->audio_ip) <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Empty ip address, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
rc += msgb_printf(msg, "\r\n");
|
||||
rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
|
||||
rc +=
|
||||
msgb_printf(msg, "m=audio %u RTP/AVP 255\r\n",
|
||||
mgcp_msg->audio_port);
|
||||
else
|
||||
rc += rc_sdp;
|
||||
}
|
||||
|
||||
if (rc != 0) {
|
||||
@@ -859,12 +1301,17 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* Retrieve the MGCP transaction ID from a msgb generated by mgcp_msg_gen() */
|
||||
/*! Retrieve the MGCP transaction ID from a msgb generated by mgcp_msg_gen()
|
||||
* \param[in] msg message buffer
|
||||
* \returns Transaction id. */
|
||||
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg)
|
||||
{
|
||||
return (mgcp_trans_id_t)msg->cb[MSGB_CB_MGCP_TRANS_ID];
|
||||
}
|
||||
|
||||
/*! Get the configuration parameters a given MGCP client instance
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \returns configuration */
|
||||
struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp)
|
||||
{
|
||||
return &mgcp->actual;
|
||||
|
694
src/libosmo-mgcp-client/mgcp_client_fsm.c
Normal file
694
src/libosmo-mgcp-client/mgcp_client_fsm.c
Normal file
@@ -0,0 +1,694 @@
|
||||
/* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_fsm.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/byteswap.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
/* Context information, this is attached to the priv pointer of the FSM and
|
||||
* is also handed back when dispatcheing events to the parent FSM. This is
|
||||
* purly intened and not meant to be accessible for the API user */
|
||||
struct mgcp_ctx {
|
||||
/* MGCP client instance that is used to interact with the MGW */
|
||||
struct mgcp_client *mgcp;
|
||||
|
||||
/* The ID of the last pending transaction. This is used internally
|
||||
* to cancel the transaction in case of an error */
|
||||
mgcp_trans_id_t mgw_pending_trans;
|
||||
|
||||
/* Flag to mark that there is a pending transaction */
|
||||
bool mgw_trans_pending;
|
||||
|
||||
/* Connection ID which has been assigned by he MGW */
|
||||
char conn_id[MGCP_CONN_ID_LENGTH];
|
||||
|
||||
/* Local RTP connection info, the MGW will send outgoing traffic to the
|
||||
* ip/port specified here. The Address does not have to be choosen right
|
||||
* on the creation of a connection. It can always be modified later by
|
||||
* the user. */
|
||||
struct mgcp_conn_peer conn_peer_local;
|
||||
|
||||
/* Remote RTP connection info, the ip/port specified here is the address
|
||||
* where the MGW expects the RTP data to be sent. This address is
|
||||
* defined by soly by the MGW and can not be influenced by the user. */
|
||||
struct mgcp_conn_peer conn_peer_remote;
|
||||
|
||||
/* The terminate flag is a way to handle cornercase sitations that
|
||||
* might occur when the user runs into an error situation and sends
|
||||
* a DLCX command while the FSM is waiting for a response. In this
|
||||
* case the DLCX command is not executed immediately. Instead the
|
||||
* terminate flag is set. When the response to from the previous
|
||||
* operation is received, we know that there is a DLCX event is
|
||||
* pending. The FSM then generates the EV_DLCX by itsself before
|
||||
* it enters ST_READY to cause the immediate execution of the
|
||||
* DLCX procedure. (If normal operations are executed too fast,
|
||||
* the API functions will return an error. In general, the user
|
||||
* should synchronize using the callback events) */
|
||||
bool terminate;
|
||||
|
||||
/* Event that is sent when the current operation is completed (except
|
||||
* for DLCX, there the specified parent_term_evt is sent instead) */
|
||||
uint32_t parent_evt;
|
||||
};
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
#define MGCP_MGW_TIMEOUT 4 /* in seconds */
|
||||
#define MGCP_MGW_TIMEOUT_TIMER_NR 1
|
||||
|
||||
enum fsm_mgcp_client_states {
|
||||
ST_CRCX,
|
||||
ST_CRCX_RESP,
|
||||
ST_READY,
|
||||
ST_MDCX_RESP,
|
||||
ST_DLCX_RESP,
|
||||
};
|
||||
|
||||
enum fsm_mgcp_client_evt {
|
||||
EV_CRCX,
|
||||
EV_CRCX_RESP,
|
||||
EV_MDCX,
|
||||
EV_MDCX_RESP,
|
||||
EV_DLCX,
|
||||
EV_DLCX_RESP,
|
||||
};
|
||||
|
||||
static const struct value_string fsm_mgcp_client_evt_names[] = {
|
||||
OSMO_VALUE_STRING(EV_CRCX),
|
||||
OSMO_VALUE_STRING(EV_CRCX_RESP),
|
||||
OSMO_VALUE_STRING(EV_MDCX),
|
||||
OSMO_VALUE_STRING(EV_MDCX_RESP),
|
||||
OSMO_VALUE_STRING(EV_DLCX),
|
||||
OSMO_VALUE_STRING(EV_DLCX_RESP),
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static struct msgb *make_crcx_msg_bind(struct mgcp_ctx *mgcp_ctx)
|
||||
{
|
||||
struct mgcp_msg mgcp_msg;
|
||||
|
||||
mgcp_msg = (struct mgcp_msg) {
|
||||
.verb = MGCP_VERB_CRCX,
|
||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
|
||||
.call_id = mgcp_ctx->conn_peer_local.call_id,
|
||||
.conn_mode = MGCP_CONN_RECV_ONLY,
|
||||
.ptime = mgcp_ctx->conn_peer_local.ptime,
|
||||
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
|
||||
|
||||
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
}
|
||||
|
||||
static struct msgb *make_crcx_msg_bind_connect(struct mgcp_ctx *mgcp_ctx)
|
||||
{
|
||||
struct mgcp_msg mgcp_msg;
|
||||
|
||||
mgcp_msg = (struct mgcp_msg) {
|
||||
.verb = MGCP_VERB_CRCX,
|
||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
|
||||
MGCP_MSG_PRESENCE_AUDIO_PORT),
|
||||
.call_id = mgcp_ctx->conn_peer_local.call_id,
|
||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||
.audio_ip = mgcp_ctx->conn_peer_local.addr,
|
||||
.audio_port = mgcp_ctx->conn_peer_local.port,
|
||||
.ptime = mgcp_ctx->conn_peer_local.ptime,
|
||||
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
|
||||
|
||||
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
}
|
||||
|
||||
static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
{
|
||||
struct mgcp_msg mgcp_msg;
|
||||
|
||||
mgcp_msg = (struct mgcp_msg) {
|
||||
.verb = MGCP_VERB_MDCX,
|
||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT),
|
||||
.call_id = mgcp_ctx->conn_peer_remote.call_id,
|
||||
.conn_id = mgcp_ctx->conn_id,
|
||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||
.audio_ip = mgcp_ctx->conn_peer_local.addr,
|
||||
.audio_port = mgcp_ctx->conn_peer_local.port,
|
||||
.ptime = mgcp_ctx->conn_peer_local.ptime,
|
||||
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
|
||||
|
||||
/* Note: We take the endpoint and the call_id from the remote
|
||||
* connection info, because we can be confident that the
|
||||
* information there is valid. For the local info, we explicitly
|
||||
* allow endpoint and call_id to be optional */
|
||||
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
}
|
||||
|
||||
struct msgb *make_dlcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
{
|
||||
struct mgcp_msg mgcp_msg;
|
||||
|
||||
mgcp_msg = (struct mgcp_msg) {
|
||||
.verb = MGCP_VERB_DLCX,
|
||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID),
|
||||
.call_id = mgcp_ctx->conn_peer_remote.call_id,
|
||||
.conn_id = mgcp_ctx->conn_id,
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
|
||||
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
}
|
||||
|
||||
static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv);
|
||||
|
||||
static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = data;
|
||||
struct mgcp_client *mgcp;
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
mgcp = mgcp_ctx->mgcp;
|
||||
OSMO_ASSERT(mgcp);
|
||||
|
||||
switch (event) {
|
||||
case EV_CRCX:
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: creating connection on MGW endpoint:%s...\n",
|
||||
mgcp_ctx->conn_peer_local.endpoint);
|
||||
|
||||
if (mgcp_ctx->conn_peer_local.port)
|
||||
msg = make_crcx_msg_bind_connect(mgcp_ctx);
|
||||
else
|
||||
msg = make_crcx_msg_bind(mgcp_ctx);
|
||||
OSMO_ASSERT(msg);
|
||||
|
||||
mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
|
||||
mgcp_ctx->mgw_trans_pending = true;
|
||||
rc = mgcp_client_tx(mgcp, msg, mgw_crcx_resp_cb, fi);
|
||||
if (rc < 0) {
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
osmo_fsm_inst_state_chg(fi, ST_CRCX_RESP, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the CI that the MGW allocated during CRCX response. This is purely informational for logging
|
||||
* and identity tracking; the mgcp_conn_*() functions take care of using the right CI internally. */
|
||||
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = fi->priv;
|
||||
return mgcp_ctx->conn_id;
|
||||
}
|
||||
|
||||
static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = priv;
|
||||
struct mgcp_ctx *mgcp_ctx;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(fi);
|
||||
mgcp_ctx = fi->priv;
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
mgcp_ctx->mgw_trans_pending = false;
|
||||
|
||||
if (r->head.response_code != 200) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"MGW/CRCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
osmo_strlcpy(mgcp_ctx->conn_id, r->head.conn_id, sizeof(mgcp_ctx->conn_id));
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with CI: %s\n", mgcp_ctx->conn_id);
|
||||
|
||||
rc = mgcp_response_parse_params(r);
|
||||
if (rc) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: Cannot parse CRCX response\n");
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
|
||||
|
||||
osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr));
|
||||
mgcp_ctx->conn_peer_remote.port = r->audio_port;
|
||||
|
||||
if (strlen(r->head.endpoint) > 0) {
|
||||
/* If we get an endpoint identifier back from the MGW, take it */
|
||||
osmo_strlcpy(mgcp_ctx->conn_peer_remote.endpoint, r->head.endpoint,
|
||||
sizeof(mgcp_ctx->conn_peer_remote.endpoint));
|
||||
} else if (strstr(mgcp_ctx->conn_peer_local.endpoint, "*") == NULL) {
|
||||
/* If we do not get an endpoint identifier back and the
|
||||
* identifier we used to create the connection is not a
|
||||
* wildcarded one, we take the local endpoint identifier
|
||||
* instead */
|
||||
osmo_strlcpy(mgcp_ctx->conn_peer_remote.endpoint, mgcp_ctx->conn_peer_local.endpoint,
|
||||
sizeof(mgcp_ctx->conn_peer_local.endpoint));
|
||||
} else {
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: CRCX yielded not suitable endpoint identifier\n");
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
mgcp_ctx->conn_peer_remote.call_id = mgcp_ctx->conn_peer_local.call_id;
|
||||
|
||||
osmo_fsm_inst_dispatch(fi, EV_CRCX_RESP, mgcp_ctx);
|
||||
}
|
||||
|
||||
static void fsm_crcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = data;
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
switch (event) {
|
||||
case EV_CRCX_RESP:
|
||||
osmo_fsm_inst_state_chg(fi, ST_READY, 0, 0);
|
||||
if (mgcp_ctx->terminate) {
|
||||
/* Trigger immediate DLCX if DLCX was requested while the FSM was
|
||||
* busy with the previous operation */
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: FSM was busy while DLCX was requested, executing now...\n");
|
||||
osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx);
|
||||
} else
|
||||
osmo_fsm_inst_dispatch(fi->proc.parent, mgcp_ctx->parent_evt, &mgcp_ctx->conn_peer_remote);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv);
|
||||
static void mgw_dlcx_resp_cb(struct mgcp_response *r, void *priv);
|
||||
|
||||
static void fsm_ready_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = data;
|
||||
struct msgb *msg;
|
||||
struct mgcp_client *mgcp;
|
||||
uint32_t new_state;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
mgcp = mgcp_ctx->mgcp;
|
||||
OSMO_ASSERT(mgcp);
|
||||
|
||||
switch (event) {
|
||||
case EV_MDCX:
|
||||
msg = make_mdcx_msg(mgcp_ctx);
|
||||
OSMO_ASSERT(msg);
|
||||
rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_resp_cb, fi);
|
||||
new_state = ST_MDCX_RESP;
|
||||
break;
|
||||
case EV_DLCX:
|
||||
msg = make_dlcx_msg(mgcp_ctx);
|
||||
OSMO_ASSERT(msg);
|
||||
rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_resp_cb, fi);
|
||||
new_state = ST_DLCX_RESP;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
|
||||
mgcp_ctx->mgw_trans_pending = true;
|
||||
|
||||
if (rc < 0) {
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
osmo_fsm_inst_state_chg(fi, new_state, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
|
||||
}
|
||||
|
||||
static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = priv;
|
||||
struct mgcp_ctx *mgcp_ctx;
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(fi);
|
||||
mgcp_ctx = fi->priv;
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
mgcp_ctx->mgw_trans_pending = false;
|
||||
|
||||
if (r->head.response_code != 200) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: response yields error: %d %s\n", r->head.response_code,
|
||||
r->head.comment);
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = mgcp_response_parse_params(r);
|
||||
if (rc) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: Cannot parse MDCX response\n");
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
LOGPFSML(fi, LOGL_DEBUG, "MGW/MDCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
|
||||
|
||||
osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr));
|
||||
mgcp_ctx->conn_peer_remote.port = r->audio_port;
|
||||
|
||||
osmo_fsm_inst_dispatch(fi, EV_MDCX_RESP, mgcp_ctx);
|
||||
}
|
||||
|
||||
static void fsm_mdcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = data;
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
switch (event) {
|
||||
case EV_MDCX_RESP:
|
||||
osmo_fsm_inst_state_chg(fi, ST_READY, 0, 0);
|
||||
if (mgcp_ctx->terminate) {
|
||||
/* Trigger immediate DLCX if DLCX was requested while the FSM was
|
||||
* busy with the previous operation */
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: FSM was busy while DLCX was requested, executing now...\n");
|
||||
osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx);
|
||||
} else
|
||||
osmo_fsm_inst_dispatch(fi->proc.parent, mgcp_ctx->parent_evt, &mgcp_ctx->conn_peer_remote);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mgw_dlcx_resp_cb(struct mgcp_response *r, void *priv)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = priv;
|
||||
struct mgcp_ctx *mgcp_ctx;
|
||||
|
||||
OSMO_ASSERT(fi);
|
||||
mgcp_ctx = fi->priv;
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
mgcp_ctx->mgw_trans_pending = false;
|
||||
|
||||
if (r->head.response_code != 250) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"MGW/DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
osmo_fsm_inst_dispatch(fi, EV_DLCX_RESP, mgcp_ctx);
|
||||
}
|
||||
|
||||
static void fsm_dlcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = data;
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
switch (event) {
|
||||
case EV_DLCX_RESP:
|
||||
/* Rub out the connection identifier, since the connection
|
||||
* is no longer present and we will use the connection id
|
||||
* to know in error cases if the connection is still present
|
||||
* or not */
|
||||
memset(mgcp_ctx->conn_id, 0, sizeof(mgcp_ctx->conn_id));
|
||||
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int fsm_timeout_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = fi->priv;
|
||||
struct mgcp_client *mgcp;
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
mgcp = mgcp_ctx->mgcp;
|
||||
OSMO_ASSERT(mgcp);
|
||||
|
||||
if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) {
|
||||
/* Note: We were unable to communicate with the MGW,
|
||||
* unfortunately there is no meaningful action we can take
|
||||
* now other than giving up. */
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
} else {
|
||||
/* Note: Ther must not be any unsolicited timers
|
||||
* in this FSM. If so, we have serious problem. */
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = fi->priv;
|
||||
struct mgcp_client *mgcp;
|
||||
struct msgb *msg;
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
mgcp = mgcp_ctx->mgcp;
|
||||
OSMO_ASSERT(mgcp);
|
||||
|
||||
/* If there is still a transaction pending, cancel it now. */
|
||||
if (mgcp_ctx->mgw_trans_pending)
|
||||
mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
|
||||
|
||||
/* Should the FSM be terminated while there are still open connections
|
||||
* on the MGW, we send an unconditional DLCX to terminate the
|
||||
* connection. This is not the normal case. The user should always use
|
||||
* mgcp_conn_delete() to instruct the FSM to perform a graceful exit */
|
||||
if (strlen(mgcp_ctx->conn_id)) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n");
|
||||
msg = make_dlcx_msg(mgcp_ctx);
|
||||
OSMO_ASSERT(msg);
|
||||
mgcp_client_tx(mgcp, msg, NULL, NULL);
|
||||
}
|
||||
|
||||
talloc_free(mgcp_ctx);
|
||||
}
|
||||
|
||||
static struct osmo_fsm_state fsm_mgcp_client_states[] = {
|
||||
|
||||
/* Initial CRCX state. This state is immediately entered and executed
|
||||
* when the FSM is started. The rationale is that we first have to
|
||||
* create a connectin before we can execute other operations on that
|
||||
* connection. */
|
||||
[ST_CRCX] = {
|
||||
.in_event_mask = S(EV_CRCX),
|
||||
.out_state_mask = S(ST_CRCX_RESP),
|
||||
.name = OSMO_STRINGIFY(ST_CRCX),
|
||||
.action = fsm_crcx_cb,
|
||||
},
|
||||
|
||||
/* Wait for the response to a CRCX operation, check and process the
|
||||
* results, change to ST_READY afterwards. */
|
||||
[ST_CRCX_RESP] = {
|
||||
.in_event_mask = S(EV_CRCX_RESP),
|
||||
.out_state_mask = S(ST_READY),
|
||||
.name = OSMO_STRINGIFY(ST_CRCX_RESP),
|
||||
.action = fsm_crcx_resp_cb,
|
||||
},
|
||||
|
||||
/* In this idle state we wait for further operations (e.g. MDCX) that
|
||||
* can be executed by the user using the API. There is no timeout in
|
||||
* this state. The connection lives on until the user decides to
|
||||
* terminate it (DLCX). */
|
||||
[ST_READY] = {
|
||||
.in_event_mask = S(EV_MDCX) | S(EV_DLCX),
|
||||
.out_state_mask = S(ST_MDCX_RESP) | S(ST_DLCX_RESP),
|
||||
.name = OSMO_STRINGIFY(ST_READY),
|
||||
.action = fsm_ready_cb,
|
||||
},
|
||||
|
||||
/* Wait for the response of a MDCX operation, check and process the
|
||||
* results, change to ST_READY afterwards. */
|
||||
[ST_MDCX_RESP] = {
|
||||
.in_event_mask = S(EV_MDCX_RESP),
|
||||
.out_state_mask = S(ST_READY),
|
||||
.name = OSMO_STRINGIFY(ST_MDCX_RESP),
|
||||
.action = fsm_mdcx_resp_cb,
|
||||
},
|
||||
|
||||
/* Wait for the response of a DLCX operation and terminate the FSM
|
||||
* normally. */
|
||||
[ST_DLCX_RESP] = {
|
||||
.in_event_mask = S(EV_DLCX_RESP),
|
||||
.out_state_mask = 0,
|
||||
.name = OSMO_STRINGIFY(ST_DLCX_RESP),
|
||||
.action = fsm_dlcx_resp_cb,
|
||||
},
|
||||
};
|
||||
|
||||
static struct osmo_fsm fsm_mgcp_client = {
|
||||
.name = "MGCP_CONN",
|
||||
.states = fsm_mgcp_client_states,
|
||||
.num_states = ARRAY_SIZE(fsm_mgcp_client_states),
|
||||
.timer_cb = fsm_timeout_cb,
|
||||
.cleanup = fsm_cleanup_cb,
|
||||
.event_names = fsm_mgcp_client_evt_names,
|
||||
};
|
||||
|
||||
/*! allocate FSM, and create a new connection on the MGW.
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \param[in] parent_fi Parent FSM instance.
|
||||
* \param[in] parent_term_evt Event to be sent to parent when terminating.
|
||||
* \param[in] parent_evt Event to be sent to parent when operation is done.
|
||||
* \param[in] conn_peer Connection parameters (ip, port...).
|
||||
* \returns newly-allocated, initialized and registered FSM instance, NULL on error. */
|
||||
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi,
|
||||
uint32_t parent_term_evt, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx;
|
||||
static bool fsm_registered = false;
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct in_addr ip_test;
|
||||
|
||||
OSMO_ASSERT(parent_fi);
|
||||
OSMO_ASSERT(mgcp);
|
||||
OSMO_ASSERT(conn_peer);
|
||||
|
||||
/* Check if IP/Port information in conn info makes sense */
|
||||
if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0)
|
||||
return NULL;
|
||||
|
||||
/* Register the fsm description (if not already done) */
|
||||
if (fsm_registered == false) {
|
||||
osmo_fsm_register(&fsm_mgcp_client);
|
||||
fsm_registered = true;
|
||||
}
|
||||
|
||||
/* Allocate and configure a new fsm instance */
|
||||
fi = osmo_fsm_inst_alloc_child(&fsm_mgcp_client, parent_fi, parent_term_evt);
|
||||
OSMO_ASSERT(fi);
|
||||
mgcp_ctx = talloc_zero(fi, struct mgcp_ctx);
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
mgcp_ctx->mgcp = mgcp;
|
||||
mgcp_ctx->parent_evt = parent_evt;
|
||||
|
||||
memcpy(&mgcp_ctx->conn_peer_local, conn_peer, sizeof(mgcp_ctx->conn_peer_local));
|
||||
fi->priv = mgcp_ctx;
|
||||
|
||||
/* start state machine */
|
||||
OSMO_ASSERT(fi->state == ST_CRCX);
|
||||
osmo_fsm_inst_dispatch(fi, EV_CRCX, mgcp_ctx);
|
||||
|
||||
return fi;
|
||||
}
|
||||
|
||||
/*! modify an existing connection on the MGW.
|
||||
* \param[in] fi FSM instance.
|
||||
* \param[in] parent_evt Event to be sent to parent when operation is done.
|
||||
* \param[in] conn_peer New connection information (ip, port...).
|
||||
* \returns 0 on success, -EINVAL on error. */
|
||||
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
|
||||
{
|
||||
OSMO_ASSERT(fi);
|
||||
struct mgcp_ctx *mgcp_ctx = fi->priv;
|
||||
struct in_addr ip_test;
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
OSMO_ASSERT(conn_peer);
|
||||
|
||||
/* The user must not issue an MDCX before the CRCX has completed,
|
||||
* if this happens, it means that the parent FSM has overhead the
|
||||
* parent_evt (mandatory!) and executed the MDCX without even
|
||||
* waiting for the results. Another reason could be that the
|
||||
* parent FSM got messed up */
|
||||
OSMO_ASSERT(fi->state != ST_CRCX_RESP);
|
||||
|
||||
/* If the user tries to issue an MDCX while an DLCX operation is
|
||||
* pending, there must be a serious problem with the paren FSM.
|
||||
* Eeither the parent_term_evt (mandatory!) has been overheard,
|
||||
* or the parant FSM got messed so badly that it still assumes
|
||||
* a live connection although it as killed it. */
|
||||
OSMO_ASSERT(fi->state != ST_DLCX_RESP);
|
||||
|
||||
/* Check if IP/Port parameters make sense */
|
||||
if (conn_peer->port == 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, port == 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (inet_aton(conn_peer->addr, &ip_test) == 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address == 0.0.0.0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! The user may supply an endpoint identifier in conn_peer. The
|
||||
* identifier is then checked. This check is optional. Later steps do
|
||||
* not depend on the endpoint identifier supplied here because it is
|
||||
* already implicitly known from the CRCX phase. */
|
||||
if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint)) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, endpoint mismatches: requested %s, should be %s\n",
|
||||
conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! Note: The call-id is implicitly known from the previous CRCX and
|
||||
* will not be checked even when it is set in conn_peer. */
|
||||
|
||||
mgcp_ctx->parent_evt = parent_evt;
|
||||
memcpy(&mgcp_ctx->conn_peer_local, conn_peer, sizeof(mgcp_ctx->conn_peer_local));
|
||||
osmo_fsm_inst_dispatch(fi, EV_MDCX, mgcp_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! delete existing connection on the MGW, destroy FSM afterwards.
|
||||
* \param[in] fi FSM instance. */
|
||||
void mgcp_conn_delete(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
OSMO_ASSERT(fi);
|
||||
struct mgcp_ctx *mgcp_ctx = fi->priv;
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
/* Unlink FSM from parent */
|
||||
osmo_fsm_inst_unlink_parent(fi, NULL);
|
||||
|
||||
/* An error situation where the parent FSM must be killed immediately
|
||||
* may lead into a situation where the DLCX can not be executed right
|
||||
* at that moment because the FSM is still busy with another operation.
|
||||
* In those cases we postpone the DLCX so that the FSM and the
|
||||
* connections on the MGW get cleaned up gracefully. */
|
||||
if (fi->state != ST_READY) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW: operation still pending, DLCX will be postponed.\n");
|
||||
mgcp_ctx->terminate = true;
|
||||
return;
|
||||
}
|
||||
osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx);
|
||||
}
|
@@ -6,16 +6,16 @@
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <talloc.h>
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
@@ -203,4 +204,6 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
|
||||
install_element(node, &cfg_mgcpgw_remote_port_cmd);
|
||||
install_element(node, &cfg_mgcpgw_endpoint_range_cmd);
|
||||
install_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
|
||||
|
||||
osmo_fsm_vty_add_cmds();
|
||||
}
|
||||
|
@@ -21,28 +21,23 @@ AM_LDFLAGS = \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
# This is not at all related to the release version, but a range of supported
|
||||
# API versions. Read TODO_RELEASE in the source tree's root!
|
||||
MGCP_LIBVERSION=1:0:0
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp.la \
|
||||
noinst_LIBRARIES = \
|
||||
libosmo-mgcp.a \
|
||||
$(NULL)
|
||||
|
||||
noinst_HEADERS = \
|
||||
g711common.h \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_la_SOURCES = \
|
||||
libosmo_mgcp_a_SOURCES = \
|
||||
mgcp_protocol.c \
|
||||
mgcp_network.c \
|
||||
mgcp_vty.c \
|
||||
mgcp_osmux.c \
|
||||
mgcp_sdp.c \
|
||||
mgcp_codec.c \
|
||||
mgcp_msg.c \
|
||||
mgcp_conn.c \
|
||||
mgcp_stat.c \
|
||||
mgcp_ep.c \
|
||||
mgcp_endp.c \
|
||||
$(NULL)
|
||||
|
||||
libosmo_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_LIBVERSION)
|
||||
|
343
src/libosmo-mgcp/mgcp_codec.c
Normal file
343
src/libosmo-mgcp/mgcp_codec.c
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2014 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Helper function to dump codec information of a specified codec to a printable
|
||||
* string, used by dump_codec_summary() */
|
||||
static char *dump_codec(struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
static char str[256];
|
||||
char *pt_str;
|
||||
|
||||
if (codec->payload_type > 76)
|
||||
pt_str = "DYNAMIC";
|
||||
else if (codec->payload_type > 72)
|
||||
pt_str = "RESERVED <!>";
|
||||
else if (codec->payload_type != PTYPE_UNDEFINED)
|
||||
pt_str = codec->subtype_name;
|
||||
else
|
||||
pt_str = "INVALID <!>";
|
||||
|
||||
snprintf(str, sizeof(str), "(pt:%i=%s, audio:%s subt=%s, rate=%u, ch=%i, t=%u/%u)", codec->payload_type, pt_str,
|
||||
codec->audio_name, codec->subtype_name, codec->rate, codec->channels, codec->frame_duration_num,
|
||||
codec->frame_duration_den);
|
||||
return str;
|
||||
}
|
||||
|
||||
/*! Dump a summary of all negotiated codecs to debug log
|
||||
* \param[in] conn related rtp-connection. */
|
||||
void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
struct mgcp_rtp_end *rtp;
|
||||
unsigned int i;
|
||||
struct mgcp_rtp_codec *codec;
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
rtp = &conn->end;
|
||||
endp = conn->conn->endp;
|
||||
|
||||
if (rtp->codecs_assigned == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x conn:%s no codecs available\n", ENDPOINT_NUMBER(endp),
|
||||
mgcp_conn_dump(conn->conn));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store parsed codec information */
|
||||
for (i = 0; i < rtp->codecs_assigned; i++) {
|
||||
codec = &rtp->codecs[i];
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x conn:%s codecs[%u]:%s", ENDPOINT_NUMBER(endp),
|
||||
mgcp_conn_dump(conn->conn), i, dump_codec(codec));
|
||||
|
||||
if (codec == rtp->codec)
|
||||
LOGPC(DLMGCP, LOGL_DEBUG, " [selected]");
|
||||
|
||||
LOGPC(DLMGCP, LOGL_DEBUG, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Initalize or reset codec information with default data. */
|
||||
void codec_init(struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
if (codec->subtype_name)
|
||||
talloc_free(codec->subtype_name);
|
||||
if (codec->audio_name)
|
||||
talloc_free(codec->audio_name);
|
||||
memset(codec, 0, sizeof(*codec));
|
||||
codec->payload_type = -1;
|
||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
||||
codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
||||
codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
||||
}
|
||||
|
||||
/*! Initalize or reset codec information with default data.
|
||||
* \param[out] conn related rtp-connection. */
|
||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
memset(conn->end.codecs, 0, sizeof(conn->end.codecs));
|
||||
conn->end.codecs_assigned = 0;
|
||||
conn->end.codec = NULL;
|
||||
}
|
||||
|
||||
/* Set members of struct mgcp_rtp_codec, extrapolate in missing information */
|
||||
static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
int payload_type, const char *audio_name, unsigned int pt_offset)
|
||||
{
|
||||
int rate;
|
||||
int channels;
|
||||
char audio_codec[64];
|
||||
|
||||
/* Initalize the codec struct with some default data to begin with */
|
||||
codec_init(codec);
|
||||
|
||||
if (payload_type != PTYPE_UNDEFINED) {
|
||||
/* Make sure we do not get any reserved or undefined type numbers */
|
||||
/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
|
||||
if (payload_type == 1 || payload_type == 2 || payload_type == 19)
|
||||
goto error;
|
||||
if (payload_type >= 72 && payload_type <= 76)
|
||||
goto error;
|
||||
if (payload_type >= 127)
|
||||
goto error;
|
||||
|
||||
codec->payload_type = payload_type;
|
||||
}
|
||||
|
||||
/* When no audio name is given, we are forced to use the payload
|
||||
* type to generate the audio name. This is only possible for
|
||||
* non dynamic payload types, which are statically defined */
|
||||
if (!audio_name) {
|
||||
switch (payload_type) {
|
||||
case 0:
|
||||
audio_name = talloc_strdup(ctx, "PCMU/8000/1");
|
||||
break;
|
||||
case 3:
|
||||
audio_name = talloc_strdup(ctx, "GSM/8000/1");
|
||||
break;
|
||||
case 8:
|
||||
audio_name = talloc_strdup(ctx, "PCMA/8000/1");
|
||||
break;
|
||||
case 18:
|
||||
audio_name = talloc_strdup(ctx, "G729/8000/1");
|
||||
break;
|
||||
default:
|
||||
/* The given payload type is not known to us, or it
|
||||
* it is a dynamic payload type for which we do not
|
||||
* know the audio name. We must give up here */
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we extract the codec subtype name, rate and channels. The latter
|
||||
* two are optional. If they are not present we use the safe defaults
|
||||
* above. */
|
||||
if (strlen(audio_name) > sizeof(audio_codec))
|
||||
goto error;
|
||||
channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
||||
rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
||||
if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1)
|
||||
goto error;
|
||||
|
||||
/* Note: We only accept configurations with one audio channel! */
|
||||
if (channels != 1)
|
||||
goto error;
|
||||
|
||||
codec->rate = rate;
|
||||
codec->channels = channels;
|
||||
codec->subtype_name = talloc_strdup(ctx, audio_codec);
|
||||
codec->audio_name = talloc_strdup(ctx, audio_name);
|
||||
codec->payload_type = payload_type;
|
||||
|
||||
if (!strcmp(audio_codec, "G729")) {
|
||||
codec->frame_duration_num = 10;
|
||||
codec->frame_duration_den = 1000;
|
||||
} else {
|
||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
||||
}
|
||||
|
||||
/* Derive the payload type if it is unknown */
|
||||
if (codec->payload_type == PTYPE_UNDEFINED) {
|
||||
|
||||
/* For the known codecs from the static range we restore
|
||||
* the IANA or 3GPP assigned payload type number */
|
||||
if (codec->rate == 8000 && codec->channels == 1) {
|
||||
/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
|
||||
if (!strcmp(codec->subtype_name, "GSM"))
|
||||
codec->payload_type = 3;
|
||||
else if (!strcmp(codec->subtype_name, "PCMA"))
|
||||
codec->payload_type = 8;
|
||||
else if (!strcmp(codec->subtype_name, "PCMU"))
|
||||
codec->payload_type = 0;
|
||||
else if (!strcmp(codec->subtype_name, "G729"))
|
||||
codec->payload_type = 18;
|
||||
|
||||
/* See also: 3GPP TS 48.103, chapter 5.4.2.2 RTP Payload
|
||||
* Note: These are not fixed payload types as the IANA
|
||||
* defined once, they still remain dymanic payload
|
||||
* types, but with a payload type number preference. */
|
||||
else if (!strcmp(codec->subtype_name, "GSM-EFR"))
|
||||
codec->payload_type = 110;
|
||||
else if (!strcmp(codec->subtype_name, "GSM-HR-08"))
|
||||
codec->payload_type = 111;
|
||||
else if (!strcmp(codec->subtype_name, "AMR"))
|
||||
codec->payload_type = 112;
|
||||
else if (!strcmp(codec->subtype_name, "AMR-WB"))
|
||||
codec->payload_type = 113;
|
||||
}
|
||||
|
||||
/* If we could not determine a payload type we assume that
|
||||
* we are dealing with a codec from the dynamic range. We
|
||||
* choose a fixed identifier from 96-109. (Note: normally,
|
||||
* the dynamic payload type rante is from 96-127, but from
|
||||
* 110 onwards 3gpp defines prefered codec types, which are
|
||||
* also fixed, see above) */
|
||||
if (codec->payload_type < 0) {
|
||||
codec->payload_type = 96 + pt_offset;
|
||||
if (codec->payload_type > 109)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
/* Make sure we leave a clean codec entry on error. */
|
||||
codec_init(codec);
|
||||
memset(codec, 0, sizeof(*codec));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! Add codec configuration depending on payload type and/or codec name. This
|
||||
* function uses the input parameters to extrapolate the full codec information.
|
||||
* \param[out] codec configuration (caller provided memory).
|
||||
* \param[out] conn related rtp-connection.
|
||||
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
|
||||
* \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
|
||||
* \returns 0 on success, -EINVAL on failure. */
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* The amount of codecs we can store is limited, make sure we do not
|
||||
* overrun this limit. */
|
||||
if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
|
||||
return -EINVAL;
|
||||
|
||||
rc = codec_set(conn->conn, &conn->end.codecs[conn->end.codecs_assigned], payload_type, audio_name,
|
||||
conn->end.codecs_assigned);
|
||||
if (rc != 0)
|
||||
return -EINVAL;
|
||||
|
||||
conn->end.codecs_assigned++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if the given codec is applicable on the specified endpoint
|
||||
* Helper function for mgcp_codec_decide() */
|
||||
static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
char codec_name[64];
|
||||
|
||||
/* A codec name must be set, if not, this might mean that the codec
|
||||
* (payload type) that was assigned is unknown to us so we must stop
|
||||
* here. */
|
||||
if (!codec->subtype_name)
|
||||
return false;
|
||||
|
||||
/* We now extract the codec_name (letters before the /, e.g. "GSM"
|
||||
* from the audio name that is stored in the trunk configuration.
|
||||
* We do not compare to the full audio_name because we expect that
|
||||
* "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the
|
||||
* audio name of the codec is set to "GSM" */
|
||||
if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
|
||||
return false;
|
||||
|
||||
/* Finally we check if the subtype_name we have generated from the
|
||||
* audio_name in the trunc struct patches the codec_name of the
|
||||
* given codec */
|
||||
if (strcasecmp(codec_name, codec->subtype_name) == 0)
|
||||
return true;
|
||||
|
||||
/* FIXME: It is questinable that the method to pick a compatible
|
||||
* codec can work properly. Since this useses tcfg->audio_name, as
|
||||
* a reference, which is set to "AMR/8000" permanently.
|
||||
* tcfg->audio_name must be updated by the first connection that
|
||||
* has been made on an endpoint, so that the second connection
|
||||
* can make a meaningful decision here */
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! Decide for one suitable codec
|
||||
* \param[in] conn related rtp-connection.
|
||||
* \returns 0 on success, -EINVAL on failure. */
|
||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
struct mgcp_rtp_end *rtp;
|
||||
unsigned int i;
|
||||
struct mgcp_endpoint *endp;
|
||||
bool codec_assigned = false;
|
||||
|
||||
endp = conn->conn->endp;
|
||||
rtp = &conn->end;
|
||||
|
||||
/* This function works on the results the SDP/LCO parser has extracted
|
||||
* from the MGCP message. The goal is to select a suitable codec for
|
||||
* the given connection. When transcoding is available, the first codec
|
||||
* from the codec list is taken without further checking. When
|
||||
* transcoding is not available, then the choice must be made more
|
||||
* carefully. Each codec in the list is checked until one is found that
|
||||
* is rated compatible. The rating is done by the helper function
|
||||
* is_codec_compatible(), which does the actual checking. */
|
||||
for (i = 0; i < rtp->codecs_assigned; i++) {
|
||||
/* When no transcoding is available, avoid codecs that would
|
||||
* require transcoding. */
|
||||
if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
|
||||
rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
rtp->codec = &rtp->codecs[i];
|
||||
codec_assigned = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* FIXME: To the reviewes: This is problematic. I do not get why we
|
||||
* need to reset the packet_duration_ms depending on the codec
|
||||
* selection. I thought it were all 20ms? Is this to address some
|
||||
* cornercase. (This piece of code was in the code path before,
|
||||
* together with the note: "TODO/XXX: Store this per codec and derive
|
||||
* it on use" */
|
||||
if (codec_assigned) {
|
||||
if (rtp->maximum_packet_time >= 0
|
||||
&& rtp->maximum_packet_time * rtp->codec->frame_duration_den >
|
||||
rtp->codec->frame_duration_num * 1500)
|
||||
rtp->packet_duration_ms = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
@@ -24,10 +24,33 @@
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_ep.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <ctype.h>
|
||||
|
||||
enum {
|
||||
IN_STREAM_ERR_TSTMP_CTR,
|
||||
OUT_STREAM_ERR_TSTMP_CTR,
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc rate_ctr_desc[] = {
|
||||
[IN_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:in", "Inbound rtp-stream timestamp errors."},
|
||||
[OUT_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:out", "Outbound rtp-stream timestamp errors."},
|
||||
};
|
||||
|
||||
|
||||
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
|
||||
.group_name_prefix = "conn_rtp",
|
||||
.group_description = "rtp connection statistics",
|
||||
.class_id = 1,
|
||||
.num_ctr = 2,
|
||||
.ctr_desc = rate_ctr_desc
|
||||
};
|
||||
|
||||
|
||||
/* Allocate a new connection identifier. According to RFC3435, they must
|
||||
* be unique only within the scope of the endpoint. (Caller must provide
|
||||
* memory for id) */
|
||||
@@ -67,38 +90,24 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Reset codec state and free memory */
|
||||
static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec)
|
||||
/* Initialize rtp connection struct with default values */
|
||||
static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
|
||||
{
|
||||
codec->payload_type = -1;
|
||||
codec->subtype_name = NULL;
|
||||
codec->audio_name = NULL;
|
||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
||||
codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
||||
codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
||||
struct mgcp_rtp_end *end = &conn_rtp->end;
|
||||
/* FIXME: Each new rate counter group requires an unique index. At the
|
||||
* moment we generate this index using this counter, but perhaps there
|
||||
* is a more concious way to assign the indexes. */
|
||||
static unsigned int rate_ctr_index = 0;
|
||||
|
||||
/* see also mgcp_sdp.c, mgcp_set_audio_info() */
|
||||
talloc_free(codec->subtype_name);
|
||||
talloc_free(codec->audio_name);
|
||||
}
|
||||
conn_rtp->type = MGCP_RTP_DEFAULT;
|
||||
conn_rtp->osmux.allocated_cid = -1;
|
||||
|
||||
/* Reset states, free memory, set defaults and reset codec state */
|
||||
static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
struct mgcp_rtp_end *end = &conn->end;
|
||||
|
||||
conn->type = MGCP_RTP_DEFAULT;
|
||||
conn->osmux.allocated_cid = -1;
|
||||
/* backpointer to the generic part of the connection */
|
||||
conn->u.rtp.conn = conn;
|
||||
|
||||
end->rtp.fd = -1;
|
||||
end->rtcp.fd = -1;
|
||||
end->local_port = 0;
|
||||
end->packets_rx = 0;
|
||||
end->octets_rx = 0;
|
||||
end->packets_tx = 0;
|
||||
end->octets_tx = 0;
|
||||
end->dropped_packets = 0;
|
||||
memset(&end->stats, 0, sizeof(end->stats));
|
||||
end->rtp_port = end->rtcp_port = 0;
|
||||
talloc_free(end->fmtp_extra);
|
||||
end->fmtp_extra = NULL;
|
||||
@@ -107,9 +116,24 @@ static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn)
|
||||
end->frames_per_packet = 0; /* unknown */
|
||||
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
|
||||
end->output_enabled = 0;
|
||||
end->maximum_packet_time = -1;
|
||||
|
||||
mgcp_rtp_codec_reset(&end->codec);
|
||||
mgcp_rtp_codec_reset(&end->alt_codec);
|
||||
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index);
|
||||
conn_rtp->state.in_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[IN_STREAM_ERR_TSTMP_CTR];
|
||||
conn_rtp->state.out_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[OUT_STREAM_ERR_TSTMP_CTR];
|
||||
rate_ctr_index++;
|
||||
|
||||
/* Make sure codec table is reset */
|
||||
mgcp_codec_reset_all(conn_rtp);
|
||||
}
|
||||
|
||||
/* Cleanup rtp connection struct */
|
||||
static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
osmux_disable_conn(conn_rtp);
|
||||
osmux_release_cid(conn_rtp);
|
||||
mgcp_free_rtp_port(&conn_rtp->end);
|
||||
rate_ctr_group_free(conn_rtp->rate_ctr_group);
|
||||
}
|
||||
|
||||
/*! allocate a new connection list entry.
|
||||
@@ -136,7 +160,6 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
conn->type = type;
|
||||
conn->mode = MGCP_CONN_NONE;
|
||||
conn->mode_orig = MGCP_CONN_NONE;
|
||||
conn->u.rtp.conn = conn;
|
||||
osmo_strlcpy(conn->name, name, sizeof(conn->name));
|
||||
rc = mgcp_alloc_id(endp, conn->id);
|
||||
if (rc < 0) {
|
||||
@@ -146,7 +169,7 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
|
||||
switch (type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
mgcp_rtp_conn_reset(&conn->u.rtp);
|
||||
mgcp_rtp_conn_init(&conn->u.rtp, conn);
|
||||
break;
|
||||
default:
|
||||
/* NOTE: This should never be called with an
|
||||
@@ -206,11 +229,15 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
/* Run endpoint cleanup action. By this we inform the endpoint about
|
||||
* the removal of the connection and allow it to clean up its inner
|
||||
* state accordingly */
|
||||
if (endp->type->cleanup_cb)
|
||||
endp->type->cleanup_cb(endp, conn);
|
||||
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
osmux_disable_conn(&conn->u.rtp);
|
||||
osmux_release_cid(&conn->u.rtp);
|
||||
mgcp_free_rtp_port(&conn->u.rtp.end);
|
||||
mgcp_rtp_conn_cleanup(&conn->u.rtp);
|
||||
break;
|
||||
default:
|
||||
/* NOTE: This should never be called with an
|
||||
@@ -254,9 +281,9 @@ void mgcp_conn_free_all(struct mgcp_endpoint *endp)
|
||||
return;
|
||||
}
|
||||
|
||||
/*! dump basic connection information to human readble string.
|
||||
/*! dump basic connection information to human readable string.
|
||||
* \param[in] conn to dump
|
||||
* \returns human readble string */
|
||||
* \returns human readable string */
|
||||
char *mgcp_conn_dump(struct mgcp_conn *conn)
|
||||
{
|
||||
static char str[sizeof(conn->name)+sizeof(conn->id)+256];
|
||||
|
@@ -21,12 +21,36 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp/mgcp_ep.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
|
||||
/* Endpoint typeset definition */
|
||||
const struct mgcp_endpoint_typeset ep_typeset = {
|
||||
/* Specify endpoint properties for RTP endpoint */
|
||||
.rtp.max_conns = 2,
|
||||
.rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb
|
||||
.rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
|
||||
.rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb
|
||||
};
|
||||
|
||||
/*! release endpoint, all open connections are closed.
|
||||
* \param[in] endp endpoint to release */
|
||||
void mgcp_endp_release(struct mgcp_endpoint *endp)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint:0x%x\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
|
||||
/* Normally this function should only be called when
|
||||
* all connections have been removed already. In case
|
||||
* that there are still connections open (e.g. when
|
||||
* RSIP is executed), free them all at once. */
|
||||
mgcp_conn_free_all(endp);
|
||||
|
||||
/* Reset endpoint parameters and states */
|
||||
talloc_free(endp->callid);
|
||||
endp->callid = NULL;
|
||||
talloc_free(endp->local_options.string);
|
||||
endp->local_options.string = NULL;
|
||||
talloc_free(endp->local_options.codec);
|
||||
endp->local_options.codec = NULL;
|
||||
endp->wildcarded_req = false;
|
||||
}
|
@@ -28,6 +28,7 @@
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
|
||||
/*! Display an mgcp message on the log output.
|
||||
* \param[in] message mgcp message string
|
||||
@@ -142,6 +143,7 @@ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
|
||||
char *rest = NULL;
|
||||
struct mgcp_trunk_config *tcfg;
|
||||
int trunk, endp;
|
||||
struct mgcp_endpoint *endp_ptr;
|
||||
|
||||
trunk = strtoul(mgcp + 6, &rest, 10);
|
||||
if (rest == NULL || rest[0] != '/' || trunk < 1) {
|
||||
@@ -178,25 +180,112 @@ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &tcfg->endpoints[endp];
|
||||
endp_ptr = &tcfg->endpoints[endp];
|
||||
endp_ptr->wildcarded_req = false;
|
||||
return endp_ptr;
|
||||
}
|
||||
|
||||
/* Find an endpoint that is not in use. Do this by going through the endpoint
|
||||
* array, check the callid. A callid nullpointer indicates that the endpoint
|
||||
* is free */
|
||||
static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
|
||||
unsigned int number_endpoints)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < number_endpoints; i++) {
|
||||
if (endpoints[i].callid == NULL) {
|
||||
endp = &endpoints[i];
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint:0x%x found free endpoint\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
endp->wildcarded_req = true;
|
||||
return endp;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if the domain name, which is supplied with the endpoint name
|
||||
* matches the configuration. */
|
||||
static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
|
||||
{
|
||||
char *domain_to_check;
|
||||
|
||||
domain_to_check = strstr(mgcp, "@");
|
||||
if (!domain_to_check)
|
||||
return -EINVAL;
|
||||
|
||||
if (strcmp(domain_to_check+1, cfg->domain) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Search the endpoint pool for the endpoint that had been selected via the
|
||||
* MGCP message (helper function for mgcp_analyze_header()) */
|
||||
static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
|
||||
const char *mgcp)
|
||||
const char *mgcp,
|
||||
int *cause)
|
||||
{
|
||||
char *endptr = NULL;
|
||||
unsigned int gw = INT_MAX;
|
||||
const char *endpoint_number_str;
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
if (strncmp(mgcp, "ds/e1", 5) == 0)
|
||||
return find_e1_endpoint(cfg, mgcp);
|
||||
*cause = 0;
|
||||
|
||||
gw = strtoul(mgcp, &endptr, 10);
|
||||
if (gw > 0 && gw < cfg->trunk.number_endpoints && endptr[0] == '@')
|
||||
return &cfg->trunk.endpoints[gw];
|
||||
/* 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);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if the E1 trunk is requested */
|
||||
if (strncmp(mgcp, "ds/e1", 5) == 0) {
|
||||
endp = find_e1_endpoint(cfg, mgcp);
|
||||
if (!endp)
|
||||
*cause = -500;
|
||||
return endp;
|
||||
}
|
||||
|
||||
/* Check if the virtual trunk is addressed (new, correct way with prefix) */
|
||||
if (strncmp
|
||||
(mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
|
||||
strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) {
|
||||
endpoint_number_str =
|
||||
mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
|
||||
if (endpoint_number_str[0] == '*') {
|
||||
endp = find_free_endpoint(cfg->trunk.endpoints,
|
||||
cfg->trunk.number_endpoints);
|
||||
if (!endp)
|
||||
*cause = -403;
|
||||
return endp;
|
||||
}
|
||||
gw = strtoul(endpoint_number_str, &endptr, 16);
|
||||
if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
|
||||
endp = &cfg->trunk.endpoints[gw];
|
||||
endp->wildcarded_req = false;
|
||||
return endp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Deprecated method without prefix */
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n",
|
||||
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp);
|
||||
gw = strtoul(mgcp, &endptr, 16);
|
||||
if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
|
||||
endp = &cfg->trunk.endpoints[gw];
|
||||
endp->wildcarded_req = false;
|
||||
return endp;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
|
||||
*cause = -500;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -209,6 +298,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
{
|
||||
int i = 0;
|
||||
char *elem, *save = NULL;
|
||||
int cause;
|
||||
|
||||
/*! This function will parse the header part of the received
|
||||
* MGCP message. The parsing results are stored in pdata.
|
||||
@@ -226,25 +316,25 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
pdata->trans = elem;
|
||||
break;
|
||||
case 1:
|
||||
pdata->endp = find_endpoint(pdata->cfg, elem);
|
||||
pdata->endp = find_endpoint(pdata->cfg, elem, &cause);
|
||||
if (!pdata->endp) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Unable to find Endpoint `%s'\n", elem);
|
||||
return -1;
|
||||
return cause;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (strcmp("MGCP", elem)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MGCP header parsing error\n");
|
||||
return -1;
|
||||
return -510;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (strcmp("1.0", elem)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
|
||||
"not supported\n", elem);
|
||||
return -1;
|
||||
return -528;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -255,7 +345,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
|
||||
pdata->trans = "000000";
|
||||
pdata->endp = NULL;
|
||||
return -1;
|
||||
return -510;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@@ -27,7 +27,6 @@
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
@@ -40,7 +39,7 @@
|
||||
#include <osmocom/mgcp/mgcp_stat.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_ep.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
|
||||
#define RTP_SEQ_MOD (1 << 16)
|
||||
@@ -223,7 +222,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
|
||||
|
||||
if (seq == sstate->last_seq) {
|
||||
if (timestamp != sstate->last_timestamp) {
|
||||
sstate->err_ts_counter += 1;
|
||||
rate_ctr_inc(sstate->err_ts_ctr);
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"The %s timestamp delta is != 0 but the sequence "
|
||||
"number %d is the same, "
|
||||
@@ -231,7 +230,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
|
||||
"on 0x%x SSRC: %u timestamp: %u "
|
||||
"from %s:%d\n",
|
||||
text, seq,
|
||||
state->timestamp_offset, state->seq_offset,
|
||||
state->patch.timestamp_offset, state->patch.seq_offset,
|
||||
ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
}
|
||||
@@ -273,7 +272,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
|
||||
ts_alignment_error(sstate, state->packet_duration, timestamp);
|
||||
|
||||
if (timestamp_error) {
|
||||
sstate->err_ts_counter += 1;
|
||||
rate_ctr_inc(sstate->err_ts_ctr);
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"The %s timestamp has an alignment error of %d "
|
||||
"on 0x%x SSRC: %u "
|
||||
@@ -311,7 +310,7 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
|
||||
ENDPOINT_NUMBER(endp), tsdelta,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
} else {
|
||||
tsdelta = rtp_end->codec.rate * 20 / 1000;
|
||||
tsdelta = rtp_end->codec->rate * 20 / 1000;
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"Fixed packet duration and last timestamp delta "
|
||||
"are not available on 0x%x, "
|
||||
@@ -325,15 +324,15 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
|
||||
out_timestamp = state->out_stream.last_timestamp + delta_seq * tsdelta;
|
||||
timestamp_offset = out_timestamp - in_timestamp;
|
||||
|
||||
if (state->timestamp_offset != timestamp_offset) {
|
||||
state->timestamp_offset = timestamp_offset;
|
||||
if (state->patch.timestamp_offset != timestamp_offset) {
|
||||
state->patch.timestamp_offset = timestamp_offset;
|
||||
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"Timestamp offset change on 0x%x SSRC: %u "
|
||||
"SeqNo delta: %d, TS offset: %d, "
|
||||
"from %s:%d\n",
|
||||
ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
|
||||
delta_seq, state->timestamp_offset,
|
||||
delta_seq, state->patch.timestamp_offset,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
}
|
||||
|
||||
@@ -354,11 +353,11 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
|
||||
/* Align according to: T + Toffs - Tlast = k * Tptime */
|
||||
|
||||
ts_error = ts_alignment_error(&state->out_stream, ptime,
|
||||
timestamp + state->timestamp_offset);
|
||||
timestamp + state->patch.timestamp_offset);
|
||||
|
||||
/* If there is an alignment error, we have to compensate it */
|
||||
if (ts_error) {
|
||||
state->timestamp_offset += ptime - ts_error;
|
||||
state->patch.timestamp_offset += ptime - ts_error;
|
||||
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"Corrected timestamp alignment error of %d on 0x%x SSRC: %u "
|
||||
@@ -366,16 +365,16 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
|
||||
"from %s:%d\n",
|
||||
ts_error,
|
||||
ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
|
||||
state->timestamp_offset, inet_ntoa(addr->sin_addr),
|
||||
state->patch.timestamp_offset, inet_ntoa(addr->sin_addr),
|
||||
ntohs(addr->sin_port));
|
||||
}
|
||||
|
||||
/* Check we really managed to compensate the timestamp
|
||||
* offset. There should not be any remaining error, failing
|
||||
* here would point to a serous problem with the alingnment
|
||||
* error computation fuction */
|
||||
* here would point to a serous problem with the alignment
|
||||
* error computation function */
|
||||
ts_check = ts_alignment_error(&state->out_stream, ptime,
|
||||
timestamp + state->timestamp_offset);
|
||||
timestamp + state->patch.timestamp_offset);
|
||||
OSMO_ASSERT(ts_check == 0);
|
||||
|
||||
/* Return alignment error before compensation */
|
||||
@@ -387,7 +386,7 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
|
||||
* \param[in] destination RTP end
|
||||
* \param[in,out] pointer to buffer with voice data
|
||||
* \param[in] voice data length
|
||||
* \param[in] maxmimum size of caller provided voice data buffer
|
||||
* \param[in] maximum size of caller provided voice data buffer
|
||||
* \returns ignores input parameters, return always 0 */
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
@@ -422,8 +421,8 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
"endpoint:0x%x conn:%s using format defaults\n",
|
||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
|
||||
|
||||
*payload_type = conn->end.codec.payload_type;
|
||||
*audio_name = conn->end.codec.audio_name;
|
||||
*payload_type = conn->end.codec->payload_type;
|
||||
*audio_name = conn->end.codec->audio_name;
|
||||
*fmtp_extra = conn->end.fmtp_extra;
|
||||
}
|
||||
|
||||
@@ -434,14 +433,14 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
|
||||
int32_t d;
|
||||
|
||||
/* initialize or re-initialize */
|
||||
if (!state->stats_initialized || state->stats_ssrc != ssrc) {
|
||||
state->stats_initialized = 1;
|
||||
state->stats_base_seq = seq;
|
||||
state->stats_max_seq = seq - 1;
|
||||
state->stats_ssrc = ssrc;
|
||||
state->stats_jitter = 0;
|
||||
state->stats_transit = transit;
|
||||
state->stats_cycles = 0;
|
||||
if (!state->stats.initialized || state->stats.ssrc != ssrc) {
|
||||
state->stats.initialized = 1;
|
||||
state->stats.base_seq = seq;
|
||||
state->stats.max_seq = seq - 1;
|
||||
state->stats.ssrc = ssrc;
|
||||
state->stats.jitter = 0;
|
||||
state->stats.transit = transit;
|
||||
state->stats.cycles = 0;
|
||||
} else {
|
||||
uint16_t udelta;
|
||||
|
||||
@@ -452,10 +451,10 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
|
||||
* It can't wrap during the initialization so let's
|
||||
* skip it here. The Appendix A probably doesn't have
|
||||
* this issue because of the probation. */
|
||||
udelta = seq - state->stats_max_seq;
|
||||
udelta = seq - state->stats.max_seq;
|
||||
if (udelta < RTP_MAX_DROPOUT) {
|
||||
if (seq < state->stats_max_seq)
|
||||
state->stats_cycles += RTP_SEQ_MOD;
|
||||
if (seq < state->stats.max_seq)
|
||||
state->stats.cycles += RTP_SEQ_MOD;
|
||||
} else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"RTP seqno made a very large jump on 0x%x delta: %u\n",
|
||||
@@ -467,12 +466,12 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
|
||||
* taken closer to the read function. This was taken from the
|
||||
* Appendix A of RFC 3550. Timestamp and arrival_time have a 1/rate
|
||||
* resolution. */
|
||||
d = transit - state->stats_transit;
|
||||
state->stats_transit = transit;
|
||||
d = transit - state->stats.transit;
|
||||
state->stats.transit = transit;
|
||||
if (d < 0)
|
||||
d = -d;
|
||||
state->stats_jitter += d - ((state->stats_jitter + 8) >> 4);
|
||||
state->stats_max_seq = seq;
|
||||
state->stats.jitter += d - ((state->stats.jitter + 8) >> 4);
|
||||
state->stats.max_seq = seq;
|
||||
}
|
||||
|
||||
/* The RFC 3550 Appendix A assumes there are multiple sources but
|
||||
@@ -491,7 +490,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
uint16_t seq;
|
||||
uint32_t timestamp, ssrc;
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
int payload = rtp_end->codec.payload_type;
|
||||
int payload = rtp_end->codec->payload_type;
|
||||
|
||||
if (len < sizeof(*rtp_hdr))
|
||||
return;
|
||||
@@ -499,7 +498,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
rtp_hdr = (struct rtp_hdr *)data;
|
||||
seq = ntohs(rtp_hdr->sequence);
|
||||
timestamp = ntohl(rtp_hdr->timestamp);
|
||||
arrival_time = get_current_ts(rtp_end->codec.rate);
|
||||
arrival_time = get_current_ts(rtp_end->codec->rate);
|
||||
ssrc = ntohl(rtp_hdr->ssrc);
|
||||
transit = arrival_time - timestamp;
|
||||
|
||||
@@ -508,22 +507,24 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
if (!state->initialized) {
|
||||
state->initialized = 1;
|
||||
state->in_stream.last_seq = seq - 1;
|
||||
state->in_stream.ssrc = state->orig_ssrc = ssrc;
|
||||
state->in_stream.ssrc = state->patch.orig_ssrc = ssrc;
|
||||
state->in_stream.last_tsdelta = 0;
|
||||
state->packet_duration =
|
||||
mgcp_rtp_packet_duration(endp, rtp_end);
|
||||
state->out_stream = state->in_stream;
|
||||
state->out_stream.last_seq = seq - 1;
|
||||
state->out_stream.ssrc = state->patch.orig_ssrc = ssrc;
|
||||
state->out_stream.last_tsdelta = 0;
|
||||
state->out_stream.last_timestamp = timestamp;
|
||||
state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */
|
||||
LOGP(DRTP, LOGL_INFO,
|
||||
"endpoint:0x%x initializing stream, SSRC: %u timestamp: %u "
|
||||
"pkt-duration: %d, from %s:%d\n",
|
||||
ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
|
||||
state->seq_offset, state->packet_duration,
|
||||
state->patch.seq_offset, state->packet_duration,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
if (state->packet_duration == 0) {
|
||||
state->packet_duration =
|
||||
rtp_end->codec.rate * 20 / 1000;
|
||||
rtp_end->codec->rate * 20 / 1000;
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"endpoint:0x%x fixed packet duration is not available, "
|
||||
"using fixed 20ms instead: %d from %s:%d\n",
|
||||
@@ -543,7 +544,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
int16_t delta_seq;
|
||||
|
||||
/* Always increment seqno by 1 */
|
||||
state->seq_offset =
|
||||
state->patch.seq_offset =
|
||||
(state->out_stream.last_seq + 1) - seq;
|
||||
|
||||
/* Estimate number of packets that would have been sent */
|
||||
@@ -555,8 +556,8 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
adjust_rtp_timestamp_offset(endp, state, rtp_end, addr,
|
||||
delta_seq, timestamp);
|
||||
|
||||
state->patch_ssrc = 1;
|
||||
ssrc = state->orig_ssrc;
|
||||
state->patch.patch_ssrc = 1;
|
||||
ssrc = state->patch.orig_ssrc;
|
||||
if (rtp_end->force_constant_ssrc != -1)
|
||||
rtp_end->force_constant_ssrc -= 1;
|
||||
|
||||
@@ -565,7 +566,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
"SeqNo offset: %d, TS offset: %d "
|
||||
"from %s:%d\n",
|
||||
ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
|
||||
state->seq_offset, state->timestamp_offset,
|
||||
state->patch.seq_offset, state->patch.timestamp_offset,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
}
|
||||
|
||||
@@ -576,8 +577,8 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
addr, seq, timestamp, "input",
|
||||
&state->in_stream.last_tsdelta);
|
||||
|
||||
if (state->patch_ssrc)
|
||||
ssrc = state->orig_ssrc;
|
||||
if (state->patch.patch_ssrc)
|
||||
ssrc = state->patch.orig_ssrc;
|
||||
}
|
||||
|
||||
/* Save before patching */
|
||||
@@ -592,15 +593,15 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
timestamp);
|
||||
|
||||
/* Store the updated SSRC back to the packet */
|
||||
if (state->patch_ssrc)
|
||||
if (state->patch.patch_ssrc)
|
||||
rtp_hdr->ssrc = htonl(ssrc);
|
||||
|
||||
/* Apply the offset and store it back to the packet.
|
||||
* This won't change anything if the offset is 0, so the conditional is
|
||||
* omitted. */
|
||||
seq += state->seq_offset;
|
||||
seq += state->patch.seq_offset;
|
||||
rtp_hdr->sequence = htons(seq);
|
||||
timestamp += state->timestamp_offset;
|
||||
timestamp += state->patch.timestamp_offset;
|
||||
rtp_hdr->timestamp = htonl(timestamp);
|
||||
|
||||
/* Check again, whether the timestamps are still valid */
|
||||
@@ -693,7 +694,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
dest_name = conn_dst->conn->name;
|
||||
|
||||
if (!rtp_end->output_enabled) {
|
||||
rtp_end->dropped_packets += 1;
|
||||
rtp_end->stats.dropped_packets += 1;
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x output disabled, drop to %s %s "
|
||||
"rtp_port:%u rtcp_port:%u\n",
|
||||
@@ -748,8 +749,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
if (len <= 0)
|
||||
return len;
|
||||
|
||||
conn_dst->end.packets_tx += 1;
|
||||
conn_dst->end.octets_tx += len;
|
||||
conn_dst->end.stats.packets_tx += 1;
|
||||
conn_dst->end.stats.octets_tx += len;
|
||||
|
||||
nbytes += len;
|
||||
buflen = cont;
|
||||
@@ -768,8 +769,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
&rtp_end->addr,
|
||||
rtp_end->rtcp_port, buf, len);
|
||||
|
||||
conn_dst->end.packets_tx += 1;
|
||||
conn_dst->end.octets_tx += len;
|
||||
conn_dst->end.stats.packets_tx += 1;
|
||||
conn_dst->end.stats.octets_tx += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -866,6 +867,15 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn)
|
||||
struct mgcp_endpoint *endp;
|
||||
endp = conn->conn->endp;
|
||||
|
||||
/* Note: it is legal to create a connection but never setting a port
|
||||
* 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));
|
||||
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",
|
||||
@@ -906,7 +916,7 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
|
||||
*proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
|
||||
|
||||
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x ", ENDPOINT_NUMBER(endp));
|
||||
LOGPC(DRTP, LOGL_DEBUG, "receiveing from %s %s %d\n",
|
||||
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),
|
||||
@@ -929,8 +939,8 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
|
||||
}
|
||||
|
||||
/* Increment RX statistics */
|
||||
conn->end.packets_rx += 1;
|
||||
conn->end.octets_rx += rc;
|
||||
conn->end.stats.packets_rx += 1;
|
||||
conn->end.stats.octets_rx += rc;
|
||||
|
||||
/* Forward a copy of the RTP data to a debug ip/port */
|
||||
forward_data(fd->fd, &conn->tap_in, buf, rc);
|
||||
@@ -1044,6 +1054,25 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
|
||||
}
|
||||
|
||||
/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
|
||||
* \param[in] endp Endpoint on which the connection resides.
|
||||
* \param[in] conn Connection that is about to be removed (ignored).
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
|
||||
{
|
||||
struct mgcp_conn *conn_cleanup;
|
||||
|
||||
/* In mgcp_dispatch_rtp_bridge_cb() we use conn->priv to cache the
|
||||
* pointer to the destination connection, so that we do not have
|
||||
* to go through the list every time an RTP packet arrives. To prevent
|
||||
* a use-after-free situation we invalidate this information for all
|
||||
* connections present when one connection is removed from the
|
||||
* endpoint. */
|
||||
llist_for_each_entry(conn_cleanup, &endp->conns, entry) {
|
||||
conn_cleanup->priv = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle incoming RTP data from NET */
|
||||
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
@@ -1118,41 +1147,17 @@ int mgcp_set_ip_tos(int fd, int tos)
|
||||
* \returns 0 on success, -1 on ERROR */
|
||||
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int on = 1;
|
||||
int rc;
|
||||
|
||||
fd->fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd->fd < 0) {
|
||||
LOGP(DRTP, LOGL_ERROR, "failed to create UDP port (%s:%i).\n",
|
||||
source_addr, port);
|
||||
return -1;
|
||||
} else {
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"created UDP port (%s:%i).\n", source_addr, port);
|
||||
}
|
||||
|
||||
if (setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"failed to set socket options (%s:%i).\n", source_addr,
|
||||
port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
inet_aton(source_addr, &addr.sin_addr);
|
||||
|
||||
if (bind(fd->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
close(fd->fd);
|
||||
fd->fd = -1;
|
||||
rc = osmo_sock_init2(AF_INET, SOCK_DGRAM, IPPROTO_UDP, source_addr, port,
|
||||
NULL, 0, OSMO_SOCK_F_BIND);
|
||||
if (rc < 0) {
|
||||
LOGP(DRTP, LOGL_ERROR, "failed to bind UDP port (%s:%i).\n",
|
||||
source_addr, port);
|
||||
return -1;
|
||||
} else {
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"bound UDP port (%s:%i).\n", source_addr, port);
|
||||
}
|
||||
fd->fd = rc;
|
||||
LOGP(DRTP, LOGL_DEBUG, "created socket + bound UDP port (%s:%i).\n", source_addr, port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
|
||||
static struct osmo_fd osmux_fd;
|
||||
|
||||
@@ -142,7 +143,7 @@ osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
|
||||
}
|
||||
|
||||
/* Lookup existing handle for a specified address, if the handle can not be
|
||||
* foud a the function will automatically allocate one */
|
||||
* found, the function will automatically allocate one */
|
||||
static struct osmux_in_handle *
|
||||
osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
|
||||
{
|
||||
@@ -255,8 +256,8 @@ static void scheduled_tx_net_cb(struct msgb *msg, void *data)
|
||||
.sin_port = conn_net->end.rtp_port,
|
||||
};
|
||||
|
||||
conn_bts->end.octets_tx += msg->len;
|
||||
conn_bts->end.packets_tx++;
|
||||
conn_bts->end.stats.octets_tx += msg->len;
|
||||
conn_bts->end.stats.packets_tx++;
|
||||
|
||||
/* Send RTP data to NET */
|
||||
/* FIXME: Get rid of conn_bts and conn_net! */
|
||||
@@ -282,8 +283,8 @@ static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
|
||||
.sin_port = conn_bts->end.rtp_port,
|
||||
};
|
||||
|
||||
conn_net->end.octets_tx += msg->len;
|
||||
conn_net->end.packets_tx++;
|
||||
conn_net->end.stats.octets_tx += msg->len;
|
||||
conn_net->end.stats.packets_tx++;
|
||||
|
||||
/* Send RTP data to BTS */
|
||||
/* FIXME: Get rid of conn_bts and conn_net! */
|
||||
@@ -321,11 +322,10 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmux_hdr *osmuxh;
|
||||
struct llist_head list;
|
||||
struct sockaddr_in addr;
|
||||
struct mgcp_config *cfg = ofd->data;
|
||||
uint32_t rem;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
struct mgcp_conn_rtp *conn_bts = NULL;
|
||||
|
||||
msg = osmux_recv(ofd, &addr);
|
||||
if (!msg)
|
||||
@@ -344,8 +344,8 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
&addr.sin_addr, MGCP_DEST_NET);
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_net)
|
||||
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||
if (!conn_bts)
|
||||
goto out;
|
||||
|
||||
if (!endp) {
|
||||
@@ -354,12 +354,11 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
osmuxh->circuit_id);
|
||||
goto out;
|
||||
}
|
||||
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||
conn_net->osmux.stats.chunks++;
|
||||
conn_bts->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||
conn_bts->osmux.stats.chunks++;
|
||||
rem = msg->len;
|
||||
|
||||
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
|
||||
osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
|
||||
osmux_xfrm_output_sched(&conn_bts->osmux.out, osmuxh);
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
@@ -425,7 +424,6 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmux_hdr *osmuxh;
|
||||
struct llist_head list;
|
||||
struct sockaddr_in addr;
|
||||
struct mgcp_config *cfg = ofd->data;
|
||||
uint32_t rem;
|
||||
@@ -462,8 +460,7 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
conn_net->osmux.stats.chunks++;
|
||||
rem = msg->len;
|
||||
|
||||
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
|
||||
osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
|
||||
osmux_xfrm_output_sched(&conn_net->osmux.out, osmuxh);
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
@@ -552,9 +549,13 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||
switch (endp->cfg->role) {
|
||||
case MGCP_BSC_NAT:
|
||||
conn->type = MGCP_OSMUX_BSC_NAT;
|
||||
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
|
||||
scheduled_tx_net_cb, endp);
|
||||
break;
|
||||
case MGCP_BSC:
|
||||
conn->type = MGCP_OSMUX_BSC;
|
||||
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
|
||||
scheduled_tx_bts_cb, endp);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -575,6 +576,11 @@ void osmux_disable_conn(struct mgcp_conn_rtp *conn)
|
||||
|
||||
LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
|
||||
conn->conn->id, conn->osmux.cid);
|
||||
|
||||
/* We are closing, we don't need pending RTP packets to be transmitted */
|
||||
osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL);
|
||||
osmux_xfrm_output_flush(&conn->osmux.out);
|
||||
|
||||
osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
|
||||
conn->osmux.state = OSMUX_STATE_DISABLED;
|
||||
conn->osmux.cid = -1;
|
||||
|
@@ -38,8 +38,9 @@
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_stat.h>
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_ep.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
|
||||
struct mgcp_request {
|
||||
char *name;
|
||||
@@ -192,17 +193,41 @@ static struct msgb *create_err_response(struct mgcp_endpoint *endp,
|
||||
return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Add MGCP parameters to a message buffer */
|
||||
static int add_params(struct msgb *msg, const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
|
||||
if (endp->wildcarded_req
|
||||
&& endp->tcfg->trunk_type == MGCP_TRUNK_VIRTUAL) {
|
||||
rc = msgb_printf(msg, "Z: %s%x@%s\r\n",
|
||||
MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
|
||||
ENDPOINT_NUMBER(endp), endp->cfg->domain);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = msgb_printf(msg, "I: %s\r\n", conn->conn->id);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Format MGCP response string (with SDP attached) */
|
||||
static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn,
|
||||
const char *msg,
|
||||
const char *trans_id)
|
||||
const char *trans_id,
|
||||
bool add_conn_params)
|
||||
{
|
||||
const char *addr = endp->cfg->local_ip;
|
||||
struct msgb *sdp;
|
||||
int rc;
|
||||
struct msgb *result;
|
||||
char osmux_extension[strlen("\nX-Osmux: 255") + 1];
|
||||
char osmux_extension[strlen("X-Osmux: 255") + 1];
|
||||
char local_ip_addr[INET_ADDRSTRLEN];
|
||||
|
||||
sdp = msgb_alloc_headroom(4096, 128, "sdp record");
|
||||
@@ -215,15 +240,28 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
|
||||
}
|
||||
|
||||
if (conn->osmux.state == OSMUX_STATE_NEGOTIATING) {
|
||||
sprintf(osmux_extension, "\nX-Osmux: %u", conn->osmux.cid);
|
||||
sprintf(osmux_extension, "X-Osmux: %u", conn->osmux.cid);
|
||||
conn->osmux.state = OSMUX_STATE_ACTIVATING;
|
||||
} else {
|
||||
osmux_extension[0] = '\0';
|
||||
}
|
||||
|
||||
rc = msgb_printf(sdp, "I: %s%s\n\n", conn->conn->id, osmux_extension);
|
||||
if (rc < 0)
|
||||
goto error;
|
||||
/* Attach optional connection parameters */
|
||||
if (add_conn_params) {
|
||||
rc = add_params(sdp, endp, conn);
|
||||
if (rc < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Attach optional OSMUX parameters */
|
||||
if (conn->osmux.state == OSMUX_STATE_NEGOTIATING) {
|
||||
rc = msgb_printf(sdp, "%s\r\n", osmux_extension);
|
||||
if (rc < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Attach line break to separate the parameters from the SDP block */
|
||||
rc = msgb_printf(sdp, "\r\n");
|
||||
|
||||
rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
|
||||
if (rc < 0)
|
||||
@@ -252,7 +290,7 @@ static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
{
|
||||
struct mgcp_parse_data pdata;
|
||||
int i, code, handled = 0;
|
||||
int rc, i, code, handled = 0;
|
||||
struct msgb *resp = NULL;
|
||||
char *data;
|
||||
|
||||
@@ -280,13 +318,19 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
memset(&pdata, 0, sizeof(pdata));
|
||||
pdata.cfg = cfg;
|
||||
data = mgcp_strline((char *)msg->l3h, &pdata.save);
|
||||
pdata.found = mgcp_parse_header(&pdata, data);
|
||||
rc = mgcp_parse_header(&pdata, data);
|
||||
if (pdata.endp && pdata.trans
|
||||
&& pdata.endp->last_trans
|
||||
&& strcmp(pdata.endp->last_trans, pdata.trans) == 0) {
|
||||
return do_retransmission(pdata.endp);
|
||||
}
|
||||
|
||||
/* check for general parser failure */
|
||||
if (rc < 0) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "%s: failed to find the endpoint\n", msg->l2h);
|
||||
return create_err_response(NULL, -rc, (const char *) msg->l2h, pdata.trans);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) {
|
||||
if (strncmp
|
||||
(mgcp_requests[i].name, (const char *)&msg->l2h[0],
|
||||
@@ -308,24 +352,20 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
|
||||
|
||||
if (p->found != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"AUEP: failed to find the endpoint.\n");
|
||||
return create_err_response(NULL, 500, "AUEP", p->trans);
|
||||
} else
|
||||
return create_ok_response(p->endp, 200, "AUEP", p->trans);
|
||||
return create_ok_response(p->endp, 200, "AUEP", p->trans);
|
||||
}
|
||||
|
||||
/* Try to find a free port by attemting to bind on it. Also handle the
|
||||
/* Try to find a free port by attempting to bind on it. Also handle the
|
||||
* counter that points on the next free port. Since we have a pointer
|
||||
* to the next free port, binding should work on the first attemt,
|
||||
* neverless, try at least the next 200 ports before giving up */
|
||||
* to the next free port, binding should in work on the first attempt in
|
||||
* general. In case of failure the next port is tryed until the whole port
|
||||
* range is tryed once. */
|
||||
static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
int i;
|
||||
struct mgcp_rtp_end *end;
|
||||
struct mgcp_port_range *range;
|
||||
unsigned int tries;
|
||||
|
||||
OSMO_ASSERT(conn);
|
||||
end = &conn->end;
|
||||
@@ -334,7 +374,8 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
range = &endp->cfg->net_ports;
|
||||
|
||||
/* attempt to find a port */
|
||||
for (i = 0; i < 200; ++i) {
|
||||
tries = (range->range_end - range->range_start) / 2;
|
||||
for (i = 0; i < tries; ++i) {
|
||||
int rc;
|
||||
|
||||
if (range->last_port >= range->range_end)
|
||||
@@ -350,39 +391,177 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Allocating a RTP/RTCP port failed 200 times 0x%x.\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
"Allocating a RTP/RTCP port failed %u times 0x%x.\n",
|
||||
tries, ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! Helper function for check_local_cx_options() to get a pointer of the next
|
||||
* lco option identifier
|
||||
* \param[in] lco string
|
||||
* \returns pointer to the beginning of the LCO identifier, NULL on failure */
|
||||
char *get_lco_identifier(const char *options)
|
||||
{
|
||||
char *ptr;
|
||||
unsigned int count = 0;
|
||||
|
||||
/* Jump to the end of the lco identifier */
|
||||
ptr = strstr(options, ":");
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
|
||||
/* Walk backwards until the pointer points to the beginning of the
|
||||
* lco identifier. We know that we stand at the beginning when we
|
||||
* are either at the beginning of the memory or see a space or
|
||||
* comma. (this is tolerant, it will accept a:10, b:11 as well as
|
||||
* a:10,b:11) */
|
||||
while (1) {
|
||||
/* Endless loop protection */
|
||||
if (count > 10000)
|
||||
return NULL;
|
||||
else if (ptr < options || *ptr == ' ' || *ptr == ',') {
|
||||
ptr++;
|
||||
break;
|
||||
}
|
||||
ptr--;
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Check if we got any result */
|
||||
if (*ptr == ':')
|
||||
return NULL;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/*! Check the LCO option. This function checks for multiple appearence of LCO
|
||||
* options, which is illegal
|
||||
* \param[in] ctx talloc context
|
||||
* \param[in] lco string
|
||||
* \returns 0 on success, -1 on failure */
|
||||
int check_local_cx_options(void *ctx, const char *options)
|
||||
{
|
||||
int i;
|
||||
char *options_copy;
|
||||
char *lco_identifier;
|
||||
char *lco_identifier_end;
|
||||
char *next_lco_identifier;
|
||||
|
||||
char **lco_seen;
|
||||
unsigned int lco_seen_n = 0;
|
||||
|
||||
if (!options)
|
||||
return -1;
|
||||
|
||||
lco_seen =
|
||||
(char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
|
||||
options_copy = talloc_strdup(ctx, options);
|
||||
lco_identifier = options_copy;
|
||||
|
||||
do {
|
||||
/* Move the lco_identifier pointer to the beginning of the
|
||||
* current lco option identifier */
|
||||
lco_identifier = get_lco_identifier(lco_identifier);
|
||||
if (!lco_identifier)
|
||||
goto error;
|
||||
|
||||
/* Look ahead to the next LCO option early, since we
|
||||
* will parse destructively */
|
||||
next_lco_identifier = strstr(lco_identifier + 1, ",");
|
||||
|
||||
/* Pinch off the end of the lco field identifier name
|
||||
* and see if we still got something, also check if
|
||||
* there is some value after the colon. */
|
||||
lco_identifier_end = strstr(lco_identifier, ":");
|
||||
if (!lco_identifier_end)
|
||||
goto error;
|
||||
if (*(lco_identifier_end + 1) == ' '
|
||||
|| *(lco_identifier_end + 1) == ','
|
||||
|| *(lco_identifier_end + 1) == '\0')
|
||||
goto error;
|
||||
*lco_identifier_end = '\0';
|
||||
if (strlen(lco_identifier) == 0)
|
||||
goto error;
|
||||
|
||||
/* Check if we have already seen the current field identifier
|
||||
* before. If yes, we must bail, an LCO must only appear once
|
||||
* in the LCO string */
|
||||
for (i = 0; i < lco_seen_n; i++) {
|
||||
if (strcmp(lco_seen[i], lco_identifier) == 0)
|
||||
goto error;
|
||||
}
|
||||
lco_seen[lco_seen_n] = lco_identifier;
|
||||
lco_seen_n++;
|
||||
|
||||
/* The first identifier must always be found at the beginnning
|
||||
* of the LCO string */
|
||||
if (lco_seen[0] != options_copy)
|
||||
goto error;
|
||||
|
||||
/* Go to the next lco option */
|
||||
lco_identifier = next_lco_identifier;
|
||||
} while (lco_identifier);
|
||||
|
||||
talloc_free(lco_seen);
|
||||
talloc_free(options_copy);
|
||||
return 0;
|
||||
error:
|
||||
talloc_free(lco_seen);
|
||||
talloc_free(options_copy);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set the LCO from a string (see RFC 3435).
|
||||
* The string is stored in the 'string' field. A NULL string is handled excatlyy
|
||||
* The string is stored in the 'string' field. A NULL string is handled exactly
|
||||
* like an empty string, the 'string' field is never NULL after this function
|
||||
* has been called. */
|
||||
static void set_local_cx_options(void *ctx, struct mgcp_lco *lco,
|
||||
static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
|
||||
const char *options)
|
||||
{
|
||||
char *p_opt, *a_opt;
|
||||
char codec[9];
|
||||
|
||||
if (!options)
|
||||
return 0;
|
||||
if (strlen(options) == 0)
|
||||
return 0;
|
||||
|
||||
/* Make sure the encoding of the LCO is consistant before we proceed */
|
||||
if (check_local_cx_options(ctx, options) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"local CX options: Internal inconsistency in Local Connection Options!\n");
|
||||
return 524;
|
||||
}
|
||||
|
||||
talloc_free(lco->string);
|
||||
talloc_free(lco->codec);
|
||||
lco->codec = NULL;
|
||||
lco->pkt_period_min = lco->pkt_period_max = 0;
|
||||
lco->string = talloc_strdup(ctx, options ? options : "");
|
||||
lco->string = talloc_strdup(ctx, options);
|
||||
|
||||
p_opt = strstr(lco->string, "p:");
|
||||
if (p_opt && sscanf(p_opt, "p:%d-%d",
|
||||
&lco->pkt_period_min, &lco->pkt_period_max) == 1)
|
||||
lco->pkt_period_max = lco->pkt_period_min;
|
||||
|
||||
/* FIXME: LCO also supports the negotiation of more then one codec.
|
||||
* (e.g. a:PCMU;G726-32) But this implementation only supports a single
|
||||
* codec only. */
|
||||
a_opt = strstr(lco->string, "a:");
|
||||
if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1)
|
||||
if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1) {
|
||||
talloc_free(lco->codec);
|
||||
lco->codec = talloc_strdup(ctx, codec);
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
|
||||
lco->pkt_period_max, lco->codec);
|
||||
|
||||
/* Check if the packetization fits the 20ms raster */
|
||||
if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"local CX options: packetization interval is not a multiple of 20ms!\n");
|
||||
return 535;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
|
||||
@@ -410,15 +589,15 @@ uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
|
||||
/* Get the number of frames per channel and packet */
|
||||
if (rtp->frames_per_packet)
|
||||
f = rtp->frames_per_packet;
|
||||
else if (rtp->packet_duration_ms && rtp->codec.frame_duration_num) {
|
||||
int den = 1000 * rtp->codec.frame_duration_num;
|
||||
f = (rtp->packet_duration_ms * rtp->codec.frame_duration_den +
|
||||
else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
|
||||
int den = 1000 * rtp->codec->frame_duration_num;
|
||||
f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
|
||||
den / 2)
|
||||
/ den;
|
||||
}
|
||||
|
||||
return rtp->codec.rate * f * rtp->codec.frame_duration_num /
|
||||
rtp->codec.frame_duration_den;
|
||||
return rtp->codec->rate * f * rtp->codec->frame_duration_num /
|
||||
rtp->codec->frame_duration_den;
|
||||
}
|
||||
|
||||
static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
|
||||
@@ -434,6 +613,68 @@ static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
|
||||
return mgcp_parse_osmux_cid(line);
|
||||
}
|
||||
|
||||
/* Process codec information contained in CRCX/MDCX */
|
||||
static int handle_codec_info(struct mgcp_conn_rtp *conn,
|
||||
struct mgcp_parse_data *p, int have_sdp, bool crcx)
|
||||
{
|
||||
struct mgcp_endpoint *endp = p->endp;
|
||||
int rc;
|
||||
char *cmd;
|
||||
|
||||
if (crcx)
|
||||
cmd = "CRCX";
|
||||
else
|
||||
cmd = "MDCX";
|
||||
|
||||
/* Collect codec information */
|
||||
if (have_sdp) {
|
||||
/* If we have SDP, we ignore the local connection options and
|
||||
* use only the SDP information. */
|
||||
mgcp_codec_reset_all(conn);
|
||||
rc = mgcp_parse_sdp_data(endp, conn, p);
|
||||
if (rc != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"%s: endpoint:%x sdp not parseable\n", cmd,
|
||||
ENDPOINT_NUMBER(endp));
|
||||
|
||||
/* See also RFC 3661: Protocol error */
|
||||
return 510;
|
||||
}
|
||||
} else if (endp->local_options.codec) {
|
||||
/* When no SDP is available, we use the codec information from
|
||||
* the local connection options (if present) */
|
||||
mgcp_codec_reset_all(conn);
|
||||
rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec);
|
||||
if (rc != 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Make sure we always set a sane default codec */
|
||||
if (conn->end.codecs_assigned == 0) {
|
||||
/* When SDP and/or LCO did not supply any codec information,
|
||||
* than it makes sense to pick a sane default: (payload-type 0,
|
||||
* PCMU), see also: OS#2658 */
|
||||
mgcp_codec_reset_all(conn);
|
||||
rc = mgcp_codec_add(conn, 0, NULL);
|
||||
if (rc != 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Make codec decision */
|
||||
if (mgcp_codec_decide(conn) != 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"%s: endpoint:0x%x codec negotiation failure\n", cmd,
|
||||
ENDPOINT_NUMBER(endp));
|
||||
|
||||
/* See also RFC 3661: Codec negotiation failure */
|
||||
return 534;
|
||||
}
|
||||
|
||||
/* CRCX command handler, processes the received command */
|
||||
static struct msgb *handle_create_con(struct mgcp_parse_data *p)
|
||||
{
|
||||
@@ -449,12 +690,10 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
struct mgcp_conn *_conn = NULL;
|
||||
char conn_name[512];
|
||||
int rc;
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
|
||||
|
||||
if (p->found != 0)
|
||||
return create_err_response(NULL, 510, "CRCX", p->trans);
|
||||
|
||||
/* parse CallID C: and LocalParameters L: */
|
||||
for_each_line(line, p->save) {
|
||||
if (!mgcp_check_param(endp, line))
|
||||
@@ -491,6 +730,7 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p)
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: endpoint:%x unhandled option: '%c'/%d\n",
|
||||
ENDPOINT_NUMBER(endp), *line, *line);
|
||||
return create_err_response(NULL, 539, "CRCX", p->trans);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -503,14 +743,14 @@ mgcp_header_done:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:%x insufficient parameters, missing callid\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return create_err_response(endp, 400, "CRCX", p->trans);
|
||||
return create_err_response(endp, 516, "CRCX", p->trans);
|
||||
}
|
||||
|
||||
if (!mode) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:%x insufficient parameters, missing mode\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return create_err_response(endp, 400, "CRCX", p->trans);
|
||||
return create_err_response(endp, 517, "CRCX", p->trans);
|
||||
}
|
||||
|
||||
/* Check if we are able to accept the creation of another connection */
|
||||
@@ -526,7 +766,7 @@ mgcp_header_done:
|
||||
} else {
|
||||
/* There is no more room for a connection, leave
|
||||
* everything as it is and return with an error */
|
||||
return create_err_response(endp, 400, "CRCX", p->trans);
|
||||
return create_err_response(endp, 540, "CRCX", p->trans);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,7 +779,7 @@ mgcp_header_done:
|
||||
if (tcfg->force_realloc)
|
||||
/* This is not our call, toss everything by releasing
|
||||
* the entire endpoint. (rude!) */
|
||||
mgcp_release_endp(endp);
|
||||
mgcp_endp_release(endp);
|
||||
else {
|
||||
/* This is not our call, leave everything as it is and
|
||||
* return with an error. */
|
||||
@@ -548,14 +788,10 @@ mgcp_header_done:
|
||||
}
|
||||
|
||||
/* Set the callid, creation of another connection will only be possible
|
||||
* when the callid matches up. (Connections are distinuished by their
|
||||
* when the callid matches up. (Connections are distinguished by their
|
||||
* connection ids) */
|
||||
endp->callid = talloc_strdup(tcfg->endpoints, callid);
|
||||
|
||||
/* Extract audio codec information */
|
||||
set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
|
||||
local_options);
|
||||
|
||||
snprintf(conn_name, sizeof(conn_name), "%s", callid);
|
||||
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, conn_name);
|
||||
if (!_conn) {
|
||||
@@ -586,12 +822,27 @@ mgcp_header_done:
|
||||
goto error2;
|
||||
}
|
||||
|
||||
/* set up RTP media parameters */
|
||||
if (have_sdp)
|
||||
mgcp_parse_sdp_data(endp, conn, p);
|
||||
else if (endp->local_options.codec)
|
||||
mgcp_set_audio_info(p->cfg, &conn->end.codec,
|
||||
PTYPE_UNDEFINED, endp->local_options.codec);
|
||||
/* Set local connection options, if present */
|
||||
if (local_options) {
|
||||
rc = set_local_cx_options(endp->tcfg->endpoints,
|
||||
&endp->local_options, local_options);
|
||||
if (rc != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:%x inavlid local connection options!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
error_code = rc;
|
||||
goto error2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle codec information and decide for a suitable codec */
|
||||
rc = handle_codec_info(conn, p, have_sdp, true);
|
||||
mgcp_codec_summary(conn);
|
||||
if (rc) {
|
||||
error_code = rc;
|
||||
goto error2;
|
||||
}
|
||||
|
||||
conn->end.fmtp_extra = talloc_strdup(tcfg->endpoints,
|
||||
tcfg->audio_fmtp_extra);
|
||||
|
||||
@@ -602,6 +853,17 @@ mgcp_header_done:
|
||||
|
||||
mgcp_rtp_end_config(endp, 0, &conn->end);
|
||||
|
||||
/* check connection mode setting */
|
||||
if (conn->conn->mode != MGCP_CONN_LOOPBACK
|
||||
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
|
||||
&& conn->end.rtp_port == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:%x selected connection mode type requires an opposite end!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
error_code = 527;
|
||||
goto error2;
|
||||
}
|
||||
|
||||
if (allocate_port(endp, conn) != 0) {
|
||||
goto error2;
|
||||
}
|
||||
@@ -623,7 +885,7 @@ mgcp_header_done:
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: endpoint:0x%x CRCX rejected by policy\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
mgcp_release_endp(endp);
|
||||
mgcp_endp_release(endp);
|
||||
return create_err_response(endp, 400, "CRCX", p->trans);
|
||||
break;
|
||||
case MGCP_POLICY_DEFER:
|
||||
@@ -651,15 +913,19 @@ mgcp_header_done:
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: endpoint:0x%x connection successfully created\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return create_response_with_sdp(endp, conn, "CRCX", p->trans);
|
||||
return create_response_with_sdp(endp, conn, "CRCX", p->trans, true);
|
||||
error2:
|
||||
mgcp_release_endp(endp);
|
||||
mgcp_endp_release(endp);
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: endpoint:0x%x unable to create connection resource error\n",
|
||||
"CRCX: endpoint:0x%x unable to create connection\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return create_err_response(endp, error_code, "CRCX", p->trans);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* MDCX command handler, processes the received command */
|
||||
static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
||||
{
|
||||
@@ -672,11 +938,17 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
||||
const char *mode = NULL;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
const char *conn_id = NULL;
|
||||
int rc;
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
|
||||
|
||||
if (p->found != 0)
|
||||
return create_err_response(NULL, 510, "MDCX", p->trans);
|
||||
/* Prohibit wildcarded requests */
|
||||
if (endp->wildcarded_req) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint:0x%x wildcarded endpoint names not supported.\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return create_err_response(endp, 507, "MDCX", p->trans);
|
||||
}
|
||||
|
||||
if (llist_count(&endp->conns) <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
@@ -691,13 +963,17 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
||||
|
||||
switch (line[0]) {
|
||||
case 'C':
|
||||
if (mgcp_verify_call_id(endp, line + 3) != 0)
|
||||
if (mgcp_verify_call_id(endp, line + 3) != 0) {
|
||||
error_code = 516;
|
||||
goto error3;
|
||||
}
|
||||
break;
|
||||
case 'I':
|
||||
conn_id = (const char *)line + 3;
|
||||
if (mgcp_verify_ci(endp, conn_id) != 0)
|
||||
if (mgcp_verify_ci(endp, conn_id) != 0) {
|
||||
error_code = 515;
|
||||
goto error3;
|
||||
}
|
||||
break;
|
||||
case 'L':
|
||||
local_options = (const char *)line + 3;
|
||||
@@ -716,6 +992,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"MDCX: endpoint:0x%x Unhandled MGCP option: '%c'/%d\n",
|
||||
ENDPOINT_NUMBER(endp), line[0], line[0]);
|
||||
return create_err_response(NULL, 539, "MDCX", p->trans);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -725,7 +1002,7 @@ mgcp_header_done:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint:0x%x insufficient parameters, missing ci (connectionIdentifier)\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return create_err_response(endp, 400, "MDCX", p->trans);
|
||||
return create_err_response(endp, 515, "MDCX", p->trans);
|
||||
}
|
||||
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
@@ -740,15 +1017,38 @@ mgcp_header_done:
|
||||
} else
|
||||
conn->conn->mode = conn->conn->mode_orig;
|
||||
|
||||
if (have_sdp)
|
||||
mgcp_parse_sdp_data(endp, conn, p);
|
||||
/* Set local connection options, if present */
|
||||
if (local_options) {
|
||||
rc = set_local_cx_options(endp->tcfg->endpoints,
|
||||
&endp->local_options, local_options);
|
||||
if (rc != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint:%x inavlid local connection options!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
error_code = rc;
|
||||
goto error3;
|
||||
}
|
||||
}
|
||||
|
||||
set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
|
||||
local_options);
|
||||
/* Handle codec information and decide for a suitable codec */
|
||||
rc = handle_codec_info(conn, p, have_sdp, false);
|
||||
mgcp_codec_summary(conn);
|
||||
if (rc) {
|
||||
error_code = rc;
|
||||
goto error3;
|
||||
}
|
||||
|
||||
/* check connection mode setting */
|
||||
if (conn->conn->mode != MGCP_CONN_LOOPBACK
|
||||
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
|
||||
&& conn->end.rtp_port == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint:%x selected connection mode type requires an opposite end!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
error_code = 527;
|
||||
goto error3;
|
||||
}
|
||||
|
||||
if (!have_sdp && endp->local_options.codec)
|
||||
mgcp_set_audio_info(p->cfg, &conn->end.codec,
|
||||
PTYPE_UNDEFINED, endp->local_options.codec);
|
||||
|
||||
if (setup_rtp_processing(endp, conn) != 0)
|
||||
goto error3;
|
||||
@@ -803,7 +1103,7 @@ mgcp_header_done:
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"MDCX: endpoint:0x%x connection successfully modified\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return create_response_with_sdp(endp, conn, "MDCX", p->trans);
|
||||
return create_response_with_sdp(endp, conn, "MDCX", p->trans, false);
|
||||
error3:
|
||||
return create_err_response(endp, error_code, "MDCX", p->trans);
|
||||
|
||||
@@ -824,18 +1124,23 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
const char *conn_id = NULL;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
|
||||
if (p->found != 0)
|
||||
return create_err_response(NULL, error_code, "DLCX", p->trans);
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"DLCX: endpoint:0x%x deleting connection ...\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
|
||||
/* Prohibit wildcarded requests */
|
||||
if (endp->wildcarded_req) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"DLCX: endpoint:0x%x wildcarded endpoint names not supported.\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return create_err_response(endp, 507, "DLCX", p->trans);
|
||||
}
|
||||
|
||||
if (llist_count(&endp->conns) <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"DLCX: endpoint:0x%x endpoint is not holding a connection.\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return create_err_response(endp, 400, "DLCX", p->trans);
|
||||
return create_err_response(endp, 515, "DLCX", p->trans);
|
||||
}
|
||||
|
||||
for_each_line(line, p->save) {
|
||||
@@ -844,13 +1149,17 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
|
||||
switch (line[0]) {
|
||||
case 'C':
|
||||
if (mgcp_verify_call_id(endp, line + 3) != 0)
|
||||
if (mgcp_verify_call_id(endp, line + 3) != 0) {
|
||||
error_code = 516;
|
||||
goto error3;
|
||||
}
|
||||
break;
|
||||
case 'I':
|
||||
conn_id = (const char *)line + 3;
|
||||
if (mgcp_verify_ci(endp, conn_id) != 0)
|
||||
if (mgcp_verify_ci(endp, conn_id) != 0) {
|
||||
error_code = 515;
|
||||
goto error3;
|
||||
}
|
||||
break;
|
||||
case 'Z':
|
||||
silent = strcmp("noanswer", line + 3) == 0;
|
||||
@@ -859,6 +1168,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]);
|
||||
return create_err_response(NULL, 539, "DLCX", p->trans);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -895,7 +1205,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
"DLCX: endpoint:0x%x missing ci (connectionIdentifier), will remove all connections at once\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
|
||||
mgcp_release_endp(endp);
|
||||
mgcp_endp_release(endp);
|
||||
|
||||
/* Note: In this case we do not return any statistics,
|
||||
* as we assume that the client is not interested in
|
||||
@@ -922,7 +1232,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
|
||||
/* When all connections are closed, the endpoint will be released
|
||||
* in order to be ready to be used by another call. */
|
||||
if (llist_count(&endp->conns) <= 0) {
|
||||
mgcp_release_endp(endp);
|
||||
mgcp_endp_release(endp);
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"DLCX: endpoint:0x%x endpoint released\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
@@ -958,12 +1268,6 @@ static struct msgb *handle_rsip(struct mgcp_parse_data *p)
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
|
||||
|
||||
if (p->found != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"RSIP: failed to find the endpoint.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (p->cfg->reset_cb)
|
||||
p->cfg->reset_cb(p->endp->tcfg);
|
||||
return NULL;
|
||||
@@ -989,9 +1293,6 @@ static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
|
||||
|
||||
if (p->found != 0)
|
||||
return create_err_response(NULL, 400, "RQNT", p->trans);
|
||||
|
||||
for_each_line(line, p->save) {
|
||||
switch (line[0]) {
|
||||
case 'S':
|
||||
@@ -1013,7 +1314,7 @@ static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
|
||||
}
|
||||
|
||||
/* Connection keepalive timer, will take care that dummy packets are send
|
||||
* regulary, so that NAT connections stay open */
|
||||
* regularly, so that NAT connections stay open */
|
||||
static void mgcp_keepalive_timer_cb(void *_tcfg)
|
||||
{
|
||||
struct mgcp_trunk_config *tcfg = _tcfg;
|
||||
@@ -1082,6 +1383,8 @@ struct mgcp_config *mgcp_config_alloc(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
|
||||
|
||||
cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
|
||||
cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
|
||||
cfg->net_ports.last_port = cfg->net_ports.range_start;
|
||||
@@ -1184,28 +1487,6 @@ int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! relase endpoint, all open connections are closed.
|
||||
* \param[in] endp endpoint to release */
|
||||
void mgcp_release_endp(struct mgcp_endpoint *endp)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint:0x%x\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
|
||||
/* Normally this function should only be called wehen
|
||||
* all connections have been removed already. In case
|
||||
* that there are still connections open (e.g. when
|
||||
* RSIP is executed), free them all at once. */
|
||||
mgcp_conn_free_all(endp);
|
||||
|
||||
/* Reset endpoint parameters and states */
|
||||
talloc_free(endp->callid);
|
||||
endp->callid = NULL;
|
||||
talloc_free(endp->local_options.string);
|
||||
endp->local_options.string = NULL;
|
||||
talloc_free(endp->local_options.codec);
|
||||
endp->local_options.codec = NULL;
|
||||
}
|
||||
|
||||
static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
|
||||
{
|
||||
return write(cfg->gw_fd.bfd.fd, buf, len);
|
||||
@@ -1218,13 +1499,16 @@ static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
|
||||
* \returns 0 on success, -1 on error */
|
||||
int mgcp_send_reset_all(struct mgcp_config *cfg)
|
||||
{
|
||||
char buf[MGCP_ENDPOINT_MAXLEN + 128];
|
||||
int len;
|
||||
int rc;
|
||||
|
||||
static const char mgcp_reset[] = {
|
||||
"RSIP 1 *@mgw MGCP 1.0\r\n"
|
||||
};
|
||||
len = snprintf(buf, sizeof(buf),
|
||||
"RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
rc = send_agent(cfg, mgcp_reset, sizeof mgcp_reset - 1);
|
||||
rc = send_agent(cfg, buf, len);
|
||||
if (rc <= 0)
|
||||
return -1;
|
||||
|
||||
@@ -1238,17 +1522,15 @@ int mgcp_send_reset_all(struct mgcp_config *cfg)
|
||||
* \returns 0 on success, -1 on error */
|
||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint)
|
||||
{
|
||||
char buf[128];
|
||||
char buf[MGCP_ENDPOINT_MAXLEN + 128];
|
||||
int len;
|
||||
int rc;
|
||||
|
||||
len = snprintf(buf, sizeof(buf),
|
||||
"RSIP 39 %x@mgw MGCP 1.0\r\n", endpoint);
|
||||
"RSIP 39 %x@%s MGCP 1.0\r\n", endpoint, endp->cfg->domain);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
|
||||
rc = send_agent(endp->cfg, buf, len);
|
||||
if (rc <= 0)
|
||||
return -1;
|
||||
|
@@ -24,9 +24,14 @@
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
/* A struct to store intermediate parsing results. The function
|
||||
* mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP
|
||||
* codec information. */
|
||||
struct sdp_rtp_map {
|
||||
/* the type */
|
||||
int payload_type;
|
||||
@@ -39,89 +44,8 @@ struct sdp_rtp_map {
|
||||
int channels;
|
||||
};
|
||||
|
||||
/*! Set codec configuration depending on payload type and codec name.
|
||||
* \param[in] ctx talloc context.
|
||||
* \param[out] codec configuration (caller provided memory).
|
||||
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
|
||||
* \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
|
||||
* \returns 0 on success, -1 on failure. */
|
||||
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
int payload_type, const char *audio_name)
|
||||
{
|
||||
int rate = codec->rate;
|
||||
int channels = codec->channels;
|
||||
char audio_codec[64];
|
||||
|
||||
talloc_free(codec->subtype_name);
|
||||
codec->subtype_name = NULL;
|
||||
talloc_free(codec->audio_name);
|
||||
codec->audio_name = NULL;
|
||||
|
||||
if (payload_type != PTYPE_UNDEFINED)
|
||||
codec->payload_type = payload_type;
|
||||
|
||||
if (!audio_name) {
|
||||
switch (payload_type) {
|
||||
case 0:
|
||||
audio_name = "PCMU/8000/1";
|
||||
break;
|
||||
case 3:
|
||||
audio_name = "GSM/8000/1";
|
||||
break;
|
||||
case 8:
|
||||
audio_name = "PCMA/8000/1";
|
||||
break;
|
||||
case 18:
|
||||
audio_name = "G729/8000/1";
|
||||
break;
|
||||
default:
|
||||
/* Payload type is unknown, don't change rate and
|
||||
* channels. */
|
||||
/* TODO: return value? */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
||||
audio_codec, &rate, &channels) < 1)
|
||||
return -EINVAL;
|
||||
|
||||
codec->rate = rate;
|
||||
codec->channels = channels;
|
||||
codec->subtype_name = talloc_strdup(ctx, audio_codec);
|
||||
codec->audio_name = talloc_strdup(ctx, audio_name);
|
||||
|
||||
if (!strcmp(audio_codec, "G729")) {
|
||||
codec->frame_duration_num = 10;
|
||||
codec->frame_duration_den = 1000;
|
||||
} else {
|
||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
||||
}
|
||||
|
||||
if (payload_type < 0) {
|
||||
payload_type = 96;
|
||||
if (rate == 8000 && channels == 1) {
|
||||
if (!strcmp(audio_codec, "GSM"))
|
||||
payload_type = 3;
|
||||
else if (!strcmp(audio_codec, "PCMA"))
|
||||
payload_type = 8;
|
||||
else if (!strcmp(audio_codec, "PCMU"))
|
||||
payload_type = 0;
|
||||
else if (!strcmp(audio_codec, "G729"))
|
||||
payload_type = 18;
|
||||
}
|
||||
|
||||
codec->payload_type = payload_type;
|
||||
}
|
||||
|
||||
if (channels != 1)
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Channels != 1 in SDP: '%s'\n", audio_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper function to extrapolate missing codec parameters in a codec mao from
|
||||
* an already filled in payload_type, called from: mgcp_parse_sdp_data() */
|
||||
static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
||||
{
|
||||
int i;
|
||||
@@ -148,10 +72,16 @@ static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
||||
codecs[i].rate = 8000;
|
||||
codecs[i].channels = 1;
|
||||
break;
|
||||
default:
|
||||
codecs[i].codec_name = NULL;
|
||||
codecs[i].rate = 0;
|
||||
codecs[i].channels = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to update codec map information with additional data from
|
||||
* SDP, called from: mgcp_parse_sdp_data() */
|
||||
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
||||
int payload, const char *audio_name)
|
||||
{
|
||||
@@ -161,8 +91,13 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
||||
char audio_codec[64];
|
||||
int rate = -1;
|
||||
int channels = -1;
|
||||
|
||||
/* Note: We can only update payload codecs that already exist
|
||||
* in our codec list. If we get an unexpected payload type,
|
||||
* we just drop it */
|
||||
if (codecs[i].payload_type != payload)
|
||||
continue;
|
||||
|
||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
||||
audio_codec, &rate, &channels) < 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
|
||||
@@ -181,43 +116,72 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
||||
audio_name);
|
||||
}
|
||||
|
||||
/* Check if the codec matches what is set up in the trunk config */
|
||||
static int is_codec_compatible(const struct mgcp_endpoint *endp,
|
||||
const struct sdp_rtp_map *codec)
|
||||
/* Extract payload types from SDP, also check for duplicates */
|
||||
static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
|
||||
unsigned int codecs_len, char *sdp)
|
||||
{
|
||||
char *codec_str;
|
||||
char audio_codec[64];
|
||||
char *str;
|
||||
char *str_ptr;
|
||||
char *pt_str;
|
||||
unsigned int pt;
|
||||
unsigned int count = 0;
|
||||
unsigned int i;
|
||||
|
||||
if (!codec->codec_name)
|
||||
return 0;
|
||||
str = talloc_zero_size(ctx, strlen(sdp) + 1);
|
||||
str_ptr = str;
|
||||
strcpy(str_ptr, sdp);
|
||||
|
||||
/* GSM, GSM/8000 and GSM/8000/1 should all be compatible...
|
||||
* let's go by name first. */
|
||||
codec_str = endp->tcfg->audio_name;
|
||||
if (sscanf(codec_str, "%63[^/]/%*d/%*d", audio_codec) < 1)
|
||||
return 0;
|
||||
str_ptr = strstr(str_ptr, "RTP/AVP ");
|
||||
if (!str_ptr)
|
||||
goto exit;
|
||||
|
||||
return strcasecmp(audio_codec, codec->codec_name) == 0;
|
||||
pt_str = strtok(str_ptr, " ");
|
||||
if (!pt_str)
|
||||
goto exit;
|
||||
|
||||
while (1) {
|
||||
/* Do not allow excessive payload types */
|
||||
if (count > codecs_len)
|
||||
goto error;
|
||||
|
||||
pt_str = strtok(NULL, " ");
|
||||
if (!pt_str)
|
||||
break;
|
||||
|
||||
pt = atoi(pt_str);
|
||||
|
||||
/* Do not allow duplicate payload types */
|
||||
for (i = 0; i < count; i++)
|
||||
if (codecs[i].payload_type == pt)
|
||||
goto error;
|
||||
|
||||
codecs[count].payload_type = pt;
|
||||
count++;
|
||||
}
|
||||
|
||||
exit:
|
||||
talloc_free(str);
|
||||
return count;
|
||||
error:
|
||||
talloc_free(str);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! Analyze SDP input string.
|
||||
* \param[in] endp trunk endpoint.
|
||||
* \param[out] conn associated rtp connection.
|
||||
* \param[out] caller provided memory to store the parsing results.
|
||||
* \returns 0 on success, -1 on failure.
|
||||
*
|
||||
* Note: In conn (conn->end) the function returns the packet duration,
|
||||
* the rtp port and the rtcp port */
|
||||
* rtp port, rtcp port and the codec information.
|
||||
* \returns 0 on success, -1 on failure. */
|
||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn,
|
||||
struct mgcp_parse_data *p)
|
||||
struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
|
||||
{
|
||||
struct sdp_rtp_map codecs[10];
|
||||
int codecs_used = 0;
|
||||
struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_used = 0;
|
||||
char *line;
|
||||
int maxptime = -1;
|
||||
int i;
|
||||
int codecs_assigned = 0;
|
||||
unsigned int i;
|
||||
void *tmp_ctx = talloc_new(NULL);
|
||||
struct mgcp_rtp_end *rtp;
|
||||
|
||||
@@ -254,30 +218,21 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
rtp->packet_duration_ms = 0;
|
||||
else
|
||||
rtp->packet_duration_ms = ptime;
|
||||
} else if (sscanf(line, "a=maxptime:%d", &ptime2)
|
||||
== 1) {
|
||||
maxptime = ptime2;
|
||||
} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
|
||||
rtp->maximum_packet_time = ptime2;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
rc = sscanf(line,
|
||||
"m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
|
||||
&port, &codecs[0].payload_type,
|
||||
&codecs[1].payload_type,
|
||||
&codecs[2].payload_type,
|
||||
&codecs[3].payload_type,
|
||||
&codecs[4].payload_type,
|
||||
&codecs[5].payload_type,
|
||||
&codecs[6].payload_type,
|
||||
&codecs[7].payload_type,
|
||||
&codecs[8].payload_type,
|
||||
&codecs[9].payload_type);
|
||||
if (rc >= 2) {
|
||||
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
|
||||
if (rc == 1) {
|
||||
rtp->rtp_port = htons(port);
|
||||
rtp->rtcp_port = htons(port + 1);
|
||||
codecs_used = rc - 1;
|
||||
codecs_initialize(tmp_ctx, codecs, codecs_used);
|
||||
}
|
||||
|
||||
rc = pt_from_sdp(conn->conn, codecs,
|
||||
ARRAY_SIZE(codecs), line);
|
||||
if (rc > 0)
|
||||
codecs_used = rc;
|
||||
break;
|
||||
case 'c':
|
||||
|
||||
@@ -298,43 +253,37 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
break;
|
||||
}
|
||||
}
|
||||
OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
|
||||
|
||||
/* Now select the primary and alt_codec */
|
||||
for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
|
||||
struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
|
||||
&rtp->codec : &rtp->alt_codec;
|
||||
/* So far we have only set the payload type in the codec struct. Now we
|
||||
* fill up the remaining fields of the codec description with some default
|
||||
* information */
|
||||
codecs_initialize(tmp_ctx, codecs, codecs_used);
|
||||
|
||||
if (endp->tcfg->no_audio_transcoding &&
|
||||
!is_codec_compatible(endp, &codecs[i])) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n",
|
||||
codecs[i].codec_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
mgcp_set_audio_info(p->cfg, codec,
|
||||
codecs[i].payload_type, codecs[i].map_line);
|
||||
codecs_assigned += 1;
|
||||
}
|
||||
|
||||
if (codecs_assigned > 0) {
|
||||
/* TODO/XXX: Store this per codec and derive it on use */
|
||||
if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
|
||||
rtp->codec.frame_duration_num * 1500) {
|
||||
/* more than 1 frame */
|
||||
rtp->packet_duration_ms = 0;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Got media info via SDP: port %d, payload %d (%s), "
|
||||
"duration %d, addr %s\n",
|
||||
ntohs(rtp->rtp_port), rtp->codec.payload_type,
|
||||
rtp->codec.subtype_name ? rtp->
|
||||
codec.subtype_name : "unknown", rtp->packet_duration_ms,
|
||||
inet_ntoa(rtp->addr));
|
||||
/* Store parsed codec information */
|
||||
for (i = 0; i < codecs_used; i++) {
|
||||
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
|
||||
if (rc < 0)
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
|
||||
}
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
return codecs_assigned > 0;
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
|
||||
ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
|
||||
rtp->packet_duration_ms);
|
||||
if (codecs_used == 0)
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "none");
|
||||
for (i = 0; i < codecs_used; i++) {
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
|
||||
rtp->codecs[i].payload_type,
|
||||
rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown");
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, " ");
|
||||
}
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Generate SDP response string.
|
||||
@@ -379,7 +328,9 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
|
||||
if (audio_name && endp->tcfg->audio_send_name) {
|
||||
/* FIXME: Check if the payload type is from the static range,
|
||||
* if yes, omitthe a=rtpmap since it is unnecessary */
|
||||
if (audio_name && endp->tcfg->audio_send_name && (payload_type >= 96 && payload_type <= 127)) {
|
||||
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n",
|
||||
payload_type, audio_name);
|
||||
|
||||
|
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include <osmocom/mgcp/mgcp_stat.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
|
||||
@@ -30,10 +31,10 @@ void calc_loss(struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *end, uint32_t *expected,
|
||||
int *loss)
|
||||
{
|
||||
*expected = state->stats_cycles + state->stats_max_seq;
|
||||
*expected = *expected - state->stats_base_seq + 1;
|
||||
*expected = state->stats.cycles + state->stats.max_seq;
|
||||
*expected = *expected - state->stats.base_seq + 1;
|
||||
|
||||
if (!state->stats_initialized) {
|
||||
if (!state->stats.initialized) {
|
||||
*expected = 0;
|
||||
*loss = 0;
|
||||
return;
|
||||
@@ -43,8 +44,8 @@ void calc_loss(struct mgcp_rtp_state *state,
|
||||
* Make sure the sign is correct and use the biggest
|
||||
* positive/negative number that fits.
|
||||
*/
|
||||
*loss = *expected - end->packets_rx;
|
||||
if (*expected < end->packets_rx) {
|
||||
*loss = *expected - end->stats.packets_rx;
|
||||
if (*expected < end->stats.packets_rx) {
|
||||
if (*loss > 0)
|
||||
*loss = INT_MIN;
|
||||
} else {
|
||||
@@ -56,9 +57,9 @@ void calc_loss(struct mgcp_rtp_state *state,
|
||||
/* Helper function for mgcp_format_stats_rtp() to calculate jitter */
|
||||
uint32_t calc_jitter(struct mgcp_rtp_state *state)
|
||||
{
|
||||
if (!state->stats_initialized)
|
||||
if (!state->stats.initialized)
|
||||
return 0;
|
||||
return state->stats_jitter >> 4;
|
||||
return state->stats.jitter >> 4;
|
||||
}
|
||||
|
||||
/* Generate statistics for an RTP connection */
|
||||
@@ -74,8 +75,8 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
|
||||
|
||||
nchars = snprintf(str, str_len,
|
||||
"\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
|
||||
conn->end.packets_tx, conn->end.octets_tx,
|
||||
conn->end.packets_rx, conn->end.octets_rx,
|
||||
conn->end.stats.packets_tx, conn->end.stats.octets_tx,
|
||||
conn->end.stats.packets_rx, conn->end.stats.octets_rx,
|
||||
ploss, jitter);
|
||||
if (nchars < 0 || nchars >= str_len)
|
||||
goto truncate;
|
||||
@@ -83,21 +84,23 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
|
||||
str += nchars;
|
||||
str_len -= nchars;
|
||||
|
||||
/* Error Counter */
|
||||
nchars = snprintf(str, str_len,
|
||||
"\r\nX-Osmo-CP: EC TI=%u, TO=%u",
|
||||
conn->state.in_stream.err_ts_counter,
|
||||
conn->state.out_stream.err_ts_counter);
|
||||
if (nchars < 0 || nchars >= str_len)
|
||||
goto truncate;
|
||||
if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) {
|
||||
/* Error Counter */
|
||||
nchars = snprintf(str, str_len,
|
||||
"\r\nX-Osmo-CP: EC TI=%lu, TO=%lu",
|
||||
conn->state.in_stream.err_ts_ctr->current,
|
||||
conn->state.out_stream.err_ts_ctr->current);
|
||||
if (nchars < 0 || nchars >= str_len)
|
||||
goto truncate;
|
||||
|
||||
str += nchars;
|
||||
str_len -= nchars;
|
||||
str += nchars;
|
||||
str_len -= nchars;
|
||||
|
||||
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
|
||||
snprintf(str, str_len,
|
||||
"\r\nX-Osmux-ST: CR=%u, BR=%u",
|
||||
conn->osmux.stats.chunks, conn->osmux.stats.octets);
|
||||
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
|
||||
snprintf(str, str_len,
|
||||
"\r\nX-Osmux-ST: CR=%u, BR=%u",
|
||||
conn->osmux.stats.chunks, conn->osmux.stats.octets);
|
||||
}
|
||||
}
|
||||
|
||||
truncate:
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/vty.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -63,6 +64,7 @@ struct cmd_node trunk_node = {
|
||||
static int config_write_mgcp(struct vty *vty)
|
||||
{
|
||||
vty_out(vty, "mgcp%s", VTY_NEWLINE);
|
||||
vty_out(vty, " domain %s%s", g_cfg->domain, VTY_NEWLINE);
|
||||
if (g_cfg->local_ip)
|
||||
vty_out(vty, " local ip %s%s", g_cfg->local_ip, VTY_NEWLINE);
|
||||
vty_out(vty, " bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE);
|
||||
@@ -155,19 +157,20 @@ static int config_write_mgcp(struct vty *vty)
|
||||
static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *end)
|
||||
{
|
||||
struct mgcp_rtp_codec *codec = &end->codec;
|
||||
struct mgcp_rtp_codec *codec = end->codec;
|
||||
|
||||
vty_out(vty,
|
||||
" Timestamp Errs: %d->%d%s"
|
||||
" Timestamp Errs: %lu->%lu%s"
|
||||
" Dropped Packets: %d%s"
|
||||
" Payload Type: %d Rate: %u Channels: %d %s"
|
||||
" Frame Duration: %u Frame Denominator: %u%s"
|
||||
" FPP: %d Packet Duration: %u%s"
|
||||
" FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"
|
||||
" Output-Enabled: %d Force-PTIME: %d%s",
|
||||
state->in_stream.err_ts_counter,
|
||||
state->out_stream.err_ts_counter, VTY_NEWLINE,
|
||||
end->dropped_packets, VTY_NEWLINE,
|
||||
state->in_stream.err_ts_ctr->current,
|
||||
state->out_stream.err_ts_ctr->current,
|
||||
VTY_NEWLINE,
|
||||
end->stats.dropped_packets, VTY_NEWLINE,
|
||||
codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
|
||||
codec->frame_duration_num, codec->frame_duration_den,
|
||||
VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms,
|
||||
@@ -176,15 +179,31 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state,
|
||||
end->force_output_ptime, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
|
||||
int verbose)
|
||||
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, int epidx, int verbose)
|
||||
{
|
||||
int i;
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
vty_out(vty, "%s trunk nr %d with %d endpoints:%s",
|
||||
cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
|
||||
cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE);
|
||||
vty_out(vty, "Endpoint %s%d:%s", MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, epidx, VTY_NEWLINE);
|
||||
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
vty_out(vty, " CONN: %s%s",
|
||||
mgcp_conn_dump(conn), VTY_NEWLINE);
|
||||
|
||||
if (verbose) {
|
||||
/* FIXME: Also add verbosity for other
|
||||
* connection types (E1) as soon as
|
||||
* the implementation is available */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
dump_rtp_end(vty, &conn->u.rtp.state,
|
||||
&conn->u.rtp.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_endpoints(struct vty *vty, struct mgcp_trunk_config *cfg, int verbose)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!cfg->endpoints) {
|
||||
vty_out(vty, "No endpoints allocated yet.%s", VTY_NEWLINE);
|
||||
@@ -193,26 +212,22 @@ static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
|
||||
|
||||
for (i = 1; i < cfg->number_endpoints; ++i) {
|
||||
struct mgcp_endpoint *endp = &cfg->endpoints[i];
|
||||
|
||||
vty_out(vty, "Endpoint 0x%.2x:%s", i, VTY_NEWLINE);
|
||||
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
vty_out(vty, " CONN: %s%s",
|
||||
mgcp_conn_dump(conn), VTY_NEWLINE);
|
||||
|
||||
if (verbose) {
|
||||
/* FIXME: Also add verbosity for other
|
||||
* connection types (E1) as soon as
|
||||
* the implementation is available */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
dump_rtp_end(vty, &conn->u.rtp.state,
|
||||
&conn->u.rtp.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
dump_endpoint(vty, endp, i, verbose);
|
||||
if (i < cfg->number_endpoints - 1)
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
|
||||
int verbose)
|
||||
{
|
||||
vty_out(vty, "%s trunk nr %d with %d endpoints:%s",
|
||||
cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
|
||||
cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE);
|
||||
|
||||
dump_endpoints(vty, cfg, verbose);
|
||||
}
|
||||
|
||||
DEFUN(show_mcgp, show_mgcp_cmd,
|
||||
"show mgcp [stats]",
|
||||
SHOW_STR
|
||||
@@ -234,6 +249,33 @@ DEFUN(show_mcgp, show_mgcp_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_mcgp_endpoint, show_mgcp_endpoint_cmd,
|
||||
"show mgcp trunk <0-255> endpoint rtpbridge/<1-65534>",
|
||||
SHOW_STR
|
||||
"Display information about MGCP Media Gateway endpoint\n"
|
||||
"Include Statistics\n")
|
||||
{
|
||||
struct mgcp_trunk_config *trunk;
|
||||
int trunkidx = atoi(argv[1]);
|
||||
int epidx = atoi(argv[2]);
|
||||
int tidx = 0, i;
|
||||
|
||||
llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
|
||||
if (tidx++ == trunkidx) {
|
||||
for (i = 1; i < trunk->number_endpoints; ++i) {
|
||||
struct mgcp_endpoint *endp = &trunk->endpoints[i];
|
||||
if (i == epidx) {
|
||||
dump_endpoint(vty, endp, i, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp, cfg_mgcp_cmd, "mgcp", "Configure the MGCP")
|
||||
{
|
||||
vty->node = MGCP_NODE;
|
||||
@@ -279,13 +321,6 @@ DEFUN(cfg_mgcp_bind_early,
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
static void parse_range(struct mgcp_port_range *range, const char **argv)
|
||||
{
|
||||
range->range_start = atoi(argv[0]);
|
||||
range->range_end = atoi(argv[1]);
|
||||
range->last_port = g_cfg->net_ports.range_start;
|
||||
}
|
||||
|
||||
#define RTP_STR "RTP configuration\n"
|
||||
#define UDP_PORT_STR "UDP Port number\n"
|
||||
#define NET_START_STR "First UDP port allocated\n"
|
||||
@@ -294,11 +329,38 @@ static void parse_range(struct mgcp_port_range *range, const char **argv)
|
||||
|
||||
DEFUN(cfg_mgcp_rtp_port_range,
|
||||
cfg_mgcp_rtp_port_range_cmd,
|
||||
"rtp port-range <0-65534> <0-65534>",
|
||||
"rtp port-range <1024-65534> <1025-65535>",
|
||||
RTP_STR "Range of ports to use for the NET side\n"
|
||||
RANGE_START_STR RANGE_END_STR)
|
||||
{
|
||||
parse_range(&g_cfg->net_ports, argv);
|
||||
int start;
|
||||
int end;
|
||||
|
||||
start = atoi(argv[0]);
|
||||
end = atoi(argv[1]);
|
||||
|
||||
if (end < start) {
|
||||
vty_out(vty, "range end port (%i) must be greater than the range start port (%i)!%s",
|
||||
end, start, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (start & 1) {
|
||||
vty_out(vty, "range must begin at an even port number, autocorrecting port (%i) to: %i%s",
|
||||
start, start & 0xFFFE, VTY_NEWLINE);
|
||||
start &= 0xFFFE;
|
||||
}
|
||||
|
||||
if ((end & 1) == 0) {
|
||||
vty_out(vty, "range must end at an odd port number, autocorrecting port (%i) to: %i%s",
|
||||
end, end | 1, VTY_NEWLINE);
|
||||
end |= 1;
|
||||
}
|
||||
|
||||
g_cfg->net_ports.range_start = start;
|
||||
g_cfg->net_ports.range_end = end;
|
||||
g_cfg->net_ports.last_port = g_cfg->net_ports.range_start;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgcp_rtp_port_range,
|
||||
@@ -1046,7 +1108,7 @@ DEFUN(free_endp, free_endp_cmd,
|
||||
}
|
||||
|
||||
endp = &trunk->endpoints[endp_no];
|
||||
mgcp_release_endp(endp);
|
||||
mgcp_endp_release(endp);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1179,9 +1241,18 @@ DEFUN(cfg_mgcp_osmux_dummy,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_domain,
|
||||
cfg_mgcp_domain_cmd,
|
||||
"domain NAME", "domain\n" "qualified domain name\n")
|
||||
{
|
||||
osmo_strlcpy(g_cfg->domain, argv[0], sizeof(g_cfg->domain));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int mgcp_vty_init(void)
|
||||
{
|
||||
install_element_ve(&show_mgcp_cmd);
|
||||
install_element_ve(&show_mgcp_endpoint_cmd);
|
||||
install_element(ENABLE_NODE, &loop_conn_cmd);
|
||||
install_element(ENABLE_NODE, &tap_rtp_cmd);
|
||||
install_element(ENABLE_NODE, &free_endp_cmd);
|
||||
@@ -1240,6 +1311,7 @@ int mgcp_vty_init(void)
|
||||
install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_domain_cmd);
|
||||
|
||||
install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
|
||||
install_node(&trunk_node, config_write_trunk);
|
||||
|
@@ -8,6 +8,7 @@ AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(LIBBCG729_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
@@ -24,6 +25,7 @@ osmo_bsc_mgcp_LDADD = \
|
||||
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(LIBBCG729_LIBS) \
|
||||
$(LIBRARY_GSM) \
|
||||
$(NULL)
|
||||
|
@@ -244,7 +244,7 @@ int main(int argc, char **argv)
|
||||
msgb_talloc_ctx_init(tall_bsc_ctx, 0);
|
||||
|
||||
osmo_init_ignore_signals();
|
||||
osmo_init_logging(&log_info);
|
||||
osmo_init_logging2(tall_bsc_ctx, &log_info);
|
||||
|
||||
cfg = mgcp_config_alloc();
|
||||
if (!cfg)
|
||||
|
@@ -8,6 +8,8 @@ AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
@@ -20,7 +22,9 @@ osmo_mgw_SOURCES = \
|
||||
$(NULL)
|
||||
|
||||
osmo_mgw_LDADD = \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(NULL)
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/vty.h>
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
@@ -45,12 +46,14 @@
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/stats.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
|
||||
#include "../../bscconfig.h"
|
||||
|
||||
@@ -188,7 +191,7 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
|
||||
/* Walk over all endpoints and trigger a release, this will release all
|
||||
* endpoints, possible open connections are forcefully dropped */
|
||||
for (i = 1; i < reset_trunk->number_endpoints; ++i)
|
||||
mgcp_release_endp(&reset_trunk->endpoints[i]);
|
||||
mgcp_endp_release(&reset_trunk->endpoints[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -250,14 +253,16 @@ const struct log_info log_info = {
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int on = 1, rc;
|
||||
unsigned int flags;
|
||||
int rc;
|
||||
|
||||
tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
|
||||
vty_info.tall_ctx = tall_bsc_ctx;
|
||||
|
||||
msgb_talloc_ctx_init(tall_bsc_ctx, 0);
|
||||
|
||||
osmo_init_ignore_signals();
|
||||
osmo_init_logging(&log_info);
|
||||
osmo_init_logging2(tall_bsc_ctx, &log_info);
|
||||
|
||||
cfg = mgcp_config_alloc();
|
||||
if (!cfg)
|
||||
@@ -266,6 +271,7 @@ int main(int argc, char **argv)
|
||||
vty_info.copyright = osmomgw_copyright;
|
||||
vty_init(&vty_info);
|
||||
logging_vty_add_cmds(NULL);
|
||||
osmo_talloc_vty_add_cmds();
|
||||
osmo_stats_vty_add_cmds(&log_info);
|
||||
mgcp_vty_init();
|
||||
|
||||
@@ -289,53 +295,28 @@ int main(int argc, char **argv)
|
||||
cfg->reset_cb = mgcp_rsip_cb;
|
||||
|
||||
/* we need to bind a socket */
|
||||
if (rc == 0) {
|
||||
cfg->gw_fd.bfd.when = BSC_FD_READ;
|
||||
cfg->gw_fd.bfd.cb = read_call_agent;
|
||||
cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (cfg->gw_fd.bfd.fd < 0) {
|
||||
perror("Gateway failed to listen");
|
||||
return -1;
|
||||
}
|
||||
flags = OSMO_SOCK_F_BIND;
|
||||
if (cfg->call_agent_addr)
|
||||
flags |= OSMO_SOCK_F_CONNECT;
|
||||
|
||||
setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(cfg->source_port);
|
||||
inet_aton(cfg->source_addr, &addr.sin_addr);
|
||||
|
||||
if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
perror("Gateway failed to bind");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
|
||||
if (!cfg->gw_fd.bfd.data) {
|
||||
fprintf(stderr, "Gateway memory error.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cfg->call_agent_addr) {
|
||||
addr.sin_port = htons(2727);
|
||||
inet_aton(cfg->call_agent_addr, &addr.sin_addr);
|
||||
if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n",
|
||||
cfg->call_agent_addr, errno);
|
||||
close(cfg->gw_fd.bfd.fd);
|
||||
cfg->gw_fd.bfd.fd = -1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) {
|
||||
LOGP(DLMGCP, LOGL_FATAL, "Failed to register the fd\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
|
||||
rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||
cfg->source_addr, cfg->source_port,
|
||||
cfg->call_agent_addr, cfg->call_agent_addr ? 2727 : 0, flags);
|
||||
if (rc < 0) {
|
||||
perror("Gateway failed to bind");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfg->gw_fd.bfd.cb = read_call_agent;
|
||||
cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
|
||||
if (!cfg->gw_fd.bfd.data) {
|
||||
fprintf(stderr, "Gateway memory error.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP, listen on %s:%u\n",
|
||||
cfg->source_addr, cfg->source_port);
|
||||
|
||||
/* initialisation */
|
||||
srand(time(NULL));
|
||||
|
||||
|
@@ -268,7 +268,9 @@ static void test_strline(void)
|
||||
"C: 2\r\n"
|
||||
|
||||
#define DLCX_RET "250 7 OK\r\n" \
|
||||
"P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" \
|
||||
"P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n"
|
||||
|
||||
#define DLCX_RET_OSMUX DLCX_RET \
|
||||
"X-Osmo-CP: EC TIS=0, TOS=0, TIR=0, TOR=0\r\n"
|
||||
|
||||
#define RQNT "RQNT 186908780 1@mgw MGCP 1.0\r\n" \
|
||||
@@ -1212,8 +1214,9 @@ const struct log_info log_info = {
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *msgb_ctx = msgb_talloc_ctx_init(NULL, 0);
|
||||
osmo_init_logging(&log_info);
|
||||
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
|
||||
void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
|
||||
osmo_init_logging2(ctx, &log_info);
|
||||
|
||||
test_strline();
|
||||
test_values();
|
||||
|
@@ -588,7 +588,8 @@ const struct log_info log_info = {
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc;
|
||||
osmo_init_logging(&log_info);
|
||||
void *ctx = talloc_named_const(NULL, 0, "mgcp_transcoding_test");
|
||||
osmo_init_logging2(ctx, &log_info);
|
||||
|
||||
printf("=== Transcoding Good Cases ===\n");
|
||||
|
||||
|
@@ -8,6 +8,8 @@ AM_CFLAGS = \
|
||||
-Wall \
|
||||
-ggdb3 \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
@@ -29,9 +31,10 @@ mgcp_test_SOURCES = \
|
||||
$(NULL)
|
||||
|
||||
mgcp_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBRARY_DL) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
-lm \
|
||||
|
@@ -25,7 +25,9 @@
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_stat.h>
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_ep.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
@@ -66,18 +68,18 @@ static void test_strline(void)
|
||||
OSMO_ASSERT(counter == EXPECTED_NUMBER_OF_LINES);
|
||||
}
|
||||
|
||||
#define AUEP1 "AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n"
|
||||
#define AUEP1 "AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0\r\n"
|
||||
#define AUEP1_RET "200 158663169 OK\r\n"
|
||||
#define AUEP2 "AUEP 18983213 ds/e1-2/1@172.16.6.66 MGCP 1.0\r\n"
|
||||
#define AUEP2 "AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0\r\n"
|
||||
#define AUEP2_RET "500 18983213 FAIL\r\n"
|
||||
#define EMPTY "\r\n"
|
||||
#define EMPTY_RET NULL
|
||||
#define SHORT "CRCX \r\n"
|
||||
#define SHORT_RET "510 000000 FAIL\r\n"
|
||||
|
||||
#define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1@172.16.6.66 MGCP 1.0\r\n"
|
||||
#define MDCX_ERR_RET "510 18983213 FAIL\r\n"
|
||||
#define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n"
|
||||
#define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0\r\n"
|
||||
#define MDCX_ERR_RET "500 18983213 FAIL\r\n"
|
||||
#define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0\r\n"
|
||||
#define MDCX_RET "400 18983214 FAIL\r\n"
|
||||
|
||||
#define MDCX3 \
|
||||
@@ -86,8 +88,7 @@ static void test_strline(void)
|
||||
|
||||
#define MDCX3_RET \
|
||||
"200 18983215 OK\r\n" \
|
||||
"I: %s\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"s=-\r\n" \
|
||||
@@ -99,8 +100,7 @@ static void test_strline(void)
|
||||
|
||||
#define MDCX3A_RET \
|
||||
"200 18983215 OK\r\n" \
|
||||
"I: %s\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"s=-\r\n" \
|
||||
@@ -112,8 +112,7 @@ static void test_strline(void)
|
||||
|
||||
#define MDCX3_FMTP_RET \
|
||||
"200 18983215 OK\r\n" \
|
||||
"I: %s\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"s=-\r\n" \
|
||||
@@ -141,8 +140,7 @@ static void test_strline(void)
|
||||
|
||||
#define MDCX4_RET(Ident) \
|
||||
"200 " Ident " OK\r\n" \
|
||||
"I: %s\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"s=-\r\n" \
|
||||
@@ -154,15 +152,14 @@ static void test_strline(void)
|
||||
|
||||
#define MDCX4_RO_RET(Ident) \
|
||||
"200 " Ident " OK\r\n" \
|
||||
"I: %s\n" \
|
||||
"\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"s=-\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 96\r\n" \
|
||||
"a=rtpmap:96 AMR\r\n" \
|
||||
"m=audio 16002 RTP/AVP 112\r\n" \
|
||||
"a=rtpmap:112 AMR\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define MDCX4_PT1 \
|
||||
@@ -252,8 +249,8 @@ static void test_strline(void)
|
||||
|
||||
#define CRCX_RET \
|
||||
"200 2 OK\r\n" \
|
||||
"I: %s\n" \
|
||||
"\n" \
|
||||
"I: %s\r\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"s=-\r\n" \
|
||||
@@ -265,8 +262,8 @@ static void test_strline(void)
|
||||
|
||||
#define CRCX_RET_NO_RTPMAP \
|
||||
"200 2 OK\r\n" \
|
||||
"I: %s\n" \
|
||||
"\n" \
|
||||
"I: %s\r\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"s=-\r\n" \
|
||||
@@ -277,8 +274,8 @@ static void test_strline(void)
|
||||
|
||||
#define CRCX_FMTP_RET \
|
||||
"200 2 OK\r\n" \
|
||||
"I: %s\n" \
|
||||
"\n" \
|
||||
"I: %s\r\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"s=-\r\n" \
|
||||
@@ -301,8 +298,8 @@ static void test_strline(void)
|
||||
|
||||
#define CRCX_ZYN_RET \
|
||||
"200 2 OK\r\n" \
|
||||
"I: %s\n" \
|
||||
"\n" \
|
||||
"I: %s\r\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"s=-\r\n" \
|
||||
@@ -319,7 +316,9 @@ static void test_strline(void)
|
||||
|
||||
#define DLCX_RET \
|
||||
"250 7 OK\r\n" \
|
||||
"P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" \
|
||||
"P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n"
|
||||
|
||||
#define DLCX_RET_OSMUX DLCX_RET \
|
||||
"X-Osmo-CP: EC TI=0, TO=0\r\n"
|
||||
|
||||
#define RQNT \
|
||||
@@ -407,7 +406,7 @@ static void test_strline(void)
|
||||
"v=0\r\n" \
|
||||
"o=- 1439038275 1439038275 IN IP4 192.168.181.247\r\n" \
|
||||
"s=-\r\nc=IN IP4 192.168.181.247\r\n" \
|
||||
"t=0 0\r\nm=audio 29084 RTP/AVP 255 0 8 3 18 4 96 97 101\r\n" \
|
||||
"t=0 0\r\nm=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101\r\n" \
|
||||
"a=rtpmap:0 PCMU/8000\r\n" \
|
||||
"a=rtpmap:8 PCMA/8000\r\n" \
|
||||
"a=rtpmap:3 gsm/8000\r\n" \
|
||||
@@ -428,7 +427,24 @@ static void test_strline(void)
|
||||
"I: %s\r\n" \
|
||||
"\r\n" \
|
||||
"c=IN IP4 8.8.8.8\r\n" \
|
||||
"m=audio 16434 RTP/AVP 255\r\n"
|
||||
"m=audio 16434 RTP/AVP 3\r\n"
|
||||
|
||||
#define CRCX_NO_LCO_NO_SDP \
|
||||
"CRCX 2 6@mgw MGCP 1.0\r\n" \
|
||||
"M: recvonly\r\n" \
|
||||
"C: 2\r\n"
|
||||
|
||||
#define CRCX_NO_LCO_NO_SDP_RET \
|
||||
"200 2 OK\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"s=-\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16008 RTP/AVP 0\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
struct mgcp_test {
|
||||
const char *name;
|
||||
@@ -465,6 +481,7 @@ static const struct mgcp_test tests[] = {
|
||||
{"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE,.extra_fmtp =
|
||||
"a=fmtp:126 0/1/2"},
|
||||
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
|
||||
{"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
|
||||
};
|
||||
|
||||
static const struct mgcp_test retransmit[] = {
|
||||
@@ -561,25 +578,39 @@ static int get_conn_id_from_response(uint8_t *resp, char *conn_id,
|
||||
{
|
||||
char *conn_id_ptr;
|
||||
int i;
|
||||
bool got_conn_id = false;
|
||||
|
||||
/* First try to get the conn_id from the I: parameter */
|
||||
conn_id_ptr = strstr((char *)resp, "I: ");
|
||||
if (!conn_id_ptr)
|
||||
return -EINVAL;
|
||||
|
||||
memset(conn_id, 0, conn_id_len);
|
||||
memcpy(conn_id, conn_id_ptr + 3, 32);
|
||||
|
||||
for (i = 0; i < conn_id_len; i++) {
|
||||
if (conn_id[i] == '\n' || conn_id[i] == '\r')
|
||||
conn_id[i] = '\0';
|
||||
if (conn_id_ptr) {
|
||||
memset(conn_id, 0, conn_id_len);
|
||||
memcpy(conn_id, conn_id_ptr + 3, 32);
|
||||
got_conn_id = true;
|
||||
} else {
|
||||
/* Alternatively try to extract the conn_id from the o=- SDP
|
||||
* parameter */
|
||||
conn_id_ptr = strstr((char *)resp, "o=- ");
|
||||
if(conn_id_ptr) {
|
||||
memset(conn_id, 0, conn_id_len);
|
||||
memcpy(conn_id, conn_id_ptr + 4, 32);
|
||||
got_conn_id = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* A valid conn_id must at least contain one digit, and must
|
||||
* not exceed a length of 32 digits */
|
||||
OSMO_ASSERT(strlen(conn_id) <= 32);
|
||||
OSMO_ASSERT(strlen(conn_id) > 0);
|
||||
if (got_conn_id) {
|
||||
for (i = 0; i < conn_id_len; i++) {
|
||||
if (conn_id[i] == '\n' || conn_id[i] == '\r')
|
||||
conn_id[i] = '\0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
/* A valid conn_id must at least contain one digit, and must
|
||||
* not exceed a length of 32 digits */
|
||||
OSMO_ASSERT(strlen(conn_id) <= 32);
|
||||
OSMO_ASSERT(strlen(conn_id) > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check response, automatically patch connection ID if needed */
|
||||
@@ -753,14 +784,14 @@ static void test_messages(void)
|
||||
fprintf(stderr, "endpoint %d: "
|
||||
"payload type %d (expected %d)\n",
|
||||
last_endpoint,
|
||||
conn->end.codec.payload_type, t->ptype);
|
||||
conn->end.codec->payload_type, t->ptype);
|
||||
|
||||
if (t->ptype != PTYPE_IGNORE)
|
||||
OSMO_ASSERT(conn->end.codec.payload_type ==
|
||||
OSMO_ASSERT(conn->end.codec->payload_type ==
|
||||
t->ptype);
|
||||
|
||||
/* Reset them again for next test */
|
||||
conn->end.codec.payload_type = PTYPE_NONE;
|
||||
conn->end.codec->payload_type = PTYPE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -916,12 +947,12 @@ static void test_packet_loss_calc(void)
|
||||
memset(&state, 0, sizeof(state));
|
||||
memset(&rtp, 0, sizeof(rtp));
|
||||
|
||||
state.stats_initialized = 1;
|
||||
state.stats_base_seq = pl_test_dat[i].base_seq;
|
||||
state.stats_max_seq = pl_test_dat[i].max_seq;
|
||||
state.stats_cycles = pl_test_dat[i].cycles;
|
||||
state.stats.initialized = 1;
|
||||
state.stats.base_seq = pl_test_dat[i].base_seq;
|
||||
state.stats.max_seq = pl_test_dat[i].max_seq;
|
||||
state.stats.cycles = pl_test_dat[i].cycles;
|
||||
|
||||
rtp.packets_rx = pl_test_dat[i].packets;
|
||||
rtp.stats.packets_rx = pl_test_dat[i].packets;
|
||||
calc_loss(&state, &rtp, &expected, &loss);
|
||||
|
||||
if (loss != pl_test_dat[i].loss
|
||||
@@ -1118,10 +1149,12 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
uint32_t last_ssrc = 0;
|
||||
uint32_t last_timestamp = 0;
|
||||
uint32_t last_seqno = 0;
|
||||
int last_in_ts_err_cnt = 0;
|
||||
int last_out_ts_err_cnt = 0;
|
||||
uint64_t last_in_ts_err_cnt = 0;
|
||||
uint64_t last_out_ts_err_cnt = 0;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
struct mgcp_conn *_conn = NULL;
|
||||
struct rate_ctr test_ctr_in;
|
||||
struct rate_ctr test_ctr_out;
|
||||
|
||||
printf("Testing packet error detection%s%s.\n",
|
||||
patch_ssrc ? ", patch SSRC" : "",
|
||||
@@ -1131,6 +1164,11 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
memset(&endp, 0, sizeof(endp));
|
||||
memset(&state, 0, sizeof(state));
|
||||
|
||||
memset(&test_ctr_in, 0, sizeof(test_ctr_in));
|
||||
memset(&test_ctr_out, 0, sizeof(test_ctr_out));
|
||||
state.in_stream.err_ts_ctr = &test_ctr_in;
|
||||
state.out_stream.err_ts_ctr = &test_ctr_out;
|
||||
|
||||
endp.type = &ep_typeset.rtp;
|
||||
|
||||
trunk.vty_number_endpoints = 1;
|
||||
@@ -1149,7 +1187,8 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
|
||||
rtp = &conn->end;
|
||||
|
||||
rtp->codec.payload_type = 98;
|
||||
OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1") == 0);
|
||||
rtp->codec = &rtp->codecs[0];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
|
||||
struct rtp_packet_info *info = test_rtp_packets1 + i;
|
||||
@@ -1175,18 +1214,18 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
state.in_stream.last_tsdelta, state.in_stream.last_seq);
|
||||
|
||||
printf("Out TS change: %d, dTS: %d, Seq change: %d, "
|
||||
"TS Err change: in %+d, out %+d\n",
|
||||
"TS Err change: in +%u, out +%u\n",
|
||||
state.out_stream.last_timestamp - last_timestamp,
|
||||
state.out_stream.last_tsdelta,
|
||||
state.out_stream.last_seq - last_seqno,
|
||||
state.in_stream.err_ts_counter - last_in_ts_err_cnt,
|
||||
state.out_stream.err_ts_counter - last_out_ts_err_cnt);
|
||||
(unsigned int) (state.in_stream.err_ts_ctr->current - last_in_ts_err_cnt),
|
||||
(unsigned int) (state.out_stream.err_ts_ctr->current - last_out_ts_err_cnt));
|
||||
|
||||
printf("Stats: Jitter = %u, Transit = %d\n",
|
||||
calc_jitter(&state), state.stats_transit);
|
||||
calc_jitter(&state), state.stats.transit);
|
||||
|
||||
last_in_ts_err_cnt = state.in_stream.err_ts_counter;
|
||||
last_out_ts_err_cnt = state.out_stream.err_ts_counter;
|
||||
last_in_ts_err_cnt = state.in_stream.err_ts_ctr->current;
|
||||
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;
|
||||
}
|
||||
@@ -1225,8 +1264,7 @@ static void test_multilple_codec(void)
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec.payload_type == 18);
|
||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == 97);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
|
||||
/* Allocate 2@mgw with three codecs, last one ignored */
|
||||
last_endpoint = -1;
|
||||
@@ -1241,10 +1279,14 @@ static void test_multilple_codec(void)
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec.payload_type == 18);
|
||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == 97);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
|
||||
/* Allocate 3@mgw with no codecs, check for PT == -1 */
|
||||
/* Allocate 3@mgw with no codecs, check for PT == 0 */
|
||||
/* Note: It usually makes no sense to leave the payload type list
|
||||
* out. However RFC 2327 does not clearly forbid this case and
|
||||
* it makes and since we already decided in OS#2658 that a missing
|
||||
* LCO should pick a sane default codec, it makes sense to expect
|
||||
* the same behaviour if SDP lacks proper payload type information */
|
||||
last_endpoint = -1;
|
||||
inp = create_msg(CRCX_MULT_3, NULL);
|
||||
resp = mgcp_handle_message(cfg, inp);
|
||||
@@ -1257,8 +1299,7 @@ static void test_multilple_codec(void)
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec.payload_type == -1);
|
||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||
|
||||
/* Allocate 4@mgw with a single codec */
|
||||
last_endpoint = -1;
|
||||
@@ -1273,8 +1314,7 @@ static void test_multilple_codec(void)
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec.payload_type == 18);
|
||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
|
||||
/* Allocate 5@mgw at select GSM.. */
|
||||
last_endpoint = -1;
|
||||
@@ -1292,8 +1332,7 @@ static void test_multilple_codec(void)
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec.payload_type == 3);
|
||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 3);
|
||||
|
||||
inp = create_msg(MDCX_NAT_DUMMY, conn_id);
|
||||
last_endpoint = -1;
|
||||
@@ -1304,8 +1343,7 @@ static void test_multilple_codec(void)
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec.payload_type == 3);
|
||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 3);
|
||||
OSMO_ASSERT(conn->end.rtp_port == htons(16434));
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
inet_aton("8.8.8.8", &addr);
|
||||
@@ -1315,7 +1353,7 @@ static void test_multilple_codec(void)
|
||||
|
||||
/* Free the previous endpoint and the data and
|
||||
* check if the connection really vanished... */
|
||||
mgcp_release_endp(endp);
|
||||
mgcp_endp_release(endp);
|
||||
talloc_free(endp->last_response);
|
||||
talloc_free(endp->last_trans);
|
||||
endp->last_response = endp->last_trans = NULL;
|
||||
@@ -1335,8 +1373,7 @@ static void test_multilple_codec(void)
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec.payload_type == 255);
|
||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == 0);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||
|
||||
talloc_free(cfg);
|
||||
}
|
||||
@@ -1362,31 +1399,31 @@ static void test_no_cycle(void)
|
||||
conn = mgcp_conn_get_rtp(endp, _conn->id);
|
||||
OSMO_ASSERT(conn);
|
||||
|
||||
OSMO_ASSERT(conn->state.stats_initialized == 0);
|
||||
OSMO_ASSERT(conn->state.stats.initialized == 0);
|
||||
|
||||
mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342);
|
||||
OSMO_ASSERT(conn->state.stats_initialized == 1);
|
||||
OSMO_ASSERT(conn->state.stats_cycles == 0);
|
||||
OSMO_ASSERT(conn->state.stats_max_seq == 0);
|
||||
OSMO_ASSERT(conn->state.stats.initialized == 1);
|
||||
OSMO_ASSERT(conn->state.stats.cycles == 0);
|
||||
OSMO_ASSERT(conn->state.stats.max_seq == 0);
|
||||
|
||||
mgcp_rtp_annex_count(endp, &conn->state, 1, 0, 2342);
|
||||
OSMO_ASSERT(conn->state.stats_initialized == 1);
|
||||
OSMO_ASSERT(conn->state.stats_cycles == 0);
|
||||
OSMO_ASSERT(conn->state.stats_max_seq == 1);
|
||||
OSMO_ASSERT(conn->state.stats.initialized == 1);
|
||||
OSMO_ASSERT(conn->state.stats.cycles == 0);
|
||||
OSMO_ASSERT(conn->state.stats.max_seq == 1);
|
||||
|
||||
/* now jump.. */
|
||||
mgcp_rtp_annex_count(endp, &conn->state, UINT16_MAX, 0, 2342);
|
||||
OSMO_ASSERT(conn->state.stats_initialized == 1);
|
||||
OSMO_ASSERT(conn->state.stats_cycles == 0);
|
||||
OSMO_ASSERT(conn->state.stats_max_seq == UINT16_MAX);
|
||||
OSMO_ASSERT(conn->state.stats.initialized == 1);
|
||||
OSMO_ASSERT(conn->state.stats.cycles == 0);
|
||||
OSMO_ASSERT(conn->state.stats.max_seq == UINT16_MAX);
|
||||
|
||||
/* and wrap */
|
||||
mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342);
|
||||
OSMO_ASSERT(conn->state.stats_initialized == 1);
|
||||
OSMO_ASSERT(conn->state.stats_cycles == UINT16_MAX + 1);
|
||||
OSMO_ASSERT(conn->state.stats_max_seq == 0);
|
||||
OSMO_ASSERT(conn->state.stats.initialized == 1);
|
||||
OSMO_ASSERT(conn->state.stats.cycles == UINT16_MAX + 1);
|
||||
OSMO_ASSERT(conn->state.stats.max_seq == 0);
|
||||
|
||||
mgcp_release_endp(endp);
|
||||
mgcp_endp_release(endp);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
@@ -1417,7 +1454,7 @@ static void test_no_name(void)
|
||||
msgb_free(inp);
|
||||
msgb_free(msg);
|
||||
|
||||
mgcp_release_endp(&cfg->trunk.endpoints[1]);
|
||||
mgcp_endp_release(&cfg->trunk.endpoints[1]);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
@@ -1454,10 +1491,113 @@ const struct log_info log_info = {
|
||||
.num_cat = ARRAY_SIZE(log_categories),
|
||||
};
|
||||
|
||||
static void test_get_lco_identifier(void)
|
||||
{
|
||||
char *test;
|
||||
printf("Testing get_lco_identifier()\n");
|
||||
|
||||
/* Normal case at the beginning */
|
||||
test = "p:10, a:PCMU";
|
||||
printf("%s -> %s\n", test, get_lco_identifier(test));
|
||||
|
||||
test = "p:10, a:PCMU";
|
||||
printf("%s -> %s\n", test, get_lco_identifier(test));
|
||||
|
||||
/* Begin parsing in the middle of the value part of
|
||||
* the previous LCO option value */
|
||||
test = "XXXX, p:10, a:PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
test = "XXXX,p:10,a:PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
test = "10,a:PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
test = "10, a:PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
test = "10,a: PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
test = "10 ,a: PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
/* Begin parsing right at the end of the previous LCO
|
||||
* option value */
|
||||
test = ", a:PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
test = " a:PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
/* Empty string, result should be (null) */
|
||||
test = "";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
/* Missing colons, result should be (null) */
|
||||
test = "p10, aPCMU";
|
||||
printf("%s -> %s\n", test, get_lco_identifier(test));
|
||||
|
||||
/* Colon separated from the identifier, result should be (null) */
|
||||
test = "10,a :PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
}
|
||||
|
||||
static void test_check_local_cx_options(void *ctx)
|
||||
{
|
||||
/* Legal cases */
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU") == 0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p:10, IN:10") == 0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "one:AAA, two:BB, tree:C") ==
|
||||
0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:G726-32") == 0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10-20, b:64") == 0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "b:32-64, e:off") == 0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU;G726-32") == 0);
|
||||
|
||||
/* Illegal spaces before and after colon */
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p :10") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "a :PCMU, p:10") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p: 10, a:PCMU") == -1);
|
||||
|
||||
/* Check if multiple appearances of LCOs are rejected */
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, p:10") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, a:PCMU, p:10") ==
|
||||
-1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, p:10") == -1);
|
||||
|
||||
/* Check if empty LCO are rejected */
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p: , a:PCMU") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p: , a: PCMU") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a: PCMU") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:, a:PCMU") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:") == -1);
|
||||
|
||||
/* Garbeled beginning and ends */
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, ":10, a:10") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "10, a:PCMU") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, ", a:PCMU") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, " a:PCMU") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU,") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, ") == -1);
|
||||
|
||||
/* Illegal strings */
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, " ") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "AAA") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, ":,") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, ",:") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *msgb_ctx = msgb_talloc_ctx_init(NULL, 0);
|
||||
osmo_init_logging(&log_info);
|
||||
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
|
||||
void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
|
||||
osmo_init_logging2(ctx, &log_info);
|
||||
|
||||
test_strline();
|
||||
test_values();
|
||||
@@ -1474,6 +1614,8 @@ int main(int argc, char **argv)
|
||||
test_no_cycle();
|
||||
test_no_name();
|
||||
test_osmux_cid();
|
||||
test_get_lco_identifier();
|
||||
test_check_local_cx_options(ctx);
|
||||
|
||||
OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
|
||||
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
|
||||
|
@@ -16,7 +16,7 @@ line: ''
|
||||
Testing AUEP1
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0
|
||||
AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -28,7 +28,7 @@ Response matches our expectations.
|
||||
Testing AUEP2
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
AUEP 18983213 ds/e1-2/1@172.16.6.66 MGCP 1.0
|
||||
AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -40,7 +40,7 @@ Response matches our expectations.
|
||||
Testing MDCX1
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983213 ds/e1-3/1@172.16.6.66 MGCP 1.0
|
||||
MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -52,7 +52,7 @@ Response matches our expectations.
|
||||
Testing MDCX2
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
MDCX 18983214 ds/e1-1/2@172.16.6.66 MGCP 1.0
|
||||
MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
@@ -404,6 +404,21 @@ using message as statically defined for comparison
|
||||
================================================
|
||||
Testing DLCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
DLCX 7 1@mgw MGCP 1.0
|
||||
I: %s
|
||||
C: 2
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message as statically defined for comparison
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
|
||||
================================================
|
||||
Testing CRCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 2 6@mgw MGCP 1.0
|
||||
M: recvonly
|
||||
C: 2
|
||||
@@ -569,7 +584,6 @@ creating message from statically defined input:
|
||||
|
||||
---------8<---------
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
---------8<---------
|
||||
DLCX 7 1@mgw MGCP 1.0
|
||||
I: %s
|
||||
@@ -1032,7 +1046,7 @@ o=- 1439038275 1439038275 IN IP4 192.168.181.247
|
||||
|
||||
---------8<---------
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
---------8<---------
|
||||
CRCX 259260421 5@mgw MGCP 1.0
|
||||
C: 1355c6041e
|
||||
L: p:20, a:GSM, nt:IN
|
||||
@@ -1055,7 +1069,7 @@ C: 1355c6041e
|
||||
a=rtpmap:97 iLBC/8000
|
||||
a=fmtp:97 mode=30
|
||||
a=rtpmap:101 telephone-event/8000
|
||||
a=fmtp:101 0-15
|
||||
a=fmtp:101 0-15
|
||||
a=recvonly
|
||||
|
||||
---------8<---------
|
||||
@@ -1070,7 +1084,7 @@ o=- 1439038275 1439038275 IN IP4 192.168.181.247
|
||||
|
||||
---------8<---------
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
---------8<---------
|
||||
CRCX 259260421 5@mgw MGCP 1.0
|
||||
C: 1355c6041e
|
||||
L: p:20, a:GSM, nt:IN
|
||||
@@ -1105,4 +1119,18 @@ a=ptime:40
|
||||
M: recvonly
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
a=ptime:40
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
Testing get_lco_identifier()
|
||||
p:10, a:PCMU -> p:10, a:PCMU
|
||||
p:10, a:PCMU -> p:10, a:PCMU
|
||||
'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU'
|
||||
|
@@ -18,6 +18,8 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
@@ -93,21 +95,26 @@ static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
|
||||
|
||||
void test_response_cb(struct mgcp_response *response, void *priv)
|
||||
{
|
||||
unsigned int i;
|
||||
OSMO_ASSERT(priv == mgcp);
|
||||
mgcp_response_parse_params(response);
|
||||
|
||||
printf("response cb received:\n"
|
||||
" head.response_code = %d\n"
|
||||
" head.trans_id = %u\n"
|
||||
" head.comment = %s\n"
|
||||
" audio_port = %u\n"
|
||||
" audio_ip = %s\n",
|
||||
response->head.response_code,
|
||||
response->head.trans_id,
|
||||
response->head.comment,
|
||||
response->audio_port,
|
||||
response->audio_ip
|
||||
);
|
||||
printf("response cb received:\n");
|
||||
printf(" head.response_code = %d\n", response->head.response_code);
|
||||
printf(" head.trans_id = %u\n", response->head.trans_id);
|
||||
printf(" head.comment = %s\n", response->head.comment);
|
||||
printf(" audio_port = %u\n", response->audio_port);
|
||||
printf(" audio_ip = %s\n", response->audio_ip);
|
||||
printf(" ptime = %u\n", response->ptime);
|
||||
printf(" codecs_len = %u\n", response->codecs_len);
|
||||
for(i=0;i<response->codecs_len;i++)
|
||||
printf(" codecs[%u] = %u\n", i, response->codecs[i]);
|
||||
printf(" ptmap_len = %u\n", response->ptmap_len);
|
||||
for(i=0;i<response->ptmap_len;i++) {
|
||||
printf(" ptmap[%u].codec = %u\n", i, response->ptmap[i].codec);
|
||||
printf(" ptmap[%u].pt = %u\n", i, response->ptmap[i].pt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
|
||||
@@ -147,8 +154,9 @@ void test_crcx(void)
|
||||
"s=-\r\n"
|
||||
"c=IN IP4 10.9.1.120\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=audio 16002 RTP/AVP 98\r\n"
|
||||
"a=rtpmap:98 AMR/8000\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");
|
||||
}
|
||||
|
||||
@@ -164,7 +172,15 @@ void test_mgcp_msg(void)
|
||||
.audio_port = 1234,
|
||||
.call_id = 47,
|
||||
.conn_id = "11",
|
||||
.conn_mode = MGCP_CONN_RECV_SEND
|
||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||
.ptime = 20,
|
||||
.codecs[0] = CODEC_GSM_8000_1,
|
||||
.codecs[1] = CODEC_AMR_8000_1,
|
||||
.codecs[2] = CODEC_GSMEFR_8000_1,
|
||||
.codecs_len = 1,
|
||||
.ptmap[0].codec = CODEC_GSMEFR_8000_1,
|
||||
.ptmap[0].pt = 96,
|
||||
.ptmap_len = 1
|
||||
};
|
||||
|
||||
if (mgcp)
|
||||
@@ -181,6 +197,26 @@ void test_mgcp_msg(void)
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated CRCX message (two codecs):\n");
|
||||
mgcp_msg.verb = MGCP_VERB_CRCX;
|
||||
mgcp_msg.presence =
|
||||
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
|
||||
mgcp_msg.codecs_len = 2;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated CRCX message (three codecs, one with custom pt):\n");
|
||||
mgcp_msg.verb = MGCP_VERB_CRCX;
|
||||
mgcp_msg.presence =
|
||||
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
|
||||
mgcp_msg.codecs_len = 3;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated MDCX message:\n");
|
||||
mgcp_msg.verb = MGCP_VERB_MDCX;
|
||||
mgcp_msg.presence =
|
||||
@@ -190,6 +226,28 @@ void test_mgcp_msg(void)
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated MDCX message (two codecs):\n");
|
||||
mgcp_msg.verb = MGCP_VERB_MDCX;
|
||||
mgcp_msg.presence =
|
||||
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
|
||||
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
|
||||
mgcp_msg.codecs_len = 2;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated MDCX message (three codecs, one with custom pt):\n");
|
||||
mgcp_msg.verb = MGCP_VERB_MDCX;
|
||||
mgcp_msg.presence =
|
||||
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
|
||||
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
|
||||
mgcp_msg.codecs_len = 3;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated DLCX message:\n");
|
||||
mgcp_msg.verb = MGCP_VERB_DLCX;
|
||||
mgcp_msg.presence =
|
||||
@@ -240,6 +298,9 @@ void test_mgcp_client_cancel()
|
||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
|
||||
| MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE),
|
||||
.ptime = 20,
|
||||
.codecs[0] = CODEC_AMR_8000_1,
|
||||
.codecs_len = 1
|
||||
};
|
||||
|
||||
printf("\n%s():\n", __func__);
|
||||
@@ -271,6 +332,202 @@ void test_mgcp_client_cancel()
|
||||
fprintf(stderr, "%s() done\n", __func__);
|
||||
}
|
||||
|
||||
struct sdp_section_start_test {
|
||||
const char *body;
|
||||
int expect_rc;
|
||||
struct mgcp_response expect_params;
|
||||
};
|
||||
|
||||
static struct sdp_section_start_test sdp_section_start_tests[] = {
|
||||
{
|
||||
.body = "",
|
||||
.expect_rc = -EINVAL,
|
||||
},
|
||||
{
|
||||
.body = "\n\n",
|
||||
},
|
||||
{
|
||||
.body = "\r\n\r\n",
|
||||
},
|
||||
{
|
||||
.body = "\n\r\n\r",
|
||||
},
|
||||
{
|
||||
.body = "some mgcp header data\r\nand header params"
|
||||
"\n\n"
|
||||
"m=audio 23\r\n",
|
||||
.expect_params = {
|
||||
.audio_port = 23,
|
||||
},
|
||||
},
|
||||
{
|
||||
.body = "some mgcp header data\r\nand header params"
|
||||
"\r\n\r\n"
|
||||
"m=audio 23\r\n",
|
||||
.expect_params = {
|
||||
.audio_port = 23,
|
||||
},
|
||||
},
|
||||
{
|
||||
.body = "some mgcp header data\r\nand header params"
|
||||
"\n\r\n\r"
|
||||
"m=audio 23\r\n",
|
||||
.expect_params = {
|
||||
.audio_port = 23,
|
||||
},
|
||||
},
|
||||
{
|
||||
.body = "some mgcp header data\r\nand header params"
|
||||
"\n\r\n"
|
||||
"m=audio 23\r\n",
|
||||
.expect_rc = -EINVAL,
|
||||
},
|
||||
{
|
||||
.body = "some mgcp header data\r\nand header params"
|
||||
"\r\n\r"
|
||||
"m=audio 23\r\n",
|
||||
.expect_rc = -EINVAL,
|
||||
},
|
||||
{
|
||||
.body = "some mgcp header data\r\nand header params"
|
||||
"\n\r\r"
|
||||
"m=audio 23\r\n",
|
||||
.expect_rc = -EINVAL,
|
||||
},
|
||||
};
|
||||
|
||||
void test_sdp_section_start()
|
||||
{
|
||||
int i;
|
||||
int failures = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sdp_section_start_tests); i++) {
|
||||
int rc;
|
||||
struct sdp_section_start_test *t = &sdp_section_start_tests[i];
|
||||
struct mgcp_response *r = talloc_zero(ctx, struct mgcp_response);
|
||||
|
||||
r->body = talloc_strdup(r, t->body);
|
||||
|
||||
printf("\n%s() test [%d]:\n", __func__, i);
|
||||
fprintf(stderr, "\n%s() test [%d]:\n", __func__, i);
|
||||
fprintf(stderr, "body: \"%s\"\n", osmo_escape_str(r->body, -1));
|
||||
|
||||
rc = mgcp_response_parse_params(r);
|
||||
|
||||
fprintf(stderr, "got rc=%d\n", rc);
|
||||
if (rc != t->expect_rc) {
|
||||
fprintf(stderr, "FAIL: Expected rc=%d\n", t->expect_rc);
|
||||
failures++;
|
||||
}
|
||||
if (rc) {
|
||||
talloc_free(r);
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(stderr, "got audio_port=%u\n", t->expect_params.audio_port);
|
||||
if (r->audio_port != t->expect_params.audio_port) {
|
||||
fprintf(stderr, "FAIL: Expected audio_port=%u\n", t->expect_params.audio_port);
|
||||
failures++;
|
||||
}
|
||||
talloc_free(r);
|
||||
}
|
||||
|
||||
OSMO_ASSERT(!failures);
|
||||
}
|
||||
|
||||
static void test_map_pt_to_codec(void)
|
||||
{
|
||||
/* Full form */
|
||||
OSMO_ASSERT(map_str_to_codec("PCMU/8000/1") == CODEC_PCMU_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("GSM/8000/1") == CODEC_GSM_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("PCMA/8000/1") == CODEC_PCMA_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("G729/8000/1") == CODEC_G729_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("GSM-EFR/8000/1") == CODEC_GSMEFR_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("GSM-HR-08/8000/1") == CODEC_GSMHR_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("AMR/8000/1") == CODEC_AMR_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("AMR-WB/16000/1") == CODEC_AMRWB_16000_1);
|
||||
|
||||
/* Short form */
|
||||
OSMO_ASSERT(map_str_to_codec("GSM-EFR") == CODEC_GSMEFR_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("G729") == CODEC_G729_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("GSM-HR-08") == CODEC_GSMHR_8000_1);
|
||||
|
||||
/* We do not care about what is after the first delimiter */
|
||||
OSMO_ASSERT(map_str_to_codec("AMR-WB/123///456") == CODEC_AMRWB_16000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("PCMA/asdf") == CODEC_PCMA_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("GSM/qwertz") == CODEC_GSM_8000_1);
|
||||
|
||||
/* A trailing delimiter should not hurt */
|
||||
OSMO_ASSERT(map_str_to_codec("AMR/") == CODEC_AMR_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("G729/") == CODEC_G729_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("GSM/") == CODEC_GSM_8000_1);
|
||||
|
||||
/* This is expected to fail */
|
||||
OSMO_ASSERT(map_str_to_codec("INVALID/1234/7") == -1);
|
||||
OSMO_ASSERT(map_str_to_codec(NULL) == -1);
|
||||
OSMO_ASSERT(map_str_to_codec("") == -1);
|
||||
OSMO_ASSERT(map_str_to_codec("/////") == -1);
|
||||
|
||||
/* The buffers are 64 bytes long, check what happens with overlong
|
||||
* strings as input (This schould still work.) */
|
||||
OSMO_ASSERT(map_str_to_codec("AMR-WB/16000/1############################################################################################################") == CODEC_AMRWB_16000_1);
|
||||
|
||||
/* This should not work, as there is no delimiter after the codec
|
||||
* name */
|
||||
OSMO_ASSERT(map_str_to_codec("AMR-WB####################################################################################################################") == -1);
|
||||
}
|
||||
|
||||
static void test_map_codec_to_pt_and_map_pt_to_codec(void)
|
||||
{
|
||||
struct ptmap ptmap[10];
|
||||
unsigned int ptmap_len;
|
||||
unsigned int i;
|
||||
|
||||
ptmap[0].codec = CODEC_GSMEFR_8000_1;
|
||||
ptmap[0].pt = 96;
|
||||
ptmap[1].codec = CODEC_GSMHR_8000_1;
|
||||
ptmap[1].pt = 97;
|
||||
ptmap[2].codec = CODEC_AMR_8000_1;
|
||||
ptmap[2].pt = 98;
|
||||
ptmap[3].codec = CODEC_AMRWB_16000_1;
|
||||
ptmap[3].pt = 99;
|
||||
ptmap_len = 4;
|
||||
|
||||
/* Mappings that are covered by the table */
|
||||
for (i = 0; i < ptmap_len; i++)
|
||||
printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
|
||||
for (i = 0; i < ptmap_len; i++)
|
||||
printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
|
||||
printf("\n");
|
||||
|
||||
/* Map some codecs/payload types from the static range, result must
|
||||
* always be a 1:1 mapping */
|
||||
printf(" %u => %u\n", CODEC_PCMU_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMU_8000_1));
|
||||
printf(" %u => %u\n", CODEC_GSM_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_GSM_8000_1));
|
||||
printf(" %u => %u\n", CODEC_PCMA_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMA_8000_1));
|
||||
printf(" %u => %u\n", CODEC_G729_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_G729_8000_1));
|
||||
printf(" %u <= %u\n", CODEC_PCMU_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMU_8000_1));
|
||||
printf(" %u <= %u\n", CODEC_GSM_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_GSM_8000_1));
|
||||
printf(" %u <= %u\n", CODEC_PCMA_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMA_8000_1));
|
||||
printf(" %u <= %u\n", CODEC_G729_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_G729_8000_1));
|
||||
printf("\n");
|
||||
|
||||
/* Try to do mappings from statically defined range to danymic range and vice versa. This
|
||||
* is illegal and should result into a 1:1 mapping */
|
||||
ptmap[3].codec = CODEC_AMRWB_16000_1;
|
||||
ptmap[3].pt = 2;
|
||||
ptmap[4].codec = CODEC_PCMU_8000_1;
|
||||
ptmap[4].pt = 100;
|
||||
ptmap_len = 5;
|
||||
|
||||
/* Apply all mappings again, the illegal ones we defined should result into 1:1 mappings */
|
||||
for (i = 0; i < ptmap_len; i++)
|
||||
printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
|
||||
for (i = 0; i < ptmap_len; i++)
|
||||
printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static const struct log_info_cat log_categories[] = {
|
||||
};
|
||||
|
||||
@@ -284,7 +541,7 @@ int main(int argc, char **argv)
|
||||
{
|
||||
ctx = talloc_named_const(NULL, 1, "mgcp_client_test");
|
||||
msgb_talloc_ctx_init(ctx, 0);
|
||||
osmo_init_logging(&log_info);
|
||||
osmo_init_logging2(ctx, &log_info);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
log_set_print_timestamp(osmo_stderr_target, 0);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
@@ -297,6 +554,9 @@ int main(int argc, char **argv)
|
||||
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();
|
||||
|
||||
printf("Done\n");
|
||||
fprintf(stderr, "Done\n");
|
||||
|
@@ -12,4 +12,58 @@ DLMGCP Cannot find matching MGCP transaction for trans_id 1
|
||||
- canceling again does nothing
|
||||
DLMGCP Cannot cancel, no such transaction: 1
|
||||
test_mgcp_client_cancel() done
|
||||
|
||||
test_sdp_section_start() test [0]:
|
||||
body: ""
|
||||
DLMGCP MGCP response: cannot find start of SDP parameters
|
||||
got rc=-22
|
||||
|
||||
test_sdp_section_start() test [1]:
|
||||
body: "\n\n"
|
||||
got rc=0
|
||||
got audio_port=0
|
||||
|
||||
test_sdp_section_start() test [2]:
|
||||
body: "\r\n\r\n"
|
||||
got rc=0
|
||||
got audio_port=0
|
||||
|
||||
test_sdp_section_start() test [3]:
|
||||
body: "\n\r\n\r"
|
||||
got rc=0
|
||||
got audio_port=0
|
||||
|
||||
test_sdp_section_start() test [4]:
|
||||
body: "some mgcp header data\r\nand header params\n\nm=audio 23\r\n"
|
||||
got rc=0
|
||||
got audio_port=23
|
||||
|
||||
test_sdp_section_start() test [5]:
|
||||
body: "some mgcp header data\r\nand header params\r\n\r\nm=audio 23\r\n"
|
||||
got rc=0
|
||||
got audio_port=23
|
||||
|
||||
test_sdp_section_start() test [6]:
|
||||
body: "some mgcp header data\r\nand header params\n\r\n\rm=audio 23\r\n"
|
||||
got rc=0
|
||||
got audio_port=23
|
||||
|
||||
test_sdp_section_start() test [7]:
|
||||
body: "some mgcp header data\r\nand header params\n\r\nm=audio 23\r\n"
|
||||
DLMGCP MGCP response: cannot find start of SDP parameters
|
||||
got rc=-22
|
||||
|
||||
test_sdp_section_start() test [8]:
|
||||
body: "some mgcp header data\r\nand header params\r\n\rm=audio 23\r\n"
|
||||
DLMGCP MGCP response: cannot find start of SDP parameters
|
||||
got rc=-22
|
||||
|
||||
test_sdp_section_start() test [9]:
|
||||
body: "some mgcp header data\r\nand header params\n\r\rm=audio 23\r\n"
|
||||
DLMGCP MGCP response: cannot find start of SDP parameters
|
||||
got rc=-22
|
||||
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
|
||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
|
||||
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
|
||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
|
||||
Done
|
||||
|
@@ -18,8 +18,9 @@ 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 98
|
||||
a=rtpmap:98 AMR/8000
|
||||
m=audio 16002 RTP/AVP 110 96
|
||||
a=rtpmap:110 AMR/8000
|
||||
a=rtpmap:96 GSM-EFR/8000
|
||||
a=ptime:20
|
||||
|
||||
-----
|
||||
@@ -29,33 +30,92 @@ response cb received:
|
||||
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
|
||||
I: 11
|
||||
L: p:20, a:AMR, nt:IN
|
||||
L: p:20, a:GSM, nt:IN
|
||||
M: sendrecv
|
||||
|
||||
Generated CRCX message (two codecs):
|
||||
CRCX 2 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
L: p:20, a:GSM;AMR, nt:IN
|
||||
M: sendrecv
|
||||
|
||||
Generated CRCX message (three codecs, one with custom pt):
|
||||
CRCX 3 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
L: p:20, a:GSM;AMR;GSM-EFR, nt:IN
|
||||
M: sendrecv
|
||||
|
||||
Generated MDCX message:
|
||||
MDCX 2 23@mgw MGCP 1.0
|
||||
MDCX 4 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
M: sendrecv
|
||||
|
||||
v=0
|
||||
o=- 2f 23 IN IP4 127.0.0.1
|
||||
s=-
|
||||
c=IN IP4 192.168.100.23
|
||||
m=audio 1234 RTP/AVP 255
|
||||
t=0 0
|
||||
m=audio 1234 RTP/AVP 3
|
||||
a=ptime:20
|
||||
|
||||
Generated MDCX message (two codecs):
|
||||
MDCX 5 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
M: sendrecv
|
||||
|
||||
v=0
|
||||
o=- 2f 23 IN IP4 127.0.0.1
|
||||
s=-
|
||||
c=IN IP4 192.168.100.23
|
||||
t=0 0
|
||||
m=audio 1234 RTP/AVP 3 112
|
||||
a=rtpmap:112 AMR/8000/1
|
||||
a=ptime:20
|
||||
|
||||
Generated MDCX message (three codecs, one with custom pt):
|
||||
MDCX 6 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
M: sendrecv
|
||||
|
||||
v=0
|
||||
o=- 2f 23 IN IP4 127.0.0.1
|
||||
s=-
|
||||
c=IN IP4 192.168.100.23
|
||||
t=0 0
|
||||
m=audio 1234 RTP/AVP 3 112 96
|
||||
a=rtpmap:112 AMR/8000/1
|
||||
a=rtpmap:96 GSM-EFR/8000/1
|
||||
a=ptime:20
|
||||
|
||||
Generated DLCX message:
|
||||
DLCX 3 23@mgw MGCP 1.0
|
||||
DLCX 7 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
|
||||
Generated AUEP message:
|
||||
AUEP 4 23@mgw MGCP 1.0
|
||||
AUEP 8 23@mgw MGCP 1.0
|
||||
|
||||
Generated RSIP message:
|
||||
RSIP 5 23@mgw MGCP 1.0
|
||||
RSIP 9 23@mgw MGCP 1.0
|
||||
|
||||
Overfolow test:
|
||||
|
||||
@@ -78,4 +138,53 @@ I: 1
|
||||
v=0
|
||||
|
||||
-----
|
||||
|
||||
test_sdp_section_start() test [0]:
|
||||
|
||||
test_sdp_section_start() test [1]:
|
||||
|
||||
test_sdp_section_start() test [2]:
|
||||
|
||||
test_sdp_section_start() test [3]:
|
||||
|
||||
test_sdp_section_start() test [4]:
|
||||
|
||||
test_sdp_section_start() test [5]:
|
||||
|
||||
test_sdp_section_start() test [6]:
|
||||
|
||||
test_sdp_section_start() test [7]:
|
||||
|
||||
test_sdp_section_start() test [8]:
|
||||
|
||||
test_sdp_section_start() test [9]:
|
||||
110 => 96
|
||||
111 => 97
|
||||
112 => 98
|
||||
113 => 99
|
||||
96 <= 110
|
||||
97 <= 111
|
||||
98 <= 112
|
||||
99 <= 113
|
||||
|
||||
0 => 0
|
||||
3 => 3
|
||||
8 => 8
|
||||
18 => 18
|
||||
0 <= 0
|
||||
3 <= 3
|
||||
8 <= 8
|
||||
18 <= 18
|
||||
|
||||
110 => 96
|
||||
111 => 97
|
||||
112 => 98
|
||||
113 => 113
|
||||
0 => 0
|
||||
96 <= 110
|
||||
97 <= 111
|
||||
98 <= 112
|
||||
2 <= 2
|
||||
100 <= 100
|
||||
|
||||
Done
|
||||
|
Reference in New Issue
Block a user