mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-04 05:53:26 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			stsp/show_
			...
			1.2.1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					ae37c7892f | ||
| 
						 | 
					f9331904bc | 
@@ -20,6 +20,7 @@ pkgconfigdir = $(libdir)/pkgconfig
 | 
			
		||||
pkgconfig_DATA = \
 | 
			
		||||
	libosmo-legacy-mgcp.pc \
 | 
			
		||||
	libosmo-mgcp-client.pc \
 | 
			
		||||
	libosmo-mgcp.pc \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
BUILT_SOURCES = $(top_srcdir)/.version
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										41
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								configure.ac
									
									
									
									
									
								
							@@ -39,40 +39,9 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
 | 
			
		||||
AC_SUBST(LIBRARY_DL)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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(
 | 
			
		||||
		[--enable-sanitize],
 | 
			
		||||
		[Compile with address sanitizer enabled],
 | 
			
		||||
	)],
 | 
			
		||||
	[sanitize=$enableval], [sanitize="no"])
 | 
			
		||||
if test x"$sanitize" = x"yes"
 | 
			
		||||
then
 | 
			
		||||
	CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
 | 
			
		||||
	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
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.10.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.10.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.1.0)
 | 
			
		||||
 | 
			
		||||
# 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.])],
 | 
			
		||||
@@ -144,15 +113,13 @@ 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 --enable-werror
 | 
			
		||||
./configure $MGCP --enable-vty-tests --enable-external-tests
 | 
			
		||||
$MAKE $PARALLEL_MAKE
 | 
			
		||||
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
 | 
			
		||||
  || cat-testlogs.sh
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Osmocom Media Gateway (MGW)
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
Restart=always
 | 
			
		||||
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
 | 
			
		||||
RestartSec=2
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
							
								
								
									
										125
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										125
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,127 +1,8 @@
 | 
			
		||||
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"
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.2.1) unstable; urgency=medium
 | 
			
		||||
  [ 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
 | 
			
		||||
  * debian: fix osmo-mgw, it should depend on libosmo-mgcp1, not 0
 | 
			
		||||
 | 
			
		||||
  [ 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
 | 
			
		||||
 -- Harald Welte <laforge@gnumonks.org>  Fri, 13 Apr 2018 08:43:29 +0200
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.2.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -16,10 +16,25 @@ Homepage: https://osmocom.org/projects/osmo-mgw
 | 
			
		||||
Package: osmo-mgw
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: foreign
 | 
			
		||||
Depends: ${misc:Depends}, ${shlibs:Depends}
 | 
			
		||||
Depends: libosmo-mgcp1, ${misc:Depends}, ${shlibs:Depends}
 | 
			
		||||
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
 | 
			
		||||
 | 
			
		||||
Package: libosmo-mgcp-client3
 | 
			
		||||
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
 | 
			
		||||
Section: libs
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
@@ -31,7 +46,7 @@ Package: libosmo-mgcp-client-dev
 | 
			
		||||
Section: libdevel
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
Depends: libosmo-mgcp-client3 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Depends: libosmo-mgcp-client2 (= ${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,25 +21,6 @@ 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
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								debian/libosmo-mgcp-dev.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
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
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/libosmo-mgcp1.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
usr/lib/*/libosmo-mgcp.so.*
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/osmo-mgw.service
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-mgw.service
									
									
									
									
										vendored
									
									
								
							@@ -1 +0,0 @@
 | 
			
		||||
../contrib/systemd/osmo-mgw.service
 | 
			
		||||
@@ -2,17 +2,12 @@
 | 
			
		||||
! MGCP configuration example
 | 
			
		||||
!
 | 
			
		||||
mgcp
 | 
			
		||||
  bind ip 127.0.0.1
 | 
			
		||||
  rtp port-range 4002 16000
 | 
			
		||||
  rtp bind-ip 10.9.1.122
 | 
			
		||||
  rtp ip-probing
 | 
			
		||||
  rtp ip-tos 184
 | 
			
		||||
 !local ip 10.23.24.2
 | 
			
		||||
 !bts ip 10.24.24.1
 | 
			
		||||
 !bind ip 10.23.24.1
 | 
			
		||||
 bind port 2427
 | 
			
		||||
 rtp force-ptime 20
 | 
			
		||||
 sdp audio payload number 98
 | 
			
		||||
  sdp audio payload name GSM
 | 
			
		||||
 sdp audio payload name AMR/8000
 | 
			
		||||
 number endpoints 31
 | 
			
		||||
  loop 0
 | 
			
		||||
  force-realloc 1
 | 
			
		||||
  rtcp-omit
 | 
			
		||||
  rtp-patch ssrc
 | 
			
		||||
  rtp-patch timestamp
 | 
			
		||||
 no rtcp-omit
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,6 @@ 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,12 +243,6 @@ 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,7 +25,6 @@
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/netif/jibuf.h>
 | 
			
		||||
 | 
			
		||||
#define CI_UNUSED 0
 | 
			
		||||
 | 
			
		||||
@@ -199,14 +198,6 @@ 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)			\
 | 
			
		||||
@@ -344,8 +335,3 @@ 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,8 +1,31 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#ifndef OPENBSC_VTY_H
 | 
			
		||||
#define OPENBSC_VTY_H
 | 
			
		||||
 | 
			
		||||
#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,8 +3,7 @@ noinst_HEADERS = \
 | 
			
		||||
	mgcp_msg.h \
 | 
			
		||||
	mgcp_conn.h \
 | 
			
		||||
	mgcp_stat.h \
 | 
			
		||||
	mgcp_endp.h \
 | 
			
		||||
	mgcp_ep.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 behaviour is controlled via the keepalive_interval member of the
 | 
			
		||||
 * sent. The beviour is controlled viw 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 timer is scheduled and the
 | 
			
		||||
 * is scheduled. For all vales greater 0, the a 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)
 | 
			
		||||
@@ -151,7 +151,6 @@ struct mgcp_trunk_config {
 | 
			
		||||
	int rtp_accept_all;
 | 
			
		||||
 | 
			
		||||
	unsigned int number_endpoints;
 | 
			
		||||
	int vty_number_endpoints;
 | 
			
		||||
	struct mgcp_endpoint *endpoints;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -212,8 +211,6 @@ struct mgcp_config {
 | 
			
		||||
	 * message.
 | 
			
		||||
	 */
 | 
			
		||||
	uint16_t osmux_dummy;
 | 
			
		||||
	/* domain name of the media gateway */
 | 
			
		||||
	char domain[255+1];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* config management */
 | 
			
		||||
@@ -222,6 +219,7 @@ 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);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +0,0 @@
 | 
			
		||||
#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,22 +68,4 @@ 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
 | 
			
		||||
 
 | 
			
		||||
@@ -28,11 +28,12 @@
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
 | 
			
		||||
				  enum mgcp_conn_type type, char *name);
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
 | 
			
		||||
				  uint32_t id, enum mgcp_conn_type type,
 | 
			
		||||
				  char *name);
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, uint32_t id);
 | 
			
		||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
					const char *id);
 | 
			
		||||
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id);
 | 
			
		||||
					uint32_t id);
 | 
			
		||||
void mgcp_conn_free(struct mgcp_endpoint *endp, uint32_t id);
 | 
			
		||||
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
 | 
			
		||||
char *mgcp_conn_dump(struct mgcp_conn *conn);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,99 +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;
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										50
									
								
								include/osmocom/mgcp/mgcp_ep.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								include/osmocom/mgcp/mgcp_ep.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
/* 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,15 +27,11 @@
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/counter.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
 | 
			
		||||
#define CI_UNUSED 0
 | 
			
		||||
 | 
			
		||||
/* FIXME: This this is only needed to compile the currently
 | 
			
		||||
 * broken OSMUX support. Remove when fixed */
 | 
			
		||||
#define CONN_ID_BTS "0"
 | 
			
		||||
#define CONN_ID_NET "1"
 | 
			
		||||
#define CONN_ID_BTS 0
 | 
			
		||||
#define CONN_ID_NET 1
 | 
			
		||||
 | 
			
		||||
enum mgcp_trunk_type {
 | 
			
		||||
	MGCP_TRUNK_VIRTUAL,
 | 
			
		||||
@@ -46,43 +42,33 @@ struct mgcp_rtp_stream_state {
 | 
			
		||||
	uint32_t ssrc;
 | 
			
		||||
	uint16_t last_seq;
 | 
			
		||||
	uint32_t last_timestamp;
 | 
			
		||||
	struct rate_ctr *err_ts_ctr;
 | 
			
		||||
	uint32_t err_ts_counter;
 | 
			
		||||
	int32_t last_tsdelta;
 | 
			
		||||
	uint32_t last_arrival_time;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_state {
 | 
			
		||||
	/* has this state structure been initialized? */
 | 
			
		||||
	int initialized;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		/* are we patching the SSRC value? */
 | 
			
		||||
	int patch_ssrc;
 | 
			
		||||
		/* original SSRC (to which we shall patch any different SSRC) */
 | 
			
		||||
		uint32_t orig_ssrc;
 | 
			
		||||
		/* offset to apply on the sequence number */
 | 
			
		||||
		int seq_offset;
 | 
			
		||||
		/* offset to apply on the timestamp number */
 | 
			
		||||
		int32_t timestamp_offset;
 | 
			
		||||
	} patch;
 | 
			
		||||
 | 
			
		||||
	/* duration of a packet (FIXME: in which unit?) */
 | 
			
		||||
	uint32_t orig_ssrc;
 | 
			
		||||
 | 
			
		||||
	int seq_offset;
 | 
			
		||||
 | 
			
		||||
	int32_t  timestamp_offset;
 | 
			
		||||
	uint32_t packet_duration;
 | 
			
		||||
 | 
			
		||||
	struct mgcp_rtp_stream_state in_stream;
 | 
			
		||||
	struct mgcp_rtp_stream_state out_stream;
 | 
			
		||||
 | 
			
		||||
	/* jitter and packet loss calculation */
 | 
			
		||||
	struct {
 | 
			
		||||
		int initialized;
 | 
			
		||||
		uint16_t base_seq;
 | 
			
		||||
		uint16_t max_seq;
 | 
			
		||||
		uint32_t ssrc;
 | 
			
		||||
		uint32_t jitter;
 | 
			
		||||
		int32_t transit;
 | 
			
		||||
		int cycles;
 | 
			
		||||
	} stats;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -97,61 +83,43 @@ struct mgcp_rtp_codec {
 | 
			
		||||
	char *subtype_name;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
 | 
			
		||||
struct mgcp_rtp_end {
 | 
			
		||||
	/* statistics */
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
	/* audio codec information */
 | 
			
		||||
	struct mgcp_rtp_codec codec;
 | 
			
		||||
	struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -187,7 +155,7 @@ struct mgcp_conn_rtp {
 | 
			
		||||
	/* Sequence bits */
 | 
			
		||||
	struct mgcp_rtp_state state;
 | 
			
		||||
 | 
			
		||||
	/* taps for the rtp connection; one per direction */
 | 
			
		||||
	/* taps for the rtp connection */
 | 
			
		||||
	struct mgcp_rtp_tap tap_in;
 | 
			
		||||
	struct mgcp_rtp_tap tap_out;
 | 
			
		||||
 | 
			
		||||
@@ -209,8 +177,6 @@ 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
 | 
			
		||||
@@ -236,8 +202,8 @@ struct mgcp_conn {
 | 
			
		||||
	/*!< copy of the mode to restore the original setting (VTY) */
 | 
			
		||||
	enum mgcp_connection_mode mode_orig;
 | 
			
		||||
 | 
			
		||||
	/*!< connection id to identify the connection */
 | 
			
		||||
	char id[MGCP_CONN_ID_LENGTH];
 | 
			
		||||
	/*!< connection id to identify the conntion */
 | 
			
		||||
	uint32_t id;
 | 
			
		||||
 | 
			
		||||
	/*!< human readable name (vty, logging) */
 | 
			
		||||
	char name[256];
 | 
			
		||||
@@ -255,9 +221,25 @@ 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
 | 
			
		||||
@@ -267,6 +249,7 @@ 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,
 | 
			
		||||
@@ -275,7 +258,6 @@ 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);
 | 
			
		||||
@@ -289,8 +271,6 @@ 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,
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line);
 | 
			
		||||
 | 
			
		||||
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid);
 | 
			
		||||
 | 
			
		||||
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id);
 | 
			
		||||
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *ci);
 | 
			
		||||
 | 
			
		||||
char *mgcp_strline(char *str, char **saveptr);
 | 
			
		||||
 | 
			
		||||
@@ -54,3 +54,5 @@ char *mgcp_strline(char *str, char **saveptr);
 | 
			
		||||
#define for_each_non_empty_line(line, save)\
 | 
			
		||||
	for (line = strtok_r(NULL, "\r\n", &save); line;\
 | 
			
		||||
	     line = strtok_r(NULL, "\r\n", &save))
 | 
			
		||||
 | 
			
		||||
int mgcp_parse_ci(uint32_t *conn_id, const char *ci);
 | 
			
		||||
 
 | 
			
		||||
@@ -21,11 +21,15 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#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,8 +1,31 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#ifndef OPENBSC_VTY_H
 | 
			
		||||
#define OPENBSC_VTY_H
 | 
			
		||||
 | 
			
		||||
#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,39 +26,10 @@ 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;
 | 
			
		||||
	char comment[MGCP_COMMENT_MAXLEN];
 | 
			
		||||
	char conn_id[MGCP_CONN_ID_LENGTH];
 | 
			
		||||
	char endpoint[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
       const char *comment;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_response {
 | 
			
		||||
@@ -66,11 +37,6 @@ 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 {
 | 
			
		||||
@@ -88,21 +54,19 @@ 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 */
 | 
			
		||||
	uint32_t presence;
 | 
			
		||||
	char endpoint[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	unsigned int call_id;
 | 
			
		||||
	char *conn_id;
 | 
			
		||||
	uint32_t conn_id;
 | 
			
		||||
        uint16_t audio_port;
 | 
			
		||||
        char *audio_ip;
 | 
			
		||||
	enum mgcp_connection_mode conn_mode;
 | 
			
		||||
	unsigned int ptime;
 | 
			
		||||
	enum mgcp_codecs codecs[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int codecs_len;
 | 
			
		||||
	struct ptmap ptmap[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int ptmap_len;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
 | 
			
		||||
@@ -128,7 +92,6 @@ int mgcp_response_parse_params(struct mgcp_response *r);
 | 
			
		||||
 | 
			
		||||
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
 | 
			
		||||
		   mgcp_response_cb_t response_cb, void *priv);
 | 
			
		||||
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id);
 | 
			
		||||
 | 
			
		||||
enum mgcp_connection_mode;
 | 
			
		||||
 | 
			
		||||
@@ -147,16 +110,9 @@ struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
 | 
			
		||||
OSMO_DEPRECATED("Use mgcp_msg_gen() instead");
 | 
			
		||||
 | 
			
		||||
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
 | 
			
		||||
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
extern const struct value_string mgcp_client_connection_mode_strs[];
 | 
			
		||||
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);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,44 +0,0 @@
 | 
			
		||||
#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);
 | 
			
		||||
							
								
								
									
										10
									
								
								libosmo-mgcp.pc.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								libosmo-mgcp.pc.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
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:1:0
 | 
			
		||||
LEGACY_MGCP_LIBVERSION=0:0:0
 | 
			
		||||
 | 
			
		||||
lib_LTLIBRARIES = \
 | 
			
		||||
	libosmo-legacy-mgcp.la \
 | 
			
		||||
 
 | 
			
		||||
@@ -584,36 +584,6 @@ 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)
 | 
			
		||||
{
 | 
			
		||||
@@ -621,7 +591,6 @@ 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",
 | 
			
		||||
@@ -643,12 +612,10 @@ 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",
 | 
			
		||||
@@ -713,9 +680,6 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
 | 
			
		||||
				rtp_state->patched_first_rtp_payload = true;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			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);
 | 
			
		||||
 
 | 
			
		||||
@@ -267,6 +267,7 @@ 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;
 | 
			
		||||
@@ -296,7 +297,8 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
		endp->osmux.stats.chunks++;
 | 
			
		||||
		rem = msg->len;
 | 
			
		||||
 | 
			
		||||
		osmux_xfrm_output_sched(&endp->osmux.out, osmuxh);
 | 
			
		||||
		osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
 | 
			
		||||
		osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
@@ -357,6 +359,7 @@ 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;
 | 
			
		||||
@@ -386,7 +389,8 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
		endp->osmux.stats.chunks++;
 | 
			
		||||
		rem = msg->len;
 | 
			
		||||
 | 
			
		||||
		osmux_xfrm_output_sched(&endp->osmux.out, osmuxh);
 | 
			
		||||
		osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
 | 
			
		||||
		osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
@@ -466,13 +470,9 @@ 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,11 +484,6 @@ 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,11 +863,6 @@ 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 */
 | 
			
		||||
@@ -903,13 +898,6 @@ 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:
 | 
			
		||||
@@ -918,14 +906,6 @@ 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);
 | 
			
		||||
@@ -1393,9 +1373,6 @@ 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;
 | 
			
		||||
 | 
			
		||||
@@ -1611,7 +1588,6 @@ void mgcp_format_stats(struct mgcp_endpoint *endp, char *msg, size_t size)
 | 
			
		||||
	msg += nchars;
 | 
			
		||||
	size -= nchars;
 | 
			
		||||
 | 
			
		||||
	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",
 | 
			
		||||
@@ -1631,7 +1607,6 @@ void mgcp_format_stats(struct mgcp_endpoint *endp, char *msg, size_t size)
 | 
			
		||||
			 endp->osmux.stats.chunks,
 | 
			
		||||
			 endp->osmux.stats.octets);
 | 
			
		||||
	}
 | 
			
		||||
	}
 | 
			
		||||
truncate:
 | 
			
		||||
	msg[size - 1] = '\0';
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,6 @@
 | 
			
		||||
#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"
 | 
			
		||||
@@ -165,13 +164,6 @@ 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -249,11 +241,6 @@ 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;
 | 
			
		||||
}
 | 
			
		||||
@@ -1357,63 +1344,6 @@ 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);
 | 
			
		||||
@@ -1481,10 +1411,6 @@ 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=3:0:0
 | 
			
		||||
MGCP_CLIENT_LIBVERSION=2:0:0
 | 
			
		||||
 | 
			
		||||
lib_LTLIBRARIES = \
 | 
			
		||||
	libosmo-mgcp-client.la \
 | 
			
		||||
@@ -29,7 +29,6 @@ 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 General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 * 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 General Public License for more details.
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * 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/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
@@ -24,7 +24,6 @@
 | 
			
		||||
#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>
 | 
			
		||||
@@ -36,152 +35,6 @@
 | 
			
		||||
#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 */
 | 
			
		||||
@@ -208,9 +61,7 @@ static bool endpoint_in_use(uint16_t id, struct mgcp_client *client)
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Pick next free endpoint ID.
 | 
			
		||||
 *  \param[in,out] client MGCP client descriptor.
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on error. */
 | 
			
		||||
/* Find and seize an unsused endpoint id */
 | 
			
		||||
int mgcp_client_next_endpoint(struct mgcp_client *client)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
@@ -243,9 +94,6 @@ 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)
 | 
			
		||||
{
 | 
			
		||||
@@ -290,12 +138,15 @@ static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg)
 | 
			
		||||
		   &comment_pos) != 2)
 | 
			
		||||
		goto response_parse_failure;
 | 
			
		||||
 | 
			
		||||
	osmo_strlcpy(r->head.comment, r->body + comment_pos, sizeof(r->head.comment));
 | 
			
		||||
	r->head.comment = r->body + comment_pos;
 | 
			
		||||
	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:
 | 
			
		||||
@@ -322,113 +173,18 @@ static bool mgcp_line_is_valid(const char *line)
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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)
 | 
			
		||||
/* Parse a line like "m=audio 16002 RTP/AVP 98" */
 | 
			
		||||
static int mgcp_parse_audio_port(struct mgcp_response *r, const char *line)
 | 
			
		||||
{
 | 
			
		||||
	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_port:
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
	     "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;
 | 
			
		||||
	}
 | 
			
		||||
        if (sscanf(line, "m=audio %hu",
 | 
			
		||||
		   &r->audio_port) != 1)
 | 
			
		||||
		goto response_parse_failure;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
response_parse_failure_ptime:
 | 
			
		||||
response_parse_failure:
 | 
			
		||||
	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);
 | 
			
		||||
	     "Failed to parse MGCP response header (audio port)\n");
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -445,7 +201,8 @@ static int mgcp_parse_audio_ip(struct mgcp_response *r, const char *line)
 | 
			
		||||
		goto response_parse_failure;
 | 
			
		||||
 | 
			
		||||
	/* Extract IP-Address */
 | 
			
		||||
	osmo_strlcpy(r->audio_ip, line + 9, sizeof(r->audio_ip));
 | 
			
		||||
	strncpy(r->audio_ip, line + 9, sizeof(r->audio_ip));
 | 
			
		||||
	r->audio_ip[sizeof(r->audio_ip) - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
	/* Check IP-Address */
 | 
			
		||||
	if (inet_aton(r->audio_ip, &ip_test) == 0)
 | 
			
		||||
@@ -459,189 +216,57 @@ 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;
 | 
			
		||||
	char *data;
 | 
			
		||||
	char *data_ptr;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	/* 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);
 | 
			
		||||
	char *data = strstr(r->body, "\n\n");
 | 
			
		||||
 | 
			
		||||
	/* Find beginning of the parameter (SDP) section */
 | 
			
		||||
	data_ptr = mgcp_find_section_end(data);
 | 
			
		||||
	if (!data_ptr) {
 | 
			
		||||
	if (!data) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "MGCP response: cannot find start of SDP parameters\n");
 | 
			
		||||
		rc = -EINVAL;
 | 
			
		||||
		goto exit;
 | 
			
		||||
		     "MGCP response: cannot find start of parameters\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* 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. */
 | 
			
		||||
	/* Advance to after the \n\n, replace the second \n with \0. That's
 | 
			
		||||
	 * where the parameters start. */
 | 
			
		||||
	data ++;
 | 
			
		||||
	*data = '\0';
 | 
			
		||||
	data ++;
 | 
			
		||||
 | 
			
		||||
	for_each_non_empty_line(line, data_ptr) {
 | 
			
		||||
	for_each_non_empty_line(line, data) {
 | 
			
		||||
		if (!mgcp_line_is_valid(line))
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		switch (line[0]) {
 | 
			
		||||
		case 'a':
 | 
			
		||||
			rc = mgcp_parse_audio_ptime_rtpmap(r, line);
 | 
			
		||||
			if (rc)
 | 
			
		||||
				goto exit;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'm':
 | 
			
		||||
			rc = mgcp_parse_audio_port_pt(r, line);
 | 
			
		||||
			rc = mgcp_parse_audio_port(r, line);
 | 
			
		||||
			if (rc)
 | 
			
		||||
				goto exit;
 | 
			
		||||
				return rc;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'c':
 | 
			
		||||
			rc = mgcp_parse_audio_ip(r, line);
 | 
			
		||||
			if (rc)
 | 
			
		||||
				goto exit;
 | 
			
		||||
				return rc;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* skip unhandled parameters */
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* 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 "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;
 | 
			
		||||
 | 
			
		||||
	/* Check if the label matches */
 | 
			
		||||
	snprintf(label_string, sizeof(label_string), "%c: ", label);
 | 
			
		||||
	if (memcmp(label_string, line, 3) != 0)
 | 
			
		||||
		goto response_parse_failure;
 | 
			
		||||
 | 
			
		||||
	/* 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 (parameter label: %c)\n", label);
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Parse MGCP parameters of the response */
 | 
			
		||||
static int parse_head_params(struct mgcp_response *r)
 | 
			
		||||
{
 | 
			
		||||
	char *line;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	OSMO_ASSERT(r->body);
 | 
			
		||||
	char *data;
 | 
			
		||||
	char *data_ptr;
 | 
			
		||||
	char *data_end;
 | 
			
		||||
 | 
			
		||||
	/* 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_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_head_param(r->head.conn_id,
 | 
			
		||||
						   sizeof(r->head.conn_id),
 | 
			
		||||
						   'I', line);
 | 
			
		||||
			if (rc)
 | 
			
		||||
				goto exit;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* skip unhandled parameters */
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
exit:
 | 
			
		||||
	talloc_free(data);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct mgcp_response_pending *mgcp_client_response_pending_get(
 | 
			
		||||
					 struct mgcp_client *mgcp,
 | 
			
		||||
					 mgcp_trans_id_t trans_id)
 | 
			
		||||
					 struct mgcp_response *r)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_response_pending *pending;
 | 
			
		||||
	if (!r)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	llist_for_each_entry(pending, &mgcp->responses_pending, entry) {
 | 
			
		||||
		if (pending->trans_id == trans_id) {
 | 
			
		||||
		if (pending->trans_id == r->head.trans_id) {
 | 
			
		||||
			llist_del(&pending->entry);
 | 
			
		||||
			return pending;
 | 
			
		||||
		}
 | 
			
		||||
@@ -657,42 +282,26 @@ 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;
 | 
			
		||||
	struct mgcp_response r = { 0 };
 | 
			
		||||
	struct mgcp_response_pending *pending;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	r = talloc_zero(mgcp, struct mgcp_response);
 | 
			
		||||
	OSMO_ASSERT(r);
 | 
			
		||||
 | 
			
		||||
	rc = mgcp_response_parse_head(r, msg);
 | 
			
		||||
	rc = mgcp_response_parse_head(&r, msg);
 | 
			
		||||
	if (rc) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head)\n");
 | 
			
		||||
		rc = 1;
 | 
			
		||||
		goto error;
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = parse_head_params(r);
 | 
			
		||||
	if (rc) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head parameters)\n");
 | 
			
		||||
		rc = 1;
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pending = mgcp_client_response_pending_get(mgcp, r->head.trans_id);
 | 
			
		||||
	pending = mgcp_client_response_pending_get(mgcp, &r);
 | 
			
		||||
	if (!pending) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Cannot find matching MGCP transaction for trans_id %d\n",
 | 
			
		||||
		     r->head.trans_id);
 | 
			
		||||
		rc = -ENOENT;
 | 
			
		||||
		goto error;
 | 
			
		||||
		     r.head.trans_id);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mgcp_client_handle_response(mgcp, pending, r);
 | 
			
		||||
	rc = 0;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	talloc_free(r);
 | 
			
		||||
	return rc;
 | 
			
		||||
	mgcp_client_handle_response(mgcp, pending, &r);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mgcp_do_read(struct osmo_fd *fd)
 | 
			
		||||
@@ -731,7 +340,7 @@ static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
 | 
			
		||||
	unsigned int l = msg->len < sizeof(strbuf) ? msg->len : sizeof(strbuf);
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	osmo_strlcpy(strbuf, (const char*)msg->data, l);
 | 
			
		||||
	strncpy(strbuf, (const char*)msg->data, l);
 | 
			
		||||
	for (i = 0; i < sizeof(strbuf); i++) {
 | 
			
		||||
		if (strbuf[i] == '\n' || strbuf[i] == '\r') {
 | 
			
		||||
			strbuf[i] = '\0';
 | 
			
		||||
@@ -779,11 +388,9 @@ 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;
 | 
			
		||||
@@ -795,19 +402,46 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
 | 
			
		||||
 | 
			
		||||
	wq = &mgcp->wq;
 | 
			
		||||
 | 
			
		||||
	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) {
 | 
			
		||||
	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) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_FATAL,
 | 
			
		||||
		     "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));
 | 
			
		||||
		     "Failed to initialize socket for MGCP GW: %s\n",
 | 
			
		||||
		     strerror(errno));
 | 
			
		||||
		rc = -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);
 | 
			
		||||
@@ -816,6 +450,11 @@ 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);
 | 
			
		||||
@@ -827,25 +466,17 @@ 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! 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) */
 | 
			
		||||
/* Return the MGCP GW binary IPv4 address in network byte order. */
 | 
			
		||||
uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	return mgcp->remote_addr;
 | 
			
		||||
@@ -872,10 +503,7 @@ struct mgcp_response_pending * mgcp_client_pending_add(
 | 
			
		||||
 * response_cb. NOTE: the response_cb still needs to call
 | 
			
		||||
 * mgcp_response_parse_params(response) to get the parsed parameters -- to
 | 
			
		||||
 * potentially save some CPU cycles, only the head line has been parsed when
 | 
			
		||||
 * the response_cb is invoked.
 | 
			
		||||
 * Before the priv pointer becomes invalid, e.g. due to transaction timeout,
 | 
			
		||||
 * mgcp_client_cancel() needs to be called for this transaction.
 | 
			
		||||
 */
 | 
			
		||||
 * the response_cb is invoked. */
 | 
			
		||||
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
 | 
			
		||||
		   mgcp_response_cb_t response_cb, void *priv)
 | 
			
		||||
{
 | 
			
		||||
@@ -918,35 +546,6 @@ mgcp_tx_error:
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! 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(). */
 | 
			
		||||
int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_response_pending *pending = mgcp_client_response_pending_get(mgcp, trans_id);
 | 
			
		||||
	if (!pending) {
 | 
			
		||||
		/*! Note: it is not harmful to cancel a transaction twice. */
 | 
			
		||||
		LOGP(DLMGCP, LOGL_INFO, "Cannot cancel, no such transaction: %u\n", trans_id);
 | 
			
		||||
		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.
 | 
			
		||||
	 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id,
 | 
			
		||||
				      const char *buf, int len)
 | 
			
		||||
{
 | 
			
		||||
@@ -1051,139 +650,20 @@ struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
 | 
			
		||||
 | 
			
		||||
#define MGCP_CRCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
 | 
			
		||||
			     MGCP_MSG_PRESENCE_CALL_ID | \
 | 
			
		||||
			     MGCP_MSG_PRESENCE_CONN_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;
 | 
			
		||||
@@ -1226,23 +706,8 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Add endpoint name */
 | 
			
		||||
	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_ENDPOINT) {
 | 
			
		||||
		if (strlen(mgcp_msg->endpoint) <= 0) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Empty endpoint name, can not generate MGCP message\n");
 | 
			
		||||
			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);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_ENDPOINT)
 | 
			
		||||
		rc += msgb_printf(msg, " %s", mgcp_msg->endpoint);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Add protocol version */
 | 
			
		||||
	rc += msgb_printf(msg, " MGCP 1.0\r\n");
 | 
			
		||||
@@ -1252,27 +717,13 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
 | 
			
		||||
		rc += msgb_printf(msg, "C: %x\r\n", mgcp_msg->call_id);
 | 
			
		||||
 | 
			
		||||
	/* Add connection id */
 | 
			
		||||
	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID) {
 | 
			
		||||
		if (strlen(mgcp_msg->conn_id) <= 0) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Empty connection id, can not generate MGCP message\n");
 | 
			
		||||
			msgb_free(msg);
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
		rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id);
 | 
			
		||||
	}
 | 
			
		||||
	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID)
 | 
			
		||||
		rc += msgb_printf(msg, "I: %u\r\n", mgcp_msg->conn_id);
 | 
			
		||||
 | 
			
		||||
	/* 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 local connection options */
 | 
			
		||||
	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID
 | 
			
		||||
	    && mgcp_msg->verb == MGCP_VERB_CRCX)
 | 
			
		||||
		rc += msgb_printf(msg, "L: p:20, a:AMR, nt:IN\r\n");
 | 
			
		||||
 | 
			
		||||
	/* Add mode */
 | 
			
		||||
	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE)
 | 
			
		||||
@@ -1280,15 +731,14 @@ 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 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;
 | 
			
		||||
		else
 | 
			
		||||
			rc += rc_sdp;
 | 
			
		||||
	/* Add RTP address and port (SDP) */
 | 
			
		||||
	if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
 | 
			
		||||
	    && mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) {
 | 
			
		||||
		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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (rc != 0) {
 | 
			
		||||
@@ -1301,17 +751,6 @@ 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()
 | 
			
		||||
 *  \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;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,694 +0,0 @@
 | 
			
		||||
/* (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 General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 2 of the License, or
 | 
			
		||||
 * 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 General Public License for more details.
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * 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/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
@@ -25,7 +25,6 @@
 | 
			
		||||
#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>
 | 
			
		||||
@@ -204,6 +203,4 @@ 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();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,6 @@ AM_CPPFLAGS = \
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
@@ -15,29 +14,33 @@ AM_CFLAGS = \
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_LIBRARIES = \
 | 
			
		||||
	libosmo-mgcp.a \
 | 
			
		||||
# 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 \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	g711common.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_mgcp_a_SOURCES = \
 | 
			
		||||
libosmo_mgcp_la_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_endp.c \
 | 
			
		||||
	mgcp_ep.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_LIBVERSION)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,343 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (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,90 +24,40 @@
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.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>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_ep.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) */
 | 
			
		||||
static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
 | 
			
		||||
/* Reset codec state and free memory */
 | 
			
		||||
static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	int k;
 | 
			
		||||
	int rc;
 | 
			
		||||
	uint8_t id_bin[16];
 | 
			
		||||
	char *id_hex;
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	/* Generate a connection id that is unique for the current endpoint.
 | 
			
		||||
	 * Technically a counter would be sufficient, but in order to
 | 
			
		||||
	 * be able to find a specific connection in large logfiles and to
 | 
			
		||||
	 * prevent unintentional connections we assign the connection
 | 
			
		||||
	 * identifiers randomly from a reasonable large number space */
 | 
			
		||||
	for (i = 0; i < 32; i++) {
 | 
			
		||||
		rc = osmo_get_rand_id(id_bin, sizeof(id_bin));
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			return rc;
 | 
			
		||||
 | 
			
		||||
		id_hex = osmo_hexdump_nospc(id_bin, sizeof(id_bin));
 | 
			
		||||
		for (k = 0; k < strlen(id_hex); k++)
 | 
			
		||||
			id_hex[k] = toupper(id_hex[k]);
 | 
			
		||||
 | 
			
		||||
		/* ensure that the generated conn_id is unique
 | 
			
		||||
		 * for this endpoint */
 | 
			
		||||
		if (!mgcp_conn_get_rtp(endp, id_hex)) {
 | 
			
		||||
			osmo_strlcpy(id, id_hex, MGCP_CONN_ID_LENGTH);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	/* see also mgcp_sdp.c, mgcp_set_audio_info() */
 | 
			
		||||
	talloc_free(codec->subtype_name);
 | 
			
		||||
	talloc_free(codec->audio_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x, unable to generate a unique connectionIdentifier\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp));
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize rtp connection struct with default values */
 | 
			
		||||
static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
 | 
			
		||||
/* 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_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;
 | 
			
		||||
	struct mgcp_rtp_end *end = &conn->end;
 | 
			
		||||
 | 
			
		||||
	conn_rtp->type = MGCP_RTP_DEFAULT;
 | 
			
		||||
	conn_rtp->osmux.allocated_cid = -1;
 | 
			
		||||
 | 
			
		||||
	/* backpointer to the generic part of the connection */
 | 
			
		||||
	conn->u.rtp.conn = conn;
 | 
			
		||||
	conn->type = MGCP_RTP_DEFAULT;
 | 
			
		||||
	conn->osmux.allocated_cid = -1;
 | 
			
		||||
 | 
			
		||||
	end->rtp.fd = -1;
 | 
			
		||||
	end->rtcp.fd = -1;
 | 
			
		||||
	memset(&end->stats, 0, sizeof(end->stats));
 | 
			
		||||
	end->local_port = 0;
 | 
			
		||||
	end->packets_rx = 0;
 | 
			
		||||
	end->octets_rx = 0;
 | 
			
		||||
	end->packets_tx = 0;
 | 
			
		||||
	end->octets_tx = 0;
 | 
			
		||||
	end->dropped_packets = 0;
 | 
			
		||||
	end->rtp_port = end->rtcp_port = 0;
 | 
			
		||||
	talloc_free(end->fmtp_extra);
 | 
			
		||||
	end->fmtp_extra = NULL;
 | 
			
		||||
@@ -116,24 +66,9 @@ static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_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;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
	mgcp_rtp_codec_reset(&end->codec);
 | 
			
		||||
	mgcp_rtp_codec_reset(&end->alt_codec);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! allocate a new connection list entry.
 | 
			
		||||
@@ -143,15 +78,22 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
 *  \param[in] type connection type (e.g. MGCP_CONN_TYPE_RTP)
 | 
			
		||||
 *  \returns pointer to allocated connection, NULL on error */
 | 
			
		||||
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
 | 
			
		||||
				  enum mgcp_conn_type type, char *name)
 | 
			
		||||
				  uint32_t id, enum mgcp_conn_type type,
 | 
			
		||||
				  char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	int rc;
 | 
			
		||||
	OSMO_ASSERT(endp);
 | 
			
		||||
	OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
 | 
			
		||||
	OSMO_ASSERT(strlen(name) < sizeof(conn->name));
 | 
			
		||||
 | 
			
		||||
	/* Do not allow more then two connections */
 | 
			
		||||
	if (llist_count(&endp->conns) >= endp->type->max_conns)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Prevent duplicate connection IDs */
 | 
			
		||||
	if (mgcp_conn_get(endp, id))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Create new connection and add it to the list */
 | 
			
		||||
	conn = talloc_zero(ctx, struct mgcp_conn);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
@@ -160,16 +102,13 @@ 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;
 | 
			
		||||
	osmo_strlcpy(conn->name, name, sizeof(conn->name));
 | 
			
		||||
	rc = mgcp_alloc_id(endp, conn->id);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		talloc_free(conn);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	conn->id = id;
 | 
			
		||||
	conn->u.rtp.conn = conn;
 | 
			
		||||
	strcpy(conn->name, name);
 | 
			
		||||
 | 
			
		||||
	switch (type) {
 | 
			
		||||
	case MGCP_CONN_TYPE_RTP:
 | 
			
		||||
		mgcp_rtp_conn_init(&conn->u.rtp, conn);
 | 
			
		||||
		mgcp_rtp_conn_reset(&conn->u.rtp);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		/* NOTE: This should never be called with an
 | 
			
		||||
@@ -187,12 +126,15 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
 | 
			
		||||
 *  \param[in] endp associated endpoint
 | 
			
		||||
 *  \param[in] id identification number of the connection
 | 
			
		||||
 *  \returns pointer to allocated connection, NULL if not found */
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, uint32_t id)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(endp);
 | 
			
		||||
	OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
 | 
			
		||||
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(conn, &endp->conns, entry) {
 | 
			
		||||
		if (strncmp(conn->id, id, sizeof(conn->id)) == 0)
 | 
			
		||||
		if (conn->id == id)
 | 
			
		||||
			return conn;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -203,9 +145,11 @@ struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
 *  \param[in] endp associated endpoint
 | 
			
		||||
 *  \param[in] id identification number of the connection
 | 
			
		||||
 *  \returns pointer to allocated connection, NULL if not found */
 | 
			
		||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
					const char *id)
 | 
			
		||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp, uint32_t id)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(endp);
 | 
			
		||||
	OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
 | 
			
		||||
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	conn = mgcp_conn_get(endp, id);
 | 
			
		||||
@@ -221,23 +165,22 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
 | 
			
		||||
/*! free a connection by its ID.
 | 
			
		||||
 *  \param[in] endp associated endpoint
 | 
			
		||||
 *  \param[in] id identification number of the connection */
 | 
			
		||||
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
void mgcp_conn_free(struct mgcp_endpoint *endp, uint32_t id)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(endp);
 | 
			
		||||
	OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
 | 
			
		||||
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	conn = mgcp_conn_get(endp, 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:
 | 
			
		||||
		mgcp_rtp_conn_cleanup(&conn->u.rtp);
 | 
			
		||||
		osmux_disable_conn(&conn->u.rtp);
 | 
			
		||||
		osmux_release_cid(&conn->u.rtp);
 | 
			
		||||
		mgcp_free_rtp_port(&conn->u.rtp.end);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		/* NOTE: This should never be called with an
 | 
			
		||||
@@ -254,6 +197,9 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
 | 
			
		||||
 *  \param[in] endp associated endpoint */
 | 
			
		||||
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(endp);
 | 
			
		||||
	OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
 | 
			
		||||
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	if (llist_empty(&endp->conns))
 | 
			
		||||
@@ -270,6 +216,9 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
 | 
			
		||||
 *  \param[in] endp associated endpoint */
 | 
			
		||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(endp);
 | 
			
		||||
	OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
 | 
			
		||||
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	struct mgcp_conn *conn_tmp;
 | 
			
		||||
 | 
			
		||||
@@ -281,12 +230,12 @@ void mgcp_conn_free_all(struct mgcp_endpoint *endp)
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! dump basic connection information to human readable string.
 | 
			
		||||
/*! dump basic connection information to human readble string.
 | 
			
		||||
 *  \param[in] conn to dump
 | 
			
		||||
 *  \returns human readable string */
 | 
			
		||||
 *  \returns human readble string */
 | 
			
		||||
char *mgcp_conn_dump(struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	static char str[sizeof(conn->name)+sizeof(conn->id)+256];
 | 
			
		||||
	static char str[256];
 | 
			
		||||
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		snprintf(str, sizeof(str), "(null connection)");
 | 
			
		||||
@@ -296,7 +245,7 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
 | 
			
		||||
	switch (conn->type) {
 | 
			
		||||
	case MGCP_CONN_TYPE_RTP:
 | 
			
		||||
		/* Dump RTP connection */
 | 
			
		||||
		snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
 | 
			
		||||
		snprintf(str, sizeof(str), "(%s/rtp, id:%u, ip:%s, "
 | 
			
		||||
			 "rtp:%u rtcp:%u)",
 | 
			
		||||
			 conn->name,
 | 
			
		||||
			 conn->id,
 | 
			
		||||
 
 | 
			
		||||
@@ -21,36 +21,12 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#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.cleanup_cb = mgcp_cleanup_rtp_bridge_cb
 | 
			
		||||
	.rtp.dispatch_rtp_cb = mgcp_dispatch_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,7 +28,6 @@
 | 
			
		||||
#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
 | 
			
		||||
@@ -83,7 +82,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
	if (!mode) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x missing connection mode\n",
 | 
			
		||||
		     "endpoint:%x missing connection mode\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -102,7 +101,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
 | 
			
		||||
		conn->mode = MGCP_CONN_LOOPBACK;
 | 
			
		||||
	else {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x unknown connection mode: '%s'\n",
 | 
			
		||||
		     "endpoint:%x unknown connection mode: '%s'\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), mode);
 | 
			
		||||
		ret = -1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -114,16 +113,16 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG,
 | 
			
		||||
	     "endpoint:0x%x conn:%s\n",
 | 
			
		||||
	     "endpoint:%x conn:%s\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn));
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG,
 | 
			
		||||
	     "endpoint:0x%x connection mode '%s' %d\n",
 | 
			
		||||
	     "endpoint:%x connection mode '%s' %d\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), mode, conn->mode);
 | 
			
		||||
 | 
			
		||||
	/* Special handling für RTP connections */
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x output_enabled %d\n",
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "endpoint:%x output_enabled %d\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), conn->u.rtp.end.output_enabled);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -143,7 +142,6 @@ 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) {
 | 
			
		||||
@@ -180,112 +178,25 @@ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	endp_ptr = &tcfg->endpoints[endp];
 | 
			
		||||
	endp_ptr->wildcarded_req = false;
 | 
			
		||||
	return endp_ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find an endpoint that is not in use. Do this by going through the endpoint
 | 
			
		||||
 * array, check the callid. A callid nullpointer indicates that the endpoint
 | 
			
		||||
 * is free */
 | 
			
		||||
static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
 | 
			
		||||
						unsigned int number_endpoints)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < number_endpoints; i++) {
 | 
			
		||||
		if (endpoints[i].callid == NULL) {
 | 
			
		||||
			endp = &endpoints[i];
 | 
			
		||||
			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;
 | 
			
		||||
	return &tcfg->endpoints[endp];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Search the endpoint pool for the endpoint that had been selected via the
 | 
			
		||||
 * MGCP message (helper function for mgcp_analyze_header()) */
 | 
			
		||||
static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
 | 
			
		||||
					   const char *mgcp,
 | 
			
		||||
					   int *cause)
 | 
			
		||||
					   const char *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	char *endptr = NULL;
 | 
			
		||||
	unsigned int gw = INT_MAX;
 | 
			
		||||
	const char *endpoint_number_str;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
 | 
			
		||||
	*cause = 0;
 | 
			
		||||
	if (strncmp(mgcp, "ds/e1", 5) == 0)
 | 
			
		||||
		return find_e1_endpoint(cfg, mgcp);
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
	}
 | 
			
		||||
	if (gw > 0 && gw < cfg->trunk.number_endpoints && endptr[0] == '@')
 | 
			
		||||
		return &cfg->trunk.endpoints[gw];
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
 | 
			
		||||
	*cause = -500;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -298,7 +209,6 @@ 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.
 | 
			
		||||
@@ -316,25 +226,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, &cause);
 | 
			
		||||
			pdata->endp = find_endpoint(pdata->cfg, elem);
 | 
			
		||||
			if (!pdata->endp) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
				     "Unable to find Endpoint `%s'\n", elem);
 | 
			
		||||
				return cause;
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case 2:
 | 
			
		||||
			if (strcmp("MGCP", elem)) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
				     "MGCP header parsing error\n");
 | 
			
		||||
				return -510;
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case 3:
 | 
			
		||||
			if (strcmp("1.0", elem)) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
 | 
			
		||||
				     "not supported\n", elem);
 | 
			
		||||
				return -528;
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
@@ -345,7 +255,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 -510;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -408,7 +318,7 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
 | 
			
		||||
 | 
			
		||||
	if (strcmp(endp->callid, callid) != 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x CallIDs does not match '%s' != '%s'\n",
 | 
			
		||||
		     "endpoint:%x CallIDs does not match '%s' != '%s'\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), endp->callid, callid);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -420,39 +330,21 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
 | 
			
		||||
  * \param[in] endp pointer to endpoint
 | 
			
		||||
  * \param{in] connection id to verify
 | 
			
		||||
  * \returns 1 when connection id seems plausible, 0 on error */
 | 
			
		||||
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
 | 
			
		||||
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *ci)
 | 
			
		||||
{
 | 
			
		||||
	/* Check for null identifiers */
 | 
			
		||||
	if (!conn_id) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x invalid ConnectionIdentifier (missing)\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	uint32_t id;
 | 
			
		||||
 | 
			
		||||
	/* Check for empty connection identifiers */
 | 
			
		||||
	if (strlen(conn_id) == 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x invalid ConnectionIdentifier (empty)\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
	if (!endp)
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check for over long connection identifiers */
 | 
			
		||||
	if (strlen(conn_id) > MGCP_CONN_ID_LENGTH) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x invalid ConnectionIdentifier (too long) 0x%s\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), conn_id);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	id = strtoul(ci, NULL, 10);
 | 
			
		||||
 | 
			
		||||
	/* Check if connection exists */
 | 
			
		||||
	if (mgcp_conn_get(endp, conn_id))
 | 
			
		||||
	if (mgcp_conn_get(endp, id))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
	     "endpoint:0x%x no connection found under ConnectionIdentifier 0x%s\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), conn_id);
 | 
			
		||||
	     "endpoint:%x No connection found under ConnectionIdentifier %u\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), id);
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
@@ -494,3 +386,20 @@ char *mgcp_strline(char *str, char **saveptr)
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Parse CI from a given string.
 | 
			
		||||
  * \param[out] caller provided memory to store the result
 | 
			
		||||
  * \param{in] string containing the connection id
 | 
			
		||||
  * \returns 0 on success, -1 on error */
 | 
			
		||||
int mgcp_parse_ci(uint32_t *conn_id, const char *ci)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(conn_id);
 | 
			
		||||
 | 
			
		||||
	if (!ci)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	*conn_id = strtoul(ci, NULL, 10);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
@@ -39,7 +40,7 @@
 | 
			
		||||
#include <osmocom/mgcp/mgcp_stat.h>
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_ep.h>
 | 
			
		||||
#include <osmocom/mgcp/debug.h>
 | 
			
		||||
 | 
			
		||||
#define RTP_SEQ_MOD		(1 << 16)
 | 
			
		||||
@@ -72,11 +73,11 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
 | 
			
		||||
		rc = osmo_sock_local_ip(addr, inet_ntoa(conn->end.addr));
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
			     "endpoint:0x%x CI:%s local interface auto detection failed, using configured addresses...\n",
 | 
			
		||||
			     "endpoint:%x CI:%i local interface auto detection failed, using configured addresses...\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp), conn->conn->id);
 | 
			
		||||
		else {
 | 
			
		||||
			LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
			     "endpoint:0x%x CI:%s selected local rtp bind ip %s by probing using remote ip %s\n",
 | 
			
		||||
			     "endpoint:%x CI:%i selected local rtp bind ip %s by probing using remote ip %s\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp), conn->conn->id, addr,
 | 
			
		||||
			     inet_ntoa(conn->end.addr));
 | 
			
		||||
			return;
 | 
			
		||||
@@ -87,17 +88,17 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
 | 
			
		||||
	if (endp->cfg->net_ports.bind_addr) {
 | 
			
		||||
		/* Check there is a bind IP for the RTP traffic configured,
 | 
			
		||||
		 * if so, use that IP-Address */
 | 
			
		||||
		osmo_strlcpy(addr, endp->cfg->net_ports.bind_addr, INET_ADDRSTRLEN);
 | 
			
		||||
		strncpy(addr, endp->cfg->net_ports.bind_addr, INET_ADDRSTRLEN);
 | 
			
		||||
		LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
		     "endpoint:0x%x CI:%s using configured rtp bind ip as local bind ip %s\n",
 | 
			
		||||
		     "endpoint:%x CI:%i using configured rtp bind ip as local bind ip %s\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), conn->conn->id, addr);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* No specific bind IP is configured for the RTP traffic, so
 | 
			
		||||
		 * assume the IP where we listen for incoming MGCP messages
 | 
			
		||||
		 * as bind IP */
 | 
			
		||||
		osmo_strlcpy(addr, endp->cfg->source_addr, INET_ADDRSTRLEN);
 | 
			
		||||
		strncpy(addr, endp->cfg->source_addr, INET_ADDRSTRLEN);
 | 
			
		||||
		LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
		     "endpoint:0x%x CI:%s using mgcp bind ip as local rtp bind ip: %s\n",
 | 
			
		||||
		     "endpoint:%x CI:%i using mgcp bind ip as local rtp bind ip: %s\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), conn->conn->id, addr);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -161,8 +162,8 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
	     "endpoint:0x%x sending dummy packet...\n", ENDPOINT_NUMBER(endp));
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x conn:%s\n",
 | 
			
		||||
	     "endpoint:%x sending dummy packet...\n", ENDPOINT_NUMBER(endp));
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:%x conn:%s\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
 | 
			
		||||
 | 
			
		||||
	rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
 | 
			
		||||
@@ -183,7 +184,7 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
 | 
			
		||||
 | 
			
		||||
failed:
 | 
			
		||||
	LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
	     "endpoint:0x%x Failed to send dummy %s packet.\n",
 | 
			
		||||
	     "endpoint:%x Failed to send dummy %s packet.\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), was_rtcp ? "RTCP" : "RTP");
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
@@ -222,7 +223,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
	if (seq == sstate->last_seq) {
 | 
			
		||||
		if (timestamp != sstate->last_timestamp) {
 | 
			
		||||
			rate_ctr_inc(sstate->err_ts_ctr);
 | 
			
		||||
			sstate->err_ts_counter += 1;
 | 
			
		||||
			LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
			     "The %s timestamp delta is != 0 but the sequence "
 | 
			
		||||
			     "number %d is the same, "
 | 
			
		||||
@@ -230,7 +231,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
 | 
			
		||||
			     "on 0x%x SSRC: %u timestamp: %u "
 | 
			
		||||
			     "from %s:%d\n",
 | 
			
		||||
			     text, seq,
 | 
			
		||||
			     state->patch.timestamp_offset, state->patch.seq_offset,
 | 
			
		||||
			     state->timestamp_offset, state->seq_offset,
 | 
			
		||||
			     ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
 | 
			
		||||
			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
 | 
			
		||||
		}
 | 
			
		||||
@@ -272,7 +273,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
 | 
			
		||||
	    ts_alignment_error(sstate, state->packet_duration, timestamp);
 | 
			
		||||
 | 
			
		||||
	if (timestamp_error) {
 | 
			
		||||
		rate_ctr_inc(sstate->err_ts_ctr);
 | 
			
		||||
		sstate->err_ts_counter += 1;
 | 
			
		||||
		LOGP(DRTP, LOGL_NOTICE,
 | 
			
		||||
		     "The %s timestamp has an alignment error of %d "
 | 
			
		||||
		     "on 0x%x SSRC: %u "
 | 
			
		||||
@@ -310,7 +311,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, "
 | 
			
		||||
@@ -324,15 +325,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->patch.timestamp_offset != timestamp_offset) {
 | 
			
		||||
		state->patch.timestamp_offset = timestamp_offset;
 | 
			
		||||
	if (state->timestamp_offset != timestamp_offset) {
 | 
			
		||||
		state->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->patch.timestamp_offset,
 | 
			
		||||
		     delta_seq, state->timestamp_offset,
 | 
			
		||||
		     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -353,11 +354,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->patch.timestamp_offset);
 | 
			
		||||
				      timestamp + state->timestamp_offset);
 | 
			
		||||
 | 
			
		||||
	/* If there is an alignment error, we have to compensate it */
 | 
			
		||||
	if (ts_error) {
 | 
			
		||||
		state->patch.timestamp_offset += ptime - ts_error;
 | 
			
		||||
		state->timestamp_offset += ptime - ts_error;
 | 
			
		||||
 | 
			
		||||
		LOGP(DRTP, LOGL_NOTICE,
 | 
			
		||||
		     "Corrected timestamp alignment error of %d on 0x%x SSRC: %u "
 | 
			
		||||
@@ -365,16 +366,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->patch.timestamp_offset, inet_ntoa(addr->sin_addr),
 | 
			
		||||
		     state->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 alignment
 | 
			
		||||
	 * error computation function */
 | 
			
		||||
	 * here would point to a serous problem with the alingnment
 | 
			
		||||
	 * error computation fuction */
 | 
			
		||||
	ts_check = ts_alignment_error(&state->out_stream, ptime,
 | 
			
		||||
				      timestamp + state->patch.timestamp_offset);
 | 
			
		||||
				      timestamp + state->timestamp_offset);
 | 
			
		||||
	OSMO_ASSERT(ts_check == 0);
 | 
			
		||||
 | 
			
		||||
	/* Return alignment error before compensation */
 | 
			
		||||
@@ -386,13 +387,13 @@ 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] maximum size of caller provided voice data buffer
 | 
			
		||||
 *  \param[in] maxmimum size of caller provided voice data buffer
 | 
			
		||||
 *  \returns ignores input parameters, return always 0 */
 | 
			
		||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
 | 
			
		||||
				struct mgcp_rtp_end *dst_end,
 | 
			
		||||
				char *data, int *len, int buf_size)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x transcoding disabled\n",
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:%x transcoding disabled\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp));
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -406,7 +407,7 @@ int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
 | 
			
		||||
				      struct mgcp_rtp_end *dst_end,
 | 
			
		||||
				      struct mgcp_rtp_end *src_end)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x transcoding disabled\n",
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:%x transcoding disabled\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp));
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -418,11 +419,11 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
 | 
			
		||||
					  struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
	     "endpoint:0x%x conn:%s using format defaults\n",
 | 
			
		||||
	     "endpoint:%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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -433,14 +434,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;
 | 
			
		||||
 | 
			
		||||
@@ -451,10 +452,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",
 | 
			
		||||
@@ -466,12 +467,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
 | 
			
		||||
@@ -490,7 +491,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;
 | 
			
		||||
@@ -498,7 +499,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;
 | 
			
		||||
 | 
			
		||||
@@ -507,33 +508,31 @@ 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->patch.orig_ssrc = ssrc;
 | 
			
		||||
		state->in_stream.ssrc = state->orig_ssrc = ssrc;
 | 
			
		||||
		state->in_stream.last_tsdelta = 0;
 | 
			
		||||
		state->packet_duration =
 | 
			
		||||
		    mgcp_rtp_packet_duration(endp, rtp_end);
 | 
			
		||||
		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 = state->in_stream;
 | 
			
		||||
		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 "
 | 
			
		||||
		     "endpoint:%x initializing stream, SSRC: %u timestamp: %u "
 | 
			
		||||
		     "pkt-duration: %d, from %s:%d\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
 | 
			
		||||
		     state->patch.seq_offset, state->packet_duration,
 | 
			
		||||
		     state->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, "
 | 
			
		||||
			     "endpoint:%x fixed packet duration is not available, "
 | 
			
		||||
			     "using fixed 20ms instead: %d from %s:%d\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp), state->packet_duration,
 | 
			
		||||
			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
 | 
			
		||||
		}
 | 
			
		||||
	} else if (state->in_stream.ssrc != ssrc) {
 | 
			
		||||
		LOGP(DRTP, LOGL_NOTICE,
 | 
			
		||||
		     "endpoint:0x%x SSRC changed: %u -> %u  "
 | 
			
		||||
		     "endpoint:%x SSRC changed: %u -> %u  "
 | 
			
		||||
		     "from %s:%d\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp),
 | 
			
		||||
		     state->in_stream.ssrc, rtp_hdr->ssrc,
 | 
			
		||||
@@ -544,7 +543,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
			
		||||
			int16_t delta_seq;
 | 
			
		||||
 | 
			
		||||
			/* Always increment seqno by 1 */
 | 
			
		||||
			state->patch.seq_offset =
 | 
			
		||||
			state->seq_offset =
 | 
			
		||||
			    (state->out_stream.last_seq + 1) - seq;
 | 
			
		||||
 | 
			
		||||
			/* Estimate number of packets that would have been sent */
 | 
			
		||||
@@ -556,17 +555,17 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
			
		||||
			adjust_rtp_timestamp_offset(endp, state, rtp_end, addr,
 | 
			
		||||
						    delta_seq, timestamp);
 | 
			
		||||
 | 
			
		||||
			state->patch.patch_ssrc = 1;
 | 
			
		||||
			ssrc = state->patch.orig_ssrc;
 | 
			
		||||
			state->patch_ssrc = 1;
 | 
			
		||||
			ssrc = state->orig_ssrc;
 | 
			
		||||
			if (rtp_end->force_constant_ssrc != -1)
 | 
			
		||||
				rtp_end->force_constant_ssrc -= 1;
 | 
			
		||||
 | 
			
		||||
			LOGP(DRTP, LOGL_NOTICE,
 | 
			
		||||
			     "endpoint:0x%x SSRC patching enabled, SSRC: %u "
 | 
			
		||||
			     "endpoint:%x SSRC patching enabled, SSRC: %u "
 | 
			
		||||
			     "SeqNo offset: %d, TS offset: %d "
 | 
			
		||||
			     "from %s:%d\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
 | 
			
		||||
			     state->patch.seq_offset, state->patch.timestamp_offset,
 | 
			
		||||
			     state->seq_offset, state->timestamp_offset,
 | 
			
		||||
			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -577,8 +576,8 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
			
		||||
				    addr, seq, timestamp, "input",
 | 
			
		||||
				    &state->in_stream.last_tsdelta);
 | 
			
		||||
 | 
			
		||||
		if (state->patch.patch_ssrc)
 | 
			
		||||
			ssrc = state->patch.orig_ssrc;
 | 
			
		||||
		if (state->patch_ssrc)
 | 
			
		||||
			ssrc = state->orig_ssrc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Save before patching */
 | 
			
		||||
@@ -593,15 +592,15 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
			
		||||
					   timestamp);
 | 
			
		||||
 | 
			
		||||
	/* Store the updated SSRC back to the packet */
 | 
			
		||||
	if (state->patch.patch_ssrc)
 | 
			
		||||
	if (state->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->patch.seq_offset;
 | 
			
		||||
	seq += state->seq_offset;
 | 
			
		||||
	rtp_hdr->sequence = htons(seq);
 | 
			
		||||
	timestamp += state->patch.timestamp_offset;
 | 
			
		||||
	timestamp += state->timestamp_offset;
 | 
			
		||||
	rtp_hdr->timestamp = htonl(timestamp);
 | 
			
		||||
 | 
			
		||||
	/* Check again, whether the timestamps are still valid */
 | 
			
		||||
@@ -620,7 +619,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
	DEBUGP(DRTP,
 | 
			
		||||
	       "endpoint:0x%x payload hdr payload %u -> endp payload %u\n",
 | 
			
		||||
	       "endpoint:%x payload hdr payload %u -> endp payload %u\n",
 | 
			
		||||
	       ENDPOINT_NUMBER(endp), rtp_hdr->payload_type, payload);
 | 
			
		||||
	rtp_hdr->payload_type = payload;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -671,16 +670,16 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
			
		||||
 | 
			
		||||
	if (is_rtp) {
 | 
			
		||||
		LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
		     "endpoint:0x%x delivering RTP packet...\n",
 | 
			
		||||
		     "endpoint:%x delivering RTP packet...\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
		     "endpoint:0x%x delivering RTCP packet...\n",
 | 
			
		||||
		     "endpoint:%x delivering RTCP packet...\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
	     "endpoint:0x%x loop:%d, mode:%d ",
 | 
			
		||||
	     "endpoint:%x loop:%d, mode:%d ",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), tcfg->audio_loop, conn_src->conn->mode);
 | 
			
		||||
	if (conn_src->conn->mode == MGCP_CONN_LOOPBACK)
 | 
			
		||||
		LOGPC(DRTP, LOGL_DEBUG, "(loopback)\n");
 | 
			
		||||
@@ -694,9 +693,9 @@ 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->stats.dropped_packets += 1;
 | 
			
		||||
		rtp_end->dropped_packets += 1;
 | 
			
		||||
		LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
		     "endpoint:0x%x output disabled, drop to %s %s "
 | 
			
		||||
		     "endpoint:%x output disabled, drop to %s %s "
 | 
			
		||||
		     "rtp_port:%u rtcp_port:%u\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp),
 | 
			
		||||
		     dest_name,
 | 
			
		||||
@@ -719,7 +718,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
			
		||||
				mgcp_patch_and_count(endp, rtp_state, rtp_end,
 | 
			
		||||
						     addr, buf, buflen);
 | 
			
		||||
			LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
			     "endpoint:0x%x process/send to %s %s "
 | 
			
		||||
			     "endpoint:%x process/send to %s %s "
 | 
			
		||||
			     "rtp_port:%u rtcp_port:%u\n",
 | 
			
		||||
			     ENDPOINT_NUMBER(endp), dest_name,
 | 
			
		||||
			     inet_ntoa(rtp_end->addr), ntohs(rtp_end->rtp_port),
 | 
			
		||||
@@ -749,8 +748,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
			
		||||
			if (len <= 0)
 | 
			
		||||
				return len;
 | 
			
		||||
 | 
			
		||||
			conn_dst->end.stats.packets_tx += 1;
 | 
			
		||||
			conn_dst->end.stats.octets_tx += len;
 | 
			
		||||
			conn_dst->end.packets_tx += 1;
 | 
			
		||||
			conn_dst->end.octets_tx += len;
 | 
			
		||||
 | 
			
		||||
			nbytes += len;
 | 
			
		||||
			buflen = cont;
 | 
			
		||||
@@ -758,7 +757,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
 | 
			
		||||
		return nbytes;
 | 
			
		||||
	} else if (!tcfg->omit_rtcp) {
 | 
			
		||||
		LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
		     "endpoint:0x%x send to %s %s rtp_port:%u rtcp_port:%u\n",
 | 
			
		||||
		     "endpoint:%x send to %s %s rtp_port:%u rtcp_port:%u\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp),
 | 
			
		||||
		     dest_name,
 | 
			
		||||
		     inet_ntoa(rtp_end->addr),
 | 
			
		||||
@@ -769,8 +768,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.stats.packets_tx += 1;
 | 
			
		||||
		conn_dst->end.stats.octets_tx += len;
 | 
			
		||||
		conn_dst->end.packets_tx += 1;
 | 
			
		||||
		conn_dst->end.octets_tx += len;
 | 
			
		||||
 | 
			
		||||
		return len;
 | 
			
		||||
	}
 | 
			
		||||
@@ -805,13 +804,13 @@ static int receive_from(struct mgcp_endpoint *endp, int fd,
 | 
			
		||||
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x failed to receive packet, errno: %d/%s\n",
 | 
			
		||||
		     "endpoint:%x failed to receive packet, errno: %d/%s\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), errno, strerror(errno));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (tossed) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR, "endpoint:0x%x packet tossed\n",
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR, "endpoint:%x packet tossed\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -831,11 +830,11 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn,
 | 
			
		||||
	if (memcmp(&addr->sin_addr, &conn->end.addr, sizeof(addr->sin_addr))
 | 
			
		||||
	    != 0) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x data from wrong address: %s, ",
 | 
			
		||||
		     "endpoint:%x data from wrong address: %s, ",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr));
 | 
			
		||||
		LOGPC(DRTP, LOGL_ERROR, "expected: %s\n",
 | 
			
		||||
		      inet_ntoa(conn->end.addr));
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR, "endpoint:0x%x packet tossed\n",
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR, "endpoint:%x packet tossed\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -847,12 +846,12 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn,
 | 
			
		||||
	if (conn->end.rtp_port != addr->sin_port &&
 | 
			
		||||
	    conn->end.rtcp_port != addr->sin_port) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x data from wrong source port: %d, ",
 | 
			
		||||
		     "endpoint:%x data from wrong source port: %d, ",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), ntohs(addr->sin_port));
 | 
			
		||||
		LOGPC(DRTP, LOGL_ERROR,
 | 
			
		||||
		      "expected: %d for RTP or %d for RTCP\n",
 | 
			
		||||
		      ntohs(conn->end.rtp_port), ntohs(conn->end.rtcp_port));
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR, "endpoint:0x%x packet tossed\n",
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR, "endpoint:%x packet tossed\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -867,25 +866,16 @@ 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",
 | 
			
		||||
		     "endpoint:%x destination IP-address is invalid\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (conn->end.rtp_port == 0) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x destination rtp port is invalid\n",
 | 
			
		||||
		     "endpoint:%x destination rtp port is invalid\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -907,7 +897,7 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
 | 
			
		||||
	endp = conn->conn->endp;
 | 
			
		||||
	tcfg = endp->tcfg;
 | 
			
		||||
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x receiving RTP/RTCP packet...\n",
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:%x receiving RTP/RTCP packet...\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp));
 | 
			
		||||
 | 
			
		||||
	rc = receive_from(endp, fd->fd, addr, buf, buf_size);
 | 
			
		||||
@@ -915,11 +905,11 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
 | 
			
		||||
		return -1;
 | 
			
		||||
	*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, "receiving from %s %s %d\n",
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:%x ", ENDPOINT_NUMBER(endp));
 | 
			
		||||
	LOGPC(DRTP, LOGL_DEBUG, "receiveing 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),
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:%x conn:%s\n", ENDPOINT_NUMBER(endp),
 | 
			
		||||
	     mgcp_conn_dump(conn->conn));
 | 
			
		||||
 | 
			
		||||
	/* Check if the origin of the RTP packet seems plausible */
 | 
			
		||||
@@ -931,16 +921,16 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
 | 
			
		||||
	/* Filter out dummy message */
 | 
			
		||||
	if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
 | 
			
		||||
		LOGP(DRTP, LOGL_NOTICE,
 | 
			
		||||
		     "endpoint:0x%x dummy message received\n",
 | 
			
		||||
		     "endpoint:%x dummy message received\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x packet tossed\n", ENDPOINT_NUMBER(endp));
 | 
			
		||||
		     "endpoint:%x packet tossed\n", ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Increment RX statistics */
 | 
			
		||||
	conn->end.stats.packets_rx += 1;
 | 
			
		||||
	conn->end.stats.octets_rx += rc;
 | 
			
		||||
	conn->end.packets_rx += 1;
 | 
			
		||||
	conn->end.octets_rx += rc;
 | 
			
		||||
 | 
			
		||||
	/* Forward a copy of the RTP data to a debug ip/port */
 | 
			
		||||
	forward_data(fd->fd, &conn->tap_in, buf, rc);
 | 
			
		||||
@@ -958,7 +948,7 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	endp = conn_src->conn->endp;
 | 
			
		||||
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x destin conn:%s\n",
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:%x destin conn:%s\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn_dst->conn));
 | 
			
		||||
 | 
			
		||||
	/* Before we try to deliver the packet, we check if the destination
 | 
			
		||||
@@ -972,7 +962,7 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
 | 
			
		||||
	switch (conn_dst->type) {
 | 
			
		||||
	case MGCP_RTP_DEFAULT:
 | 
			
		||||
		LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
		     "endpoint:0x%x endpoint type is MGCP_RTP_DEFAULT, "
 | 
			
		||||
		     "endpoint:%x endpoint type is MGCP_RTP_DEFAULT, "
 | 
			
		||||
		     "using mgcp_send() to forward data directly\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return mgcp_send(endp, proto == MGCP_PROTO_RTP,
 | 
			
		||||
@@ -980,7 +970,7 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
 | 
			
		||||
	case MGCP_OSMUX_BSC_NAT:
 | 
			
		||||
	case MGCP_OSMUX_BSC:
 | 
			
		||||
		LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
		     "endpoint:0x%x endpoint type is MGCP_OSMUX_BSC_NAT, "
 | 
			
		||||
		     "endpoint:%x endpoint type is MGCP_OSMUX_BSC_NAT, "
 | 
			
		||||
		     "using osmux_xfrm_to_osmux() to forward data through OSMUX\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return osmux_xfrm_to_osmux(buf, buf_size, conn_dst);
 | 
			
		||||
@@ -990,7 +980,7 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
 | 
			
		||||
	 * be discarded, this should not happen, normally the MGCP type
 | 
			
		||||
	 * should be properly set */
 | 
			
		||||
	LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
	     "endpoint:0x%x bad MGCP type -- data discarded!\n",
 | 
			
		||||
	     "endpoint:%x bad MGCP type -- data discarded!\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp));
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
@@ -1035,7 +1025,7 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
 | 
			
		||||
	/* There is no destination conn, stop here */
 | 
			
		||||
	if (!conn_dst) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x unable to find destination conn\n",
 | 
			
		||||
		     "endpoint:%x unable to find destination conn\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -1043,7 +1033,7 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
 | 
			
		||||
	/* The destination conn is not an RTP connection */
 | 
			
		||||
	if (conn_dst->type != MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x unable to find suitable destination conn\n",
 | 
			
		||||
		     "endpoint:%x unable to find suitable destination conn\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -1054,25 +1044,6 @@ 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)
 | 
			
		||||
{
 | 
			
		||||
@@ -1096,7 +1067,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
	endp = conn_src->conn->endp;
 | 
			
		||||
	OSMO_ASSERT(endp);
 | 
			
		||||
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x source conn:%s\n",
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "endpoint:%x source conn:%s\n",
 | 
			
		||||
	     ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn_src->conn));
 | 
			
		||||
 | 
			
		||||
	/* Receive packet */
 | 
			
		||||
@@ -1106,16 +1077,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
 | 
			
		||||
	/* Check if the connection is in loopback mode, if yes, just send the
 | 
			
		||||
	 * incoming data back to the origin */
 | 
			
		||||
 | 
			
		||||
	if (conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
 | 
			
		||||
		/* When we are in loopback mode, we loop back all incoming
 | 
			
		||||
		 * packets back to their origin. We will use the originating
 | 
			
		||||
		 * address data from the UDP packet header to patch the
 | 
			
		||||
		 * outgoing address in connection on the fly */
 | 
			
		||||
		if (conn_src->end.rtp_port == 0) {
 | 
			
		||||
			conn_src->end.addr = addr.sin_addr;
 | 
			
		||||
			conn_src->end.rtp_port = addr.sin_port;
 | 
			
		||||
		}
 | 
			
		||||
		return mgcp_send_rtp(proto, &addr, buf,
 | 
			
		||||
				     len, conn_src, conn_src);
 | 
			
		||||
	}
 | 
			
		||||
@@ -1147,17 +1109,41 @@ 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)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct sockaddr_in addr;
 | 
			
		||||
	int on = 1;
 | 
			
		||||
 | 
			
		||||
	rc = osmo_sock_init2(AF_INET, SOCK_DGRAM, IPPROTO_UDP, source_addr, port,
 | 
			
		||||
			     NULL, 0, OSMO_SOCK_F_BIND);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
	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;
 | 
			
		||||
		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;
 | 
			
		||||
}
 | 
			
		||||
@@ -1172,7 +1158,7 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
 | 
			
		||||
	if (mgcp_create_bind(source_addr, &rtp_end->rtp,
 | 
			
		||||
			     rtp_end->local_port) != 0) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x failed to create RTP port: %s:%d\n", endpno,
 | 
			
		||||
		     "endpoint:%x failed to create RTP port: %s:%d\n", endpno,
 | 
			
		||||
		     source_addr, rtp_end->local_port);
 | 
			
		||||
		goto cleanup0;
 | 
			
		||||
	}
 | 
			
		||||
@@ -1180,7 +1166,7 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
 | 
			
		||||
	if (mgcp_create_bind(source_addr, &rtp_end->rtcp,
 | 
			
		||||
			     rtp_end->local_port + 1) != 0) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x failed to create RTCP port: %s:%d\n", endpno,
 | 
			
		||||
		     "endpoint:%x failed to create RTCP port: %s:%d\n", endpno,
 | 
			
		||||
		     source_addr, rtp_end->local_port + 1);
 | 
			
		||||
		goto cleanup1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -1192,7 +1178,7 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
 | 
			
		||||
	rtp_end->rtp.when = BSC_FD_READ;
 | 
			
		||||
	if (osmo_fd_register(&rtp_end->rtp) != 0) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x failed to register RTP port %d\n", endpno,
 | 
			
		||||
		     "endpoint:%x failed to register RTP port %d\n", endpno,
 | 
			
		||||
		     rtp_end->local_port);
 | 
			
		||||
		goto cleanup2;
 | 
			
		||||
	}
 | 
			
		||||
@@ -1200,7 +1186,7 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
 | 
			
		||||
	rtp_end->rtcp.when = BSC_FD_READ;
 | 
			
		||||
	if (osmo_fd_register(&rtp_end->rtcp) != 0) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x failed to register RTCP port %d\n", endpno,
 | 
			
		||||
		     "endpoint:%x failed to register RTCP port %d\n", endpno,
 | 
			
		||||
		     rtp_end->local_port + 1);
 | 
			
		||||
		goto cleanup3;
 | 
			
		||||
	}
 | 
			
		||||
@@ -1231,12 +1217,12 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
 | 
			
		||||
	struct mgcp_rtp_end *end;
 | 
			
		||||
	char local_ip_addr[INET_ADDRSTRLEN];
 | 
			
		||||
 | 
			
		||||
	snprintf(name, sizeof(name), "%s-%s", conn->conn->name, conn->conn->id);
 | 
			
		||||
	snprintf(name, sizeof(name), "%s-%u", conn->conn->name, conn->conn->id);
 | 
			
		||||
	end = &conn->end;
 | 
			
		||||
 | 
			
		||||
	if (end->rtp.fd != -1 || end->rtcp.fd != -1) {
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "endpoint:0x%x %u was already bound on conn:%s\n",
 | 
			
		||||
		     "endpoint:%x %u was already bound on conn:%s\n",
 | 
			
		||||
		     ENDPOINT_NUMBER(endp), rtp_port,
 | 
			
		||||
		     mgcp_conn_dump(conn->conn));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,6 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
@@ -143,7 +142,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
 | 
			
		||||
 * found, the function will automatically allocate one */
 | 
			
		||||
 * foud a 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)
 | 
			
		||||
{
 | 
			
		||||
@@ -208,18 +207,12 @@ endpoint_lookup(struct mgcp_config *cfg, int cid,
 | 
			
		||||
		case MGCP_DEST_NET:
 | 
			
		||||
			/* FIXME: Get rid of CONN_ID_XXX! */
 | 
			
		||||
			conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
 | 
			
		||||
			if (conn_net)
 | 
			
		||||
			this = &conn_net->end.addr;
 | 
			
		||||
			else
 | 
			
		||||
				this = NULL;
 | 
			
		||||
			break;
 | 
			
		||||
		case MGCP_DEST_BTS:
 | 
			
		||||
			/* FIXME: Get rid of CONN_ID_XXX! */
 | 
			
		||||
			conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
 | 
			
		||||
			if (conn_bts)
 | 
			
		||||
			this = &conn_bts->end.addr;
 | 
			
		||||
			else
 | 
			
		||||
				this = NULL;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* Should not ever happen */
 | 
			
		||||
@@ -229,8 +222,7 @@ endpoint_lookup(struct mgcp_config *cfg, int cid,
 | 
			
		||||
 | 
			
		||||
		/* FIXME: Get rid of CONN_ID_XXX! */
 | 
			
		||||
		conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
 | 
			
		||||
		if (conn_net && this && conn_net->osmux.cid == cid
 | 
			
		||||
		    && this->s_addr == from_addr->s_addr)
 | 
			
		||||
		if (conn_net->osmux.cid == cid && this->s_addr == from_addr->s_addr)
 | 
			
		||||
			return endp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -256,8 +248,8 @@ static void scheduled_tx_net_cb(struct msgb *msg, void *data)
 | 
			
		||||
		.sin_port = conn_net->end.rtp_port,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	conn_bts->end.stats.octets_tx += msg->len;
 | 
			
		||||
	conn_bts->end.stats.packets_tx++;
 | 
			
		||||
	conn_bts->end.octets_tx += msg->len;
 | 
			
		||||
	conn_bts->end.packets_tx++;
 | 
			
		||||
 | 
			
		||||
	/* Send RTP data to NET */
 | 
			
		||||
	/* FIXME: Get rid of conn_bts and conn_net! */
 | 
			
		||||
@@ -283,8 +275,8 @@ static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
 | 
			
		||||
		.sin_port = conn_bts->end.rtp_port,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	conn_net->end.stats.octets_tx += msg->len;
 | 
			
		||||
	conn_net->end.stats.packets_tx++;
 | 
			
		||||
	conn_net->end.octets_tx += msg->len;
 | 
			
		||||
	conn_net->end.packets_tx++;
 | 
			
		||||
 | 
			
		||||
	/* Send RTP data to BTS */
 | 
			
		||||
	/* FIXME: Get rid of conn_bts and conn_net! */
 | 
			
		||||
@@ -322,10 +314,11 @@ 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_bts = NULL;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_net = NULL;
 | 
			
		||||
 | 
			
		||||
	msg = osmux_recv(ofd, &addr);
 | 
			
		||||
	if (!msg)
 | 
			
		||||
@@ -344,8 +337,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_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
 | 
			
		||||
		if (!conn_bts)
 | 
			
		||||
		conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
 | 
			
		||||
		if (!conn_net)
 | 
			
		||||
			goto out;
 | 
			
		||||
 | 
			
		||||
		if (!endp) {
 | 
			
		||||
@@ -354,11 +347,12 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
			     osmuxh->circuit_id);
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
		conn_bts->osmux.stats.octets += osmux_chunk_length(msg, rem);
 | 
			
		||||
		conn_bts->osmux.stats.chunks++;
 | 
			
		||||
		conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
 | 
			
		||||
		conn_net->osmux.stats.chunks++;
 | 
			
		||||
		rem = msg->len;
 | 
			
		||||
 | 
			
		||||
		osmux_xfrm_output_sched(&conn_bts->osmux.out, osmuxh);
 | 
			
		||||
		osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
 | 
			
		||||
		osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
@@ -424,6 +418,7 @@ 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;
 | 
			
		||||
@@ -460,7 +455,8 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
		conn_net->osmux.stats.chunks++;
 | 
			
		||||
		rem = msg->len;
 | 
			
		||||
 | 
			
		||||
		osmux_xfrm_output_sched(&conn_net->osmux.out, osmuxh);
 | 
			
		||||
		osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
 | 
			
		||||
		osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
@@ -549,13 +545,9 @@ 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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -574,13 +566,8 @@ void osmux_disable_conn(struct mgcp_conn_rtp *conn)
 | 
			
		||||
	if (conn->osmux.state != OSMUX_STATE_ENABLED)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
 | 
			
		||||
	LOGP(DLMGCP, LOGL_INFO, "Releasing connection %u 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;
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -24,14 +24,9 @@
 | 
			
		||||
#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;
 | 
			
		||||
@@ -44,8 +39,89 @@ struct sdp_rtp_map {
 | 
			
		||||
	int channels;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Helper function to extrapolate missing codec parameters in a codec mao from
 | 
			
		||||
 * an already filled in payload_type, called from: mgcp_parse_sdp_data() */
 | 
			
		||||
/*! 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
@@ -72,16 +148,10 @@ 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)
 | 
			
		||||
{
 | 
			
		||||
@@ -91,13 +161,8 @@ 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",
 | 
			
		||||
@@ -116,72 +181,43 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
 | 
			
		||||
	     audio_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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)
 | 
			
		||||
/* 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)
 | 
			
		||||
{
 | 
			
		||||
	char *str;
 | 
			
		||||
	char *str_ptr;
 | 
			
		||||
	char *pt_str;
 | 
			
		||||
	unsigned int pt;
 | 
			
		||||
	unsigned int count = 0;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	char *codec_str;
 | 
			
		||||
	char audio_codec[64];
 | 
			
		||||
 | 
			
		||||
	str = talloc_zero_size(ctx, strlen(sdp) + 1);
 | 
			
		||||
	str_ptr = str;
 | 
			
		||||
	strcpy(str_ptr, sdp);
 | 
			
		||||
	if (!codec->codec_name)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	str_ptr = strstr(str_ptr, "RTP/AVP ");
 | 
			
		||||
	if (!str_ptr)
 | 
			
		||||
		goto exit;
 | 
			
		||||
	/* 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;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	return strcasecmp(audio_codec, codec->codec_name) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! 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,
 | 
			
		||||
 *  rtp port, rtcp port and the codec information.
 | 
			
		||||
 *  \returns 0 on success, -1 on failure. */
 | 
			
		||||
 *  the rtp port and the rtcp port */
 | 
			
		||||
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[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int codecs_used = 0;
 | 
			
		||||
	struct sdp_rtp_map codecs[10];
 | 
			
		||||
	int codecs_used = 0;
 | 
			
		||||
	char *line;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	int maxptime = -1;
 | 
			
		||||
	int i;
 | 
			
		||||
	int codecs_assigned = 0;
 | 
			
		||||
	void *tmp_ctx = talloc_new(NULL);
 | 
			
		||||
	struct mgcp_rtp_end *rtp;
 | 
			
		||||
 | 
			
		||||
@@ -218,21 +254,30 @@ 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) {
 | 
			
		||||
				rtp->maximum_packet_time = ptime2;
 | 
			
		||||
			} else if (sscanf(line, "a=maxptime:%d", &ptime2)
 | 
			
		||||
				   == 1) {
 | 
			
		||||
				maxptime = ptime2;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case 'm':
 | 
			
		||||
			rc = sscanf(line, "m=audio %d RTP/AVP", &port);
 | 
			
		||||
			if (rc == 1) {
 | 
			
		||||
			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) {
 | 
			
		||||
				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':
 | 
			
		||||
 | 
			
		||||
@@ -253,37 +298,43 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
 | 
			
		||||
 | 
			
		||||
	/* 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);
 | 
			
		||||
	/* 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;
 | 
			
		||||
 | 
			
		||||
	/* 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));
 | 
			
		||||
		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));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(tmp_ctx);
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	return codecs_assigned > 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Generate SDP response string.
 | 
			
		||||
@@ -314,7 +365,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
	rc = msgb_printf(sdp,
 | 
			
		||||
			 "v=0\r\n"
 | 
			
		||||
			 "o=- %s 23 IN IP4 %s\r\n"
 | 
			
		||||
			 "o=- %u 23 IN IP4 %s\r\n"
 | 
			
		||||
			 "s=-\r\n"
 | 
			
		||||
			 "c=IN IP4 %s\r\n"
 | 
			
		||||
			 "t=0 0\r\n", conn->conn->id, addr, addr);
 | 
			
		||||
@@ -328,9 +379,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			goto buffer_too_small;
 | 
			
		||||
 | 
			
		||||
		/* 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)) {
 | 
			
		||||
		if (audio_name && endp->tcfg->audio_send_name) {
 | 
			
		||||
			rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n",
 | 
			
		||||
					 payload_type, audio_name);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,6 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#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 */
 | 
			
		||||
@@ -31,10 +30,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;
 | 
			
		||||
@@ -44,8 +43,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->stats.packets_rx;
 | 
			
		||||
	if (*expected < end->stats.packets_rx) {
 | 
			
		||||
	*loss = *expected - end->packets_rx;
 | 
			
		||||
	if (*expected < end->packets_rx) {
 | 
			
		||||
		if (*loss > 0)
 | 
			
		||||
			*loss = INT_MIN;
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -57,9 +56,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 */
 | 
			
		||||
@@ -75,8 +74,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.stats.packets_tx, conn->end.stats.octets_tx,
 | 
			
		||||
			  conn->end.stats.packets_rx, conn->end.stats.octets_rx,
 | 
			
		||||
			  conn->end.packets_tx, conn->end.octets_tx,
 | 
			
		||||
			  conn->end.packets_rx, conn->end.octets_rx,
 | 
			
		||||
			  ploss, jitter);
 | 
			
		||||
	if (nchars < 0 || nchars >= str_len)
 | 
			
		||||
		goto truncate;
 | 
			
		||||
@@ -84,12 +83,11 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
 | 
			
		||||
	str += nchars;
 | 
			
		||||
	str_len -= nchars;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
			  "\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;
 | 
			
		||||
 | 
			
		||||
@@ -101,7 +99,6 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
 | 
			
		||||
			 "\r\nX-Osmux-ST: CR=%u, BR=%u",
 | 
			
		||||
			 conn->osmux.stats.chunks, conn->osmux.stats.octets);
 | 
			
		||||
	}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
truncate:
 | 
			
		||||
	str[str_len - 1] = '\0';
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,6 @@
 | 
			
		||||
#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>
 | 
			
		||||
 | 
			
		||||
@@ -64,16 +63,15 @@ 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);
 | 
			
		||||
	vty_out(vty, "  bind port %u%s", g_cfg->source_port, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, "  rtp port-range %u %u%s",
 | 
			
		||||
	vty_out(vty, "  rtp net-range %u %u%s",
 | 
			
		||||
		g_cfg->net_ports.range_start, g_cfg->net_ports.range_end,
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
	if (g_cfg->net_ports.bind_addr)
 | 
			
		||||
		vty_out(vty, "  rtp bind-ip %s%s",
 | 
			
		||||
		vty_out(vty, "  rtp net-bind-ip %s%s",
 | 
			
		||||
			g_cfg->net_ports.bind_addr, VTY_NEWLINE);
 | 
			
		||||
	if (g_cfg->net_ports.bind_addr_probe)
 | 
			
		||||
		vty_out(vty, "  rtp ip-probing%s", VTY_NEWLINE);
 | 
			
		||||
@@ -117,7 +115,7 @@ static int config_write_mgcp(struct vty *vty)
 | 
			
		||||
		g_cfg->trunk.audio_send_name ? "" : "no ", VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, "  loop %u%s", ! !g_cfg->trunk.audio_loop, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, "  number endpoints %u%s",
 | 
			
		||||
		g_cfg->trunk.vty_number_endpoints - 1, VTY_NEWLINE);
 | 
			
		||||
		g_cfg->trunk.number_endpoints - 1, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, "  %sallow-transcoding%s",
 | 
			
		||||
		g_cfg->trunk.no_audio_transcoding ? "no " : "", VTY_NEWLINE);
 | 
			
		||||
	if (g_cfg->call_agent_addr)
 | 
			
		||||
@@ -157,20 +155,19 @@ 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: %lu->%lu%s"
 | 
			
		||||
		"   Timestamp Errs: %d->%d%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_ctr->current,
 | 
			
		||||
		state->out_stream.err_ts_ctr->current,
 | 
			
		||||
	        VTY_NEWLINE,
 | 
			
		||||
		end->stats.dropped_packets, VTY_NEWLINE,
 | 
			
		||||
		state->in_stream.err_ts_counter,
 | 
			
		||||
		state->out_stream.err_ts_counter, VTY_NEWLINE,
 | 
			
		||||
		end->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,
 | 
			
		||||
@@ -179,11 +176,25 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state,
 | 
			
		||||
		end->force_output_ptime, VTY_NEWLINE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, int epidx, int verbose)
 | 
			
		||||
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
 | 
			
		||||
		       int verbose)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "Endpoint %s%d:%s", MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, epidx, VTY_NEWLINE);
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	if (!cfg->endpoints) {
 | 
			
		||||
		vty_out(vty, "No endpoints allocated yet.%s", VTY_NEWLINE);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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",
 | 
			
		||||
@@ -200,32 +211,6 @@ static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, int epidx
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 1; i < cfg->number_endpoints; ++i) {
 | 
			
		||||
		struct mgcp_endpoint *endp = &cfg->endpoints[i];
 | 
			
		||||
		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,
 | 
			
		||||
@@ -249,33 +234,6 @@ 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;
 | 
			
		||||
@@ -321,70 +279,41 @@ 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"
 | 
			
		||||
#define RANGE_START_STR "Start of the range of ports\n"
 | 
			
		||||
#define RANGE_END_STR "End of the range of ports\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_rtp_port_range,
 | 
			
		||||
      cfg_mgcp_rtp_port_range_cmd,
 | 
			
		||||
      "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)
 | 
			
		||||
{
 | 
			
		||||
	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,
 | 
			
		||||
DEFUN(cfg_mgcp_rtp_net_range,
 | 
			
		||||
      cfg_mgcp_rtp_net_range_cmd,
 | 
			
		||||
      "rtp net-range <0-65534> <0-65534>",
 | 
			
		||||
      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);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_rtp_bind_ip,
 | 
			
		||||
      cfg_mgcp_rtp_bind_ip_cmd,
 | 
			
		||||
      "rtp bind-ip A.B.C.D",
 | 
			
		||||
DEFUN(cfg_mgcp_rtp_net_bind_ip,
 | 
			
		||||
      cfg_mgcp_rtp_net_bind_ip_cmd,
 | 
			
		||||
      "rtp net-bind-ip A.B.C.D",
 | 
			
		||||
      RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n")
 | 
			
		||||
{
 | 
			
		||||
	osmo_talloc_replace_string(g_cfg, &g_cfg->net_ports.bind_addr, argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgcp_rtp_bind_ip,
 | 
			
		||||
		 cfg_mgcp_rtp_net_bind_ip_cmd,
 | 
			
		||||
		 "rtp net-bind-ip A.B.C.D",
 | 
			
		||||
		 RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_rtp_no_bind_ip,
 | 
			
		||||
      cfg_mgcp_rtp_no_bind_ip_cmd,
 | 
			
		||||
      "no rtp bind-ip",
 | 
			
		||||
DEFUN(cfg_mgcp_rtp_no_net_bind_ip,
 | 
			
		||||
      cfg_mgcp_rtp_no_net_bind_ip_cmd,
 | 
			
		||||
      "no rtp net-bind-ip",
 | 
			
		||||
      NO_STR RTP_STR "Bind endpoints facing the Network\n"
 | 
			
		||||
      "Address to bind to\n")
 | 
			
		||||
{
 | 
			
		||||
@@ -392,11 +321,6 @@ DEFUN(cfg_mgcp_rtp_no_bind_ip,
 | 
			
		||||
	g_cfg->net_ports.bind_addr = NULL;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgcp_rtp_no_bind_ip,
 | 
			
		||||
		 cfg_mgcp_rtp_no_net_bind_ip_cmd,
 | 
			
		||||
		 "no rtp net-bind-ip",
 | 
			
		||||
		 NO_STR RTP_STR "Bind endpoints facing the Network\n"
 | 
			
		||||
		 "Address to bind to\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_rtp_net_bind_ip_probing,
 | 
			
		||||
      cfg_mgcp_rtp_net_bind_ip_probing_cmd,
 | 
			
		||||
@@ -586,7 +510,7 @@ DEFUN(cfg_mgcp_number_endp,
 | 
			
		||||
      "Number options\n" "Endpoints available\n" "Number endpoints\n")
 | 
			
		||||
{
 | 
			
		||||
	/* + 1 as we start counting at one */
 | 
			
		||||
	g_cfg->trunk.vty_number_endpoints = atoi(argv[0]) + 1;
 | 
			
		||||
	g_cfg->trunk.number_endpoints = atoi(argv[0]) + 1;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1032,7 +956,7 @@ DEFUN(tap_rtp,
 | 
			
		||||
	struct mgcp_trunk_config *trunk;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	struct mgcp_conn_rtp *conn;
 | 
			
		||||
        const char *conn_id = NULL;
 | 
			
		||||
	uint32_t conn_id;
 | 
			
		||||
 | 
			
		||||
	trunk = find_trunk(g_cfg, atoi(argv[0]));
 | 
			
		||||
	if (!trunk) {
 | 
			
		||||
@@ -1056,11 +980,11 @@ DEFUN(tap_rtp,
 | 
			
		||||
 | 
			
		||||
	endp = &trunk->endpoints[endp_no];
 | 
			
		||||
 | 
			
		||||
	conn_id = argv[2];
 | 
			
		||||
	conn_id = strtoul(argv[2], NULL, 10);
 | 
			
		||||
	conn = mgcp_conn_get_rtp(endp, conn_id);
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		vty_out(vty, "Conn ID %s is invalid.%s",
 | 
			
		||||
			conn_id, VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, "Conn ID %s/%d is invalid.%s",
 | 
			
		||||
			argv[2], conn_id, VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1108,7 +1032,7 @@ DEFUN(free_endp, free_endp_cmd,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	endp = &trunk->endpoints[endp_no];
 | 
			
		||||
	mgcp_endp_release(endp);
 | 
			
		||||
	mgcp_release_endp(endp);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1178,7 +1102,7 @@ DEFUN(cfg_mgcp_osmux,
 | 
			
		||||
	 * allow to turn it on yet. */
 | 
			
		||||
	vty_out(vty, "OSMUX currently unavailable in this software version.%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_WARNING;
 | 
			
		||||
#if 0
 | 
			
		||||
 | 
			
		||||
	if (strcmp(argv[0], "on") == 0)
 | 
			
		||||
		g_cfg->osmux = OSMUX_USAGE_ON;
 | 
			
		||||
	else if (strcmp(argv[0], "only") == 0)
 | 
			
		||||
@@ -1190,7 +1114,6 @@ DEFUN(cfg_mgcp_osmux,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcp_osmux_ip,
 | 
			
		||||
@@ -1241,18 +1164,9 @@ 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);
 | 
			
		||||
@@ -1267,11 +1181,8 @@ int mgcp_vty_init(void)
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_rtp_net_range_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_rtp_port_range_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_rtp_net_bind_ip_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_rtp_bind_ip_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_rtp_no_net_bind_ip_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_rtp_no_bind_ip_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_rtp_net_bind_ip_probing_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_rtp_no_net_bind_ip_probing_cmd);
 | 
			
		||||
	install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd);
 | 
			
		||||
@@ -1311,7 +1222,6 @@ 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);
 | 
			
		||||
@@ -1341,6 +1251,18 @@ int mgcp_vty_init(void)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int allocate_trunk(struct mgcp_trunk_config *trunk)
 | 
			
		||||
{
 | 
			
		||||
	if (mgcp_endpoints_allocate(trunk) != 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Failed to allocate %d endpoints on trunk %d.\n",
 | 
			
		||||
		     trunk->number_endpoints, trunk->trunk_nr);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
 | 
			
		||||
		      enum mgcp_role role)
 | 
			
		||||
{
 | 
			
		||||
@@ -1364,18 +1286,17 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (mgcp_endpoints_allocate(&g_cfg->trunk) != 0) {
 | 
			
		||||
	if (allocate_trunk(&g_cfg->trunk) != 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Failed to initialize the virtual trunk (%d endpoints)\n",
 | 
			
		||||
		     g_cfg->trunk.number_endpoints);
 | 
			
		||||
		     "Failed to initialize the virtual trunk.\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
 | 
			
		||||
		if (mgcp_endpoints_allocate(trunk) != 0) {
 | 
			
		||||
		if (allocate_trunk(trunk) != 0) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Failed to initialize trunk %d (%d endpoints)\n",
 | 
			
		||||
			     trunk->trunk_nr, trunk->number_endpoints);
 | 
			
		||||
			     "Failed to initialize E1 trunk %d.\n",
 | 
			
		||||
			     trunk->trunk_nr);
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(LIBBCG729_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
@@ -25,7 +24,6 @@ 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_logging2(tall_bsc_ctx, &log_info);
 | 
			
		||||
	osmo_init_logging(&log_info);
 | 
			
		||||
 | 
			
		||||
	cfg = mgcp_config_alloc();
 | 
			
		||||
	if (!cfg)
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,6 @@ AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
@@ -22,9 +20,7 @@ osmo_mgw_SOURCES = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
osmo_mgw_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2011 by On-Waves
 | 
			
		||||
 * (C) 2017 by sysmocom - s.f.m.c. GmbH, Author: Philipp Maier
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
@@ -37,7 +36,6 @@
 | 
			
		||||
#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>
 | 
			
		||||
@@ -46,14 +44,12 @@
 | 
			
		||||
#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"
 | 
			
		||||
 | 
			
		||||
@@ -67,16 +63,16 @@ static struct mgcp_trunk_config *reset_trunk;
 | 
			
		||||
static int reset_endpoints = 0;
 | 
			
		||||
static int daemonize = 0;
 | 
			
		||||
 | 
			
		||||
const char *osmomgw_copyright =
 | 
			
		||||
const char *openbsc_copyright =
 | 
			
		||||
	"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
 | 
			
		||||
	"Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
 | 
			
		||||
	"Contributions by Pablo Neira Ayuso, Jacob Erlbeck, Neels Hofmeyr\r\n"
 | 
			
		||||
	"Philipp Maier\r\n\r\n"
 | 
			
		||||
	"Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n"
 | 
			
		||||
	"Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\r\n"
 | 
			
		||||
	"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
 | 
			
		||||
	"This is free software: you are free to change and redistribute it.\r\n"
 | 
			
		||||
	"There is NO WARRANTY, to the extent permitted by law.\r\n";
 | 
			
		||||
 | 
			
		||||
static char *config_file = "osmo-mgw.cfg";
 | 
			
		||||
static char *config_file = "mgcp.cfg";
 | 
			
		||||
 | 
			
		||||
/* used by msgb and mgcp */
 | 
			
		||||
void *tall_bsc_ctx = NULL;
 | 
			
		||||
@@ -191,7 +187,7 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
		/* Walk over all endpoints and trigger a release, this will release all
 | 
			
		||||
		 * endpoints, possible open connections are forcefully dropped */
 | 
			
		||||
		for (i = 1; i < reset_trunk->number_endpoints; ++i)
 | 
			
		||||
			mgcp_endp_release(&reset_trunk->endpoints[i]);
 | 
			
		||||
			mgcp_release_endp(&reset_trunk->endpoints[i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -253,25 +249,22 @@ const struct log_info log_info = {
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int flags;
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct sockaddr_in addr;
 | 
			
		||||
	int on = 1, 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_logging2(tall_bsc_ctx, &log_info);
 | 
			
		||||
	osmo_init_logging(&log_info);
 | 
			
		||||
 | 
			
		||||
	cfg = mgcp_config_alloc();
 | 
			
		||||
	if (!cfg)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	vty_info.copyright = osmomgw_copyright;
 | 
			
		||||
	vty_info.copyright = openbsc_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();
 | 
			
		||||
 | 
			
		||||
@@ -286,7 +279,7 @@ int main(int argc, char **argv)
 | 
			
		||||
 | 
			
		||||
	/* start telnet after reading config for vty_get_bind_addr() */
 | 
			
		||||
	rc = telnet_init_dynif(tall_bsc_ctx, NULL,
 | 
			
		||||
			       vty_get_bind_addr(), OSMO_VTY_PORT_MGW);
 | 
			
		||||
			       vty_get_bind_addr(), OSMO_VTY_PORT_BSC_MGCP);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
@@ -295,27 +288,52 @@ int main(int argc, char **argv)
 | 
			
		||||
	cfg->reset_cb = mgcp_rsip_cb;
 | 
			
		||||
 | 
			
		||||
        /* we need to bind a socket */
 | 
			
		||||
	flags = OSMO_SOCK_F_BIND;
 | 
			
		||||
	if (cfg->call_agent_addr)
 | 
			
		||||
		flags |= OSMO_SOCK_F_CONNECT;
 | 
			
		||||
        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;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	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) {
 | 
			
		||||
		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.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);
 | 
			
		||||
		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");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* initialisation */
 | 
			
		||||
	srand(time(NULL));
 | 
			
		||||
 
 | 
			
		||||
@@ -268,9 +268,7 @@ 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"
 | 
			
		||||
 | 
			
		||||
 #define DLCX_RET_OSMUX DLCX_RET                 \
 | 
			
		||||
		 "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" \
 | 
			
		||||
		 "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"	\
 | 
			
		||||
@@ -1214,9 +1212,8 @@ const struct log_info log_info = {
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
 | 
			
		||||
	void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
 | 
			
		||||
	osmo_init_logging2(ctx, &log_info);
 | 
			
		||||
	msgb_talloc_ctx_init(NULL, 0);
 | 
			
		||||
	osmo_init_logging(&log_info);
 | 
			
		||||
 | 
			
		||||
	test_strline();
 | 
			
		||||
	test_values();
 | 
			
		||||
@@ -1234,9 +1231,6 @@ int main(int argc, char **argv)
 | 
			
		||||
	test_no_name();
 | 
			
		||||
	test_osmux_cid();
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
 | 
			
		||||
	OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
 | 
			
		||||
	talloc_free(msgb_ctx);
 | 
			
		||||
	printf("Done\n");
 | 
			
		||||
	return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -588,8 +588,7 @@ const struct log_info log_info = {
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	void *ctx = talloc_named_const(NULL, 0, "mgcp_transcoding_test");
 | 
			
		||||
	osmo_init_logging2(ctx, &log_info);
 | 
			
		||||
	osmo_init_logging(&log_info);
 | 
			
		||||
 | 
			
		||||
	printf("=== Transcoding Good Cases ===\n");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,6 @@ AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	-ggdb3 \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
@@ -31,10 +29,9 @@ mgcp_test_SOURCES = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
mgcp_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBRARY_DL) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	-lm  \
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -11,588 +11,87 @@ line: 'mixed (4 lines)'
 | 
			
		||||
line: ''
 | 
			
		||||
line: ''
 | 
			
		||||
line: ''
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing AUEP1
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing AUEP2
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX1
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX2
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0
 | 
			
		||||
 | 
			
		||||
---------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 1@mgw MGCP 1.0
 | 
			
		||||
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.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetetization period: 20-20
 | 
			
		||||
Connection mode: 1: RECV
 | 
			
		||||
Testing MDCX3
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983215 1@mgw MGCP 1.0
 | 
			
		||||
I: %s
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetization period not set
 | 
			
		||||
Connection mode: 1: RECV
 | 
			
		||||
Testing MDCX4
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983216 1@mgw MGCP 1.0
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20, a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
o=- %s 23 IN IP4 0.0.0.0
 | 
			
		||||
c=IN IP4 0.0.0.0
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetetization period: 20-20
 | 
			
		||||
Connection mode: 3: SEND RECV
 | 
			
		||||
Testing MDCX4_PT1
 | 
			
		||||
Testing MDCX4_PT1
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983217 1@mgw MGCP 1.0
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20-40, a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
o=- %s 23 IN IP4 0.0.0.0
 | 
			
		||||
c=IN IP4 0.0.0.0
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetetization period: 20-40
 | 
			
		||||
Connection mode: 3: SEND RECV
 | 
			
		||||
Testing MDCX4_PT2
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX4_PT2
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983218 1@mgw MGCP 1.0
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20-20, a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
o=- %s 23 IN IP4 0.0.0.0
 | 
			
		||||
c=IN IP4 0.0.0.0
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetetization period: 20-20
 | 
			
		||||
Connection mode: 3: SEND RECV
 | 
			
		||||
Testing MDCX4_PT3
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX4_PT3
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983219 1@mgw MGCP 1.0
 | 
			
		||||
M: sendrecv
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
o=- %s 23 IN IP4 0.0.0.0
 | 
			
		||||
c=IN IP4 0.0.0.0
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetization period not set
 | 
			
		||||
Connection mode: 3: SEND RECV
 | 
			
		||||
Testing MDCX4_SO
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX4_SO
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983220 1@mgw MGCP 1.0
 | 
			
		||||
M: sendonly
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20, a:AMR, nt:IN
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
o=- %s 23 IN IP4 0.0.0.0
 | 
			
		||||
c=IN IP4 0.0.0.0
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 4441 RTP/AVP 99
 | 
			
		||||
a=rtpmap:99 AMR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetetization period: 20-20
 | 
			
		||||
Connection mode: 2: SEND
 | 
			
		||||
Testing MDCX4_RO
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX4_RO
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983221 1@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
I: %s
 | 
			
		||||
L: p:20, a:AMR, nt:IN
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetetization period: 20-20
 | 
			
		||||
Connection mode: 1: RECV
 | 
			
		||||
Testing DLCX
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing DLCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
DLCX 7 1@mgw MGCP 1.0
 | 
			
		||||
I: %s
 | 
			
		||||
C: 2
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
Testing CRCX_ZYN
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX_ZYN
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
Detected packet duration: 20
 | 
			
		||||
Requested packetization period not set
 | 
			
		||||
Connection mode: 1: RECV
 | 
			
		||||
Testing EMPTY
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
Testing SHORT1
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing EMPTY
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing SHORT1
 | 
			
		||||
Testing SHORT2
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing SHORT3
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 1
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing SHORT4
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 1 1@mgw
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing RQNT1
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 1 1@mgw MGCP
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing RQNT1
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
Testing RQNT2
 | 
			
		||||
X: B244F267488
 | 
			
		||||
S: D/9
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing RQNT2
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
Testing DLCX
 | 
			
		||||
X: ADD4F26746F
 | 
			
		||||
R: D/[0-9#*](N), G/ft, fxr/t38
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing DLCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
Testing CRCX
 | 
			
		||||
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 1@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 97
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetetization period: 20-20
 | 
			
		||||
Connection mode: 1: RECV
 | 
			
		||||
Testing MDCX3
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
Detected packet duration: 40
 | 
			
		||||
Requested packetization period not set
 | 
			
		||||
Connection mode: 1: RECV
 | 
			
		||||
Testing DLCX
 | 
			
		||||
I: %s
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing DLCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
Testing CRCX
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
Re-transmitting CRCX
 | 
			
		||||
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.
 | 
			
		||||
Re-transmitting CRCX
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
Testing RQNT1
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Re-transmitting RQNT1
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
RQNT 186908780 1@mgw MGCP 1.0
 | 
			
		||||
X: B244F267488
 | 
			
		||||
S: D/9
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
Re-transmitting RQNT1
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
Testing RQNT2
 | 
			
		||||
RQNT 186908780 1@mgw MGCP 1.0
 | 
			
		||||
X: B244F267488
 | 
			
		||||
S: D/9
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Re-transmitting RQNT2
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
RQNT 186908781 1@mgw MGCP 1.0
 | 
			
		||||
X: ADD4F26746F
 | 
			
		||||
R: D/[0-9#*](N), G/ft, fxr/t38
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
Re-transmitting RQNT2
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
Testing MDCX3
 | 
			
		||||
RQNT 186908781 1@mgw MGCP 1.0
 | 
			
		||||
X: ADD4F26746F
 | 
			
		||||
R: D/[0-9#*](N), G/ft, fxr/t38
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
 | 
			
		||||
Re-transmitting MDCX3
 | 
			
		||||
Testing MDCX3
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983215 1@mgw MGCP 1.0
 | 
			
		||||
I: %s
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
Re-transmitting MDCX3
 | 
			
		||||
Testing DLCX
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 18983215 1@mgw MGCP 1.0
 | 
			
		||||
I: %s
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Re-transmitting 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.
 | 
			
		||||
Testing packet loss calculation.
 | 
			
		||||
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.
 | 
			
		||||
Testing packet loss calculation.
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
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<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
RQNT 186908780 1@mgw MGCP 1.0
 | 
			
		||||
Testing stat parsing
 | 
			
		||||
S: D/9
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
DLCX 7 1@mgw MGCP 1.0
 | 
			
		||||
Parsing result: 0
 | 
			
		||||
C: 2
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
Testing stat parsing
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
Parsing result: 0
 | 
			
		||||
Testing packet error detection, patch SSRC.
 | 
			
		||||
Output SSRC changed to 11223344
 | 
			
		||||
@@ -967,170 +466,6 @@ In TS: 160320, dTS: 160, Seq: 1002
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
Testing multiple payload types
 | 
			
		||||
Stats: Jitter = 21, Transit = -32888
 | 
			
		||||
In TS: 160000, dTS: 0, Seq: 1000
 | 
			
		||||
Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
In TS: 160160, dTS: 160, Seq: 1001
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
In TS: 160320, dTS: 160, Seq: 1002
 | 
			
		||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
 | 
			
		||||
Stats: Jitter = 0, Transit = -144000
 | 
			
		||||
Testing multiple payload types
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
X
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 18 97
 | 
			
		||||
a=rtpmap:18 G729/8000
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 2@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
X
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 18 97 101
 | 
			
		||||
a=rtpmap:18 G729/8000
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=rtpmap:101 FOO/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 3@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
X
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP
 | 
			
		||||
a=rtpmap:18 G729/8000
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=rtpmap:101 FOO/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 4@mgw MGCP 1.0
 | 
			
		||||
M: recvonly
 | 
			
		||||
C: 2
 | 
			
		||||
X
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 18
 | 
			
		||||
a=rtpmap:18 G729/8000
 | 
			
		||||
a=rtpmap:97 GSM-EFR/8000
 | 
			
		||||
a=rtpmap:101 FOO/8000
 | 
			
		||||
a=ptime:40
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 259260421 5@mgw MGCP 1.0
 | 
			
		||||
C: 1355c6041e
 | 
			
		||||
L: p:20, a:GSM, nt:IN
 | 
			
		||||
M: recvonly
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
o=- 1439038275 1439038275 IN IP4 192.168.181.247
 | 
			
		||||
s=-
 | 
			
		||||
c=IN IP4 192.168.181.247
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101
 | 
			
		||||
a=rtpmap:0 PCMU/8000
 | 
			
		||||
a=rtpmap:8 PCMA/8000
 | 
			
		||||
a=rtpmap:3 gsm/8000
 | 
			
		||||
a=rtpmap:18 G729/8000
 | 
			
		||||
a=fmtp:18 annexb=no
 | 
			
		||||
a=rtpmap:4 G723/8000
 | 
			
		||||
a=rtpmap:96 iLBC/8000
 | 
			
		||||
a=fmtp:96 mode=20
 | 
			
		||||
a=rtpmap:97 iLBC/8000
 | 
			
		||||
a=fmtp:97 mode=30
 | 
			
		||||
a=rtpmap:101 telephone-event/8000
 | 
			
		||||
a=fmtp:101 0-15
 | 
			
		||||
a=recvonly
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 23 5@mgw MGCP 1.0
 | 
			
		||||
C: 1355c6041e
 | 
			
		||||
I: %s
 | 
			
		||||
 | 
			
		||||
c=IN IP4 8.8.8.8
 | 
			
		||||
m=audio 16434 RTP/AVP 3
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 259260421 5@mgw MGCP 1.0
 | 
			
		||||
C: 1355c6041e
 | 
			
		||||
L: p:20, a:GSM, nt:IN
 | 
			
		||||
M: recvonly
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
o=- 1439038275 1439038275 IN IP4 192.168.181.247
 | 
			
		||||
s=-
 | 
			
		||||
c=IN IP4 192.168.181.247
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101
 | 
			
		||||
a=rtpmap:0 PCMU/8000
 | 
			
		||||
a=rtpmap:8 PCMA/8000
 | 
			
		||||
a=rtpmap:3 gsm/8000
 | 
			
		||||
a=rtpmap:18 G729/8000
 | 
			
		||||
Testing no sequence flow on initial packet
 | 
			
		||||
Testing no rtpmap name
 | 
			
		||||
a=rtpmap:96 iLBC/8000
 | 
			
		||||
a=fmtp:96 mode=20
 | 
			
		||||
a=rtpmap:97 iLBC/8000
 | 
			
		||||
a=fmtp:97 mode=30
 | 
			
		||||
a=rtpmap:101 telephone-event/8000
 | 
			
		||||
a=fmtp:101 0-15
 | 
			
		||||
a=recvonly
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
Testing no sequence flow on initial packet
 | 
			
		||||
Testing no rtpmap name
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 1@mgw MGCP 1.0
 | 
			
		||||
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
 | 
			
		||||
Done
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,3 @@ mgcp_client_test_LDADD = \
 | 
			
		||||
	$(LIBRARY_DL) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
update_exp:
 | 
			
		||||
	$(builddir)/mgcp_client_test >$(srcdir)/mgcp_client_test.ok 2>$(srcdir)/mgcp_client_test.err
 | 
			
		||||
 
 | 
			
		||||
@@ -18,15 +18,12 @@
 | 
			
		||||
 * 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>
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
void *ctx;
 | 
			
		||||
 | 
			
		||||
@@ -49,14 +46,14 @@ static struct msgb *mgcp_from_str(const char *head, const char *params)
 | 
			
		||||
	l = strlen(head);
 | 
			
		||||
	msg->l2h = msgb_put(msg, l);
 | 
			
		||||
	data = (char*)msgb_l2(msg);
 | 
			
		||||
	osmo_strlcpy(data, head, l);
 | 
			
		||||
	strncpy(data, head, l);
 | 
			
		||||
 | 
			
		||||
	data = (char*)msgb_put(msg, 1);
 | 
			
		||||
	*data = '\n';
 | 
			
		||||
 | 
			
		||||
	l = strlen(params);
 | 
			
		||||
	data = (char*)msgb_put(msg, l);
 | 
			
		||||
	osmo_strlcpy(data, params, l);
 | 
			
		||||
	strncpy(data, params, l);
 | 
			
		||||
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
@@ -69,14 +66,14 @@ static struct msgb *from_str(const char *str)
 | 
			
		||||
	char *data;
 | 
			
		||||
	msg->l2h = msgb_put(msg, l);
 | 
			
		||||
	data = (char*)msgb_l2(msg);
 | 
			
		||||
	osmo_strlcpy(data, str, l);
 | 
			
		||||
	strncpy(data, str, l);
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct mgcp_client_conf conf;
 | 
			
		||||
struct mgcp_client *mgcp = NULL;
 | 
			
		||||
 | 
			
		||||
static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
 | 
			
		||||
static void reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
 | 
			
		||||
		     int conn_id, const char *params)
 | 
			
		||||
{
 | 
			
		||||
	static char compose[4096 - 128];
 | 
			
		||||
@@ -90,31 +87,26 @@ static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
 | 
			
		||||
 | 
			
		||||
	printf("composed response:\n-----\n%s\n-----\n",
 | 
			
		||||
	       compose);
 | 
			
		||||
	return mgcp_client_rx(mgcp, from_str(compose));
 | 
			
		||||
	mgcp_client_rx(mgcp, from_str(compose));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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");
 | 
			
		||||
	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);		
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	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
 | 
			
		||||
	      );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
 | 
			
		||||
@@ -154,9 +146,8 @@ 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 110 96\r\n"
 | 
			
		||||
		"a=rtpmap:110 AMR/8000\r\n"
 | 
			
		||||
		"a=rtpmap:96 GSM-EFR/8000\r\n"
 | 
			
		||||
		"m=audio 16002 RTP/AVP 98\r\n"
 | 
			
		||||
		"a=rtpmap:98 AMR/8000\r\n"
 | 
			
		||||
		"a=ptime:20\r\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -171,16 +162,8 @@ void test_mgcp_msg(void)
 | 
			
		||||
		.endpoint = "23@mgw",
 | 
			
		||||
		.audio_port = 1234,
 | 
			
		||||
		.call_id = 47,
 | 
			
		||||
		.conn_id = "11",
 | 
			
		||||
		.conn_mode = MGCP_CONN_RECV_SEND,
 | 
			
		||||
		.ptime = 20,
 | 
			
		||||
		.codecs[0] = CODEC_GSM_8000_1,
 | 
			
		||||
		.codecs[1] = CODEC_AMR_8000_1,
 | 
			
		||||
		.codecs[2] = CODEC_GSMEFR_8000_1,
 | 
			
		||||
		.codecs_len = 1,
 | 
			
		||||
		.ptmap[0].codec = CODEC_GSMEFR_8000_1,
 | 
			
		||||
		.ptmap[0].pt = 96,
 | 
			
		||||
		.ptmap_len = 1
 | 
			
		||||
		.conn_id = 11,
 | 
			
		||||
		.conn_mode = MGCP_CONN_RECV_SEND
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (mgcp)
 | 
			
		||||
@@ -197,26 +180,6 @@ 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 =
 | 
			
		||||
@@ -226,28 +189,6 @@ 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 =
 | 
			
		||||
@@ -284,250 +225,6 @@ void test_mgcp_msg(void)
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_mgcp_client_cancel()
 | 
			
		||||
{
 | 
			
		||||
	mgcp_trans_id_t trans_id;
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	struct mgcp_msg mgcp_msg = {
 | 
			
		||||
		.verb = MGCP_VERB_CRCX,
 | 
			
		||||
		.audio_ip = "192.168.100.23",
 | 
			
		||||
		.endpoint = "23@mgw",
 | 
			
		||||
		.audio_port = 1234,
 | 
			
		||||
		.call_id = 47,
 | 
			
		||||
		.conn_id = "11",
 | 
			
		||||
		.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__);
 | 
			
		||||
	fprintf(stderr, "\n%s():\n", __func__);
 | 
			
		||||
 | 
			
		||||
	if (mgcp)
 | 
			
		||||
		talloc_free(mgcp);
 | 
			
		||||
	mgcp = mgcp_client_init(ctx, &conf);
 | 
			
		||||
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	trans_id = mgcp_msg_trans_id(msg);
 | 
			
		||||
	fprintf(stderr, "- composed msg with trans_id=%u\n", trans_id);
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "- not in queue yet, cannot cancel yet\n");
 | 
			
		||||
	OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == -ENOENT);
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "- enqueue\n");
 | 
			
		||||
	dummy_mgcp_send(msg);
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "- cancel succeeds\n");
 | 
			
		||||
	OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == 0);
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "- late response gets discarded\n");
 | 
			
		||||
	OSMO_ASSERT(reply_to(trans_id, 200, "OK", 1, "v=0\r\n") == -ENOENT);
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "- canceling again does nothing\n");
 | 
			
		||||
	OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == -ENOENT);
 | 
			
		||||
 | 
			
		||||
	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[] = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -541,22 +238,16 @@ int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	ctx = talloc_named_const(NULL, 1, "mgcp_client_test");
 | 
			
		||||
	msgb_talloc_ctx_init(ctx, 0);
 | 
			
		||||
	osmo_init_logging2(ctx, &log_info);
 | 
			
		||||
	osmo_init_logging(&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);
 | 
			
		||||
	log_set_print_category(osmo_stderr_target, 1);
 | 
			
		||||
 | 
			
		||||
	log_set_category_filter(osmo_stderr_target, DLMGCP, 1, LOGL_DEBUG);
 | 
			
		||||
 | 
			
		||||
	mgcp_client_conf_init(&conf);
 | 
			
		||||
 | 
			
		||||
	test_crcx();
 | 
			
		||||
	test_mgcp_msg();
 | 
			
		||||
	test_mgcp_client_cancel();
 | 
			
		||||
	test_sdp_section_start();
 | 
			
		||||
	test_map_codec_to_pt_and_map_pt_to_codec();
 | 
			
		||||
	test_map_pt_to_codec();
 | 
			
		||||
 | 
			
		||||
	printf("Done\n");
 | 
			
		||||
	fprintf(stderr, "Done\n");
 | 
			
		||||
 
 | 
			
		||||
@@ -1,69 +1,2 @@
 | 
			
		||||
DLMGCP message buffer to small, can not generate MGCP message
 | 
			
		||||
 | 
			
		||||
test_mgcp_client_cancel():
 | 
			
		||||
- composed msg with trans_id=1
 | 
			
		||||
- not in queue yet, cannot cancel yet
 | 
			
		||||
DLMGCP Cannot cancel, no such transaction: 1
 | 
			
		||||
- enqueue
 | 
			
		||||
- cancel succeeds
 | 
			
		||||
DLMGCP Canceled transaction 1
 | 
			
		||||
- late response gets discarded
 | 
			
		||||
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,9 +18,8 @@ o=- 1 23 IN IP4 10.9.1.120
 | 
			
		||||
s=-
 | 
			
		||||
c=IN IP4 10.9.1.120
 | 
			
		||||
t=0 0
 | 
			
		||||
m=audio 16002 RTP/AVP 110 96
 | 
			
		||||
a=rtpmap:110 AMR/8000
 | 
			
		||||
a=rtpmap:96 GSM-EFR/8000
 | 
			
		||||
m=audio 16002 RTP/AVP 98
 | 
			
		||||
a=rtpmap:98 AMR/8000
 | 
			
		||||
a=ptime:20
 | 
			
		||||
 | 
			
		||||
-----
 | 
			
		||||
@@ -30,161 +29,34 @@ 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: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 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
 | 
			
		||||
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 7 23@mgw MGCP 1.0
 | 
			
		||||
C: 2f
 | 
			
		||||
I: 11
 | 
			
		||||
 | 
			
		||||
Generated AUEP message:
 | 
			
		||||
AUEP 8 23@mgw MGCP 1.0
 | 
			
		||||
 | 
			
		||||
Generated RSIP message:
 | 
			
		||||
RSIP 9 23@mgw MGCP 1.0
 | 
			
		||||
 | 
			
		||||
Overfolow test:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
test_mgcp_client_cancel():
 | 
			
		||||
composed:
 | 
			
		||||
-----
 | 
			
		||||
CRCX 1 23@mgw MGCP 1.0
 | 
			
		||||
C: 2f
 | 
			
		||||
I: 11
 | 
			
		||||
L: p:20, a:AMR, nt:IN
 | 
			
		||||
M: sendrecv
 | 
			
		||||
 | 
			
		||||
-----
 | 
			
		||||
composed response:
 | 
			
		||||
-----
 | 
			
		||||
200 1 OK
 | 
			
		||||
I: 1
 | 
			
		||||
Generated MDCX message:
 | 
			
		||||
MDCX 2 23@mgw MGCP 1.0
 | 
			
		||||
C: 2f
 | 
			
		||||
I: 11
 | 
			
		||||
M: sendrecv
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 192.168.100.23
 | 
			
		||||
m=audio 1234 RTP/AVP 255
 | 
			
		||||
 | 
			
		||||
-----
 | 
			
		||||
Generated DLCX message:
 | 
			
		||||
DLCX 3 23@mgw MGCP 1.0
 | 
			
		||||
C: 2f
 | 
			
		||||
I: 11
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [0]:
 | 
			
		||||
Generated AUEP message:
 | 
			
		||||
AUEP 4 23@mgw MGCP 1.0
 | 
			
		||||
 | 
			
		||||
test_sdp_section_start() test [1]:
 | 
			
		||||
Generated RSIP message:
 | 
			
		||||
RSIP 5 23@mgw MGCP 1.0
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
Overfolow test:
 | 
			
		||||
 | 
			
		||||
Done
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user