mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-04 05:53:26 +00:00 
			
		
		
		
	Compare commits
	
		
			100 Commits
		
	
	
		
			1.11.2
			...
			laforge/os
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					ba59fcf912 | ||
| 
						 | 
					0188aad11c | ||
| 
						 | 
					1ce53ac6a9 | ||
| 
						 | 
					179525e07b | ||
| 
						 | 
					f12ad38d46 | ||
| 
						 | 
					dd25ba52bd | ||
| 
						 | 
					3a971ba0d1 | ||
| 
						 | 
					c4b90354ca | ||
| 
						 | 
					28fd236044 | ||
| 
						 | 
					17b5701f19 | ||
| 
						 | 
					9bea6eb78c | ||
| 
						 | 
					fd57bd5f6f | ||
| 
						 | 
					d0dbda4106 | ||
| 
						 | 
					35a738214c | ||
| 
						 | 
					afbe872045 | ||
| 
						 | 
					909be19285 | ||
| 
						 | 
					8ee08c6b78 | ||
| 
						 | 
					c053e07336 | ||
| 
						 | 
					2b30cbdfa8 | ||
| 
						 | 
					c9df010416 | ||
| 
						 | 
					3a3e1b57c6 | ||
| 
						 | 
					186d4f1a0b | ||
| 
						 | 
					cc2f7937ed | ||
| 
						 | 
					960c031a67 | ||
| 
						 | 
					4993452561 | ||
| 
						 | 
					43eed63b09 | ||
| 
						 | 
					7a6d9c2f72 | ||
| 
						 | 
					f3715dc0d3 | ||
| 
						 | 
					a68a863b9d | ||
| 
						 | 
					18717f5247 | ||
| 
						 | 
					677b5396cb | ||
| 
						 | 
					5d0e07112f | ||
| 
						 | 
					a5acaa68db | ||
| 
						 | 
					7787a3aeae | ||
| 
						 | 
					dd1ddf74fc | ||
| 
						 | 
					5e101c9fed | ||
| 
						 | 
					f6db465082 | ||
| 
						 | 
					a7e6fbc220 | ||
| 
						 | 
					cd132126ef | ||
| 
						 | 
					249d21eb1e | ||
| 
						 | 
					0127a0608d | ||
| 
						 | 
					8b663e4d7c | ||
| 
						 | 
					5e5c7bc176 | ||
| 
						 | 
					c695e9f972 | ||
| 
						 | 
					2bdad960ff | ||
| 
						 | 
					58d5b97831 | ||
| 
						 | 
					6225b5cecb | ||
| 
						 | 
					c415ed5113 | ||
| 
						 | 
					730a1f28d2 | ||
| 
						 | 
					af67178581 | ||
| 
						 | 
					dc7dfd0bbd | ||
| 
						 | 
					c364f4ab5a | ||
| 
						 | 
					e9989b9e76 | ||
| 
						 | 
					9423817311 | ||
| 
						 | 
					292ba1bdb1 | ||
| 
						 | 
					10d9f8dcf4 | ||
| 
						 | 
					4c0658955e | ||
| 
						 | 
					e9164c295d | ||
| 
						 | 
					563386e8bb | ||
| 
						 | 
					af0f58fac7 | ||
| 
						 | 
					2e411f33a0 | ||
| 
						 | 
					8e8d59ff0e | ||
| 
						 | 
					14da3a33cb | ||
| 
						 | 
					ea2035326d | ||
| 
						 | 
					2bfa708527 | ||
| 
						 | 
					72f911513e | ||
| 
						 | 
					3d0676a791 | ||
| 
						 | 
					4c4d227624 | ||
| 
						 | 
					0e8310fa6c | ||
| 
						 | 
					e3624562dc | ||
| 
						 | 
					d5057ec7b8 | ||
| 
						 | 
					72761b312b | ||
| 
						 | 
					a312523be4 | ||
| 
						 | 
					45fe47cea7 | ||
| 
						 | 
					ce356eec1f | ||
| 
						 | 
					cca1d180f5 | ||
| 
						 | 
					833ab4b083 | ||
| 
						 | 
					d358ca14a9 | ||
| 
						 | 
					3699ef06a4 | ||
| 
						 | 
					9dd80ca1f8 | ||
| 
						 | 
					621e8666ff | ||
| 
						 | 
					ec967d74f7 | ||
| 
						 | 
					3ccf02a300 | ||
| 
						 | 
					b6795ddfd2 | ||
| 
						 | 
					747fbce4ce | ||
| 
						 | 
					e1da8c9d2c | ||
| 
						 | 
					e78221d273 | ||
| 
						 | 
					6c303664f5 | ||
| 
						 | 
					544016d8e6 | ||
| 
						 | 
					263a1a3e8f | ||
| 
						 | 
					30bdf94102 | ||
| 
						 | 
					0dbaaadc06 | ||
| 
						 | 
					671c37b72b | ||
| 
						 | 
					2a9ba66922 | ||
| 
						 | 
					f8abd0ec0c | ||
| 
						 | 
					1a7048d69a | ||
| 
						 | 
					c70c08cd49 | ||
| 
						 | 
					a8f13286ff | ||
| 
						 | 
					b9e7569217 | ||
| 
						 | 
					057decfe3a | 
@@ -24,6 +24,7 @@ pkgconfig_DATA = \
 | 
			
		||||
BUILT_SOURCES = $(top_srcdir)/.version
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	     .version \
 | 
			
		||||
	     README.md \
 | 
			
		||||
	     contrib/osmo-mgw.spec.in \
 | 
			
		||||
	     debian \
 | 
			
		||||
	     git-version-gen \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								README
									
									
									
									
									
								
							@@ -1,24 +0,0 @@
 | 
			
		||||
About OsmoMGW
 | 
			
		||||
=============
 | 
			
		||||
 | 
			
		||||
OsmoMGW originated from the OpenBSC project, which started as a minimalistic
 | 
			
		||||
all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached
 | 
			
		||||
maturity and diversity (including M3UA SIGTRAN and 3G support in the form of
 | 
			
		||||
IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one
 | 
			
		||||
approach to fully independent separate programs as in typical GSM networks.
 | 
			
		||||
 | 
			
		||||
OsmoMGW was one of the parts split off from the old openbsc.git. It originated
 | 
			
		||||
as a solution to merely navigate RTP streams through a NAT, but has since
 | 
			
		||||
matured to a Media Gateway implementation that is capable of streaming RTP for
 | 
			
		||||
2G (AoIP) and 3G (IuCS) GSM networks as well as (still not implemented at time
 | 
			
		||||
of writing) transcoding between TRAU, various RTP payloads and IuUP.
 | 
			
		||||
 | 
			
		||||
The OsmoMGW program exposes an MGCP interface towards clients like OsmoMSC and
 | 
			
		||||
OsmoBSC, and receives and sends RTP streams as configured via MGCP.
 | 
			
		||||
 | 
			
		||||
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC (found
 | 
			
		||||
in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
 | 
			
		||||
 | 
			
		||||
Find OsmoMGW issue tracker and wiki online at
 | 
			
		||||
https://osmocom.org/projects/osmo-mgw
 | 
			
		||||
https://osmocom.org/projects/osmo-mgw/wiki
 | 
			
		||||
							
								
								
									
										102
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
osmo-mgw - Osmocom MGW (Media GateWay) Implementation
 | 
			
		||||
=====================================================
 | 
			
		||||
 | 
			
		||||
This repository contains a C-language implementation of an MGW (Media
 | 
			
		||||
GateWay) for use [not only] within the 2G (GSM) and/or 3G (UMTS)
 | 
			
		||||
Cellular Network built using Osmocom CNI (Cellular Network
 | 
			
		||||
Infrastructure) software.
 | 
			
		||||
 | 
			
		||||
The OsmoMGW program provides an MGCP interface towards an MGCP call agent
 | 
			
		||||
(client) like OsmoMSC and OsmoBSC, and receives and sends RTP streams as
 | 
			
		||||
configured via the MGCP control plane.
 | 
			
		||||
 | 
			
		||||
This Media Gateway implementation is capable of
 | 
			
		||||
 | 
			
		||||
* streaming RTP for 2G (3GPP AoIP and Abis-over-IP)
 | 
			
		||||
* streaming RTP for 3G (IuCS including the IuFP protocol)
 | 
			
		||||
* TDM (E1/T1) based Abis interface with TRAU frames on 16k sub-slots
 | 
			
		||||
* basic support for LCLS (Local Call, Local Switch) related features
 | 
			
		||||
* various built-in translation capabilities
 | 
			
		||||
  * between Abis TRAU frames and RTP formats
 | 
			
		||||
  * between 2G AMR/RTP and 3G AMR/IuFP/RTP
 | 
			
		||||
  * between bandwidth-efficient and octet-aligned AMR
 | 
			
		||||
  * between different standards for encapsulating GSM HR codec frames in RTP
 | 
			
		||||
 | 
			
		||||
osmo-mgw is typically co-located with
 | 
			
		||||
 | 
			
		||||
 * osmo-bsc (GSM BSC)
 | 
			
		||||
 * osmo-msc (GSM/UMTS MSC)
 | 
			
		||||
 * osmo-hnbgw (UMTS HNBGW); osmo-mgw implements RTP relay between Iuh
 | 
			
		||||
   and IuCS interfaces
 | 
			
		||||
 | 
			
		||||
The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC
 | 
			
		||||
(found in osmo-msc.git) to instruct OsmoMGW via its MGCP service.
 | 
			
		||||
 | 
			
		||||
Homepage
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
You can find the OsmoMGW issue tracker and wiki online at
 | 
			
		||||
<https://osmocom.org/projects/osmo-mgw> and <https://osmocom.org/projects/osmo-mgw/wiki>.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GIT Repository
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
You can clone from the official osmo-mgw.git repository using
 | 
			
		||||
 | 
			
		||||
        git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
 | 
			
		||||
 | 
			
		||||
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Documentation
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
User Manuals and VTY reference manuals are [optionally] built in PDF form
 | 
			
		||||
as part of the build process.
 | 
			
		||||
 | 
			
		||||
Pre-rendered PDF version of the current "master" can be found at
 | 
			
		||||
[User Manual](https://ftp.osmocom.org/docs/latest/osmomgw-usermanual.pdf)
 | 
			
		||||
as well as the [VTY Reference Manual](https://ftp.osmocom.org/docs/latest/osmomgw-vty-reference.pdf)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Mailing List
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
Discussions related to osmo-mgw are happening on the
 | 
			
		||||
openbsc@lists.osmocom.org mailing list, please see
 | 
			
		||||
<https://lists.osmocom.org/mailman/listinfo/openbsc> for subscription
 | 
			
		||||
options and the list archive.
 | 
			
		||||
 | 
			
		||||
Please observe the [Osmocom Mailing List
 | 
			
		||||
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
 | 
			
		||||
when posting.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Contributing
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
Our coding standards are described at
 | 
			
		||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
 | 
			
		||||
 | 
			
		||||
We use a gerrit based patch submission/review process for managing
 | 
			
		||||
contributions.  Please see
 | 
			
		||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> for
 | 
			
		||||
more details
 | 
			
		||||
 | 
			
		||||
The current patch queue for osmo-mgw can be seen at
 | 
			
		||||
<https://gerrit.osmocom.org/#/q/project:osmo-mgw+status:open>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
History
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
OsmoMGW originated from the OpenBSC project, which started as a minimalistic
 | 
			
		||||
all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached
 | 
			
		||||
maturity and diversity (including M3UA SIGTRAN and 3G support in the form of
 | 
			
		||||
IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one
 | 
			
		||||
approach to fully independent separate programs as in typical GSM networks.
 | 
			
		||||
 | 
			
		||||
OsmoMGW was one of the parts split off from the old openbsc.git. It originated
 | 
			
		||||
as a solution to merely navigate RTP streams through a NAT, but has since
 | 
			
		||||
matured.
 | 
			
		||||
							
								
								
									
										17
									
								
								TODO-RELEASE
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								TODO-RELEASE
									
									
									
									
									
								
							@@ -24,3 +24,20 @@
 | 
			
		||||
# If any interfaces have been removed or changed since the last public release, a=0.
 | 
			
		||||
#
 | 
			
		||||
#library		what		description / commit summary line
 | 
			
		||||
libosmo-mgcp-client	remove public API	These public API items have not been called by any of our osmo-programs
 | 
			
		||||
						for many years. Any sane caller of libosmo-mgcp-client should use the
 | 
			
		||||
						higher level osmo_mgcpc_* API instead. Move these to a private header:
 | 
			
		||||
						struct mgcp_response_head
 | 
			
		||||
						struct mgcp_response
 | 
			
		||||
						struct mgcp_msg
 | 
			
		||||
						mgcp_response_parse_params()
 | 
			
		||||
						mgcp_client_tx()
 | 
			
		||||
						mgcp_client_cancel()
 | 
			
		||||
						mgcp_msg_gen()
 | 
			
		||||
						mgcp_msg_trans_id()
 | 
			
		||||
libosmo-mgcp-client	deprecate public API	New code should no longer use codecs[], instead use ptmap[].codec. There
 | 
			
		||||
						is backwards compat code that moves codecs[] entries, if any, over to
 | 
			
		||||
						ptmap[], so callers may migrate at own leisure.
 | 
			
		||||
osmo-mgw		remove cfg		Remove VTY config item 'sdp audio fmtp-extra' (see OS#6313)
 | 
			
		||||
libosmocore		bump_dep; workaround	Bump libosmocore version dependency after I68328adb952ca8833ba047cb3b49ccc6f8a1f1b5
 | 
			
		||||
						has been merged to libosmocore.git; then remove my_msgb_copy_c wrapper function.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								configure.ac
									
									
									
									
									
								
							@@ -44,15 +44,16 @@ AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
 | 
			
		||||
AC_SUBST(LIBRARY_DLSYM)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.8.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.8.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.8.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.8.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.3.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.4.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.4.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.4.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.5.0)
 | 
			
		||||
 | 
			
		||||
CFLAGS="$CFLAGS -pthread"
 | 
			
		||||
CFLAGS="$CFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
 | 
			
		||||
CPPFLAGS="$CPPFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
 | 
			
		||||
LDFLAGS="$LDFLAGS -pthread"
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(sanitize,
 | 
			
		||||
 
 | 
			
		||||
@@ -29,30 +29,30 @@ BuildRequires:  pkgconfig >= 0.20
 | 
			
		||||
%if 0%{?suse_version}
 | 
			
		||||
BuildRequires:  systemd-rpm-macros
 | 
			
		||||
%endif
 | 
			
		||||
BuildRequires:  pkgconfig(libosmo-netif) >= 1.3.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmocore) >= 1.8.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmoctrl) >= 1.8.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmogsm) >= 1.8.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmovty) >= 1.8.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmocoding) >= 1.8.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmoabis) >= 1.4.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmotrau) >= 1.4.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmo-netif) >= 1.4.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmocore) >= 1.9.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmoctrl) >= 1.9.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmogsm) >= 1.9.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmovty) >= 1.9.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmocoding) >= 1.9.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmoabis) >= 1.5.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmotrau) >= 1.5.0
 | 
			
		||||
%{?systemd_requires}
 | 
			
		||||
 | 
			
		||||
%description
 | 
			
		||||
OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks.
 | 
			
		||||
 | 
			
		||||
%package -n libosmo-mgcp-client11
 | 
			
		||||
%package -n libosmo-mgcp-client12
 | 
			
		||||
Summary:        Osmocom's Media Gateway Control Protocol client library
 | 
			
		||||
Group:          System/Libraries
 | 
			
		||||
 | 
			
		||||
%description -n libosmo-mgcp-client11
 | 
			
		||||
%description -n libosmo-mgcp-client12
 | 
			
		||||
Osmocom's Media Gateway Control Protocol client library.
 | 
			
		||||
 | 
			
		||||
%package -n libosmo-mgcp-client-devel
 | 
			
		||||
Summary:        Development files for Osmocom's Media Gateway Control Protocol client library
 | 
			
		||||
Group:          Development/Libraries/C and C++
 | 
			
		||||
Requires:       libosmo-mgcp-client11 = %{version}
 | 
			
		||||
Requires:       libosmo-mgcp-client12 = %{version}
 | 
			
		||||
 | 
			
		||||
%description -n libosmo-mgcp-client-devel
 | 
			
		||||
Osmocom's Media Gateway Control Protocol client librarary.
 | 
			
		||||
@@ -60,16 +60,6 @@ Osmocom's Media Gateway Control Protocol client librarary.
 | 
			
		||||
This subpackage contains libraries and header files for developing
 | 
			
		||||
applications that want to make use of libosmo-mgcp-client.
 | 
			
		||||
 | 
			
		||||
%package -n libosmo-mgcp-devel
 | 
			
		||||
Summary:        Development files for Osmocom's Media Gateway server library
 | 
			
		||||
Group:          Development/Libraries/C and C++
 | 
			
		||||
 | 
			
		||||
%description -n libosmo-mgcp-devel
 | 
			
		||||
Osmocom's Media Gateway Control Protocol server library.
 | 
			
		||||
 | 
			
		||||
This subpackage contains libraries and header files for developing
 | 
			
		||||
applications that want to make use of libosmo-mgcp.
 | 
			
		||||
 | 
			
		||||
%prep
 | 
			
		||||
%setup -q
 | 
			
		||||
 | 
			
		||||
@@ -90,8 +80,8 @@ find %{buildroot} -type f -name "*.la" -delete -print
 | 
			
		||||
%check
 | 
			
		||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
 | 
			
		||||
 | 
			
		||||
%post   -n libosmo-mgcp-client11 -p /sbin/ldconfig
 | 
			
		||||
%postun -n libosmo-mgcp-client11 -p /sbin/ldconfig
 | 
			
		||||
%post   -n libosmo-mgcp-client12 -p /sbin/ldconfig
 | 
			
		||||
%postun -n libosmo-mgcp-client12 -p /sbin/ldconfig
 | 
			
		||||
 | 
			
		||||
%if 0%{?suse_version}
 | 
			
		||||
%preun
 | 
			
		||||
@@ -109,7 +99,7 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
 | 
			
		||||
 | 
			
		||||
%files
 | 
			
		||||
%license COPYING
 | 
			
		||||
%doc AUTHORS README
 | 
			
		||||
%doc AUTHORS README.md
 | 
			
		||||
%dir %{_docdir}/%{name}/examples
 | 
			
		||||
%dir %{_docdir}/%{name}/examples/osmo-mgw
 | 
			
		||||
%{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw.cfg
 | 
			
		||||
@@ -119,8 +109,8 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
 | 
			
		||||
%dir %{_sysconfdir}/osmocom
 | 
			
		||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg
 | 
			
		||||
 | 
			
		||||
%files -n libosmo-mgcp-client11
 | 
			
		||||
%{_libdir}/libosmo-mgcp-client.so.11*
 | 
			
		||||
%files -n libosmo-mgcp-client12
 | 
			
		||||
%{_libdir}/libosmo-mgcp-client.so.12*
 | 
			
		||||
 | 
			
		||||
%files -n libosmo-mgcp-client-devel
 | 
			
		||||
%{_libdir}/libosmo-mgcp-client.so
 | 
			
		||||
@@ -129,9 +119,4 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
 | 
			
		||||
%dir %{_includedir}/osmocom/mgcp_client
 | 
			
		||||
%{_includedir}/osmocom/mgcp_client/*.h
 | 
			
		||||
 | 
			
		||||
%files -n libosmo-mgcp-devel
 | 
			
		||||
%dir %{_includedir}/osmocom
 | 
			
		||||
%dir %{_includedir}/osmocom/mgcp
 | 
			
		||||
%{_includedir}/osmocom/mgcp/*.h
 | 
			
		||||
 | 
			
		||||
%changelog
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,11 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Osmocom Media Gateway (MGW)
 | 
			
		||||
After=network-online.target
 | 
			
		||||
Wants=network-online.target
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
LimitNOFILE=65536
 | 
			
		||||
StateDirectory=osmocom
 | 
			
		||||
WorkingDirectory=%S/osmocom
 | 
			
		||||
Restart=always
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										80
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										80
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,83 @@
 | 
			
		||||
osmo-mgw (1.12.1) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * mgcp-client: Fix missing include in mgcp_client_pool.h
 | 
			
		||||
  * mgcp-client: Introduce API osmo_mgcpc_ep_local_name()
 | 
			
		||||
  * mgw: Configure IuUP if codec set during MDCX
 | 
			
		||||
 | 
			
		||||
 -- Oliver Smith <osmith@sysmocom.de>  Thu, 28 Sep 2023 15:58:17 +0200
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.12.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * mgcp_sdp: add spec reference
 | 
			
		||||
  * mgcp_sdp: cosmetic: remove newline
 | 
			
		||||
  * mgcp_endp: cosmetic move mgcp_endp_release to the end
 | 
			
		||||
  * mgcp_endp: cosmetic: remove unnecessary new line
 | 
			
		||||
  * mgcp_e1: fix log output
 | 
			
		||||
  * mgcp_e1: be more frugal withe E1 line resources
 | 
			
		||||
  * mgcp_client: fix sourcecode formatting
 | 
			
		||||
  * mgcp_e1: fix typo
 | 
			
		||||
  * mgcp_e1: rewrite comment
 | 
			
		||||
  * mgcp_e1: rename e1_send to e1_send_ts_frame
 | 
			
		||||
  * mgcp_e1: cosmetic: rewrite comment
 | 
			
		||||
  * Revert "mgcp_codec: do not differentiate between oa and bwe when comparing codec"
 | 
			
		||||
  * mgcp_codec: fix oa/bwe comparison in mgcp_codec_pt_translate()
 | 
			
		||||
  * mgcp_codec: refactor payload type converstion
 | 
			
		||||
  * mgcp_codec: cosmetic: remove line break in api-doc
 | 
			
		||||
  * mgcp_network: fix apidoc
 | 
			
		||||
  * mgcp_vty: add warnings for deprecated config options
 | 
			
		||||
  * mgcp_codec: move mgcp_codec_decide down
 | 
			
		||||
  * mgcp_codec: fix codec decision
 | 
			
		||||
  * mgcp_network: do not deliver RTP packets with unpatched PT
 | 
			
		||||
  * mgcp_codec: be sensitive about IuFP when checking codecs
 | 
			
		||||
  * mgcp_client.h: also add spec ref to the other 3gpp defined payload types
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * cosmetic: Fix grammar suggesting reading _the_ user manual
 | 
			
		||||
 | 
			
		||||
  [ arehbein ]
 | 
			
		||||
  * Transition to use of 'telnet_init_default'
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * mgcp_client: mgcp_msg_gen: add more error logs
 | 
			
		||||
  * mgcp_client_pool: add mgcp_client_pool_empty()
 | 
			
		||||
  * Cosmetic: mgcp_client: fix typo
 | 
			
		||||
  * debian: set compat level to 10
 | 
			
		||||
  * systemd: depend on networking-online.target
 | 
			
		||||
  * Cosmetic: fix a typo
 | 
			
		||||
  * mgcp_client: check rc of map_str_to_codec
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * mgcp_network: Unregister osmo_fd before closing fd
 | 
			
		||||
  * mgcp-client: Call osmo_fd_unregister() before closing and changing bfd->fd
 | 
			
		||||
  * mgcp-client: Drop unused struct mgcp_client field
 | 
			
		||||
  * mgcp_client: Introduce mgcp_client_conf_alloc(), deprecate mgcp_client_conf_init()
 | 
			
		||||
  * mgcp-client: Move some static functions further above
 | 
			
		||||
  * mgw: Allow auditing speciall 'null' endpoint
 | 
			
		||||
  * mgcp-client: Add keepalive feature
 | 
			
		||||
  * mgcp_client: pool: Only pick clients with an MGCP link considered to be UP
 | 
			
		||||
  * mgcp-client: Always mark client as UP if keepalive request-interval disabled
 | 
			
		||||
  * mgcp-client: Mark client as UP when keepalive request-interval/timeout is disabled through VTY
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * */Makefile.am: libraries shall not be in AM_LDFLAGS
 | 
			
		||||
  * tests: use -no-install libtool flag to avoid ./lt-* scripts
 | 
			
		||||
  * tests: $(BUILT_SOURCES) is not defined, depend on osmo-mgw
 | 
			
		||||
  * copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * mgcp_find_section_end(): skip spaces at start of SDP
 | 
			
		||||
  * mgcp_client: simpler error handling
 | 
			
		||||
  * mgcp: fix "L: a:" header parsing: heed ";" separator
 | 
			
		||||
  * mgcp_client: tweak extract_codec_name() implementation
 | 
			
		||||
 | 
			
		||||
  [ Andreas Eversberg ]
 | 
			
		||||
  * ASCI: Add new mode for voice group/broadcast call
 | 
			
		||||
  * ASCI: Support conference briding with 1..n connections
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 12 Sep 2023 14:48:51 +0200
 | 
			
		||||
 | 
			
		||||
osmo-mgw (1.11.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							@@ -1 +1 @@
 | 
			
		||||
9
 | 
			
		||||
10
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -2,14 +2,14 @@ Source: osmo-mgw
 | 
			
		||||
Section: net
 | 
			
		||||
Priority: extra
 | 
			
		||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
 | 
			
		||||
Build-Depends: debhelper (>=9),
 | 
			
		||||
Build-Depends: debhelper (>= 10),
 | 
			
		||||
               dh-autoreconf,
 | 
			
		||||
               pkg-config,
 | 
			
		||||
               autotools-dev,
 | 
			
		||||
               libosmocore-dev (>= 1.8.0),
 | 
			
		||||
               libosmo-netif-dev (>= 1.3.0),
 | 
			
		||||
               libosmo-abis-dev (>= 1.4.0),
 | 
			
		||||
               osmo-gsm-manuals-dev (>= 1.4.0)
 | 
			
		||||
               libosmocore-dev (>= 1.9.0),
 | 
			
		||||
               libosmo-netif-dev (>= 1.4.0),
 | 
			
		||||
               libosmo-abis-dev (>= 1.5.0),
 | 
			
		||||
               osmo-gsm-manuals-dev (>= 1.5.0)
 | 
			
		||||
Standards-Version: 3.9.8
 | 
			
		||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
 | 
			
		||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
 | 
			
		||||
@@ -21,7 +21,7 @@ Multi-Arch: foreign
 | 
			
		||||
Depends: ${misc:Depends}, ${shlibs:Depends}
 | 
			
		||||
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
 | 
			
		||||
 | 
			
		||||
Package: libosmo-mgcp-client11
 | 
			
		||||
Package: libosmo-mgcp-client12
 | 
			
		||||
Section: libs
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
@@ -33,7 +33,7 @@ Package: libosmo-mgcp-client-dev
 | 
			
		||||
Section: libdevel
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
Depends: libosmo-mgcp-client11 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Depends: libosmo-mgcp-client12 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
 | 
			
		||||
 | 
			
		||||
Package: osmo-mgw-doc
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							@@ -6,7 +6,7 @@ Files:     *
 | 
			
		||||
Copyright: 2009-2014 On-Waves
 | 
			
		||||
           2009-2015 Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
           2013 Jacob Erlbeck <jerlbeck@sysmocom.de>
 | 
			
		||||
           2016-2017 sysmocom s.m.f.c. GmbH <info@sysmocom.de>
 | 
			
		||||
           2016-2017 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
License:   AGPL-3.0+
 | 
			
		||||
 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
 | 
			
		||||
@@ -22,7 +22,7 @@ License:   AGPL-3.0+
 | 
			
		||||
 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>
 | 
			
		||||
Copyright: 2016 by sysmocom s.f.m.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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,14 @@
 | 
			
		||||
!
 | 
			
		||||
! MGCP configuration example
 | 
			
		||||
!
 | 
			
		||||
log stderr
 | 
			
		||||
 logging color 1
 | 
			
		||||
 logging print category-hex 0
 | 
			
		||||
 logging print category 1
 | 
			
		||||
 logging timestamp 0
 | 
			
		||||
 logging print file basename last
 | 
			
		||||
 logging print level 1
 | 
			
		||||
 | 
			
		||||
e1_input
 | 
			
		||||
 e1_line 0 driver dahdi
 | 
			
		||||
 e1_line 0 port 0
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,14 @@
 | 
			
		||||
!
 | 
			
		||||
! MGCP configuration example
 | 
			
		||||
!
 | 
			
		||||
log stderr
 | 
			
		||||
 logging color 1
 | 
			
		||||
 logging print category-hex 0
 | 
			
		||||
 logging print category 1
 | 
			
		||||
 logging timestamp 0
 | 
			
		||||
 logging print file basename last
 | 
			
		||||
 logging print level 1
 | 
			
		||||
 | 
			
		||||
mgcp
 | 
			
		||||
  bind ip 127.0.0.1
 | 
			
		||||
  rtp port-range 4002 16000
 | 
			
		||||
 
 | 
			
		||||
@@ -92,3 +92,9 @@ not yet useable.
 | 
			
		||||
 | 
			
		||||
NOTE: the VTY command "show mgcp" can be used to get a list of all available
 | 
			
		||||
endpoints (including identifiers)
 | 
			
		||||
 | 
			
		||||
=== The `null` endpoint
 | 
			
		||||
 | 
			
		||||
OsmoMGW offers a special `null@<domain>` endpoint which can be audited at all times.
 | 
			
		||||
This is useful for MGCP clients who wish to submit requests to OsmoMGW
 | 
			
		||||
periodically to find out whether it is still reachable and in a working state.
 | 
			
		||||
@@ -23,3 +23,20 @@ arguments:
 | 
			
		||||
	Disable colors for logging to stderr. This has mostly been
 | 
			
		||||
	deprecated by VTY based logging configuration, see <<logging>>
 | 
			
		||||
	for more information.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== Configure limits
 | 
			
		||||
 | 
			
		||||
When servicing hundreds of media endpoints, it may be necessary to adjust the
 | 
			
		||||
operating system's limit on open file descriptors for the osmo-mgw process. A
 | 
			
		||||
typical default limit imposed by operating systems is 1024; this would be
 | 
			
		||||
exceeded by, for example, about 256 active voice calls with 4 RTP/RTPC ports
 | 
			
		||||
each, sockets for other interfaces not considered yet.
 | 
			
		||||
 | 
			
		||||
It should be ok to set an OS limit on open file descriptors as high as 65536
 | 
			
		||||
for osmo-mgw, which practically rules out failure from running out of file
 | 
			
		||||
descriptors anywhere (<16,000 active calls).
 | 
			
		||||
 | 
			
		||||
When using systemd, the file descriptor limit may be adjusted in the service
 | 
			
		||||
file by the `LimitNOFILE` setting ("Number of Open FILE descriptors"). OsmoMGW
 | 
			
		||||
ships a systemd service file with a high LimitNOFILE setting.
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,14 @@ SUBDIRS = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
nobase_include_HEADERS = \
 | 
			
		||||
	osmocom/mgcp_client/defs.h \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client.h \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client_endpoint_fsm.h \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client_fsm.h \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client_pool.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	osmocom/mgcp/mgcp.h \
 | 
			
		||||
	osmocom/mgcp/mgcp_common.h \
 | 
			
		||||
	osmocom/mgcp/osmux.h \
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/osmo_io.h>
 | 
			
		||||
#include <osmocom/core/write_queue.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
@@ -70,12 +71,10 @@ typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
 | 
			
		||||
/**
 | 
			
		||||
 * Return:
 | 
			
		||||
 *   <  0 in case no audio was processed
 | 
			
		||||
 *   >= 0 in case audio was processed. The remaining payload
 | 
			
		||||
 *   length will be returned.
 | 
			
		||||
 *   >= 0 in case audio was processed.
 | 
			
		||||
 */
 | 
			
		||||
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
 | 
			
		||||
			       struct mgcp_rtp_end *dst_end,
 | 
			
		||||
			       char *data, int *len, int buf_size);
 | 
			
		||||
			       struct mgcp_rtp_end *dst_end, struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
struct mgcp_conn_rtp;
 | 
			
		||||
 | 
			
		||||
@@ -138,8 +137,6 @@ struct mgcp_config {
 | 
			
		||||
	mgcp_processing rtp_processing_cb;
 | 
			
		||||
	mgcp_processing_setup setup_rtp_processing_cb;
 | 
			
		||||
 | 
			
		||||
	mgcp_get_format get_net_downlink_format_cb;
 | 
			
		||||
 | 
			
		||||
	struct osmo_wqueue gw_fd;
 | 
			
		||||
 | 
			
		||||
	struct mgcp_port_range net_ports;
 | 
			
		||||
@@ -208,6 +205,5 @@ int mgcp_send_reset_ep(struct mgcp_endpoint *endp);
 | 
			
		||||
int mgcp_send_reset_all(struct mgcp_config *cfg);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint8_t dscp,
 | 
			
		||||
		     uint8_t prio);
 | 
			
		||||
int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len);
 | 
			
		||||
int mgcp_create_bind(const char *source_addr, int port, uint8_t dscp, uint8_t prio);
 | 
			
		||||
int mgcp_udp_send(struct osmo_io_fd *iofd, const struct osmo_sockaddr *addr, const char *buf, int len);
 | 
			
		||||
 
 | 
			
		||||
@@ -13,9 +13,9 @@ struct mgcp_conn_rtp;
 | 
			
		||||
void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
 | 
			
		||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param);
 | 
			
		||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
 | 
			
		||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst);
 | 
			
		||||
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
 | 
			
		||||
								const char *subtype_name, unsigned int match_nr);
 | 
			
		||||
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec);
 | 
			
		||||
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec);
 | 
			
		||||
struct mgcp_rtp_codec *mgcp_codec_from_pt(struct mgcp_conn_rtp *conn, int payload_type);
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,7 @@ enum mgcp_connection_mode {
 | 
			
		||||
	MGCP_CONN_SEND_ONLY = 2,
 | 
			
		||||
	MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
 | 
			
		||||
	MGCP_CONN_LOOPBACK  = 4 | MGCP_CONN_RECV_SEND,
 | 
			
		||||
	MGCP_CONN_CONFECHO  = 8 | MGCP_CONN_RECV_SEND,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:"
 | 
			
		||||
@@ -67,11 +68,11 @@ struct mgcp_codec_param {
 | 
			
		||||
/* Ensure that the msg->l2h is NUL terminated. */
 | 
			
		||||
static inline int mgcp_msg_terminate_nul(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */
 | 
			
		||||
	unsigned char *tail = msg->tail; /* char after l2 data */
 | 
			
		||||
	if (tail[-1] == '\0')
 | 
			
		||||
		/* nothing to do */;
 | 
			
		||||
	else if (msgb_tailroom(msg) > 0)
 | 
			
		||||
		tail[0] = '\0';
 | 
			
		||||
		msgb_put_u8(msg, (uint8_t)'\0');
 | 
			
		||||
	else if (tail[-1] == '\r' || tail[-1] == '\n')
 | 
			
		||||
		tail[-1] = '\0';
 | 
			
		||||
	else {
 | 
			
		||||
 
 | 
			
		||||
@@ -49,6 +49,11 @@ enum mgcp_conn_rtp_type {
 | 
			
		||||
	MGCP_RTP_OSMUX,
 | 
			
		||||
	MGCP_RTP_IUUP,
 | 
			
		||||
};
 | 
			
		||||
extern const struct value_string mgcp_conn_rtp_type_names[];
 | 
			
		||||
static inline const char *mgcp_conn_rtp_type_name(enum mgcp_conn_rtp_type val)
 | 
			
		||||
{
 | 
			
		||||
	return get_value_string(mgcp_conn_rtp_type_names, val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
 | 
			
		||||
 *  contains a useful connection description (currently only RTP) */
 | 
			
		||||
 
 | 
			
		||||
@@ -23,5 +23,5 @@ static const uint8_t e1_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6,
 | 
			
		||||
 | 
			
		||||
int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs);
 | 
			
		||||
void mgcp_e1_endp_update(struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_e1_endp_release(struct mgcp_endpoint *endp);
 | 
			
		||||
void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts);
 | 
			
		||||
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg);
 | 
			
		||||
 
 | 
			
		||||
@@ -130,10 +130,10 @@ struct mgcp_endpoint {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int index);
 | 
			
		||||
void mgcp_endp_release(struct mgcp_endpoint *endp);
 | 
			
		||||
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid);
 | 
			
		||||
void mgcp_endp_update(struct mgcp_endpoint *endp);
 | 
			
		||||
bool mgcp_endp_is_wildcarded(const char *epname);
 | 
			
		||||
bool mgcp_endp_is_null(const char *epname);
 | 
			
		||||
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
 | 
			
		||||
					      const struct mgcp_trunk *trunk);
 | 
			
		||||
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
 | 
			
		||||
@@ -145,3 +145,4 @@ void mgcp_endp_strip_name(char *epname_stripped, const char *epname,
 | 
			
		||||
			 const struct mgcp_trunk *trunk);
 | 
			
		||||
struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
 | 
			
		||||
			const struct mgcp_trunk *trunk);
 | 
			
		||||
void mgcp_endp_release(struct mgcp_endpoint *endp);
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/osmo_io.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
 | 
			
		||||
@@ -108,7 +109,6 @@ struct mgcp_rtp_end {
 | 
			
		||||
	int  frames_per_packet;
 | 
			
		||||
	uint32_t packet_duration_ms;
 | 
			
		||||
	int maximum_packet_time; /* -1: not set */
 | 
			
		||||
	char *fmtp_extra;
 | 
			
		||||
	/* are we transmitting packets (true) or dropping (false) outbound packets */
 | 
			
		||||
	bool output_enabled;
 | 
			
		||||
	/* FIXME: This parameter can be set + printed, but is nowhere used! */
 | 
			
		||||
@@ -121,8 +121,8 @@ struct mgcp_rtp_end {
 | 
			
		||||
	bool rfc5993_hr_convert;
 | 
			
		||||
 | 
			
		||||
	/* Each end has a separate socket for RTP and RTCP */
 | 
			
		||||
	struct osmo_fd rtp;
 | 
			
		||||
	struct osmo_fd rtcp;
 | 
			
		||||
	struct osmo_io_fd *rtp;
 | 
			
		||||
	struct osmo_io_fd *rtcp;
 | 
			
		||||
 | 
			
		||||
	/* local UDP port number of the RTP socket; RTCP is +1 */
 | 
			
		||||
	int local_port;
 | 
			
		||||
@@ -160,8 +160,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
 | 
			
		||||
int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
 | 
			
		||||
 | 
			
		||||
/* payload processing default functions */
 | 
			
		||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
 | 
			
		||||
				char *data, int *len, int buf_size);
 | 
			
		||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
 | 
			
		||||
				      struct mgcp_conn_rtp *conn_dst,
 | 
			
		||||
@@ -181,7 +180,7 @@ void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *
 | 
			
		||||
				 int id, int inc);
 | 
			
		||||
void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
 | 
			
		||||
				 int id);
 | 
			
		||||
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg);
 | 
			
		||||
void forward_data_tap(struct osmo_io_fd *iofd, struct mgcp_rtp_tap *tap, struct msgb *msg);
 | 
			
		||||
uint32_t mgcp_get_current_ts(unsigned codec_rate);
 | 
			
		||||
 | 
			
		||||
int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg, bool target_is_oa);
 | 
			
		||||
 
 | 
			
		||||
@@ -27,12 +27,9 @@ struct mgcp_trunk {
 | 
			
		||||
	unsigned int trunk_nr;
 | 
			
		||||
	enum mgcp_trunk_type trunk_type;
 | 
			
		||||
 | 
			
		||||
	char *audio_fmtp_extra;
 | 
			
		||||
	int audio_send_ptime;
 | 
			
		||||
	int audio_send_name;
 | 
			
		||||
 | 
			
		||||
	int no_audio_transcoding;
 | 
			
		||||
 | 
			
		||||
	int omit_rtcp;
 | 
			
		||||
	int keepalive_interval;
 | 
			
		||||
 | 
			
		||||
@@ -66,7 +63,7 @@ struct mgcp_trunk {
 | 
			
		||||
		/* E1 specific */
 | 
			
		||||
		struct {
 | 
			
		||||
			unsigned int vty_line_nr;
 | 
			
		||||
			bool ts_in_use[NUM_E1_TS-1];
 | 
			
		||||
			uint8_t ts_usecount[NUM_E1_TS-1];
 | 
			
		||||
			struct osmo_i460_timeslot i460_ts[NUM_E1_TS-1];
 | 
			
		||||
			/* Note: on an E1 line TS 0 is devoted to framing and
 | 
			
		||||
			 * alignment and therefore only NUM_E1_TS-1 timeslots
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								include/osmocom/mgcp_client/defs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								include/osmocom/mgcp_client/defs.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
#include <osmocom/core/defs.h>
 | 
			
		||||
 | 
			
		||||
#if BUILDING_LIBOSMOMGCPCLIENT
 | 
			
		||||
# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT(text)
 | 
			
		||||
#else
 | 
			
		||||
# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT(text) OSMO_DEPRECATED(text)
 | 
			
		||||
#endif
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/defs.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_common.h>
 | 
			
		||||
 | 
			
		||||
/* See also: RFC 3435, chapter 3.5 Transmission over UDP */
 | 
			
		||||
@@ -10,7 +11,7 @@
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0
 | 
			
		||||
#define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
 | 
			
		||||
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
 | 
			
		||||
 | 
			
		||||
#define MGCP_CLIENT_KEEPALIVE_DEFAULT_ENDP "null"
 | 
			
		||||
#define MGCP_CLIENT_MGW_STR "Configure MGCP connection to Media Gateway\n"
 | 
			
		||||
 | 
			
		||||
struct msgb;
 | 
			
		||||
@@ -35,6 +36,12 @@ struct mgcp_client_conf {
 | 
			
		||||
 | 
			
		||||
	/* human readable name / description */
 | 
			
		||||
	char *description;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		uint32_t timeout_sec;
 | 
			
		||||
		uint32_t req_interval_sec;
 | 
			
		||||
		char req_endpoint_name[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	} keepalive;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef unsigned int mgcp_trans_id_t;
 | 
			
		||||
@@ -45,10 +52,10 @@ enum mgcp_codecs {
 | 
			
		||||
	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,
 | 
			
		||||
	CODEC_GSMEFR_8000_1 = 110,	/* 3GPP TS 48.103 table 5.4.2.2.1 */
 | 
			
		||||
	CODEC_GSMHR_8000_1 = 111,	/* 3GPP TS 48.103 table 5.4.2.2.1 */
 | 
			
		||||
	CODEC_AMR_8000_1 = 112,		/* 3GPP TS 48.103 table 5.4.2.2.1 */
 | 
			
		||||
	CODEC_AMRWB_16000_1 = 113,	/* 3GPP TS 48.103 table 5.4.2.2.1 */
 | 
			
		||||
	CODEC_IUFP = 96,
 | 
			
		||||
	CODEC_CLEARMODE = 120,		/* 3GPP TS 48.103 table 5.4.2.2.1 */
 | 
			
		||||
};
 | 
			
		||||
@@ -72,27 +79,7 @@ struct ptmap {
 | 
			
		||||
	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_MAXLEN];
 | 
			
		||||
	char endpoint[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	bool x_osmo_osmux_use;
 | 
			
		||||
	uint8_t x_osmo_osmux_cid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_response {
 | 
			
		||||
	char *body;
 | 
			
		||||
	struct mgcp_response_head head;
 | 
			
		||||
	uint16_t audio_port;
 | 
			
		||||
	char audio_ip[INET6_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;
 | 
			
		||||
};
 | 
			
		||||
int ptmap_cmp(const struct ptmap *a, const struct ptmap *b);
 | 
			
		||||
 | 
			
		||||
enum mgcp_verb {
 | 
			
		||||
	MGCP_VERB_CRCX,
 | 
			
		||||
@@ -102,38 +89,8 @@ enum mgcp_verb {
 | 
			
		||||
	MGCP_VERB_RSIP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define MGCP_MSG_PRESENCE_ENDPOINT	0x0001
 | 
			
		||||
#define MGCP_MSG_PRESENCE_CALL_ID	0x0002
 | 
			
		||||
#define MGCP_MSG_PRESENCE_CONN_ID	0x0004
 | 
			
		||||
#define MGCP_MSG_PRESENCE_AUDIO_IP	0x0008
 | 
			
		||||
#define MGCP_MSG_PRESENCE_AUDIO_PORT	0x0010
 | 
			
		||||
#define MGCP_MSG_PRESENCE_CONN_MODE	0x0020
 | 
			
		||||
#define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000
 | 
			
		||||
#define MGCP_MSG_PRESENCE_X_OSMO_IGN	0x8000
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	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;
 | 
			
		||||
	uint32_t x_osmo_ign;
 | 
			
		||||
	bool x_osmo_osmux_use;
 | 
			
		||||
	int x_osmo_osmux_cid; /* -1 is wildcard */
 | 
			
		||||
	bool param_present;
 | 
			
		||||
	struct mgcp_codec_param param;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
 | 
			
		||||
struct mgcp_client_conf *mgcp_client_conf_alloc(void *ctx);
 | 
			
		||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf) OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use mgcp_client_conf_alloc() (or even better, switch to the mgcp_client_pool API!)");
 | 
			
		||||
void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf);
 | 
			
		||||
int mgcp_client_config_write(struct vty *vty, const char *indent);
 | 
			
		||||
struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp);
 | 
			
		||||
@@ -153,20 +110,8 @@ const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp);
 | 
			
		||||
const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts,
 | 
			
		||||
				  uint8_t rate, uint8_t offset);
 | 
			
		||||
 | 
			
		||||
/* Invoked when an MGCP response is received or sending failed.  When the
 | 
			
		||||
 * response is passed as NULL, this indicates failure during transmission. */
 | 
			
		||||
typedef void (* mgcp_response_cb_t )(struct mgcp_response *response, void *priv);
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -45,6 +45,7 @@ static inline void osmo_mgcpc_ep_ci_dlcx(struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep);
 | 
			
		||||
 | 
			
		||||
const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep);
 | 
			
		||||
const char *osmo_mgcpc_ep_local_name(const struct osmo_mgcpc_ep *ep);
 | 
			
		||||
const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci);
 | 
			
		||||
const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci);
 | 
			
		||||
struct mgcp_client *osmo_mgcpc_ep_client(const struct osmo_mgcpc_ep *ep);
 | 
			
		||||
 
 | 
			
		||||
@@ -29,11 +29,11 @@ struct mgcp_conn_peer {
 | 
			
		||||
	/*! 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;
 | 
			
		||||
	/*! Deprecated. Use only ptmap[].codec in new code. */
 | 
			
		||||
	enum mgcp_codecs codecs[MGCP_MAX_CODECS]
 | 
			
		||||
		OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[i].codec instead");
 | 
			
		||||
	unsigned int codecs_len
 | 
			
		||||
		OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use ptmap[] and ptmap_len instead");
 | 
			
		||||
 | 
			
		||||
	/*! RTP payload type map (optional, only needed when payload types are
 | 
			
		||||
	 * used that differ from what IANA/3GPP defines) */
 | 
			
		||||
@@ -64,11 +64,15 @@ struct mgcp_conn_peer {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
				       uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
 | 
			
		||||
	OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_alloc() and osmo_mgcpc_ep_ci_add() instead");
 | 
			
		||||
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
 | 
			
		||||
	OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci_request() instead");
 | 
			
		||||
void mgcp_conn_delete(struct osmo_fsm_inst *fi)
 | 
			
		||||
	OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci_dlcx() instead");
 | 
			
		||||
 | 
			
		||||
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
 | 
			
		||||
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi)
 | 
			
		||||
	OSMO_DEPRECATED_OUTSIDE_LIBOSMOMGCPCLIENT("use osmo_mgcpc_ep_ci.mgcp_ci_str instead");
 | 
			
		||||
struct mgcp_client *mgcp_conn_get_client(struct osmo_fsm_inst *fi);
 | 
			
		||||
 | 
			
		||||
const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/write_queue.h>
 | 
			
		||||
#include <osmocom/core/osmo_io.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
 | 
			
		||||
#define MSGB_CB_MGCP_TRANS_ID 0
 | 
			
		||||
 | 
			
		||||
@@ -12,18 +13,39 @@ struct reset_ep {
 | 
			
		||||
 | 
			
		||||
struct mgcp_client {
 | 
			
		||||
	struct mgcp_client_conf actual;
 | 
			
		||||
	struct osmo_wqueue wq;
 | 
			
		||||
	struct osmo_io_fd *iofd;
 | 
			
		||||
	mgcp_trans_id_t next_trans_id;
 | 
			
		||||
	struct llist_head responses_pending;
 | 
			
		||||
	struct llist_head inuse_endpoints;
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
	struct osmo_timer_list keepalive_tx_timer;
 | 
			
		||||
	struct osmo_timer_list keepalive_rx_timer;
 | 
			
		||||
	bool conn_up;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_inuse_endpoint {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
	uint16_t id;
 | 
			
		||||
struct mgcp_response_head {
 | 
			
		||||
	int response_code;
 | 
			
		||||
	mgcp_trans_id_t trans_id;
 | 
			
		||||
	char comment[MGCP_COMMENT_MAXLEN];
 | 
			
		||||
	char conn_id[MGCP_CONN_ID_MAXLEN];
 | 
			
		||||
	char endpoint[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
	bool x_osmo_osmux_use;
 | 
			
		||||
	uint8_t x_osmo_osmux_cid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_response {
 | 
			
		||||
	char *body;
 | 
			
		||||
	struct mgcp_response_head head;
 | 
			
		||||
	uint16_t audio_port;
 | 
			
		||||
	char audio_ip[INET6_ADDRSTRLEN];
 | 
			
		||||
	unsigned int ptime;
 | 
			
		||||
	struct ptmap ptmap[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int ptmap_len;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Invoked when an MGCP response is received or sending failed.  When the
 | 
			
		||||
 * response is passed as NULL, this indicates failure during transmission. */
 | 
			
		||||
typedef void (*mgcp_response_cb_t)(struct mgcp_response *response, void *priv);
 | 
			
		||||
 | 
			
		||||
struct mgcp_response_pending {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
 | 
			
		||||
@@ -39,3 +61,41 @@ struct mgcp_response_pending * mgcp_client_pending_add(
 | 
			
		||||
					mgcp_trans_id_t trans_id,
 | 
			
		||||
					mgcp_response_cb_t response_cb,
 | 
			
		||||
					void *priv);
 | 
			
		||||
 | 
			
		||||
#define MGCP_MSG_PRESENCE_ENDPOINT	0x0001
 | 
			
		||||
#define MGCP_MSG_PRESENCE_CALL_ID	0x0002
 | 
			
		||||
#define MGCP_MSG_PRESENCE_CONN_ID	0x0004
 | 
			
		||||
#define MGCP_MSG_PRESENCE_AUDIO_IP	0x0008
 | 
			
		||||
#define MGCP_MSG_PRESENCE_AUDIO_PORT	0x0010
 | 
			
		||||
#define MGCP_MSG_PRESENCE_CONN_MODE	0x0020
 | 
			
		||||
#define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000
 | 
			
		||||
#define MGCP_MSG_PRESENCE_X_OSMO_IGN	0x8000
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	uint16_t audio_port;
 | 
			
		||||
	char *audio_ip;
 | 
			
		||||
	enum mgcp_connection_mode conn_mode;
 | 
			
		||||
	unsigned int ptime;
 | 
			
		||||
	struct ptmap ptmap[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int ptmap_len;
 | 
			
		||||
	uint32_t x_osmo_ign;
 | 
			
		||||
	bool x_osmo_osmux_use;
 | 
			
		||||
	int x_osmo_osmux_cid; /* -1 is wildcard */
 | 
			
		||||
	bool param_present;
 | 
			
		||||
	struct mgcp_codec_param param;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,8 @@
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/vty.h>
 | 
			
		||||
 | 
			
		||||
struct mgcp_client;
 | 
			
		||||
struct mgcp_client_pool;
 | 
			
		||||
struct mgcp_client_pool_member;
 | 
			
		||||
@@ -12,6 +14,7 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
 | 
			
		||||
int mgcp_client_pool_config_write(struct vty *vty, const char *indent);
 | 
			
		||||
unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool);
 | 
			
		||||
void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp_client *mgcp_client);
 | 
			
		||||
bool mgcp_client_pool_empty(const struct mgcp_client_pool *pool);
 | 
			
		||||
 | 
			
		||||
struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool);
 | 
			
		||||
void mgcp_client_pool_put(struct mgcp_client *mgcp_client);
 | 
			
		||||
 
 | 
			
		||||
@@ -13,12 +13,6 @@ AM_CFLAGS = \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
# Libraries
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	libosmo-mgcp-client \
 | 
			
		||||
 
 | 
			
		||||
@@ -14,14 +14,12 @@ AM_CFLAGS = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
# This is not at all related to the release version, but a range of supported
 | 
			
		||||
# API versions. Read TODO_RELEASE in the source tree's root!
 | 
			
		||||
MGCP_CLIENT_LIBVERSION=11:0:0
 | 
			
		||||
MGCP_CLIENT_LIBVERSION=13:0:1
 | 
			
		||||
 | 
			
		||||
lib_LTLIBRARIES = \
 | 
			
		||||
	libosmo-mgcp-client.la \
 | 
			
		||||
@@ -40,3 +38,8 @@ libosmo_mgcp_client_la_LDFLAGS = \
 | 
			
		||||
	-version-info $(MGCP_CLIENT_LIBVERSION) \
 | 
			
		||||
	-no-undefined \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_mgcp_client_la_LIBADD = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -173,6 +173,30 @@ const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep)
 | 
			
		||||
	return osmo_fsm_inst_name(ep->fi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Get "local endpoint name" part of the endpoint name: (local-endpoint-name@domain-name)
 | 
			
		||||
 *
 | 
			
		||||
 * \param ep  The MGCP Endpoint
 | 
			
		||||
 * \returns the local endpoint name if found, NULL on error.
 | 
			
		||||
 */
 | 
			
		||||
const char *osmo_mgcpc_ep_local_name(const struct osmo_mgcpc_ep *ep)
 | 
			
		||||
{
 | 
			
		||||
	static char buf[1024];
 | 
			
		||||
	const char *sep;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(ep);
 | 
			
		||||
	sep = strchr(ep->endpoint, '@');
 | 
			
		||||
	if (!sep) {
 | 
			
		||||
		OSMO_STRLCPY_ARRAY(buf, ep->endpoint);
 | 
			
		||||
		return buf;
 | 
			
		||||
	}
 | 
			
		||||
	if (sep - ep->endpoint >= sizeof(buf))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	memcpy(buf, ep->endpoint, sep - ep->endpoint);
 | 
			
		||||
	buf[sep - ep->endpoint] = '\0';
 | 
			
		||||
	return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *mgcp_conn_peer_name(const struct mgcp_conn_peer *info)
 | 
			
		||||
{
 | 
			
		||||
	/* I'd be fine with a smaller buffer and accept truncation, but gcc possibly refuses to build if
 | 
			
		||||
@@ -509,10 +533,42 @@ static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data)
 | 
			
		||||
	       mgcp_conn_peer_name(ci->got_port_info? &ci->rtp_info : NULL),
 | 
			
		||||
	       ci->notify.fi ? "" : " (not sending a notification)");
 | 
			
		||||
 | 
			
		||||
	/* Below ordering is a delicate decision:
 | 
			
		||||
	 *
 | 
			
		||||
	 * We want to
 | 
			
		||||
	 * - emit the resulting event to ci->notify.fi,
 | 
			
		||||
	 * - check whether we want to tx the next pending MGCP message.
 | 
			
		||||
	 * Both these steps may terminate (=deallocate) the ep.
 | 
			
		||||
	 * So whichever one goes first may cause a use-after-free in the other.
 | 
			
		||||
	 *
 | 
			
		||||
	 * When dispatching the FSM event, we don't get an rc indicating dealloc of the FSM -- it may deallocate and we
 | 
			
		||||
	 * cannot tell. The common mechanism for that is osmo_fsm_set_dealloc_ctx(OTC_SELECT) and query the still
 | 
			
		||||
	 * allocated FSM state after termination (here we would check 'if (ci->ep != NULL)'), but we cannot assume the
 | 
			
		||||
	 * caller has actually set up an osmo_fsm_set_dealloc_ctx(). At time of writing, e.g. osmo-hnbgw does not use
 | 
			
		||||
	 * it.
 | 
			
		||||
	 *
 | 
			
		||||
	 * In osmo_mgcpc_ep_fsm_check_state_chg_after_response(), we do get an rc: false means FSM has terminated.
 | 
			
		||||
	 * On termination, the ep emits a term event to the FSM's parent.
 | 
			
		||||
	 * That may cause the notify.fi to be terminated in turn, depending on how the caller set things up.
 | 
			
		||||
	 * So: we cannot store notify.fi before, then call osmo_mgcpc_ep_fsm_check_state_chg_after_response(), and then
 | 
			
		||||
	 * emit the event, because notify.fi may have deallocated. We cannot look up whether
 | 
			
		||||
	 * osmo_mgcpc_ep_cancel_notify() has been called, because ci may have deallocated along with ci->ep.
 | 
			
		||||
	 *
 | 
			
		||||
	 * We have to skip emitting below success event in case the ep is now terminated.
 | 
			
		||||
	 * - It may be the final DLCX OK: not a problem, osmo_mgcpc_ep_ci_dlcx() has no notify args on purpose, so we do
 | 
			
		||||
	 *   make all callers not set a notify event for DLCX by design. notify.fi should always be NULL when the final
 | 
			
		||||
	 *   DLCX OK terminates the local endpoint state.
 | 
			
		||||
	 * - It may also be sudden termination due to a bad problem, in which case we shouldn't emit success.
 | 
			
		||||
	 *   The osmo_fsm_inst.parent_term_event should suffice as feedback to the caller.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	if (osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi) == false) {
 | 
			
		||||
		/* false means, the ci->ep has been terminated. */
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ci->notify.fi)
 | 
			
		||||
		osmo_fsm_inst_dispatch(ci->notify.fi, ci->notify.success, ci->notify.data);
 | 
			
		||||
 | 
			
		||||
	osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Return the MGW's local RTP port information for this connection, i.e. the local port that MGW is receiving on, as
 | 
			
		||||
@@ -662,11 +718,11 @@ void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
 | 
			
		||||
	osmo_strlcpy(cleared_ci.mgcp_ci_str, ci->mgcp_ci_str, sizeof(cleared_ci.mgcp_ci_str));
 | 
			
		||||
	*ci = cleared_ci;
 | 
			
		||||
 | 
			
		||||
	LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify.fi));
 | 
			
		||||
 | 
			
		||||
	if (verb_info)
 | 
			
		||||
		ci->verb_info = *verb_info;
 | 
			
		||||
 | 
			
		||||
	LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify.fi));
 | 
			
		||||
 | 
			
		||||
	if (ep->endpoint[0]) {
 | 
			
		||||
		if (ci->verb_info.endpoint[0] && strcmp(ci->verb_info.endpoint, ep->endpoint))
 | 
			
		||||
			LOG_CI(ci, LOGL_ERROR,
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_fsm.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/fsm.h>
 | 
			
		||||
@@ -114,12 +115,10 @@ static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info
 | 
			
		||||
		.call_id = info->call_id,
 | 
			
		||||
		.conn_mode = MGCP_CONN_RECV_ONLY,
 | 
			
		||||
		.ptime = info->ptime,
 | 
			
		||||
		.codecs_len = info->codecs_len,
 | 
			
		||||
		.ptmap_len = info->ptmap_len,
 | 
			
		||||
		.param_present = info->param_present
 | 
			
		||||
	};
 | 
			
		||||
	osmo_strlcpy(mgcp_msg->endpoint, info->endpoint, MGCP_ENDPOINT_MAXLEN);
 | 
			
		||||
	memcpy(mgcp_msg->codecs, info->codecs, sizeof(mgcp_msg->codecs));
 | 
			
		||||
	memcpy(mgcp_msg->ptmap, info->ptmap, sizeof(mgcp_msg->ptmap));
 | 
			
		||||
	memcpy(&mgcp_msg->param, &info->param, sizeof(mgcp_msg->param));
 | 
			
		||||
 | 
			
		||||
@@ -136,9 +135,18 @@ static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info
 | 
			
		||||
 | 
			
		||||
static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info)
 | 
			
		||||
{
 | 
			
		||||
	mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT;
 | 
			
		||||
	bool ip_is_set = info->addr[0] != '\0' &&
 | 
			
		||||
			 strncmp(info->addr, "::", sizeof(info->addr)) != 0 &&
 | 
			
		||||
			 strncmp(info->addr, "0.0.0.0", sizeof(info->addr)) != 0;
 | 
			
		||||
	if (ip_is_set) {
 | 
			
		||||
		mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP;
 | 
			
		||||
		mgcp_msg->audio_ip = info->addr;
 | 
			
		||||
	}
 | 
			
		||||
	if (info->port) {
 | 
			
		||||
		mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_PORT;
 | 
			
		||||
		mgcp_msg->audio_port = info->port;
 | 
			
		||||
	}
 | 
			
		||||
	if (ip_is_set && info->port)
 | 
			
		||||
		mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -163,12 +171,10 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
 | 
			
		||||
		.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,
 | 
			
		||||
		.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len,
 | 
			
		||||
		.param_present = mgcp_ctx->conn_peer_local.param_present
 | 
			
		||||
	};
 | 
			
		||||
	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));
 | 
			
		||||
	memcpy(mgcp_msg.ptmap, mgcp_ctx->conn_peer_local.ptmap, sizeof(mgcp_msg.ptmap));
 | 
			
		||||
	memcpy(&mgcp_msg.param, &mgcp_ctx->conn_peer_local.param, sizeof(mgcp_ctx->conn_peer_local.param));
 | 
			
		||||
 | 
			
		||||
@@ -221,7 +227,6 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
			 mgcp_ctx->conn_peer_local.endpoint);
 | 
			
		||||
 | 
			
		||||
		make_crcx_msg(&mgcp_msg, &mgcp_ctx->conn_peer_local);
 | 
			
		||||
		if (mgcp_ctx->conn_peer_local.port)
 | 
			
		||||
		add_audio(&mgcp_msg, &mgcp_ctx->conn_peer_local);
 | 
			
		||||
		set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
 | 
			
		||||
 | 
			
		||||
@@ -364,13 +369,21 @@ static void fsm_ready_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case EV_MDCX:
 | 
			
		||||
		msg = make_mdcx_msg(mgcp_ctx);
 | 
			
		||||
		OSMO_ASSERT(msg);
 | 
			
		||||
		if (!msg) {
 | 
			
		||||
			/* make_mdcx_msg() should already have logged the error */
 | 
			
		||||
			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		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);
 | 
			
		||||
		if (!msg) {
 | 
			
		||||
			/* make_dlcx_msg() should already have logged the error */
 | 
			
		||||
			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_resp_cb, fi);
 | 
			
		||||
		new_state = ST_DLCX_RESP;
 | 
			
		||||
		break;
 | 
			
		||||
@@ -611,6 +624,72 @@ static struct osmo_fsm fsm_mgcp_client = {
 | 
			
		||||
	.log_subsys = DLMGCP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Provide backwards compat for deprecated conn_peer->codecs[]: when the caller passes in an mgcp_conn_peer instance
 | 
			
		||||
 * that has codecs[] set, apply it to ptmap[] instead. */
 | 
			
		||||
static void mgcp_conn_peer_compat(struct mgcp_conn_peer *conn_peer)
 | 
			
		||||
{
 | 
			
		||||
	struct ptmap ptmap[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int ptmap_len;
 | 
			
		||||
 | 
			
		||||
	if (!conn_peer->codecs_len)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* Before dropping codecs[], codecs[] would indicate the order in which the codecs should appear in SDP. ptmap[]
 | 
			
		||||
	 * would indicate payload type numbers when not using a default payload type number (may omit entries).
 | 
			
		||||
	 * Now, ptmap[] just indicates both at the same time; codecs[] should be empty, and ptmap[] lists all codecs.
 | 
			
		||||
	 * So if any codecs[] are present, recreate ptmap[] in the order of codecs[]. */
 | 
			
		||||
 | 
			
		||||
	ptmap_len = 0;
 | 
			
		||||
	for (int i = 0; i < conn_peer->codecs_len; i++) {
 | 
			
		||||
		enum mgcp_codecs codec = conn_peer->codecs[i];
 | 
			
		||||
		struct ptmap *found = NULL;
 | 
			
		||||
 | 
			
		||||
		/* Look up whether a specific pt was indicated for this codec */
 | 
			
		||||
		for (int p = 0; p < conn_peer->ptmap_len; p++) {
 | 
			
		||||
			if (conn_peer->ptmap[p].codec != codec)
 | 
			
		||||
				continue;
 | 
			
		||||
			found = &conn_peer->ptmap[p];
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		if (found) {
 | 
			
		||||
			ptmap[ptmap_len] = *found;
 | 
			
		||||
		} else {
 | 
			
		||||
			ptmap[ptmap_len] = (struct ptmap){
 | 
			
		||||
				.codec = codec,
 | 
			
		||||
				/* some enum mgcp_codecs correspond to their standard PT nr, so for compat: */
 | 
			
		||||
				.pt = codec,
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
		ptmap_len++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Are there any entries in the old ptmap that were omitted by codecs[]? */
 | 
			
		||||
	for (int p = 0; p < conn_peer->ptmap_len; p++) {
 | 
			
		||||
		bool exists = false;
 | 
			
		||||
		for (int i = 0; i < ptmap_len; i++) {
 | 
			
		||||
			if (ptmap_cmp(&ptmap[i], &conn_peer->ptmap[p]))
 | 
			
		||||
				continue;
 | 
			
		||||
			exists = true;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (exists)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (ptmap_len >= ARRAY_SIZE(ptmap))
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		/* Not present yet, add it to the end */
 | 
			
		||||
		ptmap[ptmap_len] = conn_peer->ptmap[p];
 | 
			
		||||
		ptmap_len++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Use the new ptmap[], and clear out legacy codecs[]. */
 | 
			
		||||
	memcpy(conn_peer->ptmap, ptmap, sizeof(conn_peer->ptmap));
 | 
			
		||||
	conn_peer->ptmap_len = ptmap_len;
 | 
			
		||||
	conn_peer->codecs_len = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! allocate FSM, and create a new connection on the MGW.
 | 
			
		||||
 *  \param[in] mgcp MGCP client descriptor.
 | 
			
		||||
 *  \param[in] parent_fi Parent FSM instance.
 | 
			
		||||
@@ -625,6 +704,7 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
	struct in6_addr ip_test;
 | 
			
		||||
 | 
			
		||||
	mgcp_conn_peer_compat(conn_peer);
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(parent_fi);
 | 
			
		||||
	OSMO_ASSERT(mgcp);
 | 
			
		||||
@@ -664,6 +744,8 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
 | 
			
		||||
	struct mgcp_ctx *mgcp_ctx = fi->priv;
 | 
			
		||||
	struct in6_addr ip_test;
 | 
			
		||||
 | 
			
		||||
	mgcp_conn_peer_compat(conn_peer);
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(mgcp_ctx);
 | 
			
		||||
	OSMO_ASSERT(conn_peer);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -75,6 +75,11 @@ void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp
 | 
			
		||||
	pool->mgcp_client_single = mgcp_client;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mgcp_client_pool_empty(const struct mgcp_client_pool *pool)
 | 
			
		||||
{
 | 
			
		||||
	return llist_empty(&pool->member_list);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Lookup the selected MGCP client config by its reference number */
 | 
			
		||||
struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr)
 | 
			
		||||
{
 | 
			
		||||
@@ -89,7 +94,7 @@ struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Not every pool member may have a functional MGCP client, we will run through the pool once until we meet a
 | 
			
		||||
 * pool member that is suitable (has a client, is not blocked, has a low load). */
 | 
			
		||||
 * pool member that is suitable (is not blocked, has a client with a working link, has a low load). */
 | 
			
		||||
static struct mgcp_client_pool_member *mgcp_client_pool_pick(struct mgcp_client_pool *pool)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
@@ -98,14 +103,15 @@ static struct mgcp_client_pool_member *mgcp_client_pool_pick(struct mgcp_client_
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pool_member, &pool->member_list, list) {
 | 
			
		||||
		n_pool_members++;
 | 
			
		||||
		if (pool_member->blocked == false && pool_member->client) {
 | 
			
		||||
		bool conn_up = pool_member->client && pool_member->client->conn_up;
 | 
			
		||||
		if (pool_member->blocked == false && conn_up) {
 | 
			
		||||
			if (!pool_member_picked)
 | 
			
		||||
				pool_member_picked = pool_member;
 | 
			
		||||
			else if (pool_member_picked->refcount > pool_member->refcount)
 | 
			
		||||
				pool_member_picked = pool_member;
 | 
			
		||||
		} else {
 | 
			
		||||
			LOGPPMGW(pool_member, LOGL_DEBUG, "%s -- MGW %u is unusable (blocked=%u, cli=%u)\n",
 | 
			
		||||
				 __func__, pool_member->nr, pool_member->blocked, !!pool_member->client);
 | 
			
		||||
			LOGPPMGW(pool_member, LOGL_DEBUG, "%s -- MGW %u is unusable (blocked=%u, cli=%u, link=%u)\n",
 | 
			
		||||
				 __func__, pool_member->nr, pool_member->blocked, !!pool_member->client, conn_up);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* MGCP client interface to quagga VTY */
 | 
			
		||||
/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
 | 
			
		||||
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
 | 
			
		||||
 * (C) 2009 by Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
 * (C) 2009-2011 by Holger Hans Peter Freyther
 | 
			
		||||
@@ -28,6 +28,7 @@
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/vty/misc.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_internal.h>
 | 
			
		||||
@@ -48,19 +49,43 @@ static struct mgcp_client_conf *global_mgcp_client_conf = NULL;
 | 
			
		||||
/* Pointer to the MGCP pool that is managed by mgcp_client_pool_vty_init() */
 | 
			
		||||
static struct mgcp_client_pool *global_mgcp_client_pool = NULL;
 | 
			
		||||
 | 
			
		||||
struct mgcp_client_conf *get_mgcp_client_config(struct vty *vty)
 | 
			
		||||
static struct mgcp_client_conf *get_mgcp_client_config(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node)
 | 
			
		||||
		return vty->index;
 | 
			
		||||
 | 
			
		||||
	/* Global single MGCP config, deprecated: */
 | 
			
		||||
	vty_out(vty, "%% MGCP commands outside of 'mgw' nodes are deprecated. "
 | 
			
		||||
		"You should consider reading User Manual and migrating to 'mgw' node.%s",
 | 
			
		||||
		"You should consider reading the User Manual and migrating to 'mgw' node.%s",
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	return global_mgcp_client_conf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct mgcp_client *get_mgcp_client(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
	struct mgcp_client_pool_member *pool_member;
 | 
			
		||||
 | 
			
		||||
	if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node) {
 | 
			
		||||
		llist_for_each_entry(pool_member, &global_mgcp_client_pool->member_list, list) {
 | 
			
		||||
			/* Find matching the conf pointer: */
 | 
			
		||||
			if (&pool_member->conf != conf)
 | 
			
		||||
				continue;
 | 
			
		||||
			return pool_member->client;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Global single MGCP config, deprecated: */
 | 
			
		||||
	vty_out(vty, "%% MGCP commands outside of 'mgw' nodes are deprecated. "
 | 
			
		||||
		"You should consider reading the User Manual and migrating to 'mgw' node.%s",
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	/* There's no way to obtain the struct mgcp_client in old interface, but anyway it's deprecated. */
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
 | 
			
		||||
      "local-ip " VTY_IPV46_CMD,
 | 
			
		||||
      "local bind to connect to MGW from\n"
 | 
			
		||||
@@ -280,6 +305,91 @@ ALIAS_DEPRECATED(cfg_mgw_no_reset_ep_name,
 | 
			
		||||
      NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
 | 
			
		||||
      "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_mgw_keepalive_req_interval,
 | 
			
		||||
      cfg_mgw_mgw_keepalive_req_interval_cmd,
 | 
			
		||||
      "keepalive request-interval <0-4294967295>",
 | 
			
		||||
      "Monitor if the MGCP link against MGW is still usable\n"
 | 
			
		||||
      "Send an MGCP command to the MGW at given interval if no other commands are sent\n"
 | 
			
		||||
      "The interval at which send MGCP commands (s), 0 to disable\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
	struct mgcp_client *mgcp = get_mgcp_client(vty);
 | 
			
		||||
 | 
			
		||||
	conf->keepalive.req_interval_sec = atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	if (!mgcp)
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
 | 
			
		||||
	/* If client already exists, apply the change immediately if possible: */
 | 
			
		||||
	mgcp->actual.keepalive.req_interval_sec = atoi(argv[0]);
 | 
			
		||||
	if (mgcp->iofd) { /* UDP MGCP socket connected */
 | 
			
		||||
		if (mgcp->actual.keepalive.req_interval_sec > 0) {
 | 
			
		||||
			/* Re-schedule: */
 | 
			
		||||
			osmo_timer_schedule(&mgcp->keepalive_tx_timer, mgcp->actual.keepalive.req_interval_sec, 0);
 | 
			
		||||
		} else {
 | 
			
		||||
			if (osmo_timer_pending(&mgcp->keepalive_tx_timer))
 | 
			
		||||
				osmo_timer_del(&mgcp->keepalive_tx_timer);
 | 
			
		||||
			/* Assume link is UP by default, so that this MGW can be selected: */
 | 
			
		||||
			mgcp->conn_up = true;
 | 
			
		||||
		}
 | 
			
		||||
	} /* else: wait until connect() to do first scheduling */
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_mgw_keepalive_req_endpoint,
 | 
			
		||||
      cfg_mgw_mgw_keepalive_req_endpoint_cmd,
 | 
			
		||||
      "keepalive request-endpoint NAME",
 | 
			
		||||
      "Monitor if the MGCP link against MGW is still usable\n"
 | 
			
		||||
      "Use a given endpoint name when sending an MGCP command to the MGW for keepalive purposes\n"
 | 
			
		||||
      "The name of the endpoint to use\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
	struct mgcp_client *mgcp = get_mgcp_client(vty);
 | 
			
		||||
 | 
			
		||||
	OSMO_STRLCPY_ARRAY(conf->keepalive.req_endpoint_name, argv[0]);
 | 
			
		||||
 | 
			
		||||
	if (!mgcp)
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
 | 
			
		||||
	/* If client already exists, apply the change immediately if possible: */
 | 
			
		||||
	OSMO_STRLCPY_ARRAY(mgcp->actual.keepalive.req_endpoint_name, argv[0]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgw_mgw_keepalive_timeout,
 | 
			
		||||
      cfg_mgw_mgw_keepalive_timeout_cmd,
 | 
			
		||||
      "keepalive timeout <0-4294967295>",
 | 
			
		||||
      "Monitor if the MGCP link against MGW is still usable\n"
 | 
			
		||||
      "Consider the link to the MGW to be down after time without receiving any message from it\n"
 | 
			
		||||
      "The timeout (s), 0 to disable\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
 | 
			
		||||
	struct mgcp_client *mgcp = get_mgcp_client(vty);
 | 
			
		||||
 | 
			
		||||
	conf->keepalive.timeout_sec = atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	if (!mgcp)
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
 | 
			
		||||
	/* If client already exists, apply the change immediately if possible: */
 | 
			
		||||
	mgcp->actual.keepalive.timeout_sec = atoi(argv[0]);
 | 
			
		||||
	if (mgcp->iofd) { /* UDP MGCP socket connected */
 | 
			
		||||
		if (mgcp->actual.keepalive.timeout_sec > 0) {
 | 
			
		||||
			/* Re-schedule: */
 | 
			
		||||
			osmo_timer_schedule(&mgcp->keepalive_rx_timer, mgcp->actual.keepalive.timeout_sec, 0);
 | 
			
		||||
		} else {
 | 
			
		||||
			if (osmo_timer_pending(&mgcp->keepalive_rx_timer))
 | 
			
		||||
				osmo_timer_del(&mgcp->keepalive_rx_timer);
 | 
			
		||||
			/* Assume link is UP by default, so that this MGW can be selected: */
 | 
			
		||||
			mgcp->conn_up = true;
 | 
			
		||||
		}
 | 
			
		||||
	} /* else: wait until connect() to do first scheduling */
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int config_write(struct vty *vty, const char *indent, struct mgcp_client_conf *conf)
 | 
			
		||||
{
 | 
			
		||||
	const char *addr;
 | 
			
		||||
@@ -317,6 +427,17 @@ static int config_write(struct vty *vty, const char *indent, struct mgcp_client_
 | 
			
		||||
	llist_for_each_entry(reset_ep, &conf->reset_epnames, list)
 | 
			
		||||
		vty_out(vty, "%s%sreset-endpoint %s%s", indent, mgw_prefix, reset_ep->name, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	if (conf->keepalive.req_interval_sec != 0)
 | 
			
		||||
		vty_out(vty, "%s%skeepalive request-interval %u%s", indent, mgw_prefix,
 | 
			
		||||
			conf->keepalive.req_interval_sec, VTY_NEWLINE);
 | 
			
		||||
	if (strncmp(conf->keepalive.req_endpoint_name, MGCP_CLIENT_KEEPALIVE_DEFAULT_ENDP,
 | 
			
		||||
		    sizeof(conf->keepalive.req_endpoint_name)) != 0)
 | 
			
		||||
		vty_out(vty, "%s%skeepalive request-endpoint %s%s", indent,  mgw_prefix,
 | 
			
		||||
			conf->keepalive.req_endpoint_name, VTY_NEWLINE);
 | 
			
		||||
	if (conf->keepalive.timeout_sec != 0)
 | 
			
		||||
		vty_out(vty, "%s%skeepalive timeout %u%s", indent,  mgw_prefix,
 | 
			
		||||
			conf->keepalive.timeout_sec, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -347,6 +468,9 @@ static void vty_init_common(void *talloc_ctx, int node)
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_endpoint_domain_name_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_reset_ep_name_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_no_reset_ep_name_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_keepalive_req_interval_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_keepalive_req_endpoint_cmd);
 | 
			
		||||
	install_lib_element(node, &cfg_mgw_mgw_keepalive_timeout_cmd);
 | 
			
		||||
 | 
			
		||||
	osmo_fsm_vty_add_cmds();
 | 
			
		||||
}
 | 
			
		||||
@@ -553,8 +677,13 @@ DEFUN(mgw_show, mgw_show_cmd, "show mgw-pool", SHOW_STR "Display information abo
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pool_member, &global_mgcp_client_pool->member_list, list) {
 | 
			
		||||
		const struct mgcp_client *cli = pool_member->client;
 | 
			
		||||
		vty_out(vty, "%%  MGW %s%s", mgcp_client_pool_member_name(pool_member), VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, "%%   mgcp-client:   %s%s", pool_member->client ? "connected" : "disconnected",
 | 
			
		||||
		vty_out(vty, "%%   MGCP link:     %s,%s%s",
 | 
			
		||||
			cli && cli->iofd ? "connected" : "disconnected",
 | 
			
		||||
			cli && cli->conn_up ?
 | 
			
		||||
				((cli->actual.keepalive.timeout_sec > 0) ? "UP" : "MAYBE") :
 | 
			
		||||
				"DOWN",
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, "%%   service:       %s%s", pool_member->blocked ? "blocked" : "unblocked", VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, "%%   ongoing calls: %u%s", pool_member->refcount, VTY_NEWLINE);
 | 
			
		||||
 
 | 
			
		||||
@@ -16,12 +16,6 @@ AM_CFLAGS = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(LIBOSMOABIS_LIBS) \
 | 
			
		||||
	$(LIBOSMOTRAU_LIBS) \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -276,87 +276,6 @@ error:
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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)
 | 
			
		||||
{
 | 
			
		||||
	/* 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 (!strlen(codec->subtype_name))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: implement meaningful checks to make sure that the given codec
 | 
			
		||||
	 * is compatible with the given endpoint */
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! 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->trunk->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
 | 
			
		||||
			     rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if the codec has a specific AMR mode (octet-aligned or bandwith-efficient) set. */
 | 
			
		||||
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	if (codec->param_present == false)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (!codec->param.amr_octet_aligned_present)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (strcmp(codec->subtype_name, "AMR") != 0)
 | 
			
		||||
		return false;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return true if octet-aligned is set in the given codec. Default to octet-aligned=0, i.e. bandwidth-efficient mode.
 | 
			
		||||
 * See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB
 | 
			
		||||
 * Media Type Registration":
 | 
			
		||||
@@ -376,10 +295,15 @@ bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
 | 
			
		||||
	return codec->param.amr_octet_aligned;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Compare two codecs, all parameters must match up, except for the payload type
 | 
			
		||||
 * number. */
 | 
			
		||||
/* Compare two codecs, all parameters must match up */
 | 
			
		||||
static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
 | 
			
		||||
{
 | 
			
		||||
	/* All codec properties must match up, except the payload type number. Even though standardisd payload numbers
 | 
			
		||||
	 * exist for certain situations, the call agent may still assign them freely. Hence we must not insist on equal
 | 
			
		||||
	 * payload type numbers. Also the audio_name is not checked since it is already parsed into subtype_name, rate,
 | 
			
		||||
	 * and channels, which are checked. */
 | 
			
		||||
	if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
 | 
			
		||||
		return false;
 | 
			
		||||
	if (codec_a->rate != codec_b->rate)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (codec_a->channels != codec_b->channels)
 | 
			
		||||
@@ -388,61 +312,172 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c
 | 
			
		||||
		return false;
 | 
			
		||||
	if (codec_a->frame_duration_den != codec_b->frame_duration_den)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/* Note: AMR allows to set the RTP payload format to octet-aligned or bandwith-efficient (octet-aligned=0)
 | 
			
		||||
	 * via SDP. This difference concerns payload format only, but not the actual codec. It is not a difference
 | 
			
		||||
	 * within the meaning of this function. */
 | 
			
		||||
	/* AMR payload may be formatted in two different payload formats, it is still the same codec but since the
 | 
			
		||||
	 * formatting of the payload is different, conversation is required, so we must treat it as a different
 | 
			
		||||
	 * codec here. */
 | 
			
		||||
	if (strcmp(codec_a->subtype_name, "AMR") == 0) {
 | 
			
		||||
		if (mgcp_codec_amr_is_octet_aligned(codec_a) != mgcp_codec_amr_is_octet_aligned(codec_b))
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Translate a given payload type number that belongs to the packet of a
 | 
			
		||||
 *  source connection to the equivalent payload type number that matches the
 | 
			
		||||
 *  configuration of a destination connection.
 | 
			
		||||
 *  \param[in] conn_src related source rtp-connection.
 | 
			
		||||
 *  \param[in] conn_dst related destination rtp-connection.
 | 
			
		||||
 *  \param[in] payload_type number from the source packet or source connection.
 | 
			
		||||
 *  \returns translated payload type number on success, -EINVAL on failure. */
 | 
			
		||||
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type)
 | 
			
		||||
/* Compare two codecs, all parameters must match up, except parameters related to payload formatting (not checked). */
 | 
			
		||||
static bool codecs_convertible(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_rtp_end *rtp_src;
 | 
			
		||||
	struct mgcp_rtp_end *rtp_dst;
 | 
			
		||||
	struct mgcp_rtp_codec *codec_src = NULL;
 | 
			
		||||
	struct mgcp_rtp_codec *codec_dst = NULL;
 | 
			
		||||
	/* OsmoMGW currently has no ability to transcode from one codec to another. However OsmoMGW is still able to
 | 
			
		||||
	 * translate between different payload formats as long as the encoded voice data itself does not change.
 | 
			
		||||
	 * Therefore we must insist on equal codecs but still allow different payload formatting. */
 | 
			
		||||
 | 
			
		||||
	/* In 3G IuUP, AMR may be encapsulated in IuFP, this means even though the codec name and negotiated rate is
 | 
			
		||||
	 * different, the formatting can still be converted by OsmoMGW. Therefore we won't insist on equal
 | 
			
		||||
	 * subtype_name and rate if we detect IuFP and AMR is used on the same tandem. */
 | 
			
		||||
	if (strcmp(codec_a->subtype_name, "AMR") == 0 && strcmp(codec_b->subtype_name, "VND.3GPP.IUFP") == 0)
 | 
			
		||||
		goto iufp;
 | 
			
		||||
	if (strcmp(codec_a->subtype_name, "VND.3GPP.IUFP") == 0 && strcmp(codec_b->subtype_name, "AMR") == 0)
 | 
			
		||||
		goto iufp;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
 | 
			
		||||
		return false;
 | 
			
		||||
	if (codec_a->rate != codec_b->rate)
 | 
			
		||||
		return false;
 | 
			
		||||
iufp:
 | 
			
		||||
	if (codec_a->channels != codec_b->channels)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (codec_a->frame_duration_num != codec_b->frame_duration_num)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (codec_a->frame_duration_den != codec_b->frame_duration_den)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_codec *mgcp_codec_find_same(struct mgcp_conn_rtp *conn, struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_rtp_end *rtp_end;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	unsigned int codecs_assigned;
 | 
			
		||||
 | 
			
		||||
	rtp_src = &conn_src->end;
 | 
			
		||||
	rtp_dst = &conn_dst->end;
 | 
			
		||||
	rtp_end = &conn->end;
 | 
			
		||||
 | 
			
		||||
	/* Find the codec information that is used on the source side */
 | 
			
		||||
	codecs_assigned = rtp_src->codecs_assigned;
 | 
			
		||||
	/* Use the codec information from the source and try to find the equivalent of it on the destination side. In
 | 
			
		||||
	 * the first run we will look for an exact match. */
 | 
			
		||||
	codecs_assigned = rtp_end->codecs_assigned;
 | 
			
		||||
	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
 | 
			
		||||
	for (i = 0; i < codecs_assigned; i++) {
 | 
			
		||||
		if (payload_type == rtp_src->codecs[i].payload_type) {
 | 
			
		||||
			codec_src = &rtp_src->codecs[i];
 | 
			
		||||
		if (codecs_same(codec, &rtp_end->codecs[i])) {
 | 
			
		||||
			return &rtp_end->codecs[i];
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (!codec_src)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Use the codec information from the source and try to find the
 | 
			
		||||
	 * equivalent of it on the destination side */
 | 
			
		||||
	codecs_assigned = rtp_dst->codecs_assigned;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* For a given codec, find a convertible codec in the given connection. */
 | 
			
		||||
static struct mgcp_rtp_codec *codec_find_convertible(struct mgcp_conn_rtp *conn, struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_rtp_end *rtp_end;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	unsigned int codecs_assigned;
 | 
			
		||||
	struct mgcp_rtp_codec *codec_convertible = NULL;
 | 
			
		||||
 | 
			
		||||
	rtp_end = &conn->end;
 | 
			
		||||
 | 
			
		||||
	/* Use the codec information from the source and try to find the equivalent of it on the destination side. In
 | 
			
		||||
	 * the first run we will look for an exact match. */
 | 
			
		||||
	codec_convertible = mgcp_codec_find_same(conn, codec);
 | 
			
		||||
	if (codec_convertible)
 | 
			
		||||
		return codec_convertible;
 | 
			
		||||
 | 
			
		||||
	/* In case we weren't able to find an exact match, we will try to find a match that is the same codec, but the
 | 
			
		||||
	 * payload format may be different. This alternative will require a frame format conversion (i.e. AMR bwe->oe) */
 | 
			
		||||
	codecs_assigned = rtp_end->codecs_assigned;
 | 
			
		||||
	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
 | 
			
		||||
	for (i = 0; i < codecs_assigned; i++) {
 | 
			
		||||
		if (codecs_same(codec_src, &rtp_dst->codecs[i])) {
 | 
			
		||||
			codec_dst = &rtp_dst->codecs[i];
 | 
			
		||||
		if (codecs_convertible(codec, &rtp_end->codecs[i])) {
 | 
			
		||||
			codec_convertible = &rtp_end->codecs[i];
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (!codec_dst)
 | 
			
		||||
 | 
			
		||||
	return codec_convertible;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Decide for one suitable codec on both of the given connections. In case a destination connection is not available,
 | 
			
		||||
 *  a tentative decision is made.
 | 
			
		||||
 *  \param[inout] conn_src related rtp-connection.
 | 
			
		||||
 *  \param[inout] conn_dst related destination rtp-connection (NULL if not present).
 | 
			
		||||
 *  \returns 0 on success, -EINVAL on failure. */
 | 
			
		||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	/* In case no destination connection is available (yet), or in case the destination connection exists but has
 | 
			
		||||
	 * no codecs assigned, we are forced to make a simple tentative decision:
 | 
			
		||||
	 * We just use the first codec of the source connection (conn_src) */
 | 
			
		||||
	OSMO_ASSERT(conn_src->end.codecs_assigned <= MGCP_MAX_CODECS);
 | 
			
		||||
	if (!conn_dst || conn_dst->end.codecs_assigned == 0) {
 | 
			
		||||
		if (conn_src->end.codecs_assigned >= 1) {
 | 
			
		||||
			conn_src->end.codec = &conn_src->end.codecs[0];
 | 
			
		||||
			return 0;
 | 
			
		||||
		} else
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Compare all codecs of the source connection (conn_src) to the codecs of the destination connection (conn_dst). In case
 | 
			
		||||
	 * of a match set this codec on both connections. This would be an ideal selection since no codec conversion would be
 | 
			
		||||
	 * required. */
 | 
			
		||||
	for (i = 0; i < conn_src->end.codecs_assigned; i++) {
 | 
			
		||||
		struct mgcp_rtp_codec *codec_conn_src = &conn_src->end.codecs[i];
 | 
			
		||||
		struct mgcp_rtp_codec *codec_conn_dst = mgcp_codec_find_same(conn_dst, codec_conn_src);
 | 
			
		||||
		if (codec_conn_dst) {
 | 
			
		||||
			/* We found the a codec that is exactly the same (same codec, same payload format etc.) on both
 | 
			
		||||
			 * sides. We now set this codec on both connections. */
 | 
			
		||||
			conn_dst->end.codec = codec_conn_dst;
 | 
			
		||||
			conn_src->end.codec = codec_conn_src;
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* In case we could not find a codec that is exactly the same, let's at least try to find a codec that we are able
 | 
			
		||||
	 * to convert. */
 | 
			
		||||
	for (i = 0; i < conn_src->end.codecs_assigned; i++) {
 | 
			
		||||
		struct mgcp_rtp_codec *codec_conn_src = &conn_src->end.codecs[i];
 | 
			
		||||
		struct mgcp_rtp_codec *codec_conn_dst = codec_find_convertible(conn_dst, codec_conn_src);
 | 
			
		||||
		if (codec_conn_dst) {
 | 
			
		||||
			/* We found the a codec that we can convert to. Set each side to its codec. */
 | 
			
		||||
			conn_dst->end.codec = codec_conn_dst;
 | 
			
		||||
			conn_src->end.codec = codec_conn_src;
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (conn_dst->end.codecs_assigned)
 | 
			
		||||
		conn_dst->end.codec = &conn_dst->end.codecs[0];
 | 
			
		||||
	else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	return codec_dst->payload_type;
 | 
			
		||||
	if (conn_src->end.codecs_assigned)
 | 
			
		||||
		conn_src->end.codec = &conn_src->end.codecs[0];
 | 
			
		||||
	else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if the codec has a specific AMR mode (octet-aligned or bandwith-efficient) set. */
 | 
			
		||||
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	if (codec->param_present == false)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (!codec->param.amr_octet_aligned_present)
 | 
			
		||||
		return false;
 | 
			
		||||
	if (strcmp(codec->subtype_name, "AMR") != 0)
 | 
			
		||||
		return false;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find the payload type number configured for a specific codec by SDP.
 | 
			
		||||
@@ -452,8 +487,7 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
 | 
			
		||||
 * \param subtype_name  SDP codec name without parameters (e.g. "AMR").
 | 
			
		||||
 * \param match_nr  Index for the match found, first being match_nr == 0. Iterate all matches by calling multiple times
 | 
			
		||||
 *                  with incrementing match_nr.
 | 
			
		||||
 * \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found.
 | 
			
		||||
 */
 | 
			
		||||
 * \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found. */
 | 
			
		||||
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
 | 
			
		||||
								const char *subtype_name, unsigned int match_nr)
 | 
			
		||||
{
 | 
			
		||||
@@ -469,3 +503,26 @@ const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Lookup a codec that is assigned to a connection by its payload type number.
 | 
			
		||||
 *  \param[in] conn related rtp-connection.
 | 
			
		||||
 *  \param[in] payload_type number of the codec to look up.
 | 
			
		||||
 *  \returns pointer to codec struct on success, NULL on failure. */
 | 
			
		||||
struct mgcp_rtp_codec *mgcp_codec_from_pt(struct mgcp_conn_rtp *conn, int payload_type)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_rtp_end *rtp_end = &conn->end;
 | 
			
		||||
	unsigned int codecs_assigned = rtp_end->codecs_assigned;
 | 
			
		||||
	struct mgcp_rtp_codec *codec = NULL;
 | 
			
		||||
	size_t i;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < codecs_assigned; i++) {
 | 
			
		||||
		if (payload_type == rtp_end->codecs[i].payload_type) {
 | 
			
		||||
			codec = &rtp_end->codecs[i];
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return codec;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -106,12 +106,10 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
 | 
			
		||||
	/* backpointer to the generic part of the connection */
 | 
			
		||||
	conn->u.rtp.conn = conn;
 | 
			
		||||
 | 
			
		||||
	end->rtp.fd = -1;
 | 
			
		||||
	end->rtcp.fd = -1;
 | 
			
		||||
	end->rtp = NULL;
 | 
			
		||||
	end->rtcp = NULL;
 | 
			
		||||
	memset(&end->addr, 0, sizeof(end->addr));
 | 
			
		||||
	end->rtcp_port = 0;
 | 
			
		||||
	talloc_free(end->fmtp_extra);
 | 
			
		||||
	end->fmtp_extra = NULL;
 | 
			
		||||
 | 
			
		||||
	/* Set default values */
 | 
			
		||||
	end->frames_per_packet = 0;	/* unknown */
 | 
			
		||||
@@ -173,8 +171,8 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	/* Do not allow more then two connections */
 | 
			
		||||
	if (llist_count(&endp->conns) >= endp->type->max_conns)
 | 
			
		||||
	/* Do not allow more than the maximum number of connections */
 | 
			
		||||
	if (endp->type->max_conns > 0 && llist_count(&endp->conns) >= endp->type->max_conns)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Create new connection and add it to the list */
 | 
			
		||||
@@ -358,53 +356,37 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	static char str[sizeof(conn->name)+sizeof(conn->id)+256];
 | 
			
		||||
	char ipbuf[INET6_ADDRSTRLEN];
 | 
			
		||||
	struct osmo_strbuf sb = { .buf = str, .len = sizeof(str) };
 | 
			
		||||
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		snprintf(str, sizeof(str), "(null connection)");
 | 
			
		||||
		return str;
 | 
			
		||||
	}
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return "NULL";
 | 
			
		||||
 | 
			
		||||
	switch (conn->type) {
 | 
			
		||||
	case MGCP_CONN_TYPE_RTP:
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, "(%s/%s C:%s r=%s:%u<->l=%s:%u",
 | 
			
		||||
				   conn->name,
 | 
			
		||||
				   mgcp_conn_rtp_type_name(conn->type),
 | 
			
		||||
				   conn->id,
 | 
			
		||||
				   osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf) ? : "NULL",
 | 
			
		||||
				   osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa),
 | 
			
		||||
				   conn->u.rtp.end.local_addr ? : "NULL",
 | 
			
		||||
				   conn->u.rtp.end.local_port);
 | 
			
		||||
 | 
			
		||||
		switch (conn->u.rtp.type) {
 | 
			
		||||
		case MGCP_RTP_DEFAULT:
 | 
			
		||||
			/* Dump RTP connection */
 | 
			
		||||
			snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
 | 
			
		||||
				"rtp:%u rtcp:%u)",
 | 
			
		||||
				conn->name, conn->id,
 | 
			
		||||
				osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
 | 
			
		||||
				osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa),
 | 
			
		||||
				ntohs(conn->u.rtp.end.rtcp_port));
 | 
			
		||||
			break;
 | 
			
		||||
		case MGCP_RTP_OSMUX:
 | 
			
		||||
			snprintf(str, sizeof(str), "(%s/osmux, id:0x%s, ip:%s, "
 | 
			
		||||
				"port:%u CID:%u)",
 | 
			
		||||
				conn->name, conn->id,
 | 
			
		||||
				osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
 | 
			
		||||
				osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa),
 | 
			
		||||
				conn->u.rtp.osmux.local_cid);
 | 
			
		||||
			break;
 | 
			
		||||
		case MGCP_RTP_IUUP:
 | 
			
		||||
			snprintf(str, sizeof(str), "(%s/iuup, id:0x%s, ip:%s, "
 | 
			
		||||
				"port:%u)",
 | 
			
		||||
				conn->name, conn->id,
 | 
			
		||||
				osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
 | 
			
		||||
				osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa));
 | 
			
		||||
			OSMO_STRBUF_PRINTF(sb, " CID=%u", conn->u.rtp.osmux.local_cid);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* Should not happen, we should be able to dump
 | 
			
		||||
			 * every possible connection type. */
 | 
			
		||||
			snprintf(str, sizeof(str), "(unknown conn_rtp connection type %u)",
 | 
			
		||||
				 conn->u.rtp.type);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		OSMO_STRBUF_PRINTF(sb, ")");
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		/* Should not happen, we should be able to dump
 | 
			
		||||
		 * every possible connection type. */
 | 
			
		||||
		snprintf(str, sizeof(str), "(unknown connection type)");
 | 
			
		||||
		break;
 | 
			
		||||
		return "(unknown connection type)";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return str;
 | 
			
		||||
@@ -441,3 +423,10 @@ struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp)
 | 
			
		||||
 | 
			
		||||
	return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct value_string mgcp_conn_rtp_type_names[] = {
 | 
			
		||||
	{ MGCP_RTP_DEFAULT, "rtp" },
 | 
			
		||||
	{ MGCP_RTP_OSMUX, "osmux" },
 | 
			
		||||
	{ MGCP_RTP_IUUP, "iuup" },
 | 
			
		||||
	{}
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -222,7 +222,7 @@ static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_dat
 | 
			
		||||
	osmo_i460_mux_enqueue(endp->e1.schan, msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* called by I.460 de-multeiplexer; feed output of I.460 demux into TRAU frame sync */
 | 
			
		||||
/* called by I.460 de-multiplexer; feed output of I.460 demux into TRAU frame sync */
 | 
			
		||||
static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits,
 | 
			
		||||
				  unsigned int num_bits)
 | 
			
		||||
{
 | 
			
		||||
@@ -301,7 +301,6 @@ static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int
 | 
			
		||||
 | 
			
		||||
	mgcp_send(endp, 1, NULL, msg, &conn_dst->u.rtp, &conn_dst->u.rtp);
 | 
			
		||||
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return;
 | 
			
		||||
skip:
 | 
			
		||||
	rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_RX_FAIL_CTR));
 | 
			
		||||
@@ -309,8 +308,8 @@ skip:
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Function to handle outgoing E1 traffic */
 | 
			
		||||
static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
 | 
			
		||||
/* handle outgoing E1 traffic */
 | 
			
		||||
static void e1_send_ts_frame(struct e1inp_ts *ts, struct mgcp_trunk *trunk)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg = msgb_alloc_c(trunk, E1_TS_BYTES, "E1-TX-timeslot-bytes");
 | 
			
		||||
	uint8_t *ptr;
 | 
			
		||||
@@ -359,19 +358,17 @@ static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
 | 
			
		||||
	osmo_i460_demux_in(&trunk->e1.i460_ts[ts->num - 1], msgb_data(msg), msgb_length(msg));
 | 
			
		||||
 | 
			
		||||
	/* Trigger sending of pending E1 traffic */
 | 
			
		||||
	e1_send(ts, trunk);
 | 
			
		||||
	e1_send_ts_frame(ts, trunk);
 | 
			
		||||
 | 
			
		||||
	/* e1inp_rx_ts() does not free() msgb */
 | 
			
		||||
	/* e1inp_rx_ts(), the caller of this callback does not free() msgb. */
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
 | 
			
		||||
static int e1_open(struct mgcp_trunk *trunk, uint8_t ts_nr)
 | 
			
		||||
{
 | 
			
		||||
	/*! Each timeslot needs only to be configured once. The Timeslot then
 | 
			
		||||
	 *  stays open and permanently receives data. It is then up to the
 | 
			
		||||
	 *  I.460 demultiplexer to add/remove subchannels as needed. It is
 | 
			
		||||
	 *  allowed to call this function multiple times since we check if the
 | 
			
		||||
	 *  timeslot is already configured. */
 | 
			
		||||
	/*! One E1 timeslot may serve multiple I.460 subslots. The timeslot is opened as soon as an I.460 subslot is
 | 
			
		||||
	 *  opened and will stay open until the last I.460 subslot is closed (see e1_close below). This function must
 | 
			
		||||
	 *  be called any time a new I.460 subslot is opened in order to maintain constancy of the ts_usecount counter. */
 | 
			
		||||
 | 
			
		||||
	struct e1inp_line *e1_line;
 | 
			
		||||
	int rc;
 | 
			
		||||
@@ -379,12 +376,14 @@ static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
 | 
			
		||||
	OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
 | 
			
		||||
	cfg = trunk->cfg;
 | 
			
		||||
 | 
			
		||||
	if (trunk->e1.ts_in_use[ts_nr - 1]) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u already set up, skipping...\n", ts_nr);
 | 
			
		||||
	if (trunk->e1.ts_usecount[ts_nr - 1] > 0) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u already set up and in use by %u subslot(s), using it as it is...\n",
 | 
			
		||||
			  ts_nr, trunk->e1.ts_usecount[ts_nr - 1]);
 | 
			
		||||
		trunk->e1.ts_usecount[ts_nr - 1]++;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Get E1 line */
 | 
			
		||||
	/* Find E1 line */
 | 
			
		||||
	e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
 | 
			
		||||
	if (!e1_line) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n",
 | 
			
		||||
@@ -401,12 +400,61 @@ static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
 | 
			
		||||
	}
 | 
			
		||||
	rc = e1inp_line_update(e1_line);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 timeslot %u.\n", ts_nr);
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 line %u.\n", ts_nr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u set up successfully.\n", ts_nr);
 | 
			
		||||
	trunk->e1.ts_in_use[ts_nr - 1] = true;
 | 
			
		||||
	trunk->e1.ts_usecount[ts_nr - 1]++;
 | 
			
		||||
	OSMO_ASSERT(trunk->e1.ts_usecount[ts_nr - 1] == 1);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int e1_close(struct mgcp_trunk *trunk, uint8_t ts_nr)
 | 
			
		||||
{
 | 
			
		||||
	/* See also comment above (e1_open). This function must be called any time an I.460 subslot is closed */
 | 
			
		||||
 | 
			
		||||
	struct e1inp_line *e1_line;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS);
 | 
			
		||||
	cfg = trunk->cfg;
 | 
			
		||||
 | 
			
		||||
	if (trunk->e1.ts_usecount[ts_nr - 1] > 1) {
 | 
			
		||||
		trunk->e1.ts_usecount[ts_nr - 1]--;
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u still in use by %u other subslot(s), leaving it open...\n",
 | 
			
		||||
			  ts_nr, trunk->e1.ts_usecount[ts_nr - 1]);
 | 
			
		||||
		return 0;
 | 
			
		||||
	} else if (trunk->e1.ts_usecount[ts_nr - 1] == 0) {
 | 
			
		||||
		/* This should not be as it means we close the timeslot too often. */
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "E1 timeslot %u already closed, leaving it as it is...\n", ts_nr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Find E1 line */
 | 
			
		||||
	e1_line = e1inp_line_find(trunk->e1.vty_line_nr);
 | 
			
		||||
	if (!e1_line) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n",
 | 
			
		||||
			  trunk->e1.vty_line_nr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Release E1 timeslot */
 | 
			
		||||
	rc = e1inp_ts_config_none(&e1_line->ts[ts_nr - 1], e1_line);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to disable E1 timeslot %u.\n", ts_nr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	rc = e1inp_line_update(e1_line);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 line %u.\n", trunk->e1.vty_line_nr);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u closed.\n", ts_nr);
 | 
			
		||||
	trunk->e1.ts_usecount[ts_nr - 1]--;
 | 
			
		||||
	OSMO_ASSERT(trunk->e1.ts_usecount[ts_nr - 1] == 0);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -496,7 +544,7 @@ static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Equip E1 endpoint with I.460 mux resources.
 | 
			
		||||
/*! Equip E1 endpoint with I.460 mux and E1 timeslot resources.
 | 
			
		||||
 *  \param[in] endp endpoint to equip
 | 
			
		||||
 *  \param[in] ts E1 timeslot number.
 | 
			
		||||
 *  \param[in] ss E1 subslot number.
 | 
			
		||||
@@ -517,7 +565,7 @@ int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8
 | 
			
		||||
	endp->e1.last_amr_ft = AMR_4_75;
 | 
			
		||||
 | 
			
		||||
	/* Set up E1 line / timeslot */
 | 
			
		||||
	rc = e1_init(endp->trunk, ts);
 | 
			
		||||
	rc = e1_open(endp->trunk, ts);
 | 
			
		||||
	if (rc != 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
@@ -604,9 +652,15 @@ void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Remove E1 resources from endpoint
 | 
			
		||||
 *  \param[in] endp endpoint to release. */
 | 
			
		||||
void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
 | 
			
		||||
 *  \param[in] endp endpoint to release.
 | 
			
		||||
 *  \param[in] ts E1 timeslot number. */
 | 
			
		||||
void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts)
 | 
			
		||||
{
 | 
			
		||||
	/* Guard against multiple calls. In case we don't see a subchannel anymore we can safely assume that all work
 | 
			
		||||
	 * is done. */
 | 
			
		||||
	if (!(endp->e1.schan || endp->e1.trau_rtp_st || endp->e1.trau_sync_fi))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n");
 | 
			
		||||
 | 
			
		||||
	if (endp->e1.schan)
 | 
			
		||||
@@ -615,8 +669,10 @@ void mgcp_e1_endp_release(struct mgcp_endpoint *endp)
 | 
			
		||||
		talloc_free(endp->e1.trau_rtp_st);
 | 
			
		||||
	if (endp->e1.trau_sync_fi)
 | 
			
		||||
		osmo_fsm_inst_term(endp->e1.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL);
 | 
			
		||||
 | 
			
		||||
	memset(&endp->e1, 0, sizeof(endp->e1));
 | 
			
		||||
 | 
			
		||||
	/* Close E1 timeslot */
 | 
			
		||||
	e1_close(endp->trunk, ts);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit.
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,6 @@
 | 
			
		||||
const struct mgcp_endpoint_typeset ep_typeset = {
 | 
			
		||||
	/* Specify endpoint properties for RTP endpoint */
 | 
			
		||||
	.rtp = {
 | 
			
		||||
		.max_conns = 2,
 | 
			
		||||
		.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
 | 
			
		||||
		.cleanup_cb = mgcp_cleanup_rtp_bridge_cb,
 | 
			
		||||
	},
 | 
			
		||||
@@ -110,36 +109,6 @@ struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk,
 | 
			
		||||
	return endp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! release endpoint, all open connections are closed.
 | 
			
		||||
 *  \param[in] endp endpoint to release */
 | 
			
		||||
void mgcp_endp_release(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
 | 
			
		||||
 | 
			
		||||
	/* 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);
 | 
			
		||||
 | 
			
		||||
	/* We must only decrement the stat item when the endpoint as actually
 | 
			
		||||
	 * claimed. An endpoint is claimed when a call-id is set */
 | 
			
		||||
	if (endp->callid)
 | 
			
		||||
		osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common,
 | 
			
		||||
								 TRUNK_STAT_ENDPOINTS_USED), 1);
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
 | 
			
		||||
	if (endp->trunk->trunk_type == MGCP_TRUNK_E1)
 | 
			
		||||
		mgcp_e1_endp_release(endp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if the endpoint name contains the prefix (e.g. "rtpbridge/" or
 | 
			
		||||
 * "ds/e1-") and write the epname without the prefix back to the memory
 | 
			
		||||
 * pointed at by epname. (per trunk the prefix is the same for all endpoints,
 | 
			
		||||
@@ -187,7 +156,6 @@ static void chop_epname_suffix(char *epname, const struct mgcp_trunk *trunk)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 /*! Convert all characters in epname to lowercase and strip trunk prefix and
 | 
			
		||||
 * endpoint name suffix (domain name) from epname. The result is written to
 | 
			
		||||
 * to the memory pointed at by epname_stripped. The expected size of the
 | 
			
		||||
@@ -260,6 +228,17 @@ bool mgcp_endp_is_wildcarded(const char *epname)
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Check if the given epname refers to a "null" endpoint.
 | 
			
		||||
 *  \param[in] epname endpoint name to check
 | 
			
		||||
 *  \returns true if epname refers to "null"" endpoint, else false. */
 | 
			
		||||
bool mgcp_endp_is_null(const char *epname)
 | 
			
		||||
{
 | 
			
		||||
	if (strncasecmp(epname, "null@", 5) == 0)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Find an endpoint by its name on a specified trunk.
 | 
			
		||||
 *  \param[out] cause pointer to store cause code, can be NULL.
 | 
			
		||||
 *  \param[in] epname endpoint name to lookup.
 | 
			
		||||
@@ -675,3 +654,36 @@ void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
 | 
			
		||||
	if (llist_empty(&endp->conns))
 | 
			
		||||
		mgcp_endp_release(endp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! release endpoint, all open connections are closed.
 | 
			
		||||
 *  \param[in] endp endpoint to release */
 | 
			
		||||
void mgcp_endp_release(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
 | 
			
		||||
 | 
			
		||||
	/* 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);
 | 
			
		||||
 | 
			
		||||
	/* We must only decrement the stat item when the endpoint as actually
 | 
			
		||||
	 * claimed. An endpoint is claimed when a call-id is set */
 | 
			
		||||
	if (endp->callid)
 | 
			
		||||
		osmo_stat_item_dec(osmo_stat_item_group_get_item(endp->trunk->stats.common,
 | 
			
		||||
								 TRUNK_STAT_ENDPOINTS_USED), 1);
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
 | 
			
		||||
	if (endp->trunk->trunk_type == MGCP_TRUNK_E1) {
 | 
			
		||||
		uint8_t ts = e1_ts_nr_from_epname(endp->name);
 | 
			
		||||
		mgcp_e1_endp_release(endp, ts);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -311,7 +311,6 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	rc = mgcp_send(conn_rtp_dst->conn->endp, true, NULL, msg, conn_rtp_src, conn_rtp_dst);
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -513,10 +512,9 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
 | 
			
		||||
		 osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port));
 | 
			
		||||
 | 
			
		||||
	/* Forward a copy of the RTP data to a debug ip/port */
 | 
			
		||||
	forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
 | 
			
		||||
		     msg);
 | 
			
		||||
	forward_data_tap(rtp_end->rtp, &conn_src->tap_out, msg);
 | 
			
		||||
 | 
			
		||||
	len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, (char *)hdr, buflen);
 | 
			
		||||
	len = mgcp_udp_send(rtp_end->rtp, &rtp_end->addr, (char *)hdr, buflen);
 | 
			
		||||
 | 
			
		||||
	if (len <= 0)
 | 
			
		||||
		return len;
 | 
			
		||||
@@ -640,7 +638,7 @@ free_ret:
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Build IuUP RNL Data primitive from msg containing an incoming RTP pkt from
 | 
			
		||||
 * peer and send it down the IuUP layer towards the destination as IuUP/RTP: */
 | 
			
		||||
 * peer and send it down the IuUP layer towards the destination as IuUP/RTP. Takes ownership of msg. */
 | 
			
		||||
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_iuup_rnl_prim *irp;
 | 
			
		||||
 
 | 
			
		||||
@@ -75,7 +75,7 @@ void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Parse connection mode.
 | 
			
		||||
 *  \param[in] mode as string (recvonly, sendrecv, sendonly or loopback)
 | 
			
		||||
 *  \param[in] mode as string (recvonly, sendrecv, sendonly confecho or loopback)
 | 
			
		||||
 *  \param[in] endp pointer to endpoint (only used for log output)
 | 
			
		||||
 *  \param[out] associated connection to be modified accordingly
 | 
			
		||||
 *  \returns 0 on success, -1 on error */
 | 
			
		||||
@@ -100,6 +100,8 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
 | 
			
		||||
		conn->mode = MGCP_CONN_RECV_SEND;
 | 
			
		||||
	else if (strcasecmp(mode, "sendonly") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_SEND_ONLY;
 | 
			
		||||
	else if (strcasecmp(mode, "confecho") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_CONFECHO;
 | 
			
		||||
	else if (strcasecmp(mode, "loopback") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_LOOPBACK;
 | 
			
		||||
	else {
 | 
			
		||||
 
 | 
			
		||||
@@ -70,6 +70,18 @@ void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *
 | 
			
		||||
	rtpconn_rate_ctr_add(conn_rtp, endp, id, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* wrapper around libosmocore msgb_copy_c, which [at least before libosmocore.git Change-Id
 | 
			
		||||
 * I68328adb952ca8833ba047cb3b49ccc6f8a1f1b5] doesn't copy the cb */
 | 
			
		||||
static inline struct msgb *mgw_msgb_copy_c(void *ctx, struct msgb *msg, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg2 = msgb_copy_c(ctx, msg, name);
 | 
			
		||||
	if (OSMO_UNLIKELY(!msg2))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	memcpy(msg2->cb, msg->cb, sizeof(msg2->cb));
 | 
			
		||||
	return msg2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int rx_rtp(struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end)
 | 
			
		||||
@@ -404,15 +416,12 @@ static int align_rtp_timestamp_offset(const struct mgcp_endpoint *endp,
 | 
			
		||||
/*! dummy callback to disable transcoding (see also cfg->rtp_processing_cb).
 | 
			
		||||
 *  \param[in] associated endpoint.
 | 
			
		||||
 *  \param[in] destination RTP end.
 | 
			
		||||
 *  \param[in,out] pointer to buffer with voice data.
 | 
			
		||||
 *  \param[in] voice data length.
 | 
			
		||||
 *  \param[in] maximum size of caller provided voice data buffer.
 | 
			
		||||
 *  \param[in,out] msg message bufffer containing data. Function might change length.
 | 
			
		||||
 *  \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)
 | 
			
		||||
				struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	LOGPENDP(endp, DRTP, LOGL_DEBUG, "transcoding disabled\n");
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -429,18 +438,6 @@ int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
 | 
			
		||||
					  const struct mgcp_rtp_codec **codec,
 | 
			
		||||
					  const char **fmtp_extra,
 | 
			
		||||
					  struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	LOGPENDP(endp, DRTP, LOGL_DEBUG, "conn:%s using format defaults\n",
 | 
			
		||||
		 mgcp_conn_dump(conn->conn));
 | 
			
		||||
 | 
			
		||||
	*codec = conn->end.codec;
 | 
			
		||||
	*fmtp_extra = conn->end.fmtp_extra;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp,
 | 
			
		||||
			  struct mgcp_rtp_state *state, const uint16_t seq,
 | 
			
		||||
			  const int32_t transit, const uint32_t ssrc,
 | 
			
		||||
@@ -492,28 +489,24 @@ void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
/* There may be different payload type numbers negotiated for two connections.
 | 
			
		||||
 * Patch the payload type of an RTP packet so that it uses the payload type
 | 
			
		||||
 * that is valid for the destination connection (conn_dst) */
 | 
			
		||||
static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
 | 
			
		||||
			 struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
 | 
			
		||||
 * of the codec that is set for the destination connection (conn_dst) */
 | 
			
		||||
static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct rtp_hdr *rtp_hdr;
 | 
			
		||||
	uint8_t pt_in;
 | 
			
		||||
	int pt_out;
 | 
			
		||||
 | 
			
		||||
	if (msgb_length(msg) < sizeof(struct rtp_hdr)) {
 | 
			
		||||
		LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
 | 
			
		||||
		LOG_CONN_RTP(conn_dst, LOGL_NOTICE, "RTP packet too short (%u < %zu)\n",
 | 
			
		||||
			     msgb_length(msg), sizeof(struct rtp_hdr));
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
 | 
			
		||||
 | 
			
		||||
	pt_in = rtp_hdr->payload_type;
 | 
			
		||||
	pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
 | 
			
		||||
	if (pt_out < 0)
 | 
			
		||||
	if (!conn_dst->end.codec) {
 | 
			
		||||
		LOG_CONN_RTP(conn_dst, LOGL_NOTICE, "no codec set on destination connection!\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	rtp_hdr->payload_type = (uint8_t) conn_dst->end.codec->payload_type;
 | 
			
		||||
 | 
			
		||||
	rtp_hdr->payload_type = (uint8_t) pt_out;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -801,16 +794,18 @@ static int amr_oa_check(char *data, int len)
 | 
			
		||||
 | 
			
		||||
/* Forward data to a debug tap. This is debug function that is intended for
 | 
			
		||||
 * debugging the voice traffic with tools like gstreamer */
 | 
			
		||||
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
 | 
			
		||||
void forward_data_tap(struct osmo_io_fd *iofd, struct mgcp_rtp_tap *tap, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (!tap->enabled)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	rc = sendto(fd, msgb_data(msg), msgb_length(msg), 0, (struct sockaddr *)&tap->forward,
 | 
			
		||||
		    sizeof(tap->forward));
 | 
			
		||||
	struct msgb *msg2 = msgb_copy(msg, "RTP TAP Tx");
 | 
			
		||||
	if (!msg2)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	rc = osmo_iofd_sendto_msgb(iofd, msg2, 0, &tap->forward);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		LOGP(DRTP, LOGL_ERROR,
 | 
			
		||||
		     "Forwarding tapped (debug) voice data failed.\n");
 | 
			
		||||
@@ -838,33 +833,28 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
 | 
			
		||||
{
 | 
			
		||||
	char ipbuf[INET6_ADDRSTRLEN];
 | 
			
		||||
 | 
			
		||||
	if (osmo_sockaddr_is_any(&conn->end.addr) != 0) {
 | 
			
		||||
		switch (conn->conn->mode) {
 | 
			
		||||
		case MGCP_CONN_LOOPBACK:
 | 
			
		||||
			/* HACK: for IuUP, we want to reply with an IuUP Initialization ACK upon the first RTP
 | 
			
		||||
			 * message received. We currently hackishly accomplish that by putting the endpoint in
 | 
			
		||||
			 * loopback mode and patching over the looped back RTP message to make it look like an
 | 
			
		||||
			 * ack. We don't know the femto cell's IP address and port until the RAB Assignment
 | 
			
		||||
			 * Response is received, but the nano3G expects an IuUP Initialization Ack before it even
 | 
			
		||||
			 * sends the RAB Assignment Response. Hence, if the remote address is 0.0.0.0 and the
 | 
			
		||||
			 * MGCP port is in loopback mode, allow looping back the packet to any source. */
 | 
			
		||||
			LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
 | 
			
		||||
				 "In loopback mode and remote address not set:"
 | 
			
		||||
				 " allowing data from address: %s\n",
 | 
			
		||||
				 osmo_sockaddr_ntop(&addr->u.sa, ipbuf));
 | 
			
		||||
	if (osmo_sockaddr_is_any(&conn->end.addr) != 0 ||
 | 
			
		||||
	    osmo_sockaddr_port(&conn->end.addr.u.sa) == 0) {
 | 
			
		||||
		if (mgcp_conn_rtp_is_iuup(conn) && !conn->iuup.configured) {
 | 
			
		||||
			/* Allow IuUP Initialization to get through even if we don't have a remote address set yet.
 | 
			
		||||
			 * This is needed because hNodeB doesn't announce its IuUP remote IP addr to the MGCP client
 | 
			
		||||
			 * (RAB Assignment Response at HNBGW) until it has gone through IuUP Initialization against
 | 
			
		||||
			 * this MGW here. Hence the MGW may not yet know the remote IuUP address and port at the time
 | 
			
		||||
			 * of receiving IuUP Initialization from the hNodeB.
 | 
			
		||||
			 */
 | 
			
		||||
			LOGPCONN(conn->conn, DRTP, LOGL_INFO,
 | 
			
		||||
				 "Rx RTP from %s: allowing unknown src for IuUP Initialization\n",
 | 
			
		||||
				 osmo_sockaddr_to_str(addr));
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
		}
 | 
			
		||||
		/* Receiving early media before the endpoint is configured. Instead of logging
 | 
			
		||||
		 * this as an error that occurs on every call, keep it more low profile to not
 | 
			
		||||
		 * confuse humans with expected errors. */
 | 
			
		||||
		LOGPCONN(conn->conn, DRTP, LOGL_INFO,
 | 
			
		||||
				 "Rx RTP from %s, but remote address not set:"
 | 
			
		||||
				 " dropping early media\n",
 | 
			
		||||
				 osmo_sockaddr_ntop(&addr->u.sa, ipbuf));
 | 
			
		||||
			 "Rx RTP from %s, but remote address not set: dropping early media\n",
 | 
			
		||||
			 osmo_sockaddr_to_str(addr));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Note: Check if the inbound RTP data comes from the same host to
 | 
			
		||||
	 * which we send our outgoing RTP traffic. */
 | 
			
		||||
@@ -875,11 +865,8 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
 | 
			
		||||
	     memcmp(&conn->end.addr.u.sin6.sin6_addr, &addr->u.sin6.sin6_addr,
 | 
			
		||||
		    sizeof(struct in6_addr)))) {
 | 
			
		||||
		LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
 | 
			
		||||
			 "data from wrong address: %s, ",
 | 
			
		||||
			 osmo_sockaddr_ntop(&addr->u.sa, ipbuf));
 | 
			
		||||
		LOGPC(DRTP, LOGL_ERROR, "expected: %s\n",
 | 
			
		||||
		      osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf));
 | 
			
		||||
		LOGPCONN(conn->conn, DRTP, LOGL_ERROR, "packet tossed\n");
 | 
			
		||||
			 "data from wrong src %s, expected IP Address %s. Packet tossed.\n",
 | 
			
		||||
			 osmo_sockaddr_to_str(addr), osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -890,12 +877,9 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
 | 
			
		||||
	if (osmo_sockaddr_port(&conn->end.addr.u.sa) != osmo_sockaddr_port(&addr->u.sa) &&
 | 
			
		||||
	    ntohs(conn->end.rtcp_port) != osmo_sockaddr_port(&addr->u.sa)) {
 | 
			
		||||
		LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
 | 
			
		||||
			 "data from wrong source port: %d, ",
 | 
			
		||||
			 osmo_sockaddr_port(&addr->u.sa));
 | 
			
		||||
		LOGPC(DRTP, LOGL_ERROR,
 | 
			
		||||
		      "expected: %d for RTP or %d for RTCP\n",
 | 
			
		||||
		      osmo_sockaddr_port(&conn->end.addr.u.sa), ntohs(conn->end.rtcp_port));
 | 
			
		||||
		LOGPCONN(conn->conn, DRTP, LOGL_ERROR, "packet tossed\n");
 | 
			
		||||
			 "data from wrong src %s, expected port: %u for RTP or %u for RTCP. Packet tossed.\n",
 | 
			
		||||
			 osmo_sockaddr_to_str(addr), osmo_sockaddr_port(&conn->end.addr.u.sa),
 | 
			
		||||
			 ntohs(conn->end.rtcp_port));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -993,7 +977,7 @@ static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Dispatch msg bridged from the sister conn in the endpoint.
 | 
			
		||||
/*! Dispatch msg bridged from the sister conn in the endpoint. Takes ownership of msgb.
 | 
			
		||||
 *  \param[in] conn_dst The destination conn that should handle and transmit the content to
 | 
			
		||||
 *			its peer outside MGW.
 | 
			
		||||
 *  \param[in] msg msgb containing an RTP pkt received by the sister conn in the endpoint,
 | 
			
		||||
@@ -1015,8 +999,10 @@ static int mgcp_conn_rtp_dispatch_rtp(struct mgcp_conn_rtp *conn_dst, struct msg
 | 
			
		||||
	/* Before we try to deliver the packet, we check if the destination
 | 
			
		||||
	 * port and IP-Address make sense at all. If not, we will be unable
 | 
			
		||||
	 * to deliver the packet. */
 | 
			
		||||
	if (check_rtp_destin(conn_dst) != 0)
 | 
			
		||||
	if (check_rtp_destin(conn_dst) != 0) {
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Depending on the RTP connection type, deliver the RTP packet to the
 | 
			
		||||
	 * destination connection. */
 | 
			
		||||
@@ -1051,33 +1037,38 @@ static int mgcp_conn_rtp_dispatch_rtp(struct mgcp_conn_rtp *conn_dst, struct msg
 | 
			
		||||
	 * be discarded, this should not happen, normally the MGCP type
 | 
			
		||||
	 * should be properly set */
 | 
			
		||||
	LOGPENDP(endp, DRTP, LOGL_ERROR, "bad MGCP type -- data discarded!\n");
 | 
			
		||||
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! send udp packet.
 | 
			
		||||
 *  \param[in] fd associated file descriptor.
 | 
			
		||||
/*! send message buffer via udp socket.
 | 
			
		||||
 *  \param[in] iofd associated file descriptor.
 | 
			
		||||
 *  \param[in] addr destination ip-address.
 | 
			
		||||
 *  \param[in] msg message buffer that holds the data to be send.
 | 
			
		||||
 *  \returns bytes sent, -1 on error. */
 | 
			
		||||
static int mgcp_udp_send_msg(struct osmo_io_fd *iofd, const struct osmo_sockaddr *addr, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "sending %i bytes length packet to %s ...\n", msgb_length(msg),
 | 
			
		||||
	     osmo_sockaddr_to_str(addr));
 | 
			
		||||
 | 
			
		||||
	return osmo_iofd_sendto_msgb(iofd, msg, 0, addr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! send udp packet from raw buffer/length.
 | 
			
		||||
 *  \param[in] iofd associated file descriptor.
 | 
			
		||||
 *  \param[in] addr destination ip-address.
 | 
			
		||||
 *  \param[in] buf buffer that holds the data to be send.
 | 
			
		||||
 *  \param[in] len length of the data to be sent.
 | 
			
		||||
 *  \returns bytes sent, -1 on error. */
 | 
			
		||||
int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len)
 | 
			
		||||
int mgcp_udp_send(struct osmo_io_fd *iofd, const struct osmo_sockaddr *addr, const char *buf, int len)
 | 
			
		||||
{
 | 
			
		||||
	char ipbuf[INET6_ADDRSTRLEN];
 | 
			
		||||
	size_t addr_len;
 | 
			
		||||
	struct msgb *msg = msgb_alloc_c(iofd, len, "mgcp_udp_send");
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	memcpy(msg->tail, buf, len);
 | 
			
		||||
	msgb_put(msg, len);
 | 
			
		||||
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG,
 | 
			
		||||
	     "sending %i bytes length packet to %s:%u ...\n", len,
 | 
			
		||||
	     osmo_sockaddr_ntop(&addr->u.sa, ipbuf),
 | 
			
		||||
	     osmo_sockaddr_port(&addr->u.sa));
 | 
			
		||||
 | 
			
		||||
	if (addr->u.sa.sa_family == AF_INET6) {
 | 
			
		||||
		addr_len = sizeof(addr->u.sin6);
 | 
			
		||||
	} else {
 | 
			
		||||
		addr_len = sizeof(addr->u.sin);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sendto(fd, buf, len, 0, &addr->u.sa, addr_len);
 | 
			
		||||
	return mgcp_udp_send_msg(iofd, addr, msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! send RTP dummy packet (to keep NAT connection open).
 | 
			
		||||
@@ -1105,8 +1096,7 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
 | 
			
		||||
	if (mgcp_conn_rtp_is_iuup(conn))
 | 
			
		||||
		rc = mgcp_conn_iuup_send_dummy(conn);
 | 
			
		||||
	else
 | 
			
		||||
		rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
 | 
			
		||||
				   rtp_dummy_payload, sizeof(rtp_dummy_payload));
 | 
			
		||||
		rc = mgcp_udp_send(conn->end.rtp, &conn->end.addr, rtp_dummy_payload, sizeof(rtp_dummy_payload));
 | 
			
		||||
 | 
			
		||||
	if (rc == -1)
 | 
			
		||||
		goto failed;
 | 
			
		||||
@@ -1117,7 +1107,7 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
 | 
			
		||||
	was_rtcp = 1;
 | 
			
		||||
	rtcp_addr = conn->end.addr;
 | 
			
		||||
	osmo_sockaddr_set_port(&rtcp_addr.u.sa, ntohs(conn->end.rtcp_port));
 | 
			
		||||
	rc = mgcp_udp_send(conn->end.rtcp.fd, &rtcp_addr,
 | 
			
		||||
	rc = mgcp_udp_send(conn->end.rtcp, &rtcp_addr,
 | 
			
		||||
			   rtp_dummy_payload, sizeof(rtp_dummy_payload));
 | 
			
		||||
 | 
			
		||||
	if (rc >= 0)
 | 
			
		||||
@@ -1131,15 +1121,14 @@ failed:
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Send RTP/RTCP data to a specified destination connection.
 | 
			
		||||
/*! Send RTP/RTCP data to a specified destination connection. Takes ownership of msg.
 | 
			
		||||
 *  \param[in] endp associated endpoint (for configuration, logging).
 | 
			
		||||
 *  \param[in] is_rtp flag to specify if the packet is of type RTP or RTCP.
 | 
			
		||||
 *  \param[in] spoofed source address (set to NULL to disable).
 | 
			
		||||
 *  \param[in] buf buffer that contains the RTP/RTCP data.
 | 
			
		||||
 *  \param[in] len length of the buffer that contains the RTP/RTCP data.
 | 
			
		||||
 *  \param[in] addr spoofed source address (set to NULL to disable).
 | 
			
		||||
 *  \param[in] msg message buffer that contains the RTP/RTCP data.
 | 
			
		||||
 *  \param[in] conn_src associated source connection.
 | 
			
		||||
 *  \param[in] conn_dst associated destination connection.
 | 
			
		||||
 *  \returns 0 on success, -1 on ERROR. */
 | 
			
		||||
 *  \returns 0 on success, negative on ERROR. */
 | 
			
		||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr,
 | 
			
		||||
	      struct msgb *msg, struct mgcp_conn_rtp *conn_src,
 | 
			
		||||
	      struct mgcp_conn_rtp *conn_dst)
 | 
			
		||||
@@ -1169,18 +1158,10 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
 | 
			
		||||
	 * IuUP -> AMR: calls this function, skip patching if conn_src is IuUP.
 | 
			
		||||
	 * {AMR or IuUP} -> IuUP: calls mgcp_udp_send() directly, skipping this function: No need to examine dst. */
 | 
			
		||||
	if (is_rtp && !mgcp_conn_rtp_is_iuup(conn_src)) {
 | 
			
		||||
		rc = mgcp_patch_pt(conn_src, conn_dst, msg);
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			/* FIXME: It is legal that the payload type on the egress connection is
 | 
			
		||||
			 * different from the payload type that has been negotiated on the
 | 
			
		||||
			 * ingress connection. Essentially the codecs are the same so we can
 | 
			
		||||
			 * match them and patch the payload type. However, if we can not find
 | 
			
		||||
			 * the codec pendant (everything ist equal except the PT), we are of
 | 
			
		||||
			 * course unable to patch the payload type. A situation like this
 | 
			
		||||
			 * should not occur if transcoding is consequently avoided. Until
 | 
			
		||||
			 * we have transcoding support in osmo-mgw we can not resolve this. */
 | 
			
		||||
			LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
			
		||||
				 "can not patch PT because no suitable egress codec was found.\n");
 | 
			
		||||
		if (mgcp_patch_pt(conn_dst, msg) < 0) {
 | 
			
		||||
			LOGPENDP(endp, DRTP, LOGL_NOTICE, "unable to patch payload type RTP packet, discarding...\n");
 | 
			
		||||
			msgb_free(msg);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1205,41 +1186,39 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
 | 
			
		||||
			 osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
 | 
			
		||||
		    );
 | 
			
		||||
	} else if (is_rtp) {
 | 
			
		||||
		int cont;
 | 
			
		||||
		int nbytes = 0;
 | 
			
		||||
		int buflen = msgb_length(msg);
 | 
			
		||||
 | 
			
		||||
		/* Make sure we have a valid RTP header, in cases where no RTP
 | 
			
		||||
		 * header is present, we will generate one. */
 | 
			
		||||
		gen_rtp_header(msg, rtp_end, rtp_state);
 | 
			
		||||
 | 
			
		||||
		do {
 | 
			
		||||
		/* Run transcoder */
 | 
			
		||||
			cont = endp->trunk->cfg->rtp_processing_cb(endp, rtp_end, (char *)msgb_data(msg), &buflen, RTP_BUF_SIZE);
 | 
			
		||||
			if (cont < 0)
 | 
			
		||||
				break;
 | 
			
		||||
		rc = endp->trunk->cfg->rtp_processing_cb(endp, rtp_end, msg);
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			LOGPENDP(endp, DRTP, LOGL_ERROR, "Error %d during transcoding\n", rc);
 | 
			
		||||
			msgb_free(msg);
 | 
			
		||||
			return rc;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (addr)
 | 
			
		||||
				mgcp_patch_and_count(endp, rtp_state, rtp_end,
 | 
			
		||||
						     addr, msg);
 | 
			
		||||
			mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, msg);
 | 
			
		||||
 | 
			
		||||
		if (mgcp_conn_rtp_is_iuup(conn_dst) || mgcp_conn_rtp_is_iuup(conn_src)) {
 | 
			
		||||
			/* the iuup code will correctly transform to the correct AMR mode */
 | 
			
		||||
		} else if (mgcp_codec_amr_align_mode_is_indicated(conn_dst->end.codec)) {
 | 
			
		||||
				rc = amr_oa_bwe_convert(endp, msg,
 | 
			
		||||
							conn_dst->end.codec->param.amr_octet_aligned);
 | 
			
		||||
			rc = amr_oa_bwe_convert(endp, msg, conn_dst->end.codec->param.amr_octet_aligned);
 | 
			
		||||
			if (rc < 0) {
 | 
			
		||||
				LOGPENDP(endp, DRTP, LOGL_ERROR,
 | 
			
		||||
					 "Error in AMR octet-aligned <-> bandwidth-efficient mode conversion (target=%s)\n",
 | 
			
		||||
					 conn_dst->end.codec->param.amr_octet_aligned ? "octet-aligned" : "bandwidth-efficient");
 | 
			
		||||
					break;
 | 
			
		||||
				msgb_free(msg);
 | 
			
		||||
				return rc;
 | 
			
		||||
			}
 | 
			
		||||
		} else if (rtp_end->rfc5993_hr_convert &&
 | 
			
		||||
			   strcmp(conn_src->end.codec->subtype_name, "GSM-HR-08") == 0) {
 | 
			
		||||
			rc = rfc5993_hr_convert(endp, msg);
 | 
			
		||||
			if (rc < 0) {
 | 
			
		||||
				LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
 | 
			
		||||
					break;
 | 
			
		||||
				msgb_free(msg);
 | 
			
		||||
				return rc;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -1252,23 +1231,21 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
		/* Forward a copy of the RTP data to a debug ip/port */
 | 
			
		||||
			forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
 | 
			
		||||
				     msg);
 | 
			
		||||
		forward_data_tap(rtp_end->rtp, &conn_src->tap_out, msg);
 | 
			
		||||
 | 
			
		||||
			len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr,
 | 
			
		||||
					    (char *)msgb_data(msg), msgb_length(msg));
 | 
			
		||||
		len = msgb_length(msg);
 | 
			
		||||
 | 
			
		||||
			if (len <= 0)
 | 
			
		||||
				return len;
 | 
			
		||||
		rc = mgcp_udp_send_msg(rtp_end->rtp, &rtp_end->addr, msg);
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			msgb_free(msg);
 | 
			
		||||
			return rc;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
 | 
			
		||||
		rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
 | 
			
		||||
		rtp_state->alt_rtp_tx_sequence++;
 | 
			
		||||
 | 
			
		||||
			nbytes += len;
 | 
			
		||||
			buflen = cont;
 | 
			
		||||
		} while (buflen > 0);
 | 
			
		||||
		return nbytes;
 | 
			
		||||
		return 0;
 | 
			
		||||
	} else if (!trunk->omit_rtcp) {
 | 
			
		||||
		struct osmo_sockaddr rtcp_addr = rtp_end->addr;
 | 
			
		||||
		osmo_sockaddr_set_port(&rtcp_addr.u.sa, rtp_end->rtcp_port);
 | 
			
		||||
@@ -1279,19 +1256,54 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
 | 
			
		||||
			 osmo_sockaddr_port(&rtcp_addr.u.sa)
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
		len = mgcp_udp_send(rtp_end->rtcp.fd, &rtcp_addr,
 | 
			
		||||
				    (char *)msgb_data(msg), msgb_length(msg));
 | 
			
		||||
		len = msgb_length(msg);
 | 
			
		||||
 | 
			
		||||
		rc = mgcp_udp_send_msg(rtp_end->rtcp, &rtcp_addr, msg);
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			msgb_free(msg);
 | 
			
		||||
			return rc;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
 | 
			
		||||
		rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
 | 
			
		||||
		rtp_state->alt_rtp_tx_sequence++;
 | 
			
		||||
 | 
			
		||||
		return len;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! determine if there's only a single recipient in endp for data received via conn_src.
 | 
			
		||||
 *  The function returns NULL in case there is no recipient, or in case there are multiple recipients.
 | 
			
		||||
 *  \param endp The MGCP endpoint whose connections to analyze
 | 
			
		||||
 *  \param conn_src The source MGCP connection [which shall not count in results]
 | 
			
		||||
 *  \returns recipient donnection if there is only one; NULL in case there are multiple */
 | 
			
		||||
static struct mgcp_conn *rtpbridge_get_only_recipient(struct mgcp_endpoint *endp, struct mgcp_conn *conn_src)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn_ret = NULL;
 | 
			
		||||
	struct mgcp_conn *conn_dst;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(conn_dst, &endp->conns, entry) {
 | 
			
		||||
		if (conn_dst == conn_src)
 | 
			
		||||
			continue;
 | 
			
		||||
		switch (conn_dst->mode) {
 | 
			
		||||
		case MGCP_CONN_SEND_ONLY:
 | 
			
		||||
		case MGCP_CONN_RECV_SEND:
 | 
			
		||||
		case MGCP_CONN_CONFECHO:
 | 
			
		||||
			if (conn_ret)
 | 
			
		||||
				return NULL;
 | 
			
		||||
			conn_ret = conn_dst;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return conn_ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Dispatch incoming RTP packet to opposite RTP connection.
 | 
			
		||||
 * \param[in] msg Message buffer to bridge, coming from source connection.
 | 
			
		||||
 *            msg shall contain "struct osmo_rtp_msg_ctx *" attached in
 | 
			
		||||
@@ -1304,8 +1316,10 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
 | 
			
		||||
	struct mgcp_conn_rtp *conn_src = mc->conn_src;
 | 
			
		||||
	struct mgcp_conn *conn = conn_src->conn;
 | 
			
		||||
	struct mgcp_conn *conn_dst;
 | 
			
		||||
	struct mgcp_endpoint *endp = conn->endp;
 | 
			
		||||
	struct osmo_sockaddr *from_addr = mc->from_addr;
 | 
			
		||||
	char ipbuf[INET6_ADDRSTRLEN];
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	/*! NOTE: This callback function implements the endpoint specific
 | 
			
		||||
	 *  dispatch behaviour of an rtp bridge/proxy endpoint. It is assumed
 | 
			
		||||
@@ -1337,36 +1351,56 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
 | 
			
		||||
		return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Find a destination connection. */
 | 
			
		||||
	/* NOTE: This code path runs every time an RTP packet is received. The
 | 
			
		||||
	 * function mgcp_find_dst_conn() we use to determine the detination
 | 
			
		||||
	 * connection will iterate the connection list inside the endpoint.
 | 
			
		||||
	 * Since list iterations are quite costly, we will figure out the
 | 
			
		||||
	 * destination only once and use the optional private data pointer of
 | 
			
		||||
	 * the connection to cache the destination connection pointer. */
 | 
			
		||||
	if (!conn->priv) {
 | 
			
		||||
		conn_dst = mgcp_find_dst_conn(conn);
 | 
			
		||||
		conn->priv = conn_dst;
 | 
			
		||||
	/* If the mode does not allow receiving RTP, we are done. */
 | 
			
		||||
	switch (conn->mode) {
 | 
			
		||||
	case MGCP_CONN_RECV_ONLY:
 | 
			
		||||
	case MGCP_CONN_RECV_SEND:
 | 
			
		||||
	case MGCP_CONN_CONFECHO:
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* All the use cases above are 1:1 where we have one source msgb and we're sending that to one
 | 
			
		||||
	 * destination.  msgb ownership had been passed to the respective _*dospatch_rtp() function.
 | 
			
		||||
	 * In the cases below, we actually [can] have multiple recipients, so we copy the original msgb
 | 
			
		||||
	 * for each of the recipients. */
 | 
			
		||||
 | 
			
		||||
	/* If the mode is "confecho", send RTP back to the sender. */
 | 
			
		||||
	if (conn->mode == MGCP_CONN_CONFECHO) {
 | 
			
		||||
		struct msgb *msg2 = mgw_msgb_copy_c(conn, msg, "RTP confecho");
 | 
			
		||||
		if (OSMO_LIKELY(msg2))
 | 
			
		||||
			rc = mgcp_conn_rtp_dispatch_rtp(conn_src, msg2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn_dst = rtpbridge_get_only_recipient(endp, conn);
 | 
			
		||||
	if (OSMO_LIKELY(conn_dst)) {
 | 
			
		||||
		/* we only have a single recipient and cann hence send the original msgb without copying */
 | 
			
		||||
		rc = mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
 | 
			
		||||
	} else {
 | 
			
		||||
		conn_dst = (struct mgcp_conn *)conn->priv;
 | 
			
		||||
		/* Dispatch RTP packet to all other connection(s) that send audio. */
 | 
			
		||||
		llist_for_each_entry(conn_dst, &endp->conns, entry) {
 | 
			
		||||
			struct msgb *msg2;
 | 
			
		||||
			if (conn_dst == conn)
 | 
			
		||||
				continue;
 | 
			
		||||
			switch (conn_dst->mode) {
 | 
			
		||||
			case MGCP_CONN_SEND_ONLY:
 | 
			
		||||
			case MGCP_CONN_RECV_SEND:
 | 
			
		||||
			case MGCP_CONN_CONFECHO:
 | 
			
		||||
				/* we have multiple recipients and must make copies for each recipient */
 | 
			
		||||
				msg2 = mgw_msgb_copy_c(conn_dst, msg, "RTP Tx copy");
 | 
			
		||||
				if (OSMO_LIKELY(msg2))
 | 
			
		||||
					rc = mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg2);
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
	/* There is no destination conn, stop here */
 | 
			
		||||
	if (!conn_dst) {
 | 
			
		||||
		LOGPCONN(conn, DRTP, LOGL_DEBUG,
 | 
			
		||||
			 "no connection to forward an incoming RTP packet to\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	/* The destination conn is not an RTP connection */
 | 
			
		||||
	if (conn_dst->type != MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		LOGPCONN(conn, DRTP, LOGL_ERROR,
 | 
			
		||||
			 "unable to find suitable destination conn\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
		/* as we only sent copies in the previous llist_for_each_entry() loop, we must free the
 | 
			
		||||
		 * original one */
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Dispatch RTP packet to destination RTP connection */
 | 
			
		||||
	return mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! dispatch incoming RTP packet to E1 subslot, handle RTCP packets locally.
 | 
			
		||||
@@ -1436,7 +1470,7 @@ void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *con
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle incoming RTP data from NET */
 | 
			
		||||
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
static void rtp_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *saddr)
 | 
			
		||||
{
 | 
			
		||||
	/* NOTE: This is a generic implementation. RTP data is received. In
 | 
			
		||||
	 * case of loopback the data is just sent back to its origin. All
 | 
			
		||||
@@ -1447,49 +1481,34 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
 | 
			
		||||
	struct mgcp_conn_rtp *conn_src;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	struct osmo_sockaddr addr;
 | 
			
		||||
	socklen_t slen = sizeof(addr);
 | 
			
		||||
	char ipbuf[INET6_ADDRSTRLEN];
 | 
			
		||||
	int ret;
 | 
			
		||||
	enum rtp_proto proto;
 | 
			
		||||
	struct osmo_rtp_msg_ctx *mc;
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	conn_src = (struct mgcp_conn_rtp *)fd->data;
 | 
			
		||||
	conn_src = (struct mgcp_conn_rtp *) osmo_iofd_get_data(iofd);
 | 
			
		||||
	OSMO_ASSERT(conn_src);
 | 
			
		||||
	endp = conn_src->conn->endp;
 | 
			
		||||
	OSMO_ASSERT(endp);
 | 
			
		||||
	msg = msgb_alloc_c(endp->trunk, RTP_BUF_SIZE, "RTP-rx");
 | 
			
		||||
 | 
			
		||||
	proto = (fd == &conn_src->end.rtp)? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
 | 
			
		||||
	proto = (iofd == conn_src->end.rtp)? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
 | 
			
		||||
 | 
			
		||||
	ret = recvfrom(fd->fd, msgb_data(msg), msg->data_len, 0, (struct sockaddr *)&addr.u.sa, &slen);
 | 
			
		||||
 | 
			
		||||
	if (ret <= 0) {
 | 
			
		||||
		LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(errno));
 | 
			
		||||
		rc = -1;
 | 
			
		||||
		goto out;
 | 
			
		||||
	if (res <= 0) {
 | 
			
		||||
		LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(-res));
 | 
			
		||||
		goto out_free;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msgb_put(msg, ret);
 | 
			
		||||
 | 
			
		||||
	LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
 | 
			
		||||
	LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s\n",
 | 
			
		||||
		     proto == MGCP_PROTO_RTP ? "RTP" : "RTCP",
 | 
			
		||||
		     msgb_length(msg), osmo_sockaddr_ntop(&addr.u.sa, ipbuf),
 | 
			
		||||
		     osmo_sockaddr_port(&addr.u.sa));
 | 
			
		||||
		     msgb_length(msg), osmo_sockaddr_to_str(saddr));
 | 
			
		||||
 | 
			
		||||
	if ((proto == MGCP_PROTO_RTP && check_rtp(conn_src, msg))
 | 
			
		||||
	    || (proto == MGCP_PROTO_RTCP && check_rtcp(conn_src, msg))) {
 | 
			
		||||
		/* Logging happened in the two check_ functions */
 | 
			
		||||
		rc = -1;
 | 
			
		||||
		goto out;
 | 
			
		||||
		goto out_free;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (mgcp_is_rtp_dummy_payload(msg)) {
 | 
			
		||||
		LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx dummy packet (dropped)\n");
 | 
			
		||||
		rc = 0;
 | 
			
		||||
		goto out;
 | 
			
		||||
		goto out_free;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Since the msgb remains owned and freed by this function, the msg ctx data struct can just be on the stack and
 | 
			
		||||
@@ -1498,7 +1517,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
	*mc = (struct osmo_rtp_msg_ctx){
 | 
			
		||||
		.proto = proto,
 | 
			
		||||
		.conn_src = conn_src,
 | 
			
		||||
		.from_addr = &addr,
 | 
			
		||||
		.from_addr = (struct osmo_sockaddr *) saddr,
 | 
			
		||||
	};
 | 
			
		||||
	LOG_CONN_RTP(conn_src, LOGL_DEBUG, "msg ctx: %d %p %s\n",
 | 
			
		||||
		     mc->proto, mc->conn_src,
 | 
			
		||||
@@ -1513,16 +1532,17 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
	/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
 | 
			
		||||
 | 
			
		||||
	/* Forward a copy of the RTP data to a debug ip/port */
 | 
			
		||||
	forward_data_tap(fd->fd, &conn_src->tap_in, msg);
 | 
			
		||||
	forward_data_tap(iofd, &conn_src->tap_in, msg);
 | 
			
		||||
 | 
			
		||||
	rc = rx_rtp(msg);
 | 
			
		||||
	rx_rtp(msg);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
out_free:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Note: This function is able to handle RTP and RTCP */
 | 
			
		||||
/* Note: This function is able to handle RTP and RTCP. msgb ownership is transferred, so this function or its
 | 
			
		||||
 * downstream consumers must make sure to [eventually] free the msgb. */
 | 
			
		||||
static int rx_rtp(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
 | 
			
		||||
@@ -1535,22 +1555,23 @@ static int rx_rtp(struct msgb *msg)
 | 
			
		||||
 | 
			
		||||
	/* Check if the origin of the RTP packet seems plausible */
 | 
			
		||||
	if (!trunk->rtp_accept_all && check_rtp_origin(conn_src, from_addr))
 | 
			
		||||
		return -1;
 | 
			
		||||
		goto out_free;
 | 
			
		||||
 | 
			
		||||
	/* Handle AMR frame format conversion (octet-aligned vs. bandwith-efficient) */
 | 
			
		||||
	if (mc->proto == MGCP_PROTO_RTP &&
 | 
			
		||||
	    mgcp_codec_amr_align_mode_is_indicated(conn_src->end.codec)) {
 | 
			
		||||
	if (mc->proto == MGCP_PROTO_RTP
 | 
			
		||||
	    && conn_src->end.codec
 | 
			
		||||
	    && mgcp_codec_amr_align_mode_is_indicated(conn_src->end.codec)) {
 | 
			
		||||
		/* Make sure that the incoming AMR frame format matches the frame format that the call agent has
 | 
			
		||||
		 * communicated via SDP when the connection was created/modfied. */
 | 
			
		||||
		int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
 | 
			
		||||
		if (oa < 0)
 | 
			
		||||
			return -1;
 | 
			
		||||
			goto out_free;
 | 
			
		||||
		if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned) {
 | 
			
		||||
			LOG_CONN_RTP(conn_src, LOGL_NOTICE,
 | 
			
		||||
				     "rx_rtp(%u bytes): Expected RTP AMR octet-aligned=%u but got octet-aligned=%u."
 | 
			
		||||
				     " check the config of your call-agent!\n",
 | 
			
		||||
				     msgb_length(msg), conn_src->end.codec->param.amr_octet_aligned, oa);
 | 
			
		||||
			return -1;
 | 
			
		||||
			goto out_free;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1559,17 +1580,36 @@ static int rx_rtp(struct msgb *msg)
 | 
			
		||||
	/* Execute endpoint specific implementation that handles the
 | 
			
		||||
	 * dispatching of the RTP data */
 | 
			
		||||
	return conn->endp->type->dispatch_rtp_cb(msg);
 | 
			
		||||
out_free:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void rtp_sendto_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *daddr)
 | 
			
		||||
{
 | 
			
		||||
	/* nothing; osmo_io takes care of msgb_free */
 | 
			
		||||
	if (res < 0) {
 | 
			
		||||
		struct mgcp_conn_rtp *conn_rtp = (struct mgcp_conn_rtp *) osmo_iofd_get_data(iofd);
 | 
			
		||||
		int priv_nr = osmo_iofd_get_priv_nr(iofd);
 | 
			
		||||
		char errbuf[129];
 | 
			
		||||
		strerror_r(-res, errbuf, sizeof(errbuf));
 | 
			
		||||
		LOG_CONN_RTP(conn_rtp, LOGL_NOTICE, "%s sendto(%s) failed: %s\n", priv_nr ? "RTCP" : "RTP",
 | 
			
		||||
			 osmo_sockaddr_to_str(daddr), errbuf);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct osmo_io_ops rtp_ioops = {
 | 
			
		||||
	.recvfrom_cb = rtp_recvfrom_cb,
 | 
			
		||||
	.sendto_cb = rtp_sendto_cb,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! bind RTP port to osmo_fd.
 | 
			
		||||
 *  \param[in] source_addr source (local) address to bind on.
 | 
			
		||||
 *  \param[in] fd associated file descriptor.
 | 
			
		||||
 *  \param[in] port to bind on.
 | 
			
		||||
 *  \param[in] dscp IP DSCP value to use.
 | 
			
		||||
 *  \param[in] prio socket priority to use.
 | 
			
		||||
 *  \returns 0 on success, -1 on ERROR. */
 | 
			
		||||
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint8_t dscp,
 | 
			
		||||
		     uint8_t prio)
 | 
			
		||||
 *  \returns file descriptor on success, -1 on ERROR. */
 | 
			
		||||
int mgcp_create_bind(const char *source_addr, int port, uint8_t dscp, uint8_t prio)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
@@ -1581,43 +1621,46 @@ int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint
 | 
			
		||||
		     source_addr, port);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	fd->fd = rc;
 | 
			
		||||
	LOGP(DRTP, LOGL_DEBUG, "created socket + bound UDP port (%s:%i).\n", source_addr, port);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Bind RTP and RTCP port (helper function for mgcp_bind_net_rtp_port()) */
 | 
			
		||||
static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
 | 
			
		||||
		    struct mgcp_rtp_end *rtp_end, struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	int rc, rtp_fd, rtcp_fd;
 | 
			
		||||
 | 
			
		||||
	/* NOTE: The port that is used for RTCP is the RTP port incremented by one
 | 
			
		||||
	 * (e.g. RTP-Port = 16000 ==> RTCP-Port = 16001) */
 | 
			
		||||
 | 
			
		||||
	if (mgcp_create_bind(source_addr, &rtp_end->rtp, rtp_end->local_port,
 | 
			
		||||
			     cfg->endp_dscp, cfg->endp_priority) != 0) {
 | 
			
		||||
	rc = mgcp_create_bind(source_addr, rtp_end->local_port, cfg->endp_dscp, cfg->endp_priority);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGPENDP(endp, DRTP, LOGL_ERROR,
 | 
			
		||||
			 "failed to create RTP port: %s:%d\n",
 | 
			
		||||
			 source_addr, rtp_end->local_port);
 | 
			
		||||
		goto cleanup0;
 | 
			
		||||
	}
 | 
			
		||||
	rtp_fd = rc;
 | 
			
		||||
 | 
			
		||||
	if (mgcp_create_bind(source_addr, &rtp_end->rtcp, rtp_end->local_port + 1,
 | 
			
		||||
			     cfg->endp_dscp, cfg->endp_priority) != 0) {
 | 
			
		||||
	rc = mgcp_create_bind(source_addr, rtp_end->local_port + 1, cfg->endp_dscp, cfg->endp_priority);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGPENDP(endp, DRTP, LOGL_ERROR,
 | 
			
		||||
			 "failed to create RTCP port: %s:%d\n",
 | 
			
		||||
			 source_addr, rtp_end->local_port + 1);
 | 
			
		||||
		goto cleanup1;
 | 
			
		||||
	}
 | 
			
		||||
	rtcp_fd = rc;
 | 
			
		||||
 | 
			
		||||
	if (osmo_fd_register(&rtp_end->rtp) != 0) {
 | 
			
		||||
	if (osmo_iofd_register(rtp_end->rtp, rtp_fd) < 0) {
 | 
			
		||||
		LOGPENDP(endp, DRTP, LOGL_ERROR,
 | 
			
		||||
			 "failed to register RTP port %d\n",
 | 
			
		||||
			 rtp_end->local_port);
 | 
			
		||||
		goto cleanup2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (osmo_fd_register(&rtp_end->rtcp) != 0) {
 | 
			
		||||
	if (osmo_iofd_register(rtp_end->rtcp, rtcp_fd) != 0) {
 | 
			
		||||
		LOGPENDP(endp, DRTP, LOGL_ERROR,
 | 
			
		||||
			 "failed to register RTCP port %d\n",
 | 
			
		||||
			 rtp_end->local_port + 1);
 | 
			
		||||
@@ -1627,13 +1670,11 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
cleanup3:
 | 
			
		||||
	osmo_fd_unregister(&rtp_end->rtp);
 | 
			
		||||
	osmo_iofd_unregister(rtp_end->rtp);
 | 
			
		||||
cleanup2:
 | 
			
		||||
	close(rtp_end->rtcp.fd);
 | 
			
		||||
	rtp_end->rtcp.fd = -1;
 | 
			
		||||
	close(rtcp_fd);
 | 
			
		||||
cleanup1:
 | 
			
		||||
	close(rtp_end->rtp.fd);
 | 
			
		||||
	rtp_end->rtp.fd = -1;
 | 
			
		||||
	close(rtp_fd);
 | 
			
		||||
cleanup0:
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
@@ -1652,7 +1693,8 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
 | 
			
		||||
	snprintf(name, sizeof(name), "%s-%s", conn->conn->name, conn->conn->id);
 | 
			
		||||
	end = &conn->end;
 | 
			
		||||
 | 
			
		||||
	if (end->rtp.fd != -1 || end->rtcp.fd != -1) {
 | 
			
		||||
	if ((end->rtp && osmo_iofd_get_fd(end->rtp) != -1) ||
 | 
			
		||||
	    (end->rtcp && osmo_iofd_get_fd(end->rtcp) != -1)) {
 | 
			
		||||
		LOGPENDP(endp, DRTP, LOGL_ERROR, "%u was already bound on conn:%s\n",
 | 
			
		||||
			 rtp_port, mgcp_conn_dump(conn->conn));
 | 
			
		||||
 | 
			
		||||
@@ -1665,8 +1707,18 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	end->local_port = rtp_port;
 | 
			
		||||
	osmo_fd_setup(&end->rtp, -1, OSMO_FD_READ, rtp_data_net, conn, 0);
 | 
			
		||||
	osmo_fd_setup(&end->rtcp, -1, OSMO_FD_READ, rtp_data_net, conn, 0);
 | 
			
		||||
	end->rtp = osmo_iofd_setup(conn->conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn);
 | 
			
		||||
	if (!end->rtp)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	osmo_iofd_set_alloc_info(end->rtp, RTP_BUF_SIZE, 0);
 | 
			
		||||
	end->rtcp = osmo_iofd_setup(conn->conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn);
 | 
			
		||||
	if (!end->rtcp) {
 | 
			
		||||
		osmo_iofd_free(end->rtp);
 | 
			
		||||
		end->rtp = NULL;
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
	osmo_iofd_set_alloc_info(end->rtcp, RTP_BUF_SIZE, 0);
 | 
			
		||||
	osmo_iofd_set_priv_nr(end->rtcp, 1); /* we use priv_nr as identifier for RTCP */
 | 
			
		||||
 | 
			
		||||
	return bind_rtp(endp->trunk->cfg, conn->end.local_addr, end, endp);
 | 
			
		||||
}
 | 
			
		||||
@@ -1675,15 +1727,13 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
 | 
			
		||||
 *  \param[in] end RTP end */
 | 
			
		||||
void mgcp_free_rtp_port(struct mgcp_rtp_end *end)
 | 
			
		||||
{
 | 
			
		||||
	if (end->rtp.fd != -1) {
 | 
			
		||||
		close(end->rtp.fd);
 | 
			
		||||
		end->rtp.fd = -1;
 | 
			
		||||
		osmo_fd_unregister(&end->rtp);
 | 
			
		||||
	if (end->rtp) {
 | 
			
		||||
		osmo_iofd_free(end->rtp);
 | 
			
		||||
		end->rtp = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (end->rtcp.fd != -1) {
 | 
			
		||||
		close(end->rtcp.fd);
 | 
			
		||||
		end->rtcp.fd = -1;
 | 
			
		||||
		osmo_fd_unregister(&end->rtcp);
 | 
			
		||||
	if (end->rtcp) {
 | 
			
		||||
		osmo_iofd_free(end->rtcp);
 | 
			
		||||
		end->rtcp = NULL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,9 +13,11 @@
 | 
			
		||||
#include <string.h> /* for memcpy */
 | 
			
		||||
#include <stdlib.h> /* for abs */
 | 
			
		||||
#include <inttypes.h> /* for PRIu64 */
 | 
			
		||||
#include <unistd.h> /* for PRIu64 */
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/osmo_io.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/netif/osmux.h>
 | 
			
		||||
@@ -30,8 +32,8 @@
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_trunk.h>
 | 
			
		||||
 | 
			
		||||
static struct osmo_fd osmux_fd_v4;
 | 
			
		||||
static struct osmo_fd osmux_fd_v6;
 | 
			
		||||
static struct osmo_io_fd *osmux_fd_v4;
 | 
			
		||||
static struct osmo_io_fd *osmux_fd_v6;
 | 
			
		||||
 | 
			
		||||
static LLIST_HEAD(osmux_handle_list);
 | 
			
		||||
 | 
			
		||||
@@ -76,34 +78,31 @@ static void rtpconn_osmux_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, int id)
 | 
			
		||||
static void osmux_deliver_cb(struct msgb *batch_msg, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct osmux_handle *handle = data;
 | 
			
		||||
	socklen_t dest_len;
 | 
			
		||||
	int rc, fd;
 | 
			
		||||
	struct mgcp_trunk *trunk = (struct mgcp_trunk *)osmux_fd_v4.data;
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct osmo_io_fd *iofd;
 | 
			
		||||
	struct mgcp_trunk *trunk = (struct mgcp_trunk *) osmo_iofd_get_data(osmux_fd_v4);
 | 
			
		||||
	struct rate_ctr_group *all_osmux_stats = trunk->ratectr.all_osmux_conn_stats;
 | 
			
		||||
 | 
			
		||||
	switch (handle->rem_addr.u.sa.sa_family) {
 | 
			
		||||
	case AF_INET6:
 | 
			
		||||
		dest_len = sizeof(handle->rem_addr.u.sin6);
 | 
			
		||||
		fd = osmux_fd_v6.fd;
 | 
			
		||||
		iofd = osmux_fd_v6;
 | 
			
		||||
		break;
 | 
			
		||||
	case AF_INET:
 | 
			
		||||
	default:
 | 
			
		||||
		dest_len = sizeof(handle->rem_addr.u.sin);
 | 
			
		||||
		fd = osmux_fd_v4.fd;
 | 
			
		||||
		iofd = osmux_fd_v4;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	rc = sendto(fd, batch_msg->data, batch_msg->len, 0,
 | 
			
		||||
		    (struct sockaddr *)&handle->rem_addr.u.sa, dest_len);
 | 
			
		||||
	rc = osmo_iofd_sendto_msgb(iofd, batch_msg, 0, &handle->rem_addr);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		char errbuf[129];
 | 
			
		||||
		strerror_r(errno, errbuf, sizeof(errbuf));
 | 
			
		||||
		strerror_r(-rc, errbuf, sizeof(errbuf));
 | 
			
		||||
		LOGP(DOSMUX, LOGL_NOTICE, "osmux sendto(%s) failed: %s\n",
 | 
			
		||||
			 osmo_sockaddr_to_str(&handle->rem_addr), errbuf);
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_DROPPED_PACKETS_CTR));
 | 
			
		||||
		msgb_free(batch_msg);
 | 
			
		||||
	} else {
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_PACKETS_TX_CTR));
 | 
			
		||||
	}
 | 
			
		||||
	msgb_free(batch_msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Lookup existing OSMUX handle for specified destination address. */
 | 
			
		||||
@@ -204,17 +203,17 @@ osmux_handle_find_or_create(const struct mgcp_trunk *trunk, const struct osmo_so
 | 
			
		||||
	return h->in;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! send RTP packet through OSMUX connection.
 | 
			
		||||
/*! send RTP packet through OSMUX connection. Takes ownership of msg.
 | 
			
		||||
 *  \param[in] conn associated RTP connection
 | 
			
		||||
 *  \param[in] msg msgb containing an RTP AMR packet
 | 
			
		||||
 *  \returns 0 on success, -1 on ERROR */
 | 
			
		||||
int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	struct msgb *msg2;
 | 
			
		||||
 | 
			
		||||
	if (!conn->end.output_enabled) {
 | 
			
		||||
		rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -222,22 +221,19 @@ int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
 | 
			
		||||
		LOGPCONN(conn->conn, DOSMUX, LOGL_INFO, "forwarding RTP to Osmux conn not yet enabled, dropping (cid=%d)\n",
 | 
			
		||||
		conn->osmux.remote_cid);
 | 
			
		||||
		rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* msg is not owned by us and will be freed by the caller stack upon return: */
 | 
			
		||||
	msg2 = msgb_copy_c(conn->conn, msg, "osmux-rtp-send");
 | 
			
		||||
	if (!msg2)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	/* Osmux implementation works with AMR OA only, make sure we convert to it if needed: */
 | 
			
		||||
	if (amr_oa_bwe_convert(conn->conn->endp, msg2, true) < 0) {
 | 
			
		||||
	if (amr_oa_bwe_convert(conn->conn->endp, msg, true) < 0) {
 | 
			
		||||
		LOGPCONN(conn->conn, DOSMUX, LOGL_ERROR,
 | 
			
		||||
			 "Error converting to AMR octet-aligned mode\n");
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while ((ret = osmux_xfrm_input(conn->osmux.in, msg2, conn->osmux.remote_cid)) > 0) {
 | 
			
		||||
	while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.remote_cid)) > 0) {
 | 
			
		||||
		/* batch full, build and deliver it */
 | 
			
		||||
		osmux_xfrm_input_deliver(conn->osmux.in);
 | 
			
		||||
	}
 | 
			
		||||
@@ -245,7 +241,7 @@ int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
 | 
			
		||||
		rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
 | 
			
		||||
	} else {
 | 
			
		||||
		rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_CTR);
 | 
			
		||||
		rtpconn_osmux_rate_ctr_add(conn, OSMUX_AMR_OCTETS_TX_CTR, msgb_length(msg2) - sizeof(struct rtp_hdr));
 | 
			
		||||
		rtpconn_osmux_rate_ctr_add(conn, OSMUX_AMR_OCTETS_TX_CTR, msgb_length(msg) - sizeof(struct rtp_hdr));
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -325,29 +321,7 @@ static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data)
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	endp->type->dispatch_rtp_cb(msg);
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct msgb *osmux_recv(struct osmo_fd *ofd, struct osmo_sockaddr *addr)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	socklen_t slen = sizeof(addr->u.sas);
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	msg = msgb_alloc(4096, "OSMUX");
 | 
			
		||||
	if (!msg) {
 | 
			
		||||
		LOGP(DOSMUX, LOGL_ERROR, "cannot allocate message\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0, &addr->u.sa, &slen);
 | 
			
		||||
	if (ret <= 0) {
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		LOGP(DOSMUX, LOGL_ERROR, "cannot receive message\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	msgb_put(msg, ret);
 | 
			
		||||
 | 
			
		||||
	return msg;
 | 
			
		||||
	/* dispatch_rtp_cb() has taken ownership of the msgb */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* To be called every time some AMR data is received on a connection
 | 
			
		||||
@@ -445,22 +419,16 @@ out:
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define osmux_chunk_length(msg, rem) ((rem) - (msg)->len)
 | 
			
		||||
static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
static void osmux_recvfrom_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *rem_addr)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	struct osmux_hdr *osmuxh;
 | 
			
		||||
	struct osmo_sockaddr rem_addr;
 | 
			
		||||
	uint32_t rem;
 | 
			
		||||
	struct mgcp_trunk *trunk = ofd->data;
 | 
			
		||||
	struct mgcp_trunk *trunk = osmo_iofd_get_data(iofd);
 | 
			
		||||
	struct rate_ctr_group *all_rtp_stats = trunk->ratectr.all_osmux_conn_stats;
 | 
			
		||||
	uint32_t rem;
 | 
			
		||||
	char addr_str[64];
 | 
			
		||||
 | 
			
		||||
	msg = osmux_recv(ofd, &rem_addr);
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(rate_ctr_group_get_ctr(all_rtp_stats, OSMUX_PACKETS_RX_CTR));
 | 
			
		||||
	osmo_sockaddr_to_str_buf(addr_str, sizeof(addr_str), &rem_addr);
 | 
			
		||||
	osmo_sockaddr_to_str_buf(addr_str, sizeof(addr_str), rem_addr);
 | 
			
		||||
 | 
			
		||||
	if (trunk->cfg->osmux.usage == OSMUX_USAGE_OFF) {
 | 
			
		||||
		LOGP(DOSMUX, LOGL_ERROR,
 | 
			
		||||
@@ -470,14 +438,16 @@ static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Catch legacy dummy message and process them separately: */
 | 
			
		||||
	if (msg->len == 2 && msg->data[0] == MGCP_DUMMY_LOAD)
 | 
			
		||||
		return osmux_handle_legacy_dummy(trunk, &rem_addr, msg);
 | 
			
		||||
	if (msg->len == 2 && msg->data[0] == MGCP_DUMMY_LOAD) {
 | 
			
		||||
		osmux_handle_legacy_dummy(trunk, rem_addr, msg);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rem = msg->len;
 | 
			
		||||
	while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
 | 
			
		||||
		struct mgcp_conn_rtp *conn_src;
 | 
			
		||||
		conn_src = osmux_conn_lookup(trunk, osmuxh->circuit_id,
 | 
			
		||||
					     &rem_addr);
 | 
			
		||||
					     rem_addr);
 | 
			
		||||
		if (!conn_src) {
 | 
			
		||||
			LOGP(DOSMUX, LOGL_DEBUG,
 | 
			
		||||
			     "Cannot find a src conn for %s CID=%d\n",
 | 
			
		||||
@@ -485,7 +455,7 @@ static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
			goto next;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (conn_osmux_event_data_received(conn_src, &rem_addr) < 0)
 | 
			
		||||
		if (conn_osmux_event_data_received(conn_src, rem_addr) < 0)
 | 
			
		||||
			goto next;
 | 
			
		||||
 | 
			
		||||
		mgcp_conn_watchdog_kick(conn_src->conn);
 | 
			
		||||
@@ -499,58 +469,94 @@ next:
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void osmux_sendto_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg, const struct osmo_sockaddr *rem_addr)
 | 
			
		||||
{
 | 
			
		||||
	/* nothing; osmo_io takes care of msgb_free */
 | 
			
		||||
	if (res < 0) {
 | 
			
		||||
		struct mgcp_trunk *trunk = (struct mgcp_trunk *) osmo_iofd_get_data(iofd);
 | 
			
		||||
		struct rate_ctr_group *all_osmux_stats = trunk->ratectr.all_osmux_conn_stats;
 | 
			
		||||
		char errbuf[129];
 | 
			
		||||
		strerror_r(-res, errbuf, sizeof(errbuf));
 | 
			
		||||
		LOGP(DOSMUX, LOGL_NOTICE, "osmux sendto(%s) failed: %s\n", osmo_sockaddr_to_str(rem_addr), errbuf);
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(all_osmux_stats, OSMUX_DROPPED_PACKETS_CTR));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct osmo_io_ops osmux_ioops = {
 | 
			
		||||
	.recvfrom_cb = osmux_recvfrom_cb,
 | 
			
		||||
	.sendto_cb = osmux_sendto_cb,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int osmux_init(struct mgcp_trunk *trunk)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	int ret, fd;
 | 
			
		||||
	struct mgcp_config *cfg = trunk->cfg;
 | 
			
		||||
 | 
			
		||||
	/* So far we only support running on one trunk: */
 | 
			
		||||
	OSMO_ASSERT(trunk == mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID));
 | 
			
		||||
 | 
			
		||||
	osmo_fd_setup(&osmux_fd_v4, -1, OSMO_FD_READ, osmux_read_fd_cb, trunk, 0);
 | 
			
		||||
	osmo_fd_setup(&osmux_fd_v6, -1, OSMO_FD_READ, osmux_read_fd_cb, trunk, 0);
 | 
			
		||||
	osmux_fd_v4 = osmo_iofd_setup(trunk, -1, "osmux_fd_v4", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &osmux_ioops, trunk);
 | 
			
		||||
	if (!osmux_fd_v4)
 | 
			
		||||
		goto out;
 | 
			
		||||
	osmo_iofd_set_alloc_info(osmux_fd_v4, 4096, 0);
 | 
			
		||||
 | 
			
		||||
	if (cfg->osmux.local_addr_v4) {
 | 
			
		||||
		ret = mgcp_create_bind(cfg->osmux.local_addr_v4, &osmux_fd_v4, cfg->osmux.local_port,
 | 
			
		||||
		ret = mgcp_create_bind(cfg->osmux.local_addr_v4, cfg->osmux.local_port,
 | 
			
		||||
					cfg->endp_dscp, cfg->endp_priority);
 | 
			
		||||
		if (ret < 0) {
 | 
			
		||||
			LOGP(DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv4 socket to %s:%u\n",
 | 
			
		||||
			     cfg->osmux.local_addr_v4, cfg->osmux.local_port);
 | 
			
		||||
			return ret;
 | 
			
		||||
			goto out_free_v4;
 | 
			
		||||
		}
 | 
			
		||||
		fd = ret;
 | 
			
		||||
 | 
			
		||||
		ret = osmo_iofd_register(osmux_fd_v4, fd);
 | 
			
		||||
		if (ret < 0) {
 | 
			
		||||
			LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv4 socket %s\n", osmo_sock_get_name2(fd));
 | 
			
		||||
			close(fd);
 | 
			
		||||
			goto out_free_v4;
 | 
			
		||||
		}
 | 
			
		||||
		LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv4 socket listening on %s\n", osmo_sock_get_name2(fd));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		ret = osmo_fd_register(&osmux_fd_v4);
 | 
			
		||||
		if (ret < 0) {
 | 
			
		||||
			LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv4 socket %s\n",
 | 
			
		||||
			     osmo_sock_get_name2(osmux_fd_v4.fd));
 | 
			
		||||
			return ret;
 | 
			
		||||
		}
 | 
			
		||||
		LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv4 socket listening on %s\n",
 | 
			
		||||
		     osmo_sock_get_name2(osmux_fd_v4.fd));
 | 
			
		||||
	}
 | 
			
		||||
	osmux_fd_v6 = osmo_iofd_setup(trunk, -1, "osmux_fd_v6", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &osmux_ioops, trunk);
 | 
			
		||||
	if (!osmux_fd_v6)
 | 
			
		||||
		goto out_free_v4;
 | 
			
		||||
	osmo_iofd_set_alloc_info(osmux_fd_v6, 4096, 0);
 | 
			
		||||
 | 
			
		||||
	if (cfg->osmux.local_addr_v6) {
 | 
			
		||||
		ret = mgcp_create_bind(cfg->osmux.local_addr_v6, &osmux_fd_v6, cfg->osmux.local_port,
 | 
			
		||||
		ret = mgcp_create_bind(cfg->osmux.local_addr_v6, cfg->osmux.local_port,
 | 
			
		||||
					cfg->endp_dscp, cfg->endp_priority);
 | 
			
		||||
		if (ret < 0) {
 | 
			
		||||
			LOGP(DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv6 socket to [%s]:%u\n",
 | 
			
		||||
			     cfg->osmux.local_addr_v6, cfg->osmux.local_port);
 | 
			
		||||
			return ret;
 | 
			
		||||
			goto out_free_v6;
 | 
			
		||||
		}
 | 
			
		||||
		fd = ret;
 | 
			
		||||
 | 
			
		||||
		ret = osmo_fd_register(&osmux_fd_v6);
 | 
			
		||||
		ret = osmo_iofd_register(osmux_fd_v6, fd);
 | 
			
		||||
		if (ret < 0) {
 | 
			
		||||
			LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv6 socket %s\n",
 | 
			
		||||
			     osmo_sock_get_name2(osmux_fd_v6.fd));
 | 
			
		||||
			return ret;
 | 
			
		||||
			LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv6 socket %s\n", osmo_sock_get_name2(fd));
 | 
			
		||||
			close(fd);
 | 
			
		||||
			goto out_free_v6;
 | 
			
		||||
		}
 | 
			
		||||
		LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv6 socket listening on %s\n",
 | 
			
		||||
		     osmo_sock_get_name2(osmux_fd_v6.fd));
 | 
			
		||||
		LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv6 socket listening on %s\n", osmo_sock_get_name2(fd));
 | 
			
		||||
	}
 | 
			
		||||
	cfg->osmux.initialized = true;
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
out_free_v6:
 | 
			
		||||
	/* osmo_iofd_free performs unregister + close */
 | 
			
		||||
	osmo_iofd_free(osmux_fd_v6);
 | 
			
		||||
	osmux_fd_v6 = NULL;
 | 
			
		||||
out_free_v4:
 | 
			
		||||
	/* osmo_iofd_free performs unregister + close */
 | 
			
		||||
	osmo_iofd_free(osmux_fd_v4);
 | 
			
		||||
	osmux_fd_v4 = NULL;
 | 
			
		||||
out:
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! relase OSXMUX cid, that had been allocated to this connection.
 | 
			
		||||
@@ -716,7 +722,7 @@ int osmux_send_dummy(struct mgcp_conn_rtp *conn)
 | 
			
		||||
		 osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf),
 | 
			
		||||
		 osmo_sockaddr_port(&conn->end.addr.u.sa), conn->osmux.remote_cid);
 | 
			
		||||
 | 
			
		||||
	return mgcp_udp_send(osmux_fd_v4.fd, &conn->end.addr, (char *)osmuxh, buf_len);
 | 
			
		||||
	return mgcp_udp_send(osmux_fd_v4, &conn->end.addr, (char *)osmuxh, buf_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Keeps track of locally allocated Osmux circuit ID. +7 to round up to 8 bit boundary. */
 | 
			
		||||
 
 | 
			
		||||
@@ -86,6 +86,9 @@ struct mgcp_request_data {
 | 
			
		||||
	/* set to true when the request has been classified as wildcarded */
 | 
			
		||||
	bool wildcarded;
 | 
			
		||||
 | 
			
		||||
	/* Set to true when the request is targeted at the "null" endpoint */
 | 
			
		||||
	bool null_endp;
 | 
			
		||||
 | 
			
		||||
	/* contains cause code in case of problems during endp/trunk resolution */
 | 
			
		||||
	int mgcp_cause;
 | 
			
		||||
};
 | 
			
		||||
@@ -390,6 +393,9 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
 | 
			
		||||
	/* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
 | 
			
		||||
	rq.pdata = &pdata;
 | 
			
		||||
	rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);
 | 
			
		||||
	if (!rq.wildcarded)
 | 
			
		||||
		rq.null_endp = mgcp_endp_is_null(pdata.epname);
 | 
			
		||||
	if (!rq.null_endp)
 | 
			
		||||
		rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
 | 
			
		||||
	rq.mgcp_cause = rc;
 | 
			
		||||
	if (!rq.endp) {
 | 
			
		||||
@@ -407,14 +413,14 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
 | 
			
		||||
				     rq.name, pdata.epname);
 | 
			
		||||
				return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
		} else if (!rq.null_endp) {
 | 
			
		||||
			/* If the endpoint name suggests that the request refers to a specific endpoint, then the
 | 
			
		||||
			 * request cannot be handled and we must stop early. */
 | 
			
		||||
			LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
			     "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
 | 
			
		||||
			     pdata.epname, -rq.mgcp_cause);
 | 
			
		||||
			return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
 | 
			
		||||
		}
 | 
			
		||||
		} /* else: Handle special "null" endpoint below (with rq.endp=NULL, rq.trunk=NULL) */
 | 
			
		||||
	} else {
 | 
			
		||||
		osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));
 | 
			
		||||
		rq.trunk = rq.endp->trunk;
 | 
			
		||||
@@ -460,6 +466,11 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
 | 
			
		||||
static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
 | 
			
		||||
{
 | 
			
		||||
	LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
 | 
			
		||||
 | 
			
		||||
	/* Auditing "null" endpoint is allowed for keepalive purposes. There's no rq->endp nor rq->trunk in this case. */
 | 
			
		||||
	if (rq->null_endp)
 | 
			
		||||
		return create_ok_response(rq->pdata->cfg, NULL, 200, "AUEP", rq->pdata->trans);
 | 
			
		||||
 | 
			
		||||
	if (!rq->endp || !mgcp_endp_avail(rq->endp)) {
 | 
			
		||||
		LOGPENDP(rq->endp, DLMGCP, LOGL_ERROR, "AUEP: selected endpoint not available!\n");
 | 
			
		||||
		return create_err_response(rq->trunk, NULL, 501, "AUEP", rq->pdata->trans);
 | 
			
		||||
@@ -661,8 +672,8 @@ static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
 | 
			
		||||
		case 'a':
 | 
			
		||||
			/* FIXME: LCO also supports the negotiation of more than one codec.
 | 
			
		||||
			 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
 | 
			
		||||
			 * codec only. */
 | 
			
		||||
			if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
 | 
			
		||||
			 * codec only. Ignoring all but the first codec. */
 | 
			
		||||
			if (sscanf(lco_id + 1, ":%16[^,;]", codec) == 1) {
 | 
			
		||||
				talloc_free(lco->codec);
 | 
			
		||||
				/* MGCP header is case insensive, and we'll need
 | 
			
		||||
				   codec in uppercase when using it later: */
 | 
			
		||||
@@ -761,6 +772,9 @@ static int handle_codec_info(struct mgcp_conn_rtp *conn,
 | 
			
		||||
			     struct mgcp_request_data *rq, int have_sdp, bool crcx)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp = rq->endp;
 | 
			
		||||
	struct mgcp_conn *conn_dst;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_dst_rtp;
 | 
			
		||||
 | 
			
		||||
	int rc;
 | 
			
		||||
	char *cmd;
 | 
			
		||||
 | 
			
		||||
@@ -802,8 +816,15 @@ static int handle_codec_info(struct mgcp_conn_rtp *conn,
 | 
			
		||||
			goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Try to find an destination RTP connection that we can include in the codec decision. */
 | 
			
		||||
	conn_dst = mgcp_find_dst_conn(conn->conn);
 | 
			
		||||
	if (conn_dst && conn_dst->type == MGCP_CONN_TYPE_RTP)
 | 
			
		||||
		conn_dst_rtp = &conn_dst->u.rtp;
 | 
			
		||||
	else
 | 
			
		||||
		conn_dst_rtp = NULL;
 | 
			
		||||
 | 
			
		||||
	/* Make codec decision */
 | 
			
		||||
	if (mgcp_codec_decide(conn) != 0)
 | 
			
		||||
	if (mgcp_codec_decide(conn, conn_dst_rtp) != 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -845,7 +866,7 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
 | 
			
		||||
	struct mgcp_parse_data *pdata = rq->pdata;
 | 
			
		||||
	struct mgcp_trunk *trunk = rq->trunk;
 | 
			
		||||
	struct mgcp_endpoint *endp = rq->endp;
 | 
			
		||||
	struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
 | 
			
		||||
	struct rate_ctr_group *rate_ctrs;
 | 
			
		||||
	int error_code = 400;
 | 
			
		||||
	const char *local_options = NULL;
 | 
			
		||||
	const char *callid = NULL;
 | 
			
		||||
@@ -859,6 +880,14 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
 | 
			
		||||
 | 
			
		||||
	if (rq->null_endp) {
 | 
			
		||||
		/* trunk not available so rate_ctr aren't available either. */
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "CRCX: Not allowed in 'null' endpoint!\n");
 | 
			
		||||
		return create_err_response(pdata->cfg, NULL, 502, "CRCX", pdata->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
 | 
			
		||||
 | 
			
		||||
	/* we must have a free ep */
 | 
			
		||||
	if (!endp) {
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
 | 
			
		||||
@@ -938,7 +967,7 @@ mgcp_header_done:
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if we are able to accept the creation of another connection */
 | 
			
		||||
	if (llist_count(&endp->conns) >= endp->type->max_conns) {
 | 
			
		||||
	if (endp->type->max_conns > 0 && llist_count(&endp->conns) >= endp->type->max_conns) {
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			"CRCX: endpoint full, max. %i connections allowed!\n",
 | 
			
		||||
			endp->type->max_conns);
 | 
			
		||||
@@ -1047,9 +1076,6 @@ mgcp_header_done:
 | 
			
		||||
		rc = mgcp_conn_iuup_init(conn);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
 | 
			
		||||
					     trunk->audio_fmtp_extra);
 | 
			
		||||
 | 
			
		||||
	if (pdata->cfg->force_ptime) {
 | 
			
		||||
		conn->end.packet_duration_ms = pdata->cfg->force_ptime;
 | 
			
		||||
		conn->end.force_output_ptime = 1;
 | 
			
		||||
@@ -1124,7 +1150,7 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
 | 
			
		||||
	struct mgcp_parse_data *pdata = rq->pdata;
 | 
			
		||||
	struct mgcp_trunk *trunk = rq->trunk;
 | 
			
		||||
	struct mgcp_endpoint *endp = rq->endp;
 | 
			
		||||
	struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
 | 
			
		||||
	struct rate_ctr_group *rate_ctrs;
 | 
			
		||||
	char new_local_addr[INET6_ADDRSTRLEN];
 | 
			
		||||
	int error_code = 500;
 | 
			
		||||
	int silent = 0;
 | 
			
		||||
@@ -1139,6 +1165,14 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
 | 
			
		||||
 | 
			
		||||
	if (rq->null_endp) {
 | 
			
		||||
		/* trunk not available so rate_ctr aren't available either. */
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "MDCX: Not allowed in 'null' endpoint!\n");
 | 
			
		||||
		return create_err_response(pdata->cfg, NULL, 502, "MDCX", pdata->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
 | 
			
		||||
 | 
			
		||||
	/* Prohibit wildcarded requests */
 | 
			
		||||
	if (rq->wildcarded) {
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
@@ -1256,6 +1290,10 @@ mgcp_header_done:
 | 
			
		||||
		error_code = rc;
 | 
			
		||||
		goto error3;
 | 
			
		||||
	}
 | 
			
		||||
	/* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */
 | 
			
		||||
	/* TODO: "codec" probably needs to be moved from endp to conn */
 | 
			
		||||
	if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0)
 | 
			
		||||
		rc = mgcp_conn_iuup_init(conn);
 | 
			
		||||
 | 
			
		||||
	/* check connection mode setting */
 | 
			
		||||
	if (conn->conn->mode != MGCP_CONN_LOOPBACK
 | 
			
		||||
@@ -1350,7 +1388,7 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
 | 
			
		||||
	struct mgcp_parse_data *pdata = rq->pdata;
 | 
			
		||||
	struct mgcp_trunk *trunk = rq->trunk;
 | 
			
		||||
	struct mgcp_endpoint *endp = rq->endp;
 | 
			
		||||
	struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
 | 
			
		||||
	struct rate_ctr_group *rate_ctrs;
 | 
			
		||||
	int error_code = 400;
 | 
			
		||||
	int silent = 0;
 | 
			
		||||
	char *line;
 | 
			
		||||
@@ -1360,10 +1398,18 @@ static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	/* NOTE: In this handler we can not take it for granted that the endp
 | 
			
		||||
	 * pointer will be populated, however a trunk is always guaranteed. */
 | 
			
		||||
	 * pointer will be populated, however a trunk is always guaranteed (except for 'null' endp).
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n");
 | 
			
		||||
 | 
			
		||||
	if (rq->null_endp) {
 | 
			
		||||
		/* trunk not available so rate_ctr aren't available either. */
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "DLCX: Not allowed in 'null' endpoint!\n");
 | 
			
		||||
		return create_err_response(pdata->cfg, NULL, 502, "DLCX", pdata->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
 | 
			
		||||
	if (endp && !mgcp_endp_avail(endp)) {
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
@@ -1514,6 +1560,12 @@ static struct msgb *handle_rsip(struct mgcp_request_data *rq)
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
 | 
			
		||||
 | 
			
		||||
	if (rq->null_endp) {
 | 
			
		||||
		/* trunk not available so rate_ctr aren't available either. */
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "RSIP: Not allowed in 'null' endpoint!\n");
 | 
			
		||||
		return create_err_response(rq->pdata->cfg, NULL, 502, "RSIP", rq->pdata->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (rq->pdata->cfg->reset_cb)
 | 
			
		||||
		rq->pdata->cfg->reset_cb(rq->endp->trunk);
 | 
			
		||||
	return NULL;
 | 
			
		||||
@@ -1539,6 +1591,12 @@ static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
 | 
			
		||||
 | 
			
		||||
	if (rq->null_endp) {
 | 
			
		||||
		/* trunk not available so rate_ctr aren't available either. */
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "RQNT: Not allowed in 'null' endpoint!\n");
 | 
			
		||||
		return create_err_response(rq->pdata->cfg, NULL, 502, "RQNT", rq->pdata->trans);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for_each_line(line, rq->pdata->save) {
 | 
			
		||||
		switch (toupper(line[0])) {
 | 
			
		||||
		case 'S':
 | 
			
		||||
@@ -1651,8 +1709,6 @@ struct mgcp_config *mgcp_config_alloc(void)
 | 
			
		||||
	cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
 | 
			
		||||
	cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
 | 
			
		||||
 | 
			
		||||
	cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&cfg->trunks);
 | 
			
		||||
 | 
			
		||||
	/* Allocate virtual trunk */
 | 
			
		||||
 
 | 
			
		||||
@@ -239,13 +239,12 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
 | 
			
		||||
		if (delimiter == ';' || delimiter == ',')
 | 
			
		||||
			str_ptr[strlen(str_ptr) - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
		/* AMR octet aligned parameter */
 | 
			
		||||
		/* AMR octet aligned parameter (see also RFC 3267, section 8.3) */
 | 
			
		||||
		if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
 | 
			
		||||
			fmtp_param->param.amr_octet_aligned_present = true;
 | 
			
		||||
			fmtp_param->param.amr_octet_aligned = false;
 | 
			
		||||
			if (amr_octet_aligned == 1)
 | 
			
		||||
				fmtp_param->param.amr_octet_aligned = true;
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		param_str = strtok(NULL, " ");
 | 
			
		||||
@@ -489,34 +488,10 @@ static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add fmtp strings to sdp payload */
 | 
			
		||||
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len,
 | 
			
		||||
		    const char *fmtp_extra)
 | 
			
		||||
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int fmtp_extra_pt = -1;
 | 
			
		||||
	char *fmtp_extra_pars = "";
 | 
			
		||||
 | 
			
		||||
	/* When no fmtp parameters ara available but an fmtp extra string
 | 
			
		||||
	 * is configured, just add the fmtp extra string */
 | 
			
		||||
	if (fmtp_params_len == 0 && fmtp_extra) {
 | 
			
		||||
		return msgb_printf(sdp, "%s\r\n", fmtp_extra);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* When there is fmtp extra configured we dissect it in order to drop
 | 
			
		||||
	 * in the configured extra parameters at the right place when
 | 
			
		||||
	 * generating the fmtp strings. */
 | 
			
		||||
	if (fmtp_extra) {
 | 
			
		||||
		if (sscanf(fmtp_extra, "a=fmtp:%d ", &fmtp_extra_pt) != 1)
 | 
			
		||||
			fmtp_extra_pt = -1;
 | 
			
		||||
 | 
			
		||||
		fmtp_extra_pars = strstr(fmtp_extra, " ");
 | 
			
		||||
 | 
			
		||||
		if (!fmtp_extra_pars)
 | 
			
		||||
			fmtp_extra_pars = "";
 | 
			
		||||
		else
 | 
			
		||||
			fmtp_extra_pars++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < fmtp_params_len; i++) {
 | 
			
		||||
		rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
 | 
			
		||||
@@ -533,13 +508,6 @@ static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsign
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Append extra parameters from fmtp extra */
 | 
			
		||||
		if (fmtp_params[i].payload_type == fmtp_extra_pt) {
 | 
			
		||||
			rc = msgb_printf(sdp, " %s", fmtp_extra_pars);
 | 
			
		||||
			if (rc < 0)
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rc = msgb_printf(sdp, "\r\n");
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
@@ -559,7 +527,6 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
			    const char *addr)
 | 
			
		||||
{
 | 
			
		||||
	const struct mgcp_rtp_codec *codec;
 | 
			
		||||
	const char *fmtp_extra;
 | 
			
		||||
	const char *audio_name;
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	struct sdp_fmtp_param fmtp_param;
 | 
			
		||||
@@ -575,10 +542,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
	OSMO_ASSERT(sdp);
 | 
			
		||||
	OSMO_ASSERT(addr);
 | 
			
		||||
 | 
			
		||||
	/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
 | 
			
		||||
	endp->trunk->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
 | 
			
		||||
					      &codec, &fmtp_extra,
 | 
			
		||||
					      (struct mgcp_conn_rtp *)conn);
 | 
			
		||||
	codec = conn->end.codec;
 | 
			
		||||
 | 
			
		||||
	audio_name = codec->audio_name;
 | 
			
		||||
	payload_type = codec->payload_type;
 | 
			
		||||
@@ -620,7 +584,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
			fmtp_params[0] = fmtp_param;
 | 
			
		||||
			fmtp_params_len = 1;
 | 
			
		||||
		}
 | 
			
		||||
		rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra);
 | 
			
		||||
		rc = add_fmtp(sdp, fmtp_params, fmtp_params_len);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			goto buffer_too_small;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -111,17 +111,12 @@ static int config_write_mgcp(struct vty *vty)
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
	} else
 | 
			
		||||
		vty_out(vty, " no rtp-patch%s", VTY_NEWLINE);
 | 
			
		||||
	if (trunk->audio_fmtp_extra)
 | 
			
		||||
		vty_out(vty, " sdp audio fmtp-extra %s%s",
 | 
			
		||||
			trunk->audio_fmtp_extra, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " %ssdp audio-payload send-ptime%s",
 | 
			
		||||
		trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " %ssdp audio-payload send-name%s",
 | 
			
		||||
		trunk->audio_send_name ? "" : "no ", VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " number endpoints %u%s",
 | 
			
		||||
		trunk->v.vty_number_endpoints, VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " %sallow-transcoding%s",
 | 
			
		||||
		trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE);
 | 
			
		||||
	if (strlen(g_cfg->call_agent_addr))
 | 
			
		||||
		vty_out(vty, " call-agent ip %s%s", g_cfg->call_agent_addr,
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
@@ -189,7 +184,7 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
 | 
			
		||||
		"   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"
 | 
			
		||||
		"   Audio-Name: %s Sub-Type: %s%s"
 | 
			
		||||
		"   Output-Enabled: %d Force-PTIME: %d%s",
 | 
			
		||||
		tx_packets->current, tx_bytes->current, VTY_NEWLINE,
 | 
			
		||||
		rx_packets->current, rx_bytes->current, VTY_NEWLINE,
 | 
			
		||||
@@ -200,7 +195,7 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
 | 
			
		||||
		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,
 | 
			
		||||
		VTY_NEWLINE, end->fmtp_extra, codec->audio_name,
 | 
			
		||||
		VTY_NEWLINE, codec->audio_name,
 | 
			
		||||
		codec->subtype_name, VTY_NEWLINE, end->output_enabled,
 | 
			
		||||
		end->force_output_ptime, VTY_NEWLINE);
 | 
			
		||||
	if (mgcp_conn_rtp_is_osmux(conn)) {
 | 
			
		||||
@@ -513,6 +508,7 @@ DEFUN_DEPRECATED(cfg_mgcp_bind_early,
 | 
			
		||||
		 BIND_STR
 | 
			
		||||
		 "Bind local ports on start up\n" "Bind on demand\n" "Bind on startup\n")
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% Deprecated 'bind early (0|1)' config no longer has any effect%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -682,53 +678,40 @@ DEFUN_USRATTR(cfg_mgcp_no_rtp_force_ptime,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_USRATTR(cfg_mgcp_sdp_fmtp_extra,
 | 
			
		||||
	      cfg_mgcp_sdp_fmtp_extra_cmd,
 | 
			
		||||
	      X(MGW_CMD_ATTR_NEWCONN),
 | 
			
		||||
	      "sdp audio fmtp-extra .NAME",
 | 
			
		||||
	      "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
 | 
			
		||||
	      "Extra Information\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
 | 
			
		||||
	OSMO_ASSERT(trunk);
 | 
			
		||||
	char *txt = argv_concat(argv, argc, 0);
 | 
			
		||||
	if (!txt)
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
 | 
			
		||||
	osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt);
 | 
			
		||||
	talloc_free(txt);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_USRATTR(cfg_mgcp_allow_transcoding,
 | 
			
		||||
	      cfg_mgcp_allow_transcoding_cmd,
 | 
			
		||||
	      X(MGW_CMD_ATTR_NEWCONN),
 | 
			
		||||
	      "allow-transcoding", "Allow transcoding\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
 | 
			
		||||
	OSMO_ASSERT(trunk);
 | 
			
		||||
	trunk->no_audio_transcoding = 0;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_USRATTR(cfg_mgcp_no_allow_transcoding,
 | 
			
		||||
	      cfg_mgcp_no_allow_transcoding_cmd,
 | 
			
		||||
	      X(MGW_CMD_ATTR_NEWCONN),
 | 
			
		||||
	      "no allow-transcoding", NO_STR "Allow transcoding\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
 | 
			
		||||
	OSMO_ASSERT(trunk);
 | 
			
		||||
	trunk->no_audio_transcoding = 1;
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define SDP_STR "SDP File related options\n"
 | 
			
		||||
#define AUDIO_STR "Audio payload options\n"
 | 
			
		||||
 | 
			
		||||
DEFUN_DEPRECATED(cfg_mgcp_sdp_fmtp_extra,
 | 
			
		||||
	      cfg_mgcp_sdp_fmtp_extra_cmd,
 | 
			
		||||
	      "sdp audio fmtp-extra .NAME",
 | 
			
		||||
	      SDP_STR AUDIO_STR "Deprecated, without effect since osmo-mgw v1.13\n" "Deprecated, without effect\n")
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% deprecated: the config option 'sdp audio fmtp-extra' has been removed.%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_DEPRECATED(cfg_mgcp_allow_transcoding,
 | 
			
		||||
		  cfg_mgcp_allow_transcoding_cmd,
 | 
			
		||||
		  "allow-transcoding", "Allow transcoding\n")
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% Deprecated 'allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_DEPRECATED(cfg_mgcp_no_allow_transcoding,
 | 
			
		||||
		 cfg_mgcp_no_allow_transcoding_cmd,
 | 
			
		||||
		 "no allow-transcoding", NO_STR "Allow transcoding\n")
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% Deprecated 'no allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_DEPRECATED(cfg_mgcp_sdp_payload_number,
 | 
			
		||||
      cfg_mgcp_sdp_payload_number_cmd,
 | 
			
		||||
      "sdp audio-payload number <0-255>",
 | 
			
		||||
      SDP_STR AUDIO_STR "Number\n" "Payload number\n")
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% Deprecated 'sdp audio-payload number <0-255>' config no longer has any effect%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -742,6 +725,7 @@ DEFUN_DEPRECATED(cfg_mgcp_sdp_payload_name,
 | 
			
		||||
      "sdp audio-payload name NAME",
 | 
			
		||||
      SDP_STR AUDIO_STR "Name\n" "Payload name\n")
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% Deprecated 'sdp audio-payload name NAME' config no longer has any effect%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -802,6 +786,7 @@ DEFUN_DEPRECATED(cfg_mgcp_loop,
 | 
			
		||||
		 "loop (0|1)",
 | 
			
		||||
		 "Loop audio for all endpoints on main trunk\n" "Don't Loop\n" "Loop\n")
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% Deprecated 'loop (0|1)' config no longer has any effect%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1066,30 +1051,17 @@ static int config_write_trunk(struct vty *vty)
 | 
			
		||||
				VTY_NEWLINE);
 | 
			
		||||
		} else
 | 
			
		||||
			vty_out(vty, "  no rtp-patch%s", VTY_NEWLINE);
 | 
			
		||||
		if (trunk->audio_fmtp_extra)
 | 
			
		||||
			vty_out(vty, "   sdp audio fmtp-extra %s%s",
 | 
			
		||||
				trunk->audio_fmtp_extra, VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, "  %sallow-transcoding%s",
 | 
			
		||||
			trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_USRATTR(cfg_trunk_sdp_fmtp_extra,
 | 
			
		||||
DEFUN_DEPRECATED(cfg_trunk_sdp_fmtp_extra,
 | 
			
		||||
	      cfg_trunk_sdp_fmtp_extra_cmd,
 | 
			
		||||
	      X(MGW_CMD_ATTR_NEWCONN),
 | 
			
		||||
	      "sdp audio fmtp-extra .NAME",
 | 
			
		||||
	      "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
 | 
			
		||||
	      "Extra Information\n")
 | 
			
		||||
	      SDP_STR AUDIO_STR "Deprecated, without effect since osmo-mgw v1.13\n" "Deprecated, without effect\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk *trunk = vty->index;
 | 
			
		||||
	char *txt = argv_concat(argv, argc, 0);
 | 
			
		||||
	if (!txt)
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
 | 
			
		||||
	osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt);
 | 
			
		||||
	talloc_free(txt);
 | 
			
		||||
	vty_out(vty, "%% deprecated: the config option 'sdp audio fmtp-extra' has been removed.%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1098,6 +1070,7 @@ DEFUN_DEPRECATED(cfg_trunk_payload_number,
 | 
			
		||||
      "sdp audio-payload number <0-255>",
 | 
			
		||||
      SDP_STR AUDIO_STR "Number\n" "Payload Number\n")
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% Deprecated 'sdp audio-payload number <0-255>' config no longer has any effect%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1110,6 +1083,7 @@ DEFUN_DEPRECATED(cfg_trunk_payload_name,
 | 
			
		||||
      "sdp audio-payload name NAME",
 | 
			
		||||
      SDP_STR AUDIO_STR "Payload\n" "Payload Name\n")
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% Deprecated 'sdp audio-payload name NAME' config no longer has any effect%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1122,6 +1096,7 @@ DEFUN_DEPRECATED(cfg_trunk_loop,
 | 
			
		||||
		 "loop (0|1)",
 | 
			
		||||
		 "Loop audio for all endpoints on this trunk\n" "Don't Loop\n" "Loop\n")
 | 
			
		||||
{
 | 
			
		||||
	vty_out(vty, "%% Deprecated 'loop (0|1)' config no longer has any effect%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1319,23 +1294,19 @@ DEFUN_ATTR(cfg_trunk_no_rtp_keepalive,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_USRATTR(cfg_trunk_allow_transcoding,
 | 
			
		||||
DEFUN_DEPRECATED(cfg_trunk_allow_transcoding,
 | 
			
		||||
		 cfg_trunk_allow_transcoding_cmd,
 | 
			
		||||
	      X(MGW_CMD_ATTR_NEWCONN),
 | 
			
		||||
		 "allow-transcoding", "Allow transcoding\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk *trunk = vty->index;
 | 
			
		||||
	trunk->no_audio_transcoding = 0;
 | 
			
		||||
	vty_out(vty, "%% Deprecated 'allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_USRATTR(cfg_trunk_no_allow_transcoding,
 | 
			
		||||
DEFUN_DEPRECATED(cfg_trunk_no_allow_transcoding,
 | 
			
		||||
		 cfg_trunk_no_allow_transcoding_cmd,
 | 
			
		||||
	      X(MGW_CMD_ATTR_NEWCONN),
 | 
			
		||||
		 "no allow-transcoding", NO_STR "Allow transcoding\n")
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_trunk *trunk = vty->index;
 | 
			
		||||
	trunk->no_audio_transcoding = 1;
 | 
			
		||||
	vty_out(vty, "%% Deprecated 'no allow-transcoding' config no longer has any effect%s", VTY_NEWLINE);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -280,6 +280,31 @@ int mgcp_vty_go_parent(struct vty *vty)
 | 
			
		||||
	return vty->node;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void signal_handler(int signum)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stdout, "signal %u received\n", signum);
 | 
			
		||||
 | 
			
		||||
	switch (signum) {
 | 
			
		||||
	case SIGABRT:
 | 
			
		||||
		/* in case of abort, we want to obtain a talloc report and
 | 
			
		||||
		 * then run default SIGABRT handler, who will generate coredump
 | 
			
		||||
		 * and abort the process. abort() should do this for us after we
 | 
			
		||||
		 * return, but program wouldn't exit if an external SIGABRT is
 | 
			
		||||
		 * received.
 | 
			
		||||
		 */
 | 
			
		||||
		talloc_report(tall_vty_ctx, stderr);
 | 
			
		||||
		talloc_report_full(tall_mgw_ctx, stderr);
 | 
			
		||||
		signal(SIGABRT, SIG_DFL);
 | 
			
		||||
		raise(SIGABRT);
 | 
			
		||||
		break;
 | 
			
		||||
	case SIGUSR1:
 | 
			
		||||
		talloc_report(tall_vty_ctx, stderr);
 | 
			
		||||
		talloc_report_full(tall_mgw_ctx, stderr);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct vty_app_info vty_info = {
 | 
			
		||||
	.name 		= "OsmoMGW",
 | 
			
		||||
@@ -328,6 +353,8 @@ int main(int argc, char **argv)
 | 
			
		||||
 | 
			
		||||
	msgb_talloc_ctx_init(tall_mgw_ctx, 0);
 | 
			
		||||
 | 
			
		||||
	signal(SIGABRT, &signal_handler);
 | 
			
		||||
	signal(SIGUSR1, &signal_handler);
 | 
			
		||||
	osmo_init_ignore_signals();
 | 
			
		||||
	osmo_init_logging2(tall_mgw_ctx, &log_info);
 | 
			
		||||
	libosmo_abis_init(tall_mgw_ctx);
 | 
			
		||||
@@ -360,8 +387,7 @@ int main(int argc, char **argv)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	/* start telnet after reading config for vty_get_bind_addr() */
 | 
			
		||||
	rc = telnet_init_dynif(tall_mgw_ctx, NULL,
 | 
			
		||||
			       vty_get_bind_addr(), OSMO_VTY_PORT_MGW);
 | 
			
		||||
	rc = telnet_init_default(tall_mgw_ctx, NULL, OSMO_VTY_PORT_MGW);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,11 +34,11 @@ DISTCLEANFILES = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
if ENABLE_EXT_TESTS
 | 
			
		||||
python-tests: $(BUILT_SOURCES)
 | 
			
		||||
python-tests: $(top_builddir)/src/osmo-mgw/osmo-mgw
 | 
			
		||||
	osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
 | 
			
		||||
	osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
 | 
			
		||||
else
 | 
			
		||||
python-tests: $(BUILT_SOURCES)
 | 
			
		||||
python-tests:
 | 
			
		||||
	echo "Not running python-based tests (determined at configure-time)"
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ AM_CFLAGS = \
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	-no-install \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
@@ -44,3 +45,6 @@ mgcp_test_LDADD = \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	-lm  \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
update_exp:
 | 
			
		||||
	$(builddir)/mgcp_test >$(srcdir)/mgcp_test.ok
 | 
			
		||||
 
 | 
			
		||||
@@ -77,6 +77,8 @@ static void test_strline(void)
 | 
			
		||||
#define AUEP1_RET "500 158663169 FAIL\r\n"
 | 
			
		||||
#define AUEP2	"AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0\r\n"
 | 
			
		||||
#define AUEP2_RET "500 18983213 FAIL\r\n"
 | 
			
		||||
#define AUEP_NULL "AUEP 18983215 null@mgw MGCP 1.0\r\n"
 | 
			
		||||
#define AUEP_NULL_RET "200 18983215 OK\r\n"
 | 
			
		||||
#define EMPTY	"\r\n"
 | 
			
		||||
#define EMPTY_RET NULL
 | 
			
		||||
#define SHORT	"CRCX \r\n"
 | 
			
		||||
@@ -125,7 +127,6 @@ static void test_strline(void)
 | 
			
		||||
	"t=0 0\r\n" \
 | 
			
		||||
	"m=audio 16006 RTP/AVP 97\r\n" \
 | 
			
		||||
	"a=rtpmap:97 GSM-EFR/8000\r\n" \
 | 
			
		||||
	"a=fmtp:126 0/1/2\r\n" \
 | 
			
		||||
	"a=ptime:40\r\n"
 | 
			
		||||
 | 
			
		||||
#define MDCX4_ADDR0000 \
 | 
			
		||||
@@ -274,6 +275,12 @@ static void test_strline(void)
 | 
			
		||||
 | 
			
		||||
#define MDCX_TOO_LONG_CI_RET "510 18983224 FAIL\r\n"
 | 
			
		||||
 | 
			
		||||
#define MDCX_NULL \
 | 
			
		||||
	"MDCX 9 null@mgw MGCP 1.0\r\n" \
 | 
			
		||||
	"I: %s\n"
 | 
			
		||||
 | 
			
		||||
#define MDCX_NULL_RET "502 9 FAIL\r\n"
 | 
			
		||||
 | 
			
		||||
#define SHORT2	"CRCX 1"
 | 
			
		||||
#define SHORT2_RET "510 000000 FAIL\r\n"
 | 
			
		||||
#define SHORT3	"CRCX 1 1@mgw"
 | 
			
		||||
@@ -328,7 +335,6 @@ static void test_strline(void)
 | 
			
		||||
	"t=0 0\r\n" \
 | 
			
		||||
	"m=audio 16006 RTP/AVP 97\r\n" \
 | 
			
		||||
	"a=rtpmap:97 GSM-EFR/8000\r\n" \
 | 
			
		||||
	"a=fmtp:126 0/1/2\r\n" \
 | 
			
		||||
	"a=ptime:40\r\n"
 | 
			
		||||
 | 
			
		||||
#define CRCX_ZYN \
 | 
			
		||||
@@ -392,6 +398,13 @@ static void test_strline(void)
 | 
			
		||||
 #define DLCX_RET_OSMUX DLCX_RET \
 | 
			
		||||
	"X-Osmo-CP: EC TI=0, TO=0\r\n"
 | 
			
		||||
 | 
			
		||||
#define DLCX_NULL \
 | 
			
		||||
	"DLCX 8 null@mgw MGCP 1.0\r\n" \
 | 
			
		||||
	"I: %s\r\n" \
 | 
			
		||||
	"C: 2\r\n"
 | 
			
		||||
 | 
			
		||||
#define DLCX_NULL_RET "502 8 FAIL\r\n"
 | 
			
		||||
 | 
			
		||||
#define RQNT \
 | 
			
		||||
	"RQNT 186908780 1@mgw MGCP 1.0\r\n" \
 | 
			
		||||
	"X: B244F267488\r\n" \
 | 
			
		||||
@@ -405,6 +418,13 @@ static void test_strline(void)
 | 
			
		||||
#define RQNT1_RET "200 186908780 OK\r\n"
 | 
			
		||||
#define RQNT2_RET "200 186908781 OK\r\n"
 | 
			
		||||
 | 
			
		||||
#define RQNT_NULL \
 | 
			
		||||
	"RQNT 186908782 null@mgw MGCP 1.0\r\n" \
 | 
			
		||||
	"X: B244F267488\r\n" \
 | 
			
		||||
	"S: D/9\r\n"
 | 
			
		||||
 | 
			
		||||
#define RQNT_NULL_RET "502 186908782 FAIL\r\n"
 | 
			
		||||
 | 
			
		||||
#define PTYPE_IGNORE 0		/* == default initializer */
 | 
			
		||||
#define PTYPE_NONE 128
 | 
			
		||||
#define PTYPE_NYI  PTYPE_NONE
 | 
			
		||||
@@ -545,12 +565,25 @@ static void test_strline(void)
 | 
			
		||||
	"m=audio 16008 RTP/AVP 0\r\n" \
 | 
			
		||||
	"a=ptime:20\r\n"
 | 
			
		||||
 | 
			
		||||
#define CRCX_NULL \
 | 
			
		||||
	"CRCX 2 null@mgw MGCP 1.0\r\n" \
 | 
			
		||||
	"m: recvonly\r\n" \
 | 
			
		||||
	"C: 2\r\n" \
 | 
			
		||||
	"L: p:20\r\n" \
 | 
			
		||||
	"\r\n" \
 | 
			
		||||
	"v=0\r\n" \
 | 
			
		||||
	"c=IN IP4 123.12.12.123\r\n" \
 | 
			
		||||
	"m=audio 5904 RTP/AVP 97\r\n" \
 | 
			
		||||
	"a=rtpmap:97 GSM-EFR/8000\r\n" \
 | 
			
		||||
	"a=ptime:40\r\n"
 | 
			
		||||
 | 
			
		||||
#define CRCX_NULL_RET "502 2 FAIL\r\n"
 | 
			
		||||
 | 
			
		||||
struct mgcp_test {
 | 
			
		||||
	const char *name;
 | 
			
		||||
	const char *req;
 | 
			
		||||
	const char *exp_resp;
 | 
			
		||||
	int ptype;
 | 
			
		||||
	const char *extra_fmtp;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct mgcp_test tests[] = {
 | 
			
		||||
@@ -578,14 +611,18 @@ static const struct mgcp_test tests[] = {
 | 
			
		||||
	{"RQNT1", RQNT, RQNT1_RET},
 | 
			
		||||
	{"RQNT2", RQNT2, RQNT2_RET},
 | 
			
		||||
	{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
 | 
			
		||||
	{"CRCX", CRCX, CRCX_FMTP_RET, 97,.extra_fmtp = "a=fmtp:126 0/1/2"},
 | 
			
		||||
	{"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE,.extra_fmtp =
 | 
			
		||||
	 "a=fmtp:126 0/1/2"},
 | 
			
		||||
	{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
 | 
			
		||||
	{"CRCX", CRCX, CRCX_FMTP_RET, 97},
 | 
			
		||||
	{"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE},
 | 
			
		||||
	{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
 | 
			
		||||
	{"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
 | 
			
		||||
	{"CRCX", CRCX_X_OSMO_IGN, CRCX_X_OSMO_IGN_RET, 97},
 | 
			
		||||
	{"MDCX_TOO_LONG_CI", MDCX_TOO_LONG_CI, MDCX_TOO_LONG_CI_RET},
 | 
			
		||||
	{"CRCX", CRCX_AMR_WITH_FMTP, CRCX_AMR_WITH_FMTP_RET},
 | 
			
		||||
	{"AUEP_NULL", AUEP_NULL, AUEP_NULL_RET},
 | 
			
		||||
	{"CRCX_NULL", CRCX_NULL, CRCX_NULL_RET},
 | 
			
		||||
	{"MDCX_NULL", MDCX_NULL, MDCX_NULL_RET},
 | 
			
		||||
	{"DLCX_NULL", DLCX_NULL, DLCX_NULL_RET},
 | 
			
		||||
	{"RQNT_NULL", RQNT_NULL, RQNT_NULL_RET},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct mgcp_test retransmit[] = {
 | 
			
		||||
@@ -790,9 +827,6 @@ static void test_messages(void)
 | 
			
		||||
 | 
			
		||||
		dummy_packets = 0;
 | 
			
		||||
 | 
			
		||||
		osmo_talloc_replace_string(cfg, &trunk->audio_fmtp_extra,
 | 
			
		||||
					   t->extra_fmtp);
 | 
			
		||||
 | 
			
		||||
		inp = create_msg(t->req, last_conn_id);
 | 
			
		||||
		msg = mgcp_handle_message(cfg, inp);
 | 
			
		||||
		msgb_free(inp);
 | 
			
		||||
@@ -1467,7 +1501,6 @@ static void test_multilple_codec(void)
 | 
			
		||||
	/* Allocate 5@mgw and let osmo-mgw pick a codec from the list */
 | 
			
		||||
	last_endpoint[0] = '\0';
 | 
			
		||||
	inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
 | 
			
		||||
	trunk->no_audio_transcoding = 1;
 | 
			
		||||
	resp = mgcp_handle_message(cfg, inp);
 | 
			
		||||
	OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
 | 
			
		||||
					      sizeof(conn_id)) == 0);
 | 
			
		||||
@@ -1511,7 +1544,6 @@ static void test_multilple_codec(void)
 | 
			
		||||
 | 
			
		||||
	last_endpoint[0] = '\0';
 | 
			
		||||
	inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
 | 
			
		||||
	trunk->no_audio_transcoding = 0;
 | 
			
		||||
	resp = mgcp_handle_message(cfg, inp);
 | 
			
		||||
	OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
 | 
			
		||||
					      sizeof(conn_id)) == 0);
 | 
			
		||||
@@ -1769,26 +1801,25 @@ static const struct mgcp_codec_param amr_param_octet_aligned_unset = {
 | 
			
		||||
	.amr_octet_aligned_present = false,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct testcase_mgcp_codec_pt_translate_codec {
 | 
			
		||||
struct testcase_mgcp_codec_decide_codec {
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	const char *audio_name;
 | 
			
		||||
	const struct mgcp_codec_param *param;
 | 
			
		||||
	int expect_rc;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct testcase_mgcp_codec_pt_translate_expect {
 | 
			
		||||
	bool end;
 | 
			
		||||
struct testcase_mgcp_codec_decide_expect {
 | 
			
		||||
	int payload_type_map[2];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct testcase_mgcp_codec_pt_translate {
 | 
			
		||||
struct testcase_mgcp_codec_decide {
 | 
			
		||||
	const char *descr;
 | 
			
		||||
	/* two conns on an endpoint, each with N configured codecs */
 | 
			
		||||
	struct testcase_mgcp_codec_pt_translate_codec codecs[2][10];
 | 
			
		||||
	struct testcase_mgcp_codec_pt_translate_expect expect[32];
 | 
			
		||||
	struct testcase_mgcp_codec_decide_codec codecs[2][10];
 | 
			
		||||
	struct testcase_mgcp_codec_decide_expect expect[2];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translate_cases[] = {
 | 
			
		||||
static const struct testcase_mgcp_codec_decide test_mgcp_codec_find_convertible_cases[] = {
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "same order, but differing payload type numbers",
 | 
			
		||||
		.codecs = {
 | 
			
		||||
@@ -1805,10 +1836,7 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {112, 96}, },
 | 
			
		||||
			{ .payload_type_map = {0, 0}, },
 | 
			
		||||
			{ .payload_type_map = {111, 97} },
 | 
			
		||||
			{ .payload_type_map = {123, -EINVAL} },
 | 
			
		||||
			{ .end = true },
 | 
			
		||||
			{ .payload_type_map = {112, 96}, },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
@@ -1826,11 +1854,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {112, 96}, },
 | 
			
		||||
			{ .payload_type_map = {0, 0}, },
 | 
			
		||||
			{ .payload_type_map = {111, 97} },
 | 
			
		||||
			{ .payload_type_map = {123, -EINVAL} },
 | 
			
		||||
			{ .end = true },
 | 
			
		||||
			{ .payload_type_map = {111, 97}, },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
@@ -1848,10 +1873,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {96, 97}, },
 | 
			
		||||
			{ .payload_type_map = {97, 96}, },
 | 
			
		||||
			{ .payload_type_map = {0, 0}, },
 | 
			
		||||
			{ .end = true },
 | 
			
		||||
			{ .payload_type_map = {96, 97}, },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
@@ -1867,10 +1890,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {112, -EINVAL}, },
 | 
			
		||||
			{ .payload_type_map = {0, -EINVAL}, },
 | 
			
		||||
			{ .payload_type_map = {111, -EINVAL} },
 | 
			
		||||
			{ .end = true },
 | 
			
		||||
			{ .payload_type_map = {-EINVAL, -EINVAL}, },
 | 
			
		||||
			{ .payload_type_map = {-EINVAL, 96}, },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
@@ -1887,28 +1908,60 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {112, -EINVAL}, },
 | 
			
		||||
			{ .payload_type_map = {0, -EINVAL}, },
 | 
			
		||||
			{ .payload_type_map = {111, -EINVAL} },
 | 
			
		||||
			{ .end = true },
 | 
			
		||||
			{ .payload_type_map = {-EINVAL, -EINVAL}, },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "test AMR with differing octet-aligned settings",
 | 
			
		||||
		.descr = "test AMR with differing octet-aligned settings (both <-> both)",
 | 
			
		||||
		.codecs = {
 | 
			
		||||
			{
 | 
			
		||||
				{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
			
		||||
				{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				{ 122, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
			
		||||
				{ 121, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {111, 121}, },
 | 
			
		||||
			{ .payload_type_map = {112, 122} },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "test AMR with differing octet-aligned settings (oa <-> both)",
 | 
			
		||||
		.codecs = {
 | 
			
		||||
			{
 | 
			
		||||
				{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				{ 122, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
			
		||||
				{ 121, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {111, 122}, },
 | 
			
		||||
			{ .end = true },
 | 
			
		||||
			{ .payload_type_map = {111, 121}, },
 | 
			
		||||
			{ .payload_type_map = {111, 121}, },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "test AMR with missing octet-aligned settings (defaults to 0)",
 | 
			
		||||
		.descr = "test AMR with differing octet-aligned settings (bwe <-> both)",
 | 
			
		||||
		.codecs = {
 | 
			
		||||
			{
 | 
			
		||||
				{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				{ 122, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
			
		||||
				{ 121, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {112, 122}, },
 | 
			
		||||
			{ .payload_type_map = {112, 122}, },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "test AMR with missing octet-aligned settings (oa <-> unset)",
 | 
			
		||||
		.codecs = {
 | 
			
		||||
			{
 | 
			
		||||
				{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
			
		||||
@@ -1919,22 +1972,54 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {111, 122}, },
 | 
			
		||||
			{ .end = true },
 | 
			
		||||
			{ .payload_type_map = {111, 122}, },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "test AMR with NULL param (defaults to 0)",
 | 
			
		||||
		.descr = "test AMR with missing octet-aligned settings (bwe <-> unset)",
 | 
			
		||||
		.codecs = {
 | 
			
		||||
			{
 | 
			
		||||
				{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
			
		||||
				{ 111, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				{ 122, "AMR/8000", &amr_param_octet_aligned_unset, },
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {111, 122}, },
 | 
			
		||||
			{ .payload_type_map = {111, 122}, },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "test AMR with NULL param (oa <-> null)",
 | 
			
		||||
		.codecs = {
 | 
			
		||||
			{
 | 
			
		||||
				{ 112, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				{ 122, "AMR/8000", NULL, },
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {111, 122}, },
 | 
			
		||||
			{ .end = true },
 | 
			
		||||
			/* Note: Both 111, anbd 112 will translate to 122. The translation from 112 */
 | 
			
		||||
			{ .payload_type_map = {112, 122} },
 | 
			
		||||
			{ .payload_type_map = {112, 122}, },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.descr = "test AMR with NULL param (bwe <-> null)",
 | 
			
		||||
		.codecs = {
 | 
			
		||||
			{
 | 
			
		||||
				{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				{ 122, "AMR/8000", NULL, },
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			/* Note: Both 111, anbd 112 will translate to 122. The translation from 112 */
 | 
			
		||||
			{ .payload_type_map = {112, 122} },
 | 
			
		||||
			{ .payload_type_map = {112, 122}, },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
@@ -1952,11 +2037,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {112, 96}, },
 | 
			
		||||
			{ .payload_type_map = {0, 0}, },
 | 
			
		||||
			{ .payload_type_map = {111, 97} },
 | 
			
		||||
			{ .payload_type_map = {123, -EINVAL} },
 | 
			
		||||
			{ .end = true },
 | 
			
		||||
			{ .payload_type_map = {111, 97}, },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
@@ -1974,11 +2056,8 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {112, 96}, },
 | 
			
		||||
			{ .payload_type_map = {0, 0}, },
 | 
			
		||||
			{ .payload_type_map = {111, 97} },
 | 
			
		||||
			{ .payload_type_map = {123, -EINVAL} },
 | 
			
		||||
			{ .end = true },
 | 
			
		||||
			{ .payload_type_map = {111, 97}, },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
@@ -1996,45 +2075,98 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {111, 121}, },
 | 
			
		||||
			{ .payload_type_map = {112, -EINVAL} },
 | 
			
		||||
			{ .payload_type_map = {113, -EINVAL} },
 | 
			
		||||
			{ .end = true },
 | 
			
		||||
			{ .payload_type_map = {111, 121} },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void test_mgcp_codec_pt_translate(void)
 | 
			
		||||
static bool codec_decision(struct mgcp_conn_rtp *conn, unsigned int index_conn_src, unsigned int index_conn_dst,
 | 
			
		||||
			   const struct testcase_mgcp_codec_decide_expect *expect)
 | 
			
		||||
{
 | 
			
		||||
	bool ok = true;
 | 
			
		||||
	int payload_type_conn_src;
 | 
			
		||||
	int payload_type_conn_dst;
 | 
			
		||||
 | 
			
		||||
	printf(" - mgcp_codec_decide(&conn[%u], &conn[%u]):\n", index_conn_src, index_conn_dst);
 | 
			
		||||
	if (mgcp_codec_decide(&conn[index_conn_src], &conn[index_conn_dst]) != 0) {
 | 
			
		||||
		if (expect->payload_type_map[index_conn_src] == -EINVAL
 | 
			
		||||
		    && expect->payload_type_map[index_conn_dst] == -EINVAL)
 | 
			
		||||
			printf("    codec decision failed (expected)!\n");
 | 
			
		||||
		else {
 | 
			
		||||
			printf("    ERROR: codec decision failed!\n");
 | 
			
		||||
			ok = false;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		printf("    Codec decision result:\n");
 | 
			
		||||
		if (conn[index_conn_src].end.codec) {
 | 
			
		||||
			payload_type_conn_src = conn[index_conn_src].end.codec->payload_type;
 | 
			
		||||
			printf("    conn[%u]: codec:%s, pt:%d\n",
 | 
			
		||||
			       index_conn_src, conn[index_conn_src].end.codec->subtype_name, payload_type_conn_src);
 | 
			
		||||
		} else {
 | 
			
		||||
			payload_type_conn_src = -EINVAL;
 | 
			
		||||
			printf("    conn[%u]: codec:none, pt:none\n", index_conn_src);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (conn[index_conn_dst].end.codec) {
 | 
			
		||||
			payload_type_conn_dst = conn[index_conn_dst].end.codec->payload_type;
 | 
			
		||||
			printf("    conn[%u]: codec:%s, pt:%d\n",
 | 
			
		||||
			       index_conn_dst, conn[index_conn_dst].end.codec->subtype_name,
 | 
			
		||||
			       payload_type_conn_dst);
 | 
			
		||||
		} else {
 | 
			
		||||
			payload_type_conn_dst = -EINVAL;
 | 
			
		||||
			printf("    conn[%u]: codec:none, pt:none\n", index_conn_dst);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (payload_type_conn_src != expect->payload_type_map[index_conn_src]) {
 | 
			
		||||
			printf("    ERROR: conn[%u] unexpected codec decision, expected pt=%d, got pt=%d\n",
 | 
			
		||||
			       index_conn_src, expect->payload_type_map[index_conn_src], payload_type_conn_src);
 | 
			
		||||
			ok = false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (payload_type_conn_dst != expect->payload_type_map[index_conn_dst]) {
 | 
			
		||||
			printf("    ERROR: conn[%u] unexpected codec decision, expected pt=%d, got pt=%d\n",
 | 
			
		||||
			       index_conn_dst, expect->payload_type_map[index_conn_dst],
 | 
			
		||||
			       payload_type_conn_dst);
 | 
			
		||||
			ok = false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_mgcp_codec_decide(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	bool ok = true;
 | 
			
		||||
	printf("\nTesting mgcp_codec_pt_translate()\n");
 | 
			
		||||
	bool ok_all = true;
 | 
			
		||||
	printf("\nTesting mgcp_codec_find_convertible()\n");
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(test_mgcp_codec_pt_translate_cases); i++) {
 | 
			
		||||
		const struct testcase_mgcp_codec_pt_translate *t = &test_mgcp_codec_pt_translate_cases[i];
 | 
			
		||||
		struct mgcp_conn_rtp conn[2] = {};
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(test_mgcp_codec_find_convertible_cases); i++) {
 | 
			
		||||
		const struct testcase_mgcp_codec_decide *t = &test_mgcp_codec_find_convertible_cases[i];
 | 
			
		||||
		struct mgcp_conn_rtp conn[2] = { };
 | 
			
		||||
		int rc;
 | 
			
		||||
		int conn_i;
 | 
			
		||||
		int c;
 | 
			
		||||
		bool ok = true;
 | 
			
		||||
 | 
			
		||||
		printf("#%d: %s\n", i, t->descr);
 | 
			
		||||
 | 
			
		||||
		/* Build testvector (add codecs to conn, set properties etc... */
 | 
			
		||||
		for (conn_i = 0; conn_i < 2; conn_i++) {
 | 
			
		||||
			printf(" - add codecs on conn%d:\n", conn_i);
 | 
			
		||||
			for (c = 0; c < ARRAY_SIZE(t->codecs[conn_i]); c++) {
 | 
			
		||||
				const struct testcase_mgcp_codec_pt_translate_codec *codec = &t->codecs[conn_i][c];
 | 
			
		||||
				const struct testcase_mgcp_codec_decide_codec *codec = &t->codecs[conn_i][c];
 | 
			
		||||
				if (!codec->audio_name)
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name, codec->param);
 | 
			
		||||
				rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name,
 | 
			
		||||
						    codec->param);
 | 
			
		||||
 | 
			
		||||
				printf("   %2d: %3d %s%s  -> rc=%d\n", c, codec->payload_type, codec->audio_name,
 | 
			
		||||
				       codec->param ?
 | 
			
		||||
					       (codec->param->amr_octet_aligned_present?
 | 
			
		||||
							(codec->param->amr_octet_aligned ?
 | 
			
		||||
								" octet-aligned=1" : " octet-aligned=0")
 | 
			
		||||
				       (codec->param->amr_octet_aligned_present ?
 | 
			
		||||
					(codec->param->amr_octet_aligned ? " octet-aligned=1" : " octet-aligned=0")
 | 
			
		||||
					: " octet-aligned=unset")
 | 
			
		||||
						: "",
 | 
			
		||||
						rc);
 | 
			
		||||
				       : "", rc);
 | 
			
		||||
				if (rc != codec->expect_rc) {
 | 
			
		||||
					printf("     ERROR: expected rc=%d\n", codec->expect_rc);
 | 
			
		||||
					ok = false;
 | 
			
		||||
@@ -2044,39 +2176,23 @@ static void test_mgcp_codec_pt_translate(void)
 | 
			
		||||
				printf("    (none)\n");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (c = 0; c < ARRAY_SIZE(t->expect); c++) {
 | 
			
		||||
			const struct testcase_mgcp_codec_pt_translate_expect *expect = &t->expect[c];
 | 
			
		||||
			int result;
 | 
			
		||||
 | 
			
		||||
			if (expect->end)
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			result = mgcp_codec_pt_translate(&conn[0], &conn[1], expect->payload_type_map[0]);
 | 
			
		||||
			printf(" - mgcp_codec_pt_translate(conn0, conn1, %d) -> %d\n",
 | 
			
		||||
			       expect->payload_type_map[0], result);
 | 
			
		||||
			if (result != expect->payload_type_map[1]) {
 | 
			
		||||
				printf("     ERROR: expected -> %d\n", expect->payload_type_map[1]);
 | 
			
		||||
		/* Run codec decision and check expectation */
 | 
			
		||||
		if (!codec_decision(conn, 0, 1, &t->expect[0]))
 | 
			
		||||
			ok = false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* If the expected result is an error, don't do reverse map test */
 | 
			
		||||
			if (expect->payload_type_map[1] < 0)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			result = mgcp_codec_pt_translate(&conn[1], &conn[0], expect->payload_type_map[1]);
 | 
			
		||||
			printf(" - mgcp_codec_pt_translate(conn1, conn0, %d) -> %d\n",
 | 
			
		||||
			       expect->payload_type_map[1], result);
 | 
			
		||||
			if (result != expect->payload_type_map[0]) {
 | 
			
		||||
				printf("     ERROR: expected -> %d\n", expect->payload_type_map[0]);
 | 
			
		||||
		if (!codec_decision(conn, 1, 0, &t->expect[1]))
 | 
			
		||||
			ok = false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		if (ok)
 | 
			
		||||
			printf(" ===> SUCCESS: codec decision as expected!\n");
 | 
			
		||||
		else
 | 
			
		||||
			printf(" ===> FAIL: unexpected codec decision!\n");
 | 
			
		||||
 | 
			
		||||
		if (!ok)
 | 
			
		||||
			ok_all = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		for (conn_i = 0; conn_i < 2; conn_i++)
 | 
			
		||||
			mgcp_codec_reset_all(&conn[conn_i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(ok);
 | 
			
		||||
	OSMO_ASSERT(ok_all);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_conn_id_matching(void)
 | 
			
		||||
@@ -2233,7 +2349,7 @@ int main(int argc, char **argv)
 | 
			
		||||
	test_osmux_cid();
 | 
			
		||||
	test_get_lco_identifier();
 | 
			
		||||
	test_check_local_cx_options(ctx);
 | 
			
		||||
	test_mgcp_codec_pt_translate();
 | 
			
		||||
	test_mgcp_codec_decide();
 | 
			
		||||
	test_conn_id_matching();
 | 
			
		||||
	test_e1_trunk_nr_from_epname();
 | 
			
		||||
	test_mgcp_is_rtp_dummy_payload();
 | 
			
		||||
 
 | 
			
		||||
@@ -525,6 +525,80 @@ Response matches our expectations.
 | 
			
		||||
L: p:20
 | 
			
		||||
 | 
			
		||||
v=0
 | 
			
		||||
c=IN IP4 123.12.12.123
 | 
			
		||||
m=audio 5904 RTP/AVP 111
 | 
			
		||||
a=rtpmap:111 AMR/8000/1
 | 
			
		||||
a=ptime:20
 | 
			
		||||
a=fmtp:111 mode-change-capability=2; octet-align=1
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message with patched conn_id for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response contains a connection id)
 | 
			
		||||
Dummy packets: 2
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing AUEP_NULL
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
AUEP 18983215 null@mgw MGCP 1.0
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing CRCX_NULL
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
CRCX 2 null@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 as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing MDCX_NULL
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
MDCX 9 null@mgw MGCP 1.0
 | 
			
		||||
I: %s
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing DLCX_NULL
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
DLCX 8 null@mgw MGCP 1.0
 | 
			
		||||
I: %s
 | 
			
		||||
C: 2
 | 
			
		||||
 | 
			
		||||
---------8<---------
 | 
			
		||||
checking response:
 | 
			
		||||
using message as statically defined for comparison
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
(response does not contain a connection id)
 | 
			
		||||
 | 
			
		||||
================================================
 | 
			
		||||
Testing RQNT_NULL
 | 
			
		||||
creating message from statically defined input:
 | 
			
		||||
---------8<---------
 | 
			
		||||
@@ -1276,7 +1350,7 @@ p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
Response matches our expectations.
 | 
			
		||||
Testing get_lco_identifier()
 | 
			
		||||
p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
p:10, a:PCMU -> p:10, a:PCMU
 | 
			
		||||
'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU'
 | 
			
		||||
'XXXX,p:10,a:PCMU' -> 'p:10,a:PCMU'
 | 
			
		||||
'10,a:PCMU' -> 'a:PCMU'
 | 
			
		||||
@@ -1286,13 +1360,15 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
', a:PCMU' -> 'a:PCMU'
 | 
			
		||||
' a:PCMU' -> 'a:PCMU'
 | 
			
		||||
'' -> '(null)'
 | 
			
		||||
p10, aPCMU -> (null)
 | 
			
		||||
'10,a :PCMU' -> '(null)'
 | 
			
		||||
 | 
			
		||||
Testing mgcp_codec_pt_translate()
 | 
			
		||||
#0: same order, but differing payload type numbers
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
p10, aPCMU -> (null)
 | 
			
		||||
'10,a :PCMU' -> '(null)'
 | 
			
		||||
 | 
			
		||||
Testing mgcp_codec_find_convertible()
 | 
			
		||||
#0: same order, but differing payload type numbers
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    2: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0:  96 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
@@ -1302,13 +1378,15 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
    conn[1]: codec:AMR, pt:96
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
 | 
			
		||||
#1: different order and different payload type numbers
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:96
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#1: different order and different payload type numbers
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    1: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    2: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0:  97 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
@@ -1318,12 +1396,15 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
    conn[0]: codec:PCMU, pt:0
 | 
			
		||||
    conn[1]: codec:PCMU, pt:0
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
 | 
			
		||||
#2: both sides have the same payload_type numbers assigned to differing codecs
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:GSM-HR-08, pt:97
 | 
			
		||||
    conn[0]: codec:GSM-HR-08, pt:111
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#2: both sides have the same payload_type numbers assigned to differing codecs
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    1:  96 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    2:  97 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0:  97 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
@@ -1331,9 +1412,13 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:PCMU, pt:0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 96) -> 97
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 96
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 97) -> 96
 | 
			
		||||
    conn[1]: codec:PCMU, pt:0
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:GSM-HR-08, pt:97
 | 
			
		||||
    conn[0]: codec:GSM-HR-08, pt:96
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#3: conn0 has no codecs
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    (none)
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
@@ -1341,31 +1426,116 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    2:  97 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    0:  96 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    2:  97 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
 | 
			
		||||
    codec decision failed (expected)!
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:96
 | 
			
		||||
    conn[0]: codec:none, pt:none
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#4: conn1 has no codecs
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    2: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    (none)
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
    conn[1]: codec:none, pt:none
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    codec decision failed (expected)!
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#5: test AMR with differing octet-aligned settings (both <-> both)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 111 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
    1: 112 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
    1: 121 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    0: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    2: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:111
 | 
			
		||||
    conn[1]: codec:AMR, pt:121
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#6: test AMR with differing octet-aligned settings (oa <-> both)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 111 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
    1: 121 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:111
 | 
			
		||||
    conn[1]: codec:AMR, pt:121
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:121
 | 
			
		||||
    conn[0]: codec:AMR, pt:111
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#7: test AMR with differing octet-aligned settings (bwe <-> both)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 112 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
    1: 121 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
			
		||||
#5: test AMR with differing octet-aligned settings
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#8: test AMR with missing octet-aligned settings (oa <-> unset)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 111 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000 octet-aligned=unset  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:111
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
    conn[0]: codec:AMR, pt:111
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#9: test AMR with missing octet-aligned settings (bwe <-> unset)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 111 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000 octet-aligned=unset  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 122) -> 111
 | 
			
		||||
#6: test AMR with missing octet-aligned settings (defaults to 0)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:111
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
    conn[0]: codec:AMR, pt:111
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#10: test AMR with NULL param (oa <-> null)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 112 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000  -> rc=0
 | 
			
		||||
 - mgcp_codec_decide(&conn[0], &conn[1]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#11: test AMR with NULL param (bwe <-> null)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 112 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000  -> rc=0
 | 
			
		||||
@@ -1374,14 +1544,16 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 122
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 122) -> 111
 | 
			
		||||
#8: match FOO/8000/1 and FOO/8000 as identical, single channel is implicit
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    1: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:AMR, pt:122
 | 
			
		||||
    conn[0]: codec:AMR, pt:112
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#12: match FOO/8000/1 and FOO/8000 as identical, single channel is implicit
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    1: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    2: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0:  97 GSM-HR-08/8000  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000  -> rc=0
 | 
			
		||||
    2:  96 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
@@ -1390,14 +1562,16 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
    conn[0]: codec:PCMU, pt:0
 | 
			
		||||
    conn[1]: codec:PCMU, pt:0
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
 | 
			
		||||
#9: match FOO/8000/1 and FOO as identical, 8k and single channel are implicit
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    1: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:GSM-HR-08, pt:97
 | 
			
		||||
    conn[0]: codec:GSM-HR-08, pt:111
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#13: match FOO/8000/1 and FOO as identical, 8k and single channel are implicit
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    1: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
    2: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0:  97 GSM-HR-08  -> rc=0
 | 
			
		||||
    1:   0 PCMU  -> rc=0
 | 
			
		||||
    2:  96 AMR octet-aligned=1  -> rc=0
 | 
			
		||||
@@ -1405,10 +1579,15 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[0]: codec:PCMU, pt:0
 | 
			
		||||
    conn[1]: codec:PCMU, pt:0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
 | 
			
		||||
 - mgcp_codec_decide(&conn[1], &conn[0]):
 | 
			
		||||
    Codec decision result:
 | 
			
		||||
    conn[1]: codec:GSM-HR-08, pt:97
 | 
			
		||||
    conn[0]: codec:GSM-HR-08, pt:111
 | 
			
		||||
 ===> SUCCESS: codec decision as expected!
 | 
			
		||||
#14: test whether channel number matching is waterproof
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 111 GSM-HR-08/8000  -> rc=0
 | 
			
		||||
    1: 112 GSM-HR-08/8000/2  -> rc=-22
 | 
			
		||||
    2: 113 GSM-HR-08/8000/3  -> rc=-22
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 GSM-HR-08/8000/2  -> rc=-22
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ AM_CFLAGS = \
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	-no-install \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,7 @@ static struct msgb *from_str(const char *str)
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct mgcp_client_conf conf;
 | 
			
		||||
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,
 | 
			
		||||
@@ -107,9 +107,6 @@ void test_response_cb(struct mgcp_response *response, void *priv)
 | 
			
		||||
	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);
 | 
			
		||||
@@ -149,12 +146,11 @@ void test_mgcp_msg(void)
 | 
			
		||||
		.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 = {
 | 
			
		||||
			{ .codec = CODEC_GSM_8000_1, .pt = CODEC_GSM_8000_1 },
 | 
			
		||||
			{ .codec = CODEC_AMR_8000_1, .pt = CODEC_AMR_8000_1 },
 | 
			
		||||
			{ .codec = CODEC_GSMEFR_8000_1, .pt = 96 },
 | 
			
		||||
		},
 | 
			
		||||
		.ptmap_len = 1,
 | 
			
		||||
		.x_osmo_ign = MGCP_X_OSMO_IGN_CALLID,
 | 
			
		||||
		.x_osmo_osmux_cid = -1, /* wildcard */
 | 
			
		||||
@@ -162,7 +158,7 @@ void test_mgcp_msg(void)
 | 
			
		||||
 | 
			
		||||
	if (mgcp)
 | 
			
		||||
		talloc_free(mgcp);
 | 
			
		||||
	mgcp = mgcp_client_init(ctx, &conf);
 | 
			
		||||
	mgcp = mgcp_client_init(ctx, conf);
 | 
			
		||||
 | 
			
		||||
	printf("\n");
 | 
			
		||||
 | 
			
		||||
@@ -179,9 +175,9 @@ void test_mgcp_msg(void)
 | 
			
		||||
	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;
 | 
			
		||||
	mgcp_msg.ptmap_len = 2;
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	mgcp_msg.codecs_len = 1;
 | 
			
		||||
	mgcp_msg.ptmap_len = 1;
 | 
			
		||||
	printf("%s\n", (char *)msg->data);
 | 
			
		||||
 | 
			
		||||
	printf("Generated CRCX message (three codecs, one with custom pt):\n");
 | 
			
		||||
@@ -189,9 +185,9 @@ void test_mgcp_msg(void)
 | 
			
		||||
	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;
 | 
			
		||||
	mgcp_msg.ptmap_len = 3;
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	mgcp_msg.codecs_len = 1;
 | 
			
		||||
	mgcp_msg.ptmap_len = 1;
 | 
			
		||||
	printf("%s\n", (char *)msg->data);
 | 
			
		||||
 | 
			
		||||
	printf("Generated MDCX message:\n");
 | 
			
		||||
@@ -209,9 +205,9 @@ void test_mgcp_msg(void)
 | 
			
		||||
	    (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;
 | 
			
		||||
	mgcp_msg.ptmap_len = 2;
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	mgcp_msg.codecs_len = 1;
 | 
			
		||||
	mgcp_msg.ptmap_len = 1;
 | 
			
		||||
	printf("%s\n", (char *)msg->data);
 | 
			
		||||
 | 
			
		||||
	printf("Generated MDCX message (three codecs, one with custom pt):\n");
 | 
			
		||||
@@ -220,9 +216,9 @@ void test_mgcp_msg(void)
 | 
			
		||||
	    (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;
 | 
			
		||||
	mgcp_msg.ptmap_len = 3;
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	mgcp_msg.codecs_len = 1;
 | 
			
		||||
	mgcp_msg.ptmap_len = 1;
 | 
			
		||||
	printf("%s\n", (char *)msg->data);
 | 
			
		||||
 | 
			
		||||
	printf("Generated DLCX message:\n");
 | 
			
		||||
@@ -330,8 +326,10 @@ void test_mgcp_client_cancel(void)
 | 
			
		||||
		.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
 | 
			
		||||
		.ptmap = {
 | 
			
		||||
			{ .codec = CODEC_AMR_8000_1, .pt = CODEC_AMR_8000_1 },
 | 
			
		||||
		},
 | 
			
		||||
		.ptmap_len = 1
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	printf("\n%s():\n", __func__);
 | 
			
		||||
@@ -339,7 +337,7 @@ void test_mgcp_client_cancel(void)
 | 
			
		||||
 | 
			
		||||
	if (mgcp)
 | 
			
		||||
		talloc_free(mgcp);
 | 
			
		||||
	mgcp = mgcp_client_init(ctx, &conf);
 | 
			
		||||
	mgcp = mgcp_client_init(ctx, conf);
 | 
			
		||||
 | 
			
		||||
	msg = mgcp_msg_gen(mgcp, &mgcp_msg);
 | 
			
		||||
	trans_id = mgcp_msg_trans_id(msg);
 | 
			
		||||
@@ -531,7 +529,7 @@ void test_sdp_section_start(void)
 | 
			
		||||
	OSMO_ASSERT(!failures);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_map_pt_to_codec(void)
 | 
			
		||||
static void test_map_str_to_codec(void)
 | 
			
		||||
{
 | 
			
		||||
	/* Full form */
 | 
			
		||||
	OSMO_ASSERT(map_str_to_codec("PCMU/8000/1") == CODEC_PCMU_8000_1);
 | 
			
		||||
@@ -630,7 +628,7 @@ void test_mgcp_client_e1_epname(void)
 | 
			
		||||
 | 
			
		||||
	if (mgcp)
 | 
			
		||||
		talloc_free(mgcp);
 | 
			
		||||
	mgcp = mgcp_client_init(ctx, &conf);
 | 
			
		||||
	mgcp = mgcp_client_init(ctx, conf);
 | 
			
		||||
 | 
			
		||||
	/* Valid endpoint names */
 | 
			
		||||
	epname = (char *)mgcp_client_e1_epname(ctx, mgcp, 1, 15, 64, 0);
 | 
			
		||||
@@ -675,6 +673,214 @@ void test_mgcp_client_e1_epname(void)
 | 
			
		||||
	OSMO_ASSERT(epname == NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct parse_response_test {
 | 
			
		||||
	const char *body;
 | 
			
		||||
	int expect_rc;
 | 
			
		||||
	struct mgcp_response expect_params;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct parse_response_test parse_response_tests[] = {
 | 
			
		||||
	{
 | 
			
		||||
		.body = "200 2 OK\r\n"
 | 
			
		||||
			"I: foo\r\n"
 | 
			
		||||
			"\r\n"
 | 
			
		||||
			"v=0\r\n"
 | 
			
		||||
			"o=- name 23 IN IP4 0.0.0.0\r\n"
 | 
			
		||||
			"s=-\r\n"
 | 
			
		||||
			"c=IN IP4 1.2.3.4\r\n"
 | 
			
		||||
			"t=0 0\r\n"
 | 
			
		||||
			"m=audio 23 RTP/AVP 112 3\r\n" /* <-- implicit: 3 = GSM-FR */
 | 
			
		||||
			"a=rtpmap:112 AMR/8000\r\n"
 | 
			
		||||
			"a=ptime:20\r\n",
 | 
			
		||||
		.expect_rc = 0,
 | 
			
		||||
		.expect_params = {
 | 
			
		||||
			.audio_port = 23,
 | 
			
		||||
			.audio_ip = "1.2.3.4",
 | 
			
		||||
			.ptmap = {
 | 
			
		||||
				{ .codec = CODEC_AMR_8000_1, .pt = 112 },
 | 
			
		||||
				{ .codec = CODEC_GSM_8000_1, .pt = 3 },
 | 
			
		||||
			},
 | 
			
		||||
			.ptmap_len = 2,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.body = "200 2 OK\r\n"
 | 
			
		||||
			"I: foo\r\n"
 | 
			
		||||
			"\r\n"
 | 
			
		||||
			"v=0\r\n"
 | 
			
		||||
			"o=- name 23 IN IP4 0.0.0.0\r\n"
 | 
			
		||||
			"s=-\r\n"
 | 
			
		||||
			"c=IN IP4 1.2.3.4\r\n"
 | 
			
		||||
			"t=0 0\r\n"
 | 
			
		||||
			"m=audio 23 RTP/AVP 112 3\r\n"
 | 
			
		||||
			"a=rtpmap:112 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:3 GSM/8000\r\n" /* 3 == GSM-FR implicitly, is an explicit entry a problem? */
 | 
			
		||||
			"a=ptime:20\r\n",
 | 
			
		||||
		.expect_rc = 0,
 | 
			
		||||
		.expect_params = {
 | 
			
		||||
			.audio_port = 23,
 | 
			
		||||
			.audio_ip = "1.2.3.4",
 | 
			
		||||
			.ptmap = {
 | 
			
		||||
				{ .codec = CODEC_AMR_8000_1, .pt = 112 },
 | 
			
		||||
				{ .codec = CODEC_GSM_8000_1, .pt = 3 }, /* no, not a problem */
 | 
			
		||||
			},
 | 
			
		||||
			.ptmap_len = 2,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.body = "200 2 OK\r\n"
 | 
			
		||||
			"I: foo\r\n"
 | 
			
		||||
			"\r\n"
 | 
			
		||||
			"v=0\r\n"
 | 
			
		||||
			"o=- name 23 IN IP4 0.0.0.0\r\n"
 | 
			
		||||
			"s=-\r\n"
 | 
			
		||||
			"c=IN IP4 1.2.3.4\r\n"
 | 
			
		||||
			"t=0 0\r\n"
 | 
			
		||||
			"m=audio 23 RTP/AVP 3\r\n" /* <-- 112 is missing here. Will it still appear? */
 | 
			
		||||
			"a=rtpmap:112 AMR/8000\r\n"
 | 
			
		||||
			"a=ptime:20\r\n",
 | 
			
		||||
		.expect_rc = 0,
 | 
			
		||||
		.expect_params = {
 | 
			
		||||
			.audio_port = 23,
 | 
			
		||||
			.audio_ip = "1.2.3.4",
 | 
			
		||||
			.ptmap = {
 | 
			
		||||
				{ .codec = CODEC_GSM_8000_1, .pt = 3 },
 | 
			
		||||
				{ .codec = CODEC_AMR_8000_1, .pt = 112 }, /* <-- yes, it was added to the end. */
 | 
			
		||||
			},
 | 
			
		||||
			.ptmap_len = 2,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		/* test MGCP_MAX_CODECS */
 | 
			
		||||
		.body = "200 2 OK\r\n"
 | 
			
		||||
			"I: foo\r\n"
 | 
			
		||||
			"\r\n"
 | 
			
		||||
			"v=0\r\n"
 | 
			
		||||
			"o=- name 23 IN IP4 0.0.0.0\r\n"
 | 
			
		||||
			"s=-\r\n"
 | 
			
		||||
			"c=IN IP4 1.2.3.4\r\n"
 | 
			
		||||
			"t=0 0\r\n"
 | 
			
		||||
			"m=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110\r\n" /* <-- 10 codecs max */
 | 
			
		||||
			"a=rtpmap:101 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:102 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:103 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:104 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:105 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:106 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:107 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:108 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:109 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:110 AMR/8000\r\n"
 | 
			
		||||
			"a=ptime:20\r\n",
 | 
			
		||||
		.expect_rc = 0,
 | 
			
		||||
		.expect_params = {
 | 
			
		||||
			.audio_port = 23,
 | 
			
		||||
			.audio_ip = "1.2.3.4",
 | 
			
		||||
			.ptmap = {
 | 
			
		||||
				{ .codec = CODEC_AMR_8000_1, .pt = 101 },
 | 
			
		||||
				{ .codec = CODEC_AMR_8000_1, .pt = 102 },
 | 
			
		||||
				{ .codec = CODEC_AMR_8000_1, .pt = 103 },
 | 
			
		||||
				{ .codec = CODEC_AMR_8000_1, .pt = 104 },
 | 
			
		||||
				{ .codec = CODEC_AMR_8000_1, .pt = 105 },
 | 
			
		||||
				{ .codec = CODEC_AMR_8000_1, .pt = 106 },
 | 
			
		||||
				{ .codec = CODEC_AMR_8000_1, .pt = 107 },
 | 
			
		||||
				{ .codec = CODEC_AMR_8000_1, .pt = 108 },
 | 
			
		||||
				{ .codec = CODEC_AMR_8000_1, .pt = 109 },
 | 
			
		||||
				{ .codec = CODEC_AMR_8000_1, .pt = 110 },
 | 
			
		||||
			},
 | 
			
		||||
			.ptmap_len = 10,
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		/* test MGCP_MAX_CODECS */
 | 
			
		||||
		.body = "200 2 OK\r\n"
 | 
			
		||||
			"I: foo\r\n"
 | 
			
		||||
			"\r\n"
 | 
			
		||||
			"v=0\r\n"
 | 
			
		||||
			"o=- name 23 IN IP4 0.0.0.0\r\n"
 | 
			
		||||
			"s=-\r\n"
 | 
			
		||||
			"c=IN IP4 1.2.3.4\r\n"
 | 
			
		||||
			"t=0 0\r\n"
 | 
			
		||||
			"m=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110 3\r\n" /* <-- 11 > MGCP_MAX_CODECS */
 | 
			
		||||
			"a=rtpmap:101 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:102 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:103 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:104 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:105 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:106 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:107 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:108 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:109 AMR/8000\r\n"
 | 
			
		||||
			"a=rtpmap:110 AMR/8000\r\n"
 | 
			
		||||
			"a=ptime:20\r\n",
 | 
			
		||||
		.expect_rc = -EINVAL,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void test_parse_response(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	int failures = 0;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(parse_response_tests); i++) {
 | 
			
		||||
		int rc;
 | 
			
		||||
		struct parse_response_test *t = &parse_response_tests[i];
 | 
			
		||||
		struct mgcp_response *r = talloc_zero(ctx, struct mgcp_response);
 | 
			
		||||
		int p;
 | 
			
		||||
 | 
			
		||||
		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_ip=\"%s\"\n", r->audio_ip);
 | 
			
		||||
		if (strcmp(r->audio_ip, t->expect_params.audio_ip)) {
 | 
			
		||||
			fprintf(stderr, "FAIL: Expected audio_ip=\"%s\"\n", t->expect_params.audio_ip);
 | 
			
		||||
			failures++;
 | 
			
		||||
		}
 | 
			
		||||
		fprintf(stderr, "got audio_port=%u\n", r->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++;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (p = 0; p < r->ptmap_len; p++) {
 | 
			
		||||
			struct ptmap *got = &r->ptmap[p];
 | 
			
		||||
			struct ptmap *expect = NULL;
 | 
			
		||||
			fprintf(stderr, "  %d %s\n", got->pt, osmo_mgcpc_codec_name(got->codec));
 | 
			
		||||
			if (p >= t->expect_params.ptmap_len) {
 | 
			
		||||
				fprintf(stderr, "    - ERROR: too many codec entries\n");
 | 
			
		||||
				failures++;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			expect = &t->expect_params.ptmap[p];
 | 
			
		||||
			if (ptmap_cmp(got, expect)) {
 | 
			
		||||
				fprintf(stderr, "    - ERROR: expected: %d %s\n",
 | 
			
		||||
					expect->pt, osmo_mgcpc_codec_name(expect->codec));
 | 
			
		||||
				failures++;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		talloc_free(r);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(!failures);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static const struct log_info_cat log_categories[] = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -697,15 +903,17 @@ int main(int argc, char **argv)
 | 
			
		||||
 | 
			
		||||
	log_set_category_filter(osmo_stderr_target, DLMGCP, 1, LOGL_DEBUG);
 | 
			
		||||
 | 
			
		||||
	mgcp_client_conf_init(&conf);
 | 
			
		||||
	conf = mgcp_client_conf_alloc(ctx);
 | 
			
		||||
 | 
			
		||||
	test_mgcp_msg();
 | 
			
		||||
	test_mgcp_client_cancel();
 | 
			
		||||
	test_sdp_section_start();
 | 
			
		||||
	test_map_codec_to_pt_and_map_pt_to_codec();
 | 
			
		||||
	test_map_pt_to_codec();
 | 
			
		||||
	test_map_str_to_codec();
 | 
			
		||||
	test_mgcp_client_e1_epname();
 | 
			
		||||
 | 
			
		||||
	test_parse_response();
 | 
			
		||||
 | 
			
		||||
	printf("Done\n");
 | 
			
		||||
	fprintf(stderr, "Done\n");
 | 
			
		||||
	return EXIT_SUCCESS;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
 | 
			
		||||
DLMGCP MGW(mgw) Message buffer to small, can not generate MGCP message (SDP)
 | 
			
		||||
DLMGCP MGW(mgw) Message buffer too small, can not generate MGCP message (SDP)
 | 
			
		||||
DLMGCP MGW(mgw) Failed to add SDP, can not generate MGCP message
 | 
			
		||||
 | 
			
		||||
test_mgcp_client_cancel():
 | 
			
		||||
DLMGCP MGW(mgw) MGCP client: using endpoint domain '@mgw'
 | 
			
		||||
@@ -10,6 +11,7 @@ DLMGCP MGW(mgw) Cannot cancel, no such transaction: 1
 | 
			
		||||
- cancel succeeds
 | 
			
		||||
DLMGCP MGW(mgw) Canceled transaction 1
 | 
			
		||||
- late response gets discarded
 | 
			
		||||
DLMGCP MGW(mgw) MGCP link to MGW now considered UP
 | 
			
		||||
DLMGCP MGW(mgw) MGCP client: Rx 200 1 OK
 | 
			
		||||
DLMGCP MGW(mgw) Cannot find matching MGCP transaction for trans_id 1
 | 
			
		||||
- canceling again does nothing
 | 
			
		||||
@@ -135,4 +137,51 @@ DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su128-0@mgw),
 | 
			
		||||
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-1/su8-16@mgw), rate(8)/offset(16) combination is invalid!
 | 
			
		||||
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-0/su8-2@mgw), E1-timeslot number (0) is invalid!
 | 
			
		||||
DLMGCP MGW(mgw) Cannot compose MGCP e1-endpoint name (ds/e1-15/s-64/su8-2@mgw), E1-timeslot number (64) is invalid!
 | 
			
		||||
 | 
			
		||||
test_parse_response() test [0]:
 | 
			
		||||
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 112 3\r\na=rtpmap:112 AMR/8000\r\na=ptime:20\r\n"
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip="1.2.3.4"
 | 
			
		||||
got audio_port=23
 | 
			
		||||
  112 AMR/8000/1
 | 
			
		||||
  3 GSM/8000/1
 | 
			
		||||
 | 
			
		||||
test_parse_response() test [1]:
 | 
			
		||||
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 112 3\r\na=rtpmap:112 AMR/8000\r\na=rtpmap:3 GSM/8000\r\na=ptime:20\r\n"
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip="1.2.3.4"
 | 
			
		||||
got audio_port=23
 | 
			
		||||
  112 AMR/8000/1
 | 
			
		||||
  3 GSM/8000/1
 | 
			
		||||
 | 
			
		||||
test_parse_response() test [2]:
 | 
			
		||||
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 3\r\na=rtpmap:112 AMR/8000\r\na=ptime:20\r\n"
 | 
			
		||||
DLMGCP error in MGCP message: 'a=rtpmap:112' has no matching entry in 'm=audio ... 112'
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip="1.2.3.4"
 | 
			
		||||
got audio_port=23
 | 
			
		||||
  3 GSM/8000/1
 | 
			
		||||
  112 AMR/8000/1
 | 
			
		||||
 | 
			
		||||
test_parse_response() test [3]:
 | 
			
		||||
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110\r\na=rtpmap:101 AMR/8000\r\na=rtpmap:102 AMR/8000\r\na=rtpmap:103 AMR/8000\r\na=rtpmap:104 AMR/8"
 | 
			
		||||
got rc=0
 | 
			
		||||
got audio_ip="1.2.3.4"
 | 
			
		||||
got audio_port=23
 | 
			
		||||
  101 AMR/8000/1
 | 
			
		||||
  102 AMR/8000/1
 | 
			
		||||
  103 AMR/8000/1
 | 
			
		||||
  104 AMR/8000/1
 | 
			
		||||
  105 AMR/8000/1
 | 
			
		||||
  106 AMR/8000/1
 | 
			
		||||
  107 AMR/8000/1
 | 
			
		||||
  108 AMR/8000/1
 | 
			
		||||
  109 AMR/8000/1
 | 
			
		||||
  110 AMR/8000/1
 | 
			
		||||
 | 
			
		||||
test_parse_response() test [4]:
 | 
			
		||||
body: "200 2 OK\r\nI: foo\r\n\r\nv=0\r\no=- name 23 IN IP4 0.0.0.0\r\ns=-\r\nc=IN IP4 1.2.3.4\r\nt=0 0\r\nm=audio 23 RTP/AVP 101 102 103 104 105 106 107 108 109 110 3\r\na=rtpmap:101 AMR/8000\r\na=rtpmap:102 AMR/8000\r\na=rtpmap:103 AMR/8000\r\na=rtpmap:104 AMR"
 | 
			
		||||
DLMGCP SDP: can parse only up to 10 payload type numbers
 | 
			
		||||
DLMGCP Failed to parse SDP parameter payload types (RTP/AVP)
 | 
			
		||||
got rc=-22
 | 
			
		||||
Done
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user